diff --git a/backend/src/database/schemas/users.ts b/backend/src/database/schemas/users.ts index 2b7d9b5..5a44a4f 100644 --- a/backend/src/database/schemas/users.ts +++ b/backend/src/database/schemas/users.ts @@ -1,21 +1,45 @@ -import {pgTable, varchar, timestamp, uuid, pgEnum, index} from 'drizzle-orm/pg-core'; +import {pgTable, varchar, timestamp, uuid, pgEnum, index, boolean, customType} from 'drizzle-orm/pg-core'; + +// Type personnalisé pour les données chiffrées PGP (stockées en bytea dans Postgres) +const pgpEncrypted = customType<{ data: string; driverData: string }>({ + dataType() { + return 'bytea'; + }, +}); export const userStatus = pgEnum("user_status", ["active", "verification", "suspended", "pending", "deleted"]) export const users = pgTable('users', { - status: userStatus('status').default('pending').notNull(), uuid: uuid().primaryKey().defaultRandom(), - email: varchar('email', { length: 128 }).notNull().unique(), - username: varchar('username', { length: 32 }).notNull().unique(), + status: userStatus('status').default('pending').notNull(), + + // Données Personnelles (PII) - Chiffrées nativement + email: pgpEncrypted('email').notNull(), + emailHash: varchar('email_hash', { length: 64 }).notNull().unique(), // Indexé pour recherche rapide et unicité displayName: varchar('display_name', { length: 32 }), + + username: varchar('username', { length: 32 }).notNull().unique(), passwordHash: varchar('password_hash', { length: 72 }).notNull(), + + // Sécurité + twoFactorSecret: pgpEncrypted('two_factor_secret'), + isTwoFactorEnabled: boolean('is_two_factor_enabled').notNull().default(false), + + // RGPD & Conformité + termsVersion: varchar('terms_version', { length: 16 }), // Version des CGU acceptées + privacyVersion: varchar('privacy_version', { length: 16 }), // Version de la Privacy Policy acceptée + gdprAcceptedAt: timestamp('gdpr_accepted_at', { withTimezone: true }), + + // Dates de cycle de vie (Standards Entreprise) + lastLoginAt: timestamp('last_login_at', { withTimezone: true }), createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(), - gdprAcceptation: timestamp('gdpr_acceptation', { withTimezone: true }), + deletedAt: timestamp('deleted_at', { withTimezone: true }), // Soft delete (Droit à l'oubli) }, (table) => ({ uuidIdx: index('users_uuid_idx').on(table.uuid), - emailIdx: index('users_email_idx').on(table.email), + emailHashIdx: index('users_email_hash_idx').on(table.emailHash), usernameIdx: index('users_username_idx').on(table.username), + statusIdx: index('users_status_idx').on(table.status), })); export type UserInDb = typeof users.$inferSelect;