feat: PKCE auth + CI/CD deploy
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 1m2s

- Frontend: PKCE flow (oauth.ts, AuthCallback code exchange, 401 interceptor)
- Backend: token introspection via SuperOAuth (no more JWT secret)
- User model: superOauthId (unified) replaces oauthId+provider
- Cookies httpOnly session + refresh token
- POST /auth/refresh endpoint
- Gitea CI workflow (vps-runner pattern)
- DB_SYNC env var for initial schema creation
This commit is contained in:
2026-03-24 13:01:14 +01:00
parent c1bf793234
commit 8c6777c980
61 changed files with 5850 additions and 66 deletions

292
SPRINT4.md Normal file
View File

@@ -0,0 +1,292 @@
# TetaRdPG — Brief Sprint 4
> Statut : ⬜ À lancer
> Objectif : Succès individuels + Succès communautaires + Hall of Fame + Profil joueur enrichi
> Stack : NestJS · MySQL · TypeORM
> Prérequis : Sprint 3 livré ✅ (items, forge, craft, economy, twitch)
> Source design : `TetaRdPG/Sprint 4 _ Focus Succès & Hall of Fame.docx` + `Annexes/5. Système de succès.docx`
---
## Scope Sprint 4
### ✅ In scope
- Entité `achievements` — catalogue de succès avec critères de déblocage
- Entité `player_achievements` — suivi progression par joueur
- 5 catégories : Progression, Combat, Zones, Équipements, Économie
- Récompenses au déblocage : Or, XP bonus, titres honorifiques
- Entité `community_goals` — objectifs collectifs (monstres tués, TetardCoin cumulés)
- Barre de progression communautaire
- Récompenses communautaires : boosts globaux temporaires (XP/loot)
- Hall of Fame mensuel — classement contributeurs + badges
- Interface profil enrichi : badges, titres, % progression succès
- Seeds : 15 succès individuels + 3 objectifs communautaires
- API : voir section dédiée
### ❌ Out of scope
- Notifications Twitch temps réel (extension Twitch) — Sprint 5
- GIGABOSS communautaire (événement 72h) — Sprint événements
- Marché communautaire (échange joueurs) — Sprint économie avancée
- Guildes et alliances — Sprint social
- Boutique événementielle — Sprint économie avancée
- Frontend React complet
---
## Décisions de design (game-designer)
| Décision | Valeur | Justification |
|----------|--------|---------------|
| Tracking succès | Event-driven : chaque action (combat, craft, forge, level) émet un check | Pas de cron — cohérent avec le pattern lazy du projet |
| Catégories succès | 5 : progression, combat, zones, equipment, economy | GDD §5.1 |
| Paliers succès | 3 niveaux par succès (bronze/silver/gold) | Engagement long terme |
| Récompenses déblocage | Or + titre. Pas d'item pour éviter la complexité inventaire Sprint 4 | Simplification — items récompense = Sprint 5 |
| Titres joueur | 1 titre actif à la fois, affiché sur le profil | GDD §4 titres liés aux zones |
| Community goals | Reset mensuel, contribution individuelle trackée | GDD §5.2 |
| Hall of Fame | Classement mensuel, top 10, badges persistants | GDD §5.3 |
| Boost communautaire | Stocké en DB, appliqué comme multiplicateur dans combat/craft | Ex: +20% XP pendant 3j |
| Progression communautaire | Compteur global incrémenté à chaque action qualifiante | Pas de WebSocket — poll GET |
---
## Schéma DB
### `achievements`
```
id uuid PK
key varchar(50) UNIQUE -- 'combat_100', 'level_50', 'zone_marais_complete'
name varchar(100)
description text
category varchar(20) -- 'progression' | 'combat' | 'zones' | 'equipment' | 'economy'
tier varchar(10) -- 'bronze' | 'silver' | 'gold'
criteria_type varchar(30) -- 'combat_wins' | 'level_reached' | 'gold_accumulated' | ...
criteria_value int -- seuil à atteindre
reward_gold int default 0
reward_title varchar(100) NULL -- titre débloqué (nullable)
```
### `player_achievements`
```
id uuid PK
character_id uuid FK characters
achievement_id uuid FK achievements
progress int default 0 -- compteur courant
unlocked boolean default false
unlocked_at timestamp NULL
```
### `community_goals`
```
id uuid PK
name varchar(100)
description text
criteria_type varchar(30) -- 'total_monsters_killed' | 'total_tetardcoin' | ...
target_value bigint -- objectif collectif
current_value bigint default 0
reward_type varchar(30) -- 'xp_boost' | 'loot_boost'
reward_multiplier decimal(3,2) -- ex: 1.20 = +20%
reward_duration_hours int -- durée du boost
period_start date
period_end date
completed boolean default false
completed_at timestamp NULL
```
### `community_contributions`
```
id uuid PK
community_goal_id uuid FK community_goals
character_id uuid FK characters
contribution_value bigint default 0
```
### `hall_of_fame`
```
id uuid PK
character_id uuid FK characters
period varchar(7) -- '2026-04' format YYYY-MM
rank int
contribution_total bigint
badge varchar(50) -- 'top1_april_2026'
```
### `active_boosts` (communautaires)
```
id uuid PK
boost_type varchar(30) -- 'xp_boost' | 'loot_boost'
multiplier decimal(3,2)
expires_at timestamp
source_goal_id uuid FK community_goals
```
---
## Seeds
### Succès individuels (15)
| Key | Nom | Catégorie | Tier | Critère | Seuil | Récompense Or | Titre |
|-----|-----|-----------|------|---------|-------|---------------|-------|
| `combat_10` | Apprenti Guerrier | combat | bronze | combat_wins | 10 | 50 | — |
| `combat_100` | Guerrier Aguerri | combat | silver | combat_wins | 100 | 200 | Guerrier Aguerri |
| `combat_1000` | Légende du Combat | combat | gold | combat_wins | 1000 | 1000 | Légende |
| `level_10` | Aventurier | progression | bronze | level_reached | 10 | 100 | — |
| `level_50` | Héros | progression | silver | level_reached | 50 | 500 | Héros |
| `level_100` | Légende Vivante | progression | gold | level_reached | 100 | 2000 | Légende Vivante |
| `gold_1000` | Marchand | economy | bronze | gold_accumulated | 1000 | 100 | — |
| `gold_10000` | Négociant | economy | silver | gold_accumulated | 10000 | 500 | Négociant |
| `gold_100000` | Magnat | economy | gold | gold_accumulated | 100000 | 2000 | Magnat |
| `forge_5` | Apprenti Forgeron | equipment | bronze | forge_upgrades | 5 | 100 | — |
| `forge_25` | Maître Forgeron | equipment | silver | forge_upgrades | 25 | 500 | Maître Forgeron |
| `forge_100` | Forgeron Légendaire | equipment | gold | forge_upgrades | 100 | 2000 | Forgeron Légendaire |
| `craft_5` | Artisan Novice | equipment | bronze | craft_completed | 5 | 75 | — |
| `craft_25` | Artisan Confirmé | equipment | silver | craft_completed | 25 | 300 | Artisan |
| `craft_100` | Grand Artisan | equipment | gold | craft_completed | 100 | 1500 | Grand Artisan |
### Objectifs communautaires (3)
| Nom | Critère | Cible | Boost | Durée |
|-----|---------|-------|-------|-------|
| Chasse aux Monstres | total_monsters_killed | 10 000 | +20% XP | 72h |
| Trésor Communautaire | total_gold_earned | 1 000 000 | +15% loot | 48h |
| Fièvre de la Forge | total_forge_upgrades | 500 | +10% XP | 48h |
---
## API Sprint 4
```
# Succès individuels
GET /api/achievements → catalogue complet des succès
GET /api/achievements/me → progression du joueur (avec %)
POST /api/achievements/claim/:id → réclamer la récompense d'un succès débloqué
# Succès communautaires
GET /api/community/goals → objectifs en cours + barre progression
GET /api/community/goals/:id/top → top 10 contributeurs d'un objectif
GET /api/community/boosts → boosts actifs (multiplicateurs en cours)
# Hall of Fame
GET /api/halloffame/current → classement du mois en cours
GET /api/halloffame/:period → classement historique (ex: 2026-04)
# Profil enrichi
GET /api/profile/me → stats + titre actif + badges + succès count
PUT /api/profile/title → { title: "Héros" } → changer titre actif
```
---
## Architecture modules
```
src/
├── achievement/
│ ├── achievement.entity.ts
│ ├── player-achievement.entity.ts
│ ├── achievement.module.ts
│ ├── achievement.service.ts → check + unlock logic
│ ├── achievement.controller.ts
│ └── achievement.listener.ts → écoute events combat/craft/forge/levelup
├── community/
│ ├── community-goal.entity.ts
│ ├── community-contribution.entity.ts
│ ├── active-boost.entity.ts
│ ├── community.module.ts
│ ├── community.service.ts
│ └── community.controller.ts
├── halloffame/
│ ├── hall-of-fame.entity.ts
│ ├── halloffame.module.ts
│ ├── halloffame.service.ts → calcul mensuel + badge attribution
│ └── halloffame.controller.ts
├── profile/
│ ├── profile.module.ts
│ ├── profile.service.ts
│ └── profile.controller.ts
└── database/
├── achievements-seed.ts
└── community-goals-seed.ts
```
---
## Intégration modules existants
### CombatService — émission événements succès
```typescript
// Après résolution combat — émettre pour achievement tracker
if (result.winner === 'player') {
this.eventEmitter.emit('achievement.check', {
characterId: character.id,
type: 'combat_wins',
increment: 1,
});
this.eventEmitter.emit('community.contribute', {
characterId: character.id,
type: 'total_monsters_killed',
increment: 1,
});
}
```
### ForgeService / CraftService — même pattern
```typescript
// Après forge réussie
this.eventEmitter.emit('achievement.check', {
characterId, type: 'forge_upgrades', increment: 1,
});
this.eventEmitter.emit('community.contribute', {
characterId, type: 'total_forge_upgrades', increment: 1,
});
```
### Boosts actifs — application dans CombatEngine
```typescript
// Dans CombatService — vérifier boosts communautaires actifs
const xpBoost = await this.communityService.getActiveMultiplier('xp_boost');
rewards.xp = Math.floor(baseXp * xpBoost); // xpBoost = 1.0 si aucun boost
```
---
## Migration TypeORM
```
Sprint4Achievements — 6 tables :
achievements, player_achievements,
community_goals, community_contributions,
hall_of_fame, active_boosts
+ ALTER characters ADD active_title VARCHAR(100) NULL
+ ALTER characters ADD total_gold_earned BIGINT DEFAULT 0 -- tracking cumulé pour succès
```
---
## Critères de validation integrator
- [ ] `GET /api/achievements` → 15 succès seedés, 5 catégories
- [ ] `POST /api/combat/start` → victoire → `player_achievements.progress` incrémenté pour `combat_wins`
- [ ] 10 victoires → succès `combat_10` débloqué automatiquement
- [ ] `GET /api/achievements/me` → progression visible avec %
- [ ] `POST /api/achievements/claim/:id` → or crédité, titre disponible
- [ ] Claim succès déjà réclamé → 400
- [ ] `PUT /api/profile/title` → titre actif changé
- [ ] `GET /api/profile/me` → titre, badges, count succès
- [ ] `GET /api/community/goals` → 3 objectifs avec barre progression
- [ ] Combat victoire → `community_contributions` incrémentée
- [ ] `GET /api/community/goals/:id/top` → top 10 contributeurs
- [ ] Objectif communautaire atteint → boost créé dans `active_boosts`
- [ ] `GET /api/community/boosts` → multiplicateur actif
- [ ] Combat avec boost actif → XP = baseXp × multiplier
- [ ] Boost expiré → non retourné par GET
- [ ] `GET /api/halloffame/current` → classement mois en cours
- [ ] Sans cookie → 401 sur toutes les routes protégées
- [ ] Level up → `player_achievements.progress` incrémenté pour `level_reached`
- [ ] Forge → incrémente `forge_upgrades` + `total_forge_upgrades` communautaire