fertig-classic-games/server/puzzles/routes.js

55 lines
1.9 KiB
JavaScript

import { Router } from 'express';
import db from '../db/index.js';
import { requireAuth } from '../auth/middleware.js';
import { getGame } from '../games/registry.js';
const router = Router();
// GET /api/puzzles/:slug/progress
// Returns the highest contiguous level the signed-in user has cleared.
router.get('/:slug/progress', requireAuth, (req, res) => {
const { slug } = req.params;
if (!getGame(slug)) return res.status(400).json({ error: 'Unknown game slug.' });
const row = db
.prepare('SELECT levels_completed FROM puzzle_progress WHERE user_id = ? AND slug = ?')
.get(req.user.id, slug);
res.json({ levelsCompleted: row?.levels_completed ?? 0 });
});
// POST /api/puzzles/:slug/complete body: { level }
// Records completion of `level`. Levels must be cleared in order, so this only
// advances progress when level === current + 1; replaying an earlier level is a
// no-op that still returns the current high-water mark.
router.post('/:slug/complete', requireAuth, (req, res) => {
const { slug } = req.params;
const { level } = req.body ?? {};
if (!getGame(slug)) return res.status(400).json({ error: 'Unknown game slug.' });
if (!Number.isInteger(level) || level < 1) {
return res.status(400).json({ error: 'Invalid level.' });
}
const row = db
.prepare('SELECT levels_completed FROM puzzle_progress WHERE user_id = ? AND slug = ?')
.get(req.user.id, slug);
const current = row?.levels_completed ?? 0;
if (level !== current + 1) {
return res.json({ levelsCompleted: current });
}
db.prepare(
`INSERT INTO puzzle_progress (user_id, slug, levels_completed, updated_at)
VALUES (?, ?, ?, datetime('now'))
ON CONFLICT(user_id, slug)
DO UPDATE SET levels_completed = excluded.levels_completed,
updated_at = excluded.updated_at`,
).run(req.user.id, slug, level);
res.json({ levelsCompleted: level });
});
export default router;