From eb1f626d17ab1bc7e6d46b8da53aa87e22ac4a97 Mon Sep 17 00:00:00 2001 From: Mathis Date: Sun, 4 Oct 2026 21:38:47 +0200 Subject: [PATCH] fix : le premier membre qui rejoint une classe devient admin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - joinClass : si aucun admin 'réel' (is_creator=0) n'existe, le nouveau membre est admin - Seed : ne crée plus de membre admin — la première personne qui rejoint gère la classe - Régression : 'Mathis' rejoignant TERMS2 devient admin au lieu d'élève - v0.4.1 --- server/src/auth.ts | 16 ++++++++++++++-- server/src/seed.ts | 24 ++++++++++-------------- shared/src/index.ts | 2 +- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/server/src/auth.ts b/server/src/auth.ts index 7cdbfd5..9a7e3a5 100644 --- a/server/src/auth.ts +++ b/server/src/auth.ts @@ -79,6 +79,7 @@ export function joinClass(code: string, nickname: string): NewSession | null { const klass: ClassInfo = { id: row.id, name: row.name, level: row.level, flashMessage: row.flash_message } const token = newToken() + // Membre existant : on lui rouvre une session avec son rôle d'origine. if (existing) { db.prepare('UPDATE members SET token = ? WHERE id = ?').run(token, existing.id) return { @@ -88,12 +89,23 @@ export function joinClass(code: string, nickname: string): NewSession | null { } } + // Premier vrai membre de la classe (aucun admin « réel » encore) : il devient admin. + // Un admin « réel » = role='admin' ET is_creator=0 (créé via promotion, pas par le seed). + const realAdminCount = db + .prepare<[number], { n: number }>( + `SELECT COUNT(*) AS n FROM members WHERE class_id = ? AND role = 'admin' AND is_creator = 0`, + ) + .get(row.id)! + const isFirstRealMember = realAdminCount.n === 0 + + const role: Role = isFirstRealMember ? 'admin' : 'student' const memberId = db .prepare('INSERT INTO members (class_id, nickname, role, is_creator, token, created_at) VALUES (?, ?, ?, ?, ?, ?)') - .run(row.id, nickname, 'student', 0, token, nowIso()).lastInsertRowid as number + .run(row.id, nickname, role, 0, token, nowIso()).lastInsertRowid as number + return { token, - member: { id: memberId, classId: row.id, nickname, role: 'student', isCreator: false }, + member: { id: memberId, classId: row.id, nickname, role, isCreator: false }, klass, } } diff --git a/server/src/seed.ts b/server/src/seed.ts index 5df9dc9..dcdc028 100644 --- a/server/src/seed.ts +++ b/server/src/seed.ts @@ -28,7 +28,6 @@ if (existing.n > 0) { const CLASS_NAME = 'Terminale S2' const LEVEL = 'Lycée' -const ADMIN_NICK = 'Thomas S.' const CLASS_CODE = 'TERMS2' const DAY_MS = 86_400_000 @@ -89,15 +88,12 @@ const seed = db.transaction(() => { const classId = db.prepare<[string], { id: number }>('SELECT id FROM classes WHERE code = ?') .get(CLASS_CODE)!.id - const adminToken = newToken() - const adminId = db - .prepare('INSERT INTO members (class_id, nickname, role, token, created_at) VALUES (?, ?, ?, ?, ?)') - .run(classId, ADMIN_NICK, 'admin', adminToken, nowIso()).lastInsertRowid as number - - // Quelques élèves : ils donneront du volume aux votes du radar plus tard. + // Pas de membre admin créé par le seed : la première personne qui rejoint + // la classe via l'app devient automatiquement admin (cf. joinClass). + // Quelques élèves fictifs pour donner du volume aux votes du radar plus tard. for (const nick of ['Léa M.', 'Karim B.', 'Chloé D.', 'Inès R.', 'Lucas P.', 'Mehdi A.']) { - db.prepare('INSERT INTO members (class_id, nickname, role, token, created_at) VALUES (?, ?, ?, ?, ?)') - .run(classId, nick, 'student', newToken(), nowIso()) + db.prepare('INSERT INTO members (class_id, nickname, role, is_creator, token, created_at) VALUES (?, ?, ?, ?, ?, ?)') + .run(classId, nick, 'student', 0, newToken(), nowIso()) } const insert = db.prepare( @@ -113,18 +109,18 @@ const seed = db.transaction(() => { insert.run( classId, e.subject, e.teacher, e.room, e.chapter, date.toISOString(), e.durationMin, e.weight, e.kind, e.note, - adminId, now, now, + null, now, now, ) } - return { classId, adminToken } + return { classId } }) -const { adminToken } = seed() +const { classId } = seed() console.log('Seed terminé.') console.log(` Classe : ${CLASS_NAME} (${LEVEL})`) console.log(` Code invite : ${CLASS_CODE}`) -console.log(` Admin : ${ADMIN_NICK}`) +console.log(` Classe ID : ${classId}`) console.log(` Évaluations : ${exams.length}`) -console.log(` Jeton admin : ${adminToken}`) +console.log(' → La première personne qui rejoint avec ce code devient admin.') diff --git a/shared/src/index.ts b/shared/src/index.ts index d8b57d8..336d759 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -199,4 +199,4 @@ export type StreamEvent = export const CLASS_CODE_LENGTH = 6 /** Version de l'application. Incrémentée à chaque release APK. */ -export const APP_VERSION = '0.4.0' +export const APP_VERSION = '0.4.1'