Initial commit: itinova-podcasts v1

Stack: Node.js/Express + React/Vite + tRPC + MySQL (Drizzle ORM)
Features: Gestion de podcasts, établissements, mots-clés, upload audio S3
Migrations: 0000-0002 (users, etablissements, mots_cles, podcasts, podcast_mots_cles)
This commit is contained in:
manus-admin
2026-04-12 18:34:56 -04:00
commit aab11c8308
138 changed files with 27782 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
CREATE TABLE `users` (
`id` int AUTO_INCREMENT NOT NULL,
`openId` varchar(64) NOT NULL,
`name` text,
`email` varchar(320),
`loginMethod` varchar(64),
`role` enum('user','admin') NOT NULL DEFAULT 'user',
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
`lastSignedIn` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `users_id` PRIMARY KEY(`id`),
CONSTRAINT `users_openId_unique` UNIQUE(`openId`)
);

View File

@@ -0,0 +1,45 @@
CREATE TABLE `etablissements` (
`id` int AUTO_INCREMENT NOT NULL,
`nom` varchar(255) NOT NULL,
`description` text,
`logoUrl` text,
`actif` boolean NOT NULL DEFAULT true,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `etablissements_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `mots_cles` (
`id` int AUTO_INCREMENT NOT NULL,
`label` varchar(100) NOT NULL,
`createdAt` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `mots_cles_id` PRIMARY KEY(`id`),
CONSTRAINT `mots_cles_label_unique` UNIQUE(`label`)
);
--> statement-breakpoint
CREATE TABLE `podcast_mots_cles` (
`podcastId` int NOT NULL,
`motCleId` int NOT NULL,
CONSTRAINT `podcast_mots_cles_podcastId_motCleId_pk` PRIMARY KEY(`podcastId`,`motCleId`)
);
--> statement-breakpoint
CREATE TABLE `podcasts` (
`id` int AUTO_INCREMENT NOT NULL,
`titre` varchar(255) NOT NULL,
`resume` text NOT NULL,
`etablissementId` int NOT NULL,
`audioUrl` text,
`audioKey` text,
`dureeSecondes` int,
`statut` enum('brouillon','publie') NOT NULL DEFAULT 'brouillon',
`auteurId` int,
`imageUrl` text,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `podcasts_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `podcast_mots_cles` ADD CONSTRAINT `podcast_mots_cles_podcastId_podcasts_id_fk` FOREIGN KEY (`podcastId`) REFERENCES `podcasts`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `podcast_mots_cles` ADD CONSTRAINT `podcast_mots_cles_motCleId_mots_cles_id_fk` FOREIGN KEY (`motCleId`) REFERENCES `mots_cles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `podcasts` ADD CONSTRAINT `podcasts_etablissementId_etablissements_id_fk` FOREIGN KEY (`etablissementId`) REFERENCES `etablissements`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `podcasts` ADD CONSTRAINT `podcasts_auteurId_users_id_fk` FOREIGN KEY (`auteurId`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;

View File

@@ -0,0 +1,4 @@
ALTER TABLE `users` ADD `username` varchar(64);--> statement-breakpoint
ALTER TABLE `users` ADD `passwordHash` varchar(255);--> statement-breakpoint
ALTER TABLE `users` ADD `immutable` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `users` ADD CONSTRAINT `users_username_unique` UNIQUE(`username`);

View File

@@ -0,0 +1,110 @@
{
"version": "5",
"dialect": "mysql",
"id": "b04049b4-e4f3-4358-a31e-56390324c61b",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -0,0 +1,417 @@
{
"version": "5",
"dialect": "mysql",
"id": "4fe63ed8-4a51-434d-960c-4ade08966a9d",
"prevId": "b04049b4-e4f3-4358-a31e-56390324c61b",
"tables": {
"etablissements": {
"name": "etablissements",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"logoUrl": {
"name": "logoUrl",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actif": {
"name": "actif",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"etablissements_id": {
"name": "etablissements_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"mots_cles": {
"name": "mots_cles",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"label": {
"name": "label",
"type": "varchar(100)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"mots_cles_id": {
"name": "mots_cles_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"mots_cles_label_unique": {
"name": "mots_cles_label_unique",
"columns": [
"label"
]
}
},
"checkConstraint": {}
},
"podcast_mots_cles": {
"name": "podcast_mots_cles",
"columns": {
"podcastId": {
"name": "podcastId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"motCleId": {
"name": "motCleId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"podcast_mots_cles_podcastId_podcasts_id_fk": {
"name": "podcast_mots_cles_podcastId_podcasts_id_fk",
"tableFrom": "podcast_mots_cles",
"tableTo": "podcasts",
"columnsFrom": [
"podcastId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"podcast_mots_cles_motCleId_mots_cles_id_fk": {
"name": "podcast_mots_cles_motCleId_mots_cles_id_fk",
"tableFrom": "podcast_mots_cles",
"tableTo": "mots_cles",
"columnsFrom": [
"motCleId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"podcast_mots_cles_podcastId_motCleId_pk": {
"name": "podcast_mots_cles_podcastId_motCleId_pk",
"columns": [
"podcastId",
"motCleId"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"podcasts": {
"name": "podcasts",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"titre": {
"name": "titre",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"resume": {
"name": "resume",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"etablissementId": {
"name": "etablissementId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"audioUrl": {
"name": "audioUrl",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"audioKey": {
"name": "audioKey",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"dureeSecondes": {
"name": "dureeSecondes",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"statut": {
"name": "statut",
"type": "enum('brouillon','publie')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'brouillon'"
},
"auteurId": {
"name": "auteurId",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"imageUrl": {
"name": "imageUrl",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {
"podcasts_etablissementId_etablissements_id_fk": {
"name": "podcasts_etablissementId_etablissements_id_fk",
"tableFrom": "podcasts",
"tableTo": "etablissements",
"columnsFrom": [
"etablissementId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"podcasts_auteurId_users_id_fk": {
"name": "podcasts_auteurId_users_id_fk",
"tableFrom": "podcasts",
"tableTo": "users",
"columnsFrom": [
"auteurId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"podcasts_id": {
"name": "podcasts_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -0,0 +1,445 @@
{
"version": "5",
"dialect": "mysql",
"id": "ebb8face-8ef8-42db-815d-dc0986c6026b",
"prevId": "4fe63ed8-4a51-434d-960c-4ade08966a9d",
"tables": {
"etablissements": {
"name": "etablissements",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"logoUrl": {
"name": "logoUrl",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actif": {
"name": "actif",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"etablissements_id": {
"name": "etablissements_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"mots_cles": {
"name": "mots_cles",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"label": {
"name": "label",
"type": "varchar(100)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"mots_cles_id": {
"name": "mots_cles_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"mots_cles_label_unique": {
"name": "mots_cles_label_unique",
"columns": [
"label"
]
}
},
"checkConstraint": {}
},
"podcast_mots_cles": {
"name": "podcast_mots_cles",
"columns": {
"podcastId": {
"name": "podcastId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"motCleId": {
"name": "motCleId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"podcast_mots_cles_podcastId_podcasts_id_fk": {
"name": "podcast_mots_cles_podcastId_podcasts_id_fk",
"tableFrom": "podcast_mots_cles",
"tableTo": "podcasts",
"columnsFrom": [
"podcastId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"podcast_mots_cles_motCleId_mots_cles_id_fk": {
"name": "podcast_mots_cles_motCleId_mots_cles_id_fk",
"tableFrom": "podcast_mots_cles",
"tableTo": "mots_cles",
"columnsFrom": [
"motCleId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"podcast_mots_cles_podcastId_motCleId_pk": {
"name": "podcast_mots_cles_podcastId_motCleId_pk",
"columns": [
"podcastId",
"motCleId"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"podcasts": {
"name": "podcasts",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"titre": {
"name": "titre",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"resume": {
"name": "resume",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"etablissementId": {
"name": "etablissementId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"audioUrl": {
"name": "audioUrl",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"audioKey": {
"name": "audioKey",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"dureeSecondes": {
"name": "dureeSecondes",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"statut": {
"name": "statut",
"type": "enum('brouillon','publie')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'brouillon'"
},
"auteurId": {
"name": "auteurId",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"imageUrl": {
"name": "imageUrl",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {
"podcasts_etablissementId_etablissements_id_fk": {
"name": "podcasts_etablissementId_etablissements_id_fk",
"tableFrom": "podcasts",
"tableTo": "etablissements",
"columnsFrom": [
"etablissementId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"podcasts_auteurId_users_id_fk": {
"name": "podcasts_auteurId_users_id_fk",
"tableFrom": "podcasts",
"tableTo": "users",
"columnsFrom": [
"auteurId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"podcasts_id": {
"name": "podcasts_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"username": {
"name": "username",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"passwordHash": {
"name": "passwordHash",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"immutable": {
"name": "immutable",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
},
"users_username_unique": {
"name": "users_username_unique",
"columns": [
"username"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -0,0 +1,27 @@
{
"version": "7",
"dialect": "mysql",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1773330570755,
"tag": "0000_glamorous_skaar",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1773330712368,
"tag": "0001_shocking_bucky",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1773332222380,
"tag": "0002_blue_jane_foster",
"breakpoints": true
}
]
}

View File

1
drizzle/relations.ts Normal file
View File

@@ -0,0 +1 @@
import {} from "./schema";

98
drizzle/schema.ts Normal file
View File

@@ -0,0 +1,98 @@
import {
int,
mysqlEnum,
mysqlTable,
text,
timestamp,
varchar,
primaryKey,
boolean,
} from "drizzle-orm/mysql-core";
export const users = mysqlTable("users", {
id: int("id").autoincrement().primaryKey(),
openId: varchar("openId", { length: 64 }).notNull().unique(),
name: text("name"),
email: varchar("email", { length: 320 }),
loginMethod: varchar("loginMethod", { length: 64 }),
role: mysqlEnum("role", ["user", "admin"]).default("user").notNull(),
/** Identifiant de connexion locale (ex: adminServPodcast) */
username: varchar("username", { length: 64 }).unique(),
/** Hash bcrypt du mot de passe local */
passwordHash: varchar("passwordHash", { length: 255 }),
/** Empêche la modification ou suppression de ce compte */
immutable: boolean("immutable").default(false).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
lastSignedIn: timestamp("lastSignedIn").defaultNow().notNull(),
});
export type User = typeof users.$inferSelect;
export type InsertUser = typeof users.$inferInsert;
// ─── Établissements ────────────────────────────────────────────────────────────
export const etablissements = mysqlTable("etablissements", {
id: int("id").autoincrement().primaryKey(),
nom: varchar("nom", { length: 255 }).notNull(),
description: text("description"),
logoUrl: text("logoUrl"),
actif: boolean("actif").default(true).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Etablissement = typeof etablissements.$inferSelect;
export type InsertEtablissement = typeof etablissements.$inferInsert;
// ─── Mots-clés ─────────────────────────────────────────────────────────────────
export const motsCles = mysqlTable("mots_cles", {
id: int("id").autoincrement().primaryKey(),
label: varchar("label", { length: 100 }).notNull().unique(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type MotCle = typeof motsCles.$inferSelect;
export type InsertMotCle = typeof motsCles.$inferInsert;
// ─── Podcasts ──────────────────────────────────────────────────────────────────
export const podcasts = mysqlTable("podcasts", {
id: int("id").autoincrement().primaryKey(),
titre: varchar("titre", { length: 255 }).notNull(),
resume: text("resume").notNull(),
etablissementId: int("etablissementId")
.notNull()
.references(() => etablissements.id),
audioUrl: text("audioUrl"),
audioKey: text("audioKey"),
dureeSecondes: int("dureeSecondes"),
statut: mysqlEnum("statut", ["brouillon", "publie"]).default("brouillon").notNull(),
auteurId: int("auteurId").references(() => users.id),
imageUrl: text("imageUrl"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Podcast = typeof podcasts.$inferSelect;
export type InsertPodcast = typeof podcasts.$inferInsert;
// ─── Relation Podcast ↔ Mots-clés ─────────────────────────────────────────────
export const podcastMotsCles = mysqlTable(
"podcast_mots_cles",
{
podcastId: int("podcastId")
.notNull()
.references(() => podcasts.id, { onDelete: "cascade" }),
motCleId: int("motCleId")
.notNull()
.references(() => motsCles.id, { onDelete: "cascade" }),
},
(table) => ({
pk: primaryKey({ columns: [table.podcastId, table.motCleId] }),
})
);
export type PodcastMotCle = typeof podcastMotsCles.$inferSelect;