chore: reformat database schema files for readability
Applied consistent formatting and indentation across all schema files using Drizzle ORM to enhance code clarity and maintainability.
This commit is contained in:
@@ -6,28 +6,30 @@
|
|||||||
* is strictly prohibited.
|
* is strictly prohibited.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable, Logger, OnModuleDestroy, OnModuleInit} from "@nestjs/common";
|
import {
|
||||||
|
Injectable,
|
||||||
|
Logger,
|
||||||
|
OnModuleDestroy,
|
||||||
|
OnModuleInit,
|
||||||
|
} from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { drizzle } from "drizzle-orm/node-postgres";
|
import { drizzle } from "drizzle-orm/node-postgres";
|
||||||
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
||||||
import * as schema from "./schemas";
|
import * as schema from "./schemas";
|
||||||
import {Pool} from "pg";
|
import { Pool } from "pg";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
|
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
|
||||||
private readonly logger = new Logger(DatabaseService.name);
|
private readonly logger = new Logger(DatabaseService.name);
|
||||||
private readonly pool!: Pool
|
private readonly pool!: Pool;
|
||||||
public readonly db: ReturnType<typeof drizzle>;
|
public readonly db: ReturnType<typeof drizzle>;
|
||||||
|
|
||||||
constructor(
|
constructor(private configService: ConfigService) {
|
||||||
private configService: ConfigService
|
|
||||||
) {
|
|
||||||
// Create the PostgreSQL client
|
// Create the PostgreSQL client
|
||||||
const connectionString = this.getDatabaseConnectionString();
|
const connectionString = this.getDatabaseConnectionString();
|
||||||
this.pool = new Pool({ connectionString });
|
this.pool = new Pool({ connectionString });
|
||||||
// Recreate drizzle with initialized pool
|
// Recreate drizzle with initialized pool
|
||||||
this.db = drizzle(this.pool, { schema });
|
this.db = drizzle(this.pool, { schema });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onModuleInit() {
|
async onModuleInit() {
|
||||||
@@ -59,7 +61,9 @@ export class DatabaseService implements OnModuleInit, OnModuleDestroy {
|
|||||||
|
|
||||||
// Get the database connection string from environment variables
|
// Get the database connection string from environment variables
|
||||||
private getDatabaseConnectionString(): string {
|
private getDatabaseConnectionString(): string {
|
||||||
this.logger.debug('Getting database connection string from environment variables');
|
this.logger.debug(
|
||||||
|
"Getting database connection string from environment variables",
|
||||||
|
);
|
||||||
|
|
||||||
const password = this.configService.get<string>("POSTGRES_PASSWORD");
|
const password = this.configService.get<string>("POSTGRES_PASSWORD");
|
||||||
const username = this.configService.get<string>("POSTGRES_USER");
|
const username = this.configService.get<string>("POSTGRES_USER");
|
||||||
@@ -80,7 +84,9 @@ export class DatabaseService implements OnModuleInit, OnModuleDestroy {
|
|||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug(`Database connection configured for ${username}@${host}:${port}/${database}`);
|
this.logger.debug(
|
||||||
|
`Database connection configured for ${username}@${host}:${port}/${database}`,
|
||||||
|
);
|
||||||
return `postgres://${username}:${password}@${host}:${port}/${database}`;
|
return `postgres://${username}:${password}@${host}:${port}/${database}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,35 @@
|
|||||||
import { pgTable, varchar, timestamp, uuid, index, boolean } from 'drizzle-orm/pg-core';
|
import {
|
||||||
import { users } from './users';
|
pgTable,
|
||||||
|
varchar,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
index,
|
||||||
|
boolean,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { users } from "./users";
|
||||||
|
|
||||||
export const apiKeys = pgTable('api_keys', {
|
export const apiKeys = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"api_keys",
|
||||||
userId: uuid('user_id').notNull().references(() => users.uuid, { onDelete: 'cascade' }),
|
{
|
||||||
keyHash: varchar('key_hash', { length: 128 }).notNull().unique(), // Haché pour la sécurité (SHA-256)
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
name: varchar('name', { length: 128 }).notNull(), // Nom donné par l'utilisateur (ex: "My App")
|
userId: uuid("user_id")
|
||||||
prefix: varchar('prefix', { length: 8 }).notNull(), // Pour identification visuelle (ex: "mg_...")
|
.notNull()
|
||||||
isActive: boolean('is_active').notNull().default(true),
|
.references(() => users.uuid, { onDelete: "cascade" }),
|
||||||
lastUsedAt: timestamp('last_used_at', { withTimezone: true }),
|
keyHash: varchar("key_hash", { length: 128 }).notNull().unique(), // Haché pour la sécurité (SHA-256)
|
||||||
expiresAt: timestamp('expires_at', { withTimezone: true }),
|
name: varchar("name", { length: 128 }).notNull(), // Nom donné par l'utilisateur (ex: "My App")
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
prefix: varchar("prefix", { length: 8 }).notNull(), // Pour identification visuelle (ex: "mg_...")
|
||||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
isActive: boolean("is_active").notNull().default(true),
|
||||||
}, (table) => ({
|
lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
|
||||||
userIdIdx: index('api_keys_user_id_idx').on(table.userId),
|
expiresAt: timestamp("expires_at", { withTimezone: true }),
|
||||||
keyHashIdx: index('api_keys_key_hash_idx').on(table.keyHash),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
}));
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
userIdIdx: index("api_keys_user_id_idx").on(table.userId),
|
||||||
|
keyHashIdx: index("api_keys_key_hash_idx").on(table.keyHash),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,25 +1,43 @@
|
|||||||
import { pgTable, varchar, timestamp, uuid, index, jsonb } from 'drizzle-orm/pg-core';
|
import {
|
||||||
import { users } from './users';
|
pgTable,
|
||||||
|
varchar,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
index,
|
||||||
|
jsonb,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { users } from "./users";
|
||||||
|
|
||||||
export const auditLogs = pgTable('audit_logs', {
|
export const auditLogs = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"audit_logs",
|
||||||
userId: uuid('user_id').references(() => users.uuid, { onDelete: 'set null' }), // L'utilisateur qui a fait l'action
|
{
|
||||||
action: varchar('action', { length: 64 }).notNull(), // ex: 'PII_ACCESS', 'USER_DELETE', 'ROLE_CHANGE'
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
entityType: varchar('entity_type', { length: 64 }).notNull(), // ex: 'users', 'contents'
|
userId: uuid("user_id").references(() => users.uuid, {
|
||||||
entityId: uuid('entity_id'), // ID de l'entité concernée
|
onDelete: "set null",
|
||||||
|
}), // L'utilisateur qui a fait l'action
|
||||||
|
action: varchar("action", { length: 64 }).notNull(), // ex: 'PII_ACCESS', 'USER_DELETE', 'ROLE_CHANGE'
|
||||||
|
entityType: varchar("entity_type", { length: 64 }).notNull(), // ex: 'users', 'contents'
|
||||||
|
entityId: uuid("entity_id"), // ID de l'entité concernée
|
||||||
|
|
||||||
// Détails de l'action pour la conformité
|
// Détails de l'action pour la conformité
|
||||||
details: jsonb('details'), // Données supplémentaires (ex: quelles colonnes ont changé)
|
details: jsonb("details"), // Données supplémentaires (ex: quelles colonnes ont changé)
|
||||||
ipHash: varchar('ip_hash', { length: 64 }), // IP de l'auteur (hachée pour RGPD)
|
ipHash: varchar("ip_hash", { length: 64 }), // IP de l'auteur (hachée pour RGPD)
|
||||||
userAgent: varchar('user_agent', { length: 255 }),
|
userAgent: varchar("user_agent", { length: 255 }),
|
||||||
|
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
}, (table) => ({
|
.notNull()
|
||||||
userIdIdx: index('audit_logs_user_id_idx').on(table.userId),
|
.defaultNow(),
|
||||||
actionIdx: index('audit_logs_action_idx').on(table.action),
|
},
|
||||||
entityIdx: index('audit_logs_entity_idx').on(table.entityType, table.entityId),
|
(table) => ({
|
||||||
createdAtIdx: index('audit_logs_created_at_idx').on(table.createdAt),
|
userIdIdx: index("audit_logs_user_id_idx").on(table.userId),
|
||||||
}));
|
actionIdx: index("audit_logs_action_idx").on(table.action),
|
||||||
|
entityIdx: index("audit_logs_entity_idx").on(
|
||||||
|
table.entityType,
|
||||||
|
table.entityId,
|
||||||
|
),
|
||||||
|
createdAtIdx: index("audit_logs_created_at_idx").on(table.createdAt),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export type AuditLogInDb = typeof auditLogs.$inferSelect;
|
export type AuditLogInDb = typeof auditLogs.$inferSelect;
|
||||||
export type NewAuditLogInDb = typeof auditLogs.$inferInsert;
|
export type NewAuditLogInDb = typeof auditLogs.$inferInsert;
|
||||||
|
|||||||
@@ -1,32 +1,59 @@
|
|||||||
import { pgTable, varchar, timestamp, uuid, pgEnum, index, primaryKey, integer } from 'drizzle-orm/pg-core';
|
import {
|
||||||
import { users } from './users';
|
pgTable,
|
||||||
import { tags } from './tags';
|
varchar,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
pgEnum,
|
||||||
|
index,
|
||||||
|
primaryKey,
|
||||||
|
integer,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { users } from "./users";
|
||||||
|
import { tags } from "./tags";
|
||||||
|
|
||||||
export const contentType = pgEnum('content_type', ['meme', 'gif']);
|
export const contentType = pgEnum("content_type", ["meme", "gif"]);
|
||||||
|
|
||||||
export const contents = pgTable('contents', {
|
export const contents = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"contents",
|
||||||
userId: uuid('user_id').notNull().references(() => users.uuid, { onDelete: 'cascade' }),
|
{
|
||||||
type: contentType('type').notNull(),
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
title: varchar('title', { length: 255 }).notNull(),
|
userId: uuid("user_id")
|
||||||
storageKey: varchar('storage_key', { length: 512 }).notNull().unique(), // Clé interne S3
|
.notNull()
|
||||||
mimeType: varchar('mime_type', { length: 128 }).notNull(), // Pour le Content-Type HTTP
|
.references(() => users.uuid, { onDelete: "cascade" }),
|
||||||
fileSize: integer('file_size').notNull(), // Taille en octets
|
type: contentType("type").notNull(),
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
title: varchar("title", { length: 255 }).notNull(),
|
||||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
storageKey: varchar("storage_key", { length: 512 }).notNull().unique(), // Clé interne S3
|
||||||
deletedAt: timestamp('deleted_at', { withTimezone: true }), // Soft delete
|
mimeType: varchar("mime_type", { length: 128 }).notNull(), // Pour le Content-Type HTTP
|
||||||
}, (table) => ({
|
fileSize: integer("file_size").notNull(), // Taille en octets
|
||||||
userIdIdx: index('contents_user_id_idx').on(table.userId),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
storageKeyIdx: index('contents_storage_key_idx').on(table.storageKey),
|
.notNull()
|
||||||
deletedAtIdx: index('contents_deleted_at_idx').on(table.deletedAt),
|
.defaultNow(),
|
||||||
}));
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
deletedAt: timestamp("deleted_at", { withTimezone: true }), // Soft delete
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
userIdIdx: index("contents_user_id_idx").on(table.userId),
|
||||||
|
storageKeyIdx: index("contents_storage_key_idx").on(table.storageKey),
|
||||||
|
deletedAtIdx: index("contents_deleted_at_idx").on(table.deletedAt),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const contentsToTags = pgTable('contents_to_tags', {
|
export const contentsToTags = pgTable(
|
||||||
contentId: uuid('content_id').notNull().references(() => contents.id, { onDelete: 'cascade' }),
|
"contents_to_tags",
|
||||||
tagId: uuid('tag_id').notNull().references(() => tags.id, { onDelete: 'cascade' }),
|
{
|
||||||
}, (t) => ({
|
contentId: uuid("content_id")
|
||||||
pk: primaryKey({ columns: [t.contentId, t.tagId] }),
|
.notNull()
|
||||||
}));
|
.references(() => contents.id, { onDelete: "cascade" }),
|
||||||
|
tagId: uuid("tag_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => tags.id, { onDelete: "cascade" }),
|
||||||
|
},
|
||||||
|
(t) => ({
|
||||||
|
pk: primaryKey({ columns: [t.contentId, t.tagId] }),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export type ContentInDb = typeof contents.$inferSelect;
|
export type ContentInDb = typeof contents.$inferSelect;
|
||||||
export type NewContentInDb = typeof contents.$inferInsert;
|
export type NewContentInDb = typeof contents.$inferInsert;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export * from './users';
|
export * from "./users";
|
||||||
export * from './rbac';
|
export * from "./rbac";
|
||||||
export * from './sessions';
|
export * from "./sessions";
|
||||||
export * from './api_keys';
|
export * from "./api_keys";
|
||||||
export * from './tags';
|
export * from "./tags";
|
||||||
export * from './content';
|
export * from "./content";
|
||||||
export * from './reports';
|
export * from "./reports";
|
||||||
export * from './audit_logs';
|
export * from "./audit_logs";
|
||||||
|
|||||||
@@ -1,36 +1,71 @@
|
|||||||
import { pgTable, varchar, timestamp, uuid, primaryKey, index } from 'drizzle-orm/pg-core';
|
import {
|
||||||
import { users } from './users';
|
pgTable,
|
||||||
|
varchar,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
primaryKey,
|
||||||
|
index,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { users } from "./users";
|
||||||
|
|
||||||
export const roles = pgTable('roles', {
|
export const roles = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"roles",
|
||||||
name: varchar('name', { length: 64 }).notNull().unique(),
|
{
|
||||||
slug: varchar('slug', { length: 64 }).notNull().unique(),
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
description: varchar('description', { length: 128 }),
|
name: varchar("name", { length: 64 }).notNull().unique(),
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
slug: varchar("slug", { length: 64 }).notNull().unique(),
|
||||||
}, (table) => ({
|
description: varchar("description", { length: 128 }),
|
||||||
slugIdx: index('roles_slug_idx').on(table.slug),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
}));
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
slugIdx: index("roles_slug_idx").on(table.slug),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const permissions = pgTable('permissions', {
|
export const permissions = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"permissions",
|
||||||
name: varchar('name', { length: 64 }).notNull().unique(),
|
{
|
||||||
slug: varchar('slug', { length: 64 }).notNull().unique(),
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
description: varchar('description', { length: 128 }),
|
name: varchar("name", { length: 64 }).notNull().unique(),
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
slug: varchar("slug", { length: 64 }).notNull().unique(),
|
||||||
}, (table) => ({
|
description: varchar("description", { length: 128 }),
|
||||||
slugIdx: index('permissions_slug_idx').on(table.slug),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
}));
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
slugIdx: index("permissions_slug_idx").on(table.slug),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const rolesToPermissions = pgTable('roles_to_permissions', {
|
export const rolesToPermissions = pgTable(
|
||||||
roleId: uuid('role_id').notNull().references(() => roles.id, { onDelete: 'cascade' }),
|
"roles_to_permissions",
|
||||||
permissionId: uuid('permission_id').notNull().references(() => permissions.id, { onDelete: 'cascade' }),
|
{
|
||||||
}, (t) => ({
|
roleId: uuid("role_id")
|
||||||
pk: primaryKey({ columns: [t.roleId, t.permissionId] }),
|
.notNull()
|
||||||
}));
|
.references(() => roles.id, { onDelete: "cascade" }),
|
||||||
|
permissionId: uuid("permission_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => permissions.id, { onDelete: "cascade" }),
|
||||||
|
},
|
||||||
|
(t) => ({
|
||||||
|
pk: primaryKey({ columns: [t.roleId, t.permissionId] }),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const usersToRoles = pgTable('users_to_roles', {
|
export const usersToRoles = pgTable(
|
||||||
userId: uuid('user_id').notNull().references(() => users.uuid, { onDelete: 'cascade' }),
|
"users_to_roles",
|
||||||
roleId: uuid('role_id').notNull().references(() => roles.id, { onDelete: 'cascade' }),
|
{
|
||||||
}, (t) => ({
|
userId: uuid("user_id")
|
||||||
pk: primaryKey({ columns: [t.userId, t.roleId] }),
|
.notNull()
|
||||||
}));
|
.references(() => users.uuid, { onDelete: "cascade" }),
|
||||||
|
roleId: uuid("role_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => roles.id, { onDelete: "cascade" }),
|
||||||
|
},
|
||||||
|
(t) => ({
|
||||||
|
pk: primaryKey({ columns: [t.userId, t.roleId] }),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,33 +1,63 @@
|
|||||||
import { pgTable, varchar, timestamp, uuid, pgEnum, index, text } from 'drizzle-orm/pg-core';
|
import {
|
||||||
import { users } from './users';
|
pgTable,
|
||||||
import { contents } from './content';
|
varchar,
|
||||||
import { tags } from './tags';
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
pgEnum,
|
||||||
|
index,
|
||||||
|
text,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { users } from "./users";
|
||||||
|
import { contents } from "./content";
|
||||||
|
import { tags } from "./tags";
|
||||||
|
|
||||||
export const reportStatus = pgEnum('report_status', ['pending', 'reviewed', 'resolved', 'dismissed']);
|
export const reportStatus = pgEnum("report_status", [
|
||||||
export const reportReason = pgEnum('report_reason', ['inappropriate', 'spam', 'copyright', 'other']);
|
"pending",
|
||||||
|
"reviewed",
|
||||||
|
"resolved",
|
||||||
|
"dismissed",
|
||||||
|
]);
|
||||||
|
export const reportReason = pgEnum("report_reason", [
|
||||||
|
"inappropriate",
|
||||||
|
"spam",
|
||||||
|
"copyright",
|
||||||
|
"other",
|
||||||
|
]);
|
||||||
|
|
||||||
export const reports = pgTable('reports', {
|
export const reports = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"reports",
|
||||||
reporterId: uuid('reporter_id').notNull().references(() => users.uuid, { onDelete: 'cascade' }),
|
{
|
||||||
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
|
reporterId: uuid("reporter_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.uuid, { onDelete: "cascade" }),
|
||||||
|
|
||||||
// Le signalement peut porter sur un contenu OU un tag
|
// Le signalement peut porter sur un contenu OU un tag
|
||||||
contentId: uuid('content_id').references(() => contents.id, { onDelete: 'cascade' }),
|
contentId: uuid("content_id").references(() => contents.id, {
|
||||||
tagId: uuid('tag_id').references(() => tags.id, { onDelete: 'cascade' }),
|
onDelete: "cascade",
|
||||||
|
}),
|
||||||
|
tagId: uuid("tag_id").references(() => tags.id, { onDelete: "cascade" }),
|
||||||
|
|
||||||
reason: reportReason('reason').notNull(),
|
reason: reportReason("reason").notNull(),
|
||||||
description: text('description'),
|
description: text("description"),
|
||||||
status: reportStatus('status').default('pending').notNull(),
|
status: reportStatus("status").default("pending").notNull(),
|
||||||
|
|
||||||
expiresAt: timestamp('expires_at', { withTimezone: true }), // Pour purge automatique RGPD
|
expiresAt: timestamp("expires_at", { withTimezone: true }), // Pour purge automatique RGPD
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
.notNull()
|
||||||
}, (table) => ({
|
.defaultNow(),
|
||||||
reporterIdx: index('reports_reporter_id_idx').on(table.reporterId),
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
contentIdx: index('reports_content_id_idx').on(table.contentId),
|
.notNull()
|
||||||
tagIdx: index('reports_tag_id_idx').on(table.tagId),
|
.defaultNow(),
|
||||||
statusIdx: index('reports_status_idx').on(table.status),
|
},
|
||||||
expiresAtIdx: index('reports_expires_at_idx').on(table.expiresAt),
|
(table) => ({
|
||||||
}));
|
reporterIdx: index("reports_reporter_id_idx").on(table.reporterId),
|
||||||
|
contentIdx: index("reports_content_id_idx").on(table.contentId),
|
||||||
|
tagIdx: index("reports_tag_id_idx").on(table.tagId),
|
||||||
|
statusIdx: index("reports_status_idx").on(table.status),
|
||||||
|
expiresAtIdx: index("reports_expires_at_idx").on(table.expiresAt),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export type ReportInDb = typeof reports.$inferSelect;
|
export type ReportInDb = typeof reports.$inferSelect;
|
||||||
export type NewReportInDb = typeof reports.$inferInsert;
|
export type NewReportInDb = typeof reports.$inferInsert;
|
||||||
|
|||||||
@@ -1,18 +1,35 @@
|
|||||||
import { pgTable, varchar, timestamp, uuid, index, boolean } from 'drizzle-orm/pg-core';
|
import {
|
||||||
import { users } from './users';
|
pgTable,
|
||||||
|
varchar,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
index,
|
||||||
|
boolean,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { users } from "./users";
|
||||||
|
|
||||||
export const sessions = pgTable('sessions', {
|
export const sessions = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"sessions",
|
||||||
userId: uuid('user_id').notNull().references(() => users.uuid, { onDelete: 'cascade' }),
|
{
|
||||||
refreshToken: varchar('refresh_token', { length: 512 }).notNull().unique(),
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
userAgent: varchar('user_agent', { length: 255 }),
|
userId: uuid("user_id")
|
||||||
ipHash: varchar('ip_hash', { length: 64 }), // IP hachée pour la protection de la vie privée (RGPD)
|
.notNull()
|
||||||
isValid: boolean('is_valid').notNull().default(true),
|
.references(() => users.uuid, { onDelete: "cascade" }),
|
||||||
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
|
refreshToken: varchar("refresh_token", { length: 512 }).notNull().unique(),
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
userAgent: varchar("user_agent", { length: 255 }),
|
||||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
ipHash: varchar("ip_hash", { length: 64 }), // IP hachée pour la protection de la vie privée (RGPD)
|
||||||
}, (table) => ({
|
isValid: boolean("is_valid").notNull().default(true),
|
||||||
userIdIdx: index('sessions_user_id_idx').on(table.userId),
|
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
||||||
refreshTokenIdx: index('sessions_refresh_token_idx').on(table.refreshToken),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
expiresAtIdx: index('sessions_expires_at_idx').on(table.expiresAt),
|
.notNull()
|
||||||
}));
|
.defaultNow(),
|
||||||
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
userIdIdx: index("sessions_user_id_idx").on(table.userId),
|
||||||
|
refreshTokenIdx: index("sessions_refresh_token_idx").on(table.refreshToken),
|
||||||
|
expiresAtIdx: index("sessions_expires_at_idx").on(table.expiresAt),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
import { pgTable, varchar, timestamp, uuid, index } from 'drizzle-orm/pg-core';
|
import { pgTable, varchar, timestamp, uuid, index } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const tags = pgTable('tags', {
|
export const tags = pgTable(
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
"tags",
|
||||||
name: varchar('name', { length: 64 }).notNull().unique(),
|
{
|
||||||
slug: varchar('slug', { length: 64 }).notNull().unique(),
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
name: varchar("name", { length: 64 }).notNull().unique(),
|
||||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
slug: varchar("slug", { length: 64 }).notNull().unique(),
|
||||||
}, (table) => ({
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
slugIdx: index('tags_slug_idx').on(table.slug),
|
.notNull()
|
||||||
}));
|
.defaultNow(),
|
||||||
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
slugIdx: index("tags_slug_idx").on(table.slug),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export type TagInDb = typeof tags.$inferSelect;
|
export type TagInDb = typeof tags.$inferSelect;
|
||||||
export type NewTagInDb = typeof tags.$inferInsert;
|
export type NewTagInDb = typeof tags.$inferInsert;
|
||||||
|
|||||||
@@ -1,46 +1,69 @@
|
|||||||
import {pgTable, varchar, timestamp, uuid, pgEnum, index, boolean, customType} 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)
|
// Type personnalisé pour les données chiffrées PGP (stockées en bytea dans Postgres)
|
||||||
const pgpEncrypted = customType<{ data: string; driverData: string }>({
|
const pgpEncrypted = customType<{ data: string; driverData: string }>({
|
||||||
dataType() {
|
dataType() {
|
||||||
return 'bytea';
|
return "bytea";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userStatus = pgEnum("user_status", ["active", "verification", "suspended", "pending", "deleted"])
|
export const userStatus = pgEnum("user_status", [
|
||||||
|
"active",
|
||||||
|
"verification",
|
||||||
|
"suspended",
|
||||||
|
"pending",
|
||||||
|
"deleted",
|
||||||
|
]);
|
||||||
|
|
||||||
export const users = pgTable('users', {
|
export const users = pgTable(
|
||||||
uuid: uuid().primaryKey().defaultRandom(),
|
"users",
|
||||||
status: userStatus('status').default('pending').notNull(),
|
{
|
||||||
|
uuid: uuid().primaryKey().defaultRandom(),
|
||||||
|
status: userStatus("status").default("pending").notNull(),
|
||||||
|
|
||||||
// Données Personnelles (PII) - Chiffrées nativement
|
// Données Personnelles (PII) - Chiffrées nativement
|
||||||
email: pgpEncrypted('email').notNull(),
|
email: pgpEncrypted("email").notNull(),
|
||||||
emailHash: varchar('email_hash', { length: 64 }).notNull().unique(), // Indexé pour recherche rapide et unicité
|
emailHash: varchar("email_hash", { length: 64 }).notNull().unique(), // Indexé pour recherche rapide et unicité
|
||||||
displayName: varchar('display_name', { length: 32 }),
|
displayName: varchar("display_name", { length: 32 }),
|
||||||
|
|
||||||
username: varchar('username', { length: 32 }).notNull().unique(),
|
username: varchar("username", { length: 32 }).notNull().unique(),
|
||||||
passwordHash: varchar('password_hash', { length: 72 }).notNull(),
|
passwordHash: varchar("password_hash", { length: 72 }).notNull(),
|
||||||
|
|
||||||
// Sécurité
|
// Sécurité
|
||||||
twoFactorSecret: pgpEncrypted('two_factor_secret'),
|
twoFactorSecret: pgpEncrypted("two_factor_secret"),
|
||||||
isTwoFactorEnabled: boolean('is_two_factor_enabled').notNull().default(false),
|
isTwoFactorEnabled: boolean("is_two_factor_enabled").notNull().default(false),
|
||||||
|
|
||||||
// RGPD & Conformité
|
// RGPD & Conformité
|
||||||
termsVersion: varchar('terms_version', { length: 16 }), // Version des CGU acceptées
|
termsVersion: varchar("terms_version", { length: 16 }), // Version des CGU acceptées
|
||||||
privacyVersion: varchar('privacy_version', { length: 16 }), // Version de la Privacy Policy acceptée
|
privacyVersion: varchar("privacy_version", { length: 16 }), // Version de la Privacy Policy acceptée
|
||||||
gdprAcceptedAt: timestamp('gdpr_accepted_at', { withTimezone: true }),
|
gdprAcceptedAt: timestamp("gdpr_accepted_at", { withTimezone: true }),
|
||||||
|
|
||||||
// Dates de cycle de vie (Standards Entreprise)
|
// Dates de cycle de vie (Standards Entreprise)
|
||||||
lastLoginAt: timestamp('last_login_at', { withTimezone: true }),
|
lastLoginAt: timestamp("last_login_at", { withTimezone: true }),
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
.notNull()
|
||||||
deletedAt: timestamp('deleted_at', { withTimezone: true }), // Soft delete (Droit à l'oubli)
|
.defaultNow(),
|
||||||
}, (table) => ({
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
uuidIdx: index('users_uuid_idx').on(table.uuid),
|
.notNull()
|
||||||
emailHashIdx: index('users_email_hash_idx').on(table.emailHash),
|
.defaultNow(),
|
||||||
usernameIdx: index('users_username_idx').on(table.username),
|
deletedAt: timestamp("deleted_at", { withTimezone: true }), // Soft delete (Droit à l'oubli)
|
||||||
statusIdx: index('users_status_idx').on(table.status),
|
},
|
||||||
}));
|
(table) => ({
|
||||||
|
uuidIdx: index("users_uuid_idx").on(table.uuid),
|
||||||
|
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;
|
export type UserInDb = typeof users.$inferSelect;
|
||||||
export type NewUserInDb = typeof users.$inferInsert;
|
export type NewUserInDb = typeof users.$inferInsert;
|
||||||
|
|||||||
Reference in New Issue
Block a user