feat: Erfolge (Achievements) – Unlock-Erkennung + Listing
- Tabelle user_achievements (Migration in db-migrate.js) - src/lib/achievements.js: Definitionen + dedup-sichere Freischaltung (ON CONFLICT DO NOTHING … RETURNING → nur Neues), Listing mit Status - /auth/progress liefert unlocked_achievements (defensiv gekapselt) - neue Route GET /auth/achievements Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { query } = require('../db');
|
||||
const { levelForEp, levelInfo } = require('../lib/leveling');
|
||||
const { evaluateAchievements, listAchievements } = require('../lib/achievements');
|
||||
|
||||
function signToken(user) {
|
||||
return jwt.sign(
|
||||
@@ -221,6 +222,16 @@ router.post('/progress', requireJwt, async (req, res, next) => {
|
||||
const r = upd.rows[0];
|
||||
const daily_ep = day.rows[0]?.ep_earned ?? pts;
|
||||
const daily_goal_ep = r.daily_goal_ep || 30;
|
||||
|
||||
// Erfolge auswerten (nur neu freigeschaltete kommen zurück). Fehler hier dürfen
|
||||
// die Buchung nicht kippen → defensiv leer.
|
||||
let unlocked_achievements = [];
|
||||
try {
|
||||
unlocked_achievements = await evaluateAchievements(userId, {
|
||||
total_ep: r.total_ep, streak_days: r.streak_days,
|
||||
});
|
||||
} catch (e) { /* Erfolge optional – Buchung steht bereits */ }
|
||||
|
||||
res.json({
|
||||
total_ep: r.total_ep,
|
||||
level: levelForEp(r.total_ep),
|
||||
@@ -231,10 +242,18 @@ router.post('/progress', requireJwt, async (req, res, next) => {
|
||||
daily_goal_ep,
|
||||
// Schwellen-Übergang: jetzt erreicht, vorher (ohne diese Karte) noch nicht
|
||||
goal_just_reached: daily_ep >= daily_goal_ep && (daily_ep - pts) < daily_goal_ep,
|
||||
unlocked_achievements,
|
||||
});
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// GET /auth/achievements — alle Erfolge mit Freischalt-Status (für die Profil-Sektion)
|
||||
router.get('/achievements', requireJwt, async (req, res, next) => {
|
||||
try {
|
||||
res.json(await listAchievements(req.user.userId));
|
||||
} catch (err) { next(err); }
|
||||
});
|
||||
|
||||
// GET /auth/stats — Fortschrittsdaten für das Profil (Verlauf, Tagesziel, Skills)
|
||||
router.get('/stats', requireJwt, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user