Checkpoint: Ajout de l'interface de configuration SMTP avec Resend : formulaire de configuration (API key, email expéditeur, nom), basculement simulation/production, test d'envoi immédiat, stockage sécurisé en base de données, et guide de configuration intégré

This commit is contained in:
Manus Sandbox
2025-11-18 02:00:48 -05:00
parent 20dc80cd74
commit 9d67724e83
11 changed files with 1298 additions and 13 deletions

View File

@@ -0,0 +1,13 @@
CREATE TABLE `emailConfig` (
`id` int AUTO_INCREMENT NOT NULL,
`provider` varchar(50) NOT NULL DEFAULT 'resend',
`apiKey` text,
`fromEmail` varchar(320) NOT NULL,
`fromName` varchar(255) NOT NULL DEFAULT 'Formation Manager Itinova',
`mode` enum('simulation','production') NOT NULL DEFAULT 'simulation',
`domainVerified` boolean NOT NULL DEFAULT false,
`active` boolean NOT NULL DEFAULT true,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `emailConfig_id` PRIMARY KEY(`id`)
);

View File

@@ -0,0 +1,779 @@
{
"version": "5",
"dialect": "mysql",
"id": "a099e856-0b0c-430d-8160-ca950c994fe5",
"prevId": "73d62a50-d4a5-41dd-b5a2-4d2d72b7f119",
"tables": {
"apprenants": {
"name": "apprenants",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"prenom": {
"name": "prenom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"codeEtablissement": {
"name": "codeEtablissement",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"fonction": {
"name": "fonction",
"type": "enum('directeur','chef_service','autre')",
"primaryKey": false,
"notNull": true,
"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": {},
"compositePrimaryKeys": {
"apprenants_id": {
"name": "apprenants_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"apprenants_email_unique": {
"name": "apprenants_email_unique",
"columns": [
"email"
]
}
},
"checkConstraint": {}
},
"datesFormation": {
"name": "datesFormation",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"sequenceId": {
"name": "sequenceId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"dateDebut": {
"name": "dateDebut",
"type": "datetime",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"dateFin": {
"name": "dateFin",
"type": "datetime",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"ordre": {
"name": "ordre",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"datesFormation_id": {
"name": "datesFormation_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"emailConfig": {
"name": "emailConfig",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"provider": {
"name": "provider",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'resend'"
},
"apiKey": {
"name": "apiKey",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"fromEmail": {
"name": "fromEmail",
"type": "varchar(320)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"fromName": {
"name": "fromName",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'Formation Manager Itinova'"
},
"mode": {
"name": "mode",
"type": "enum('simulation','production')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'simulation'"
},
"domainVerified": {
"name": "domainVerified",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"active": {
"name": "active",
"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": {
"emailConfig_id": {
"name": "emailConfig_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"emailTemplates": {
"name": "emailTemplates",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"type": {
"name": "type",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"logoUrl": {
"name": "logoUrl",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"primaryColor": {
"name": "primaryColor",
"type": "varchar(7)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'#2563eb'"
},
"headerBgColor": {
"name": "headerBgColor",
"type": "varchar(7)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'#2563eb'"
},
"headerTextColor": {
"name": "headerTextColor",
"type": "varchar(7)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'#ffffff'"
},
"headerTitle": {
"name": "headerTitle",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'Formation Manager Itinova'"
},
"footerText": {
"name": "footerText",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"active": {
"name": "active",
"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": {
"emailTemplates_id": {
"name": "emailTemplates_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"emailTemplates_type_unique": {
"name": "emailTemplates_type_unique",
"columns": [
"type"
]
}
},
"checkConstraint": {}
},
"formations": {
"name": "formations",
"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
},
"lienUnique": {
"name": "lienUnique",
"type": "varchar(100)",
"primaryKey": false,
"notNull": true,
"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": {
"formations_id": {
"name": "formations_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"formations_lienUnique_unique": {
"name": "formations_lienUnique_unique",
"columns": [
"lienUnique"
]
}
},
"checkConstraint": {}
},
"inscriptions": {
"name": "inscriptions",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"apprenantId": {
"name": "apprenantId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"sequenceId": {
"name": "sequenceId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"statut": {
"name": "statut",
"type": "enum('confirmee','liste_attente','annulee')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"dateInscription": {
"name": "dateInscription",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"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": {
"inscriptions_id": {
"name": "inscriptions_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"passwordResetTokens": {
"name": "passwordResetTokens",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"userId": {
"name": "userId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"token": {
"name": "token",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expiresAt": {
"name": "expiresAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"used": {
"name": "used",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"passwordResetTokens_id": {
"name": "passwordResetTokens_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"passwordResetTokens_token_unique": {
"name": "passwordResetTokens_token_unique",
"columns": [
"token"
]
}
},
"checkConstraint": {}
},
"sequences": {
"name": "sequences",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"formationId": {
"name": "formationId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"lieu": {
"name": "lieu",
"type": "varchar(500)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"publicCible": {
"name": "publicCible",
"type": "enum('directeur','chef_service','autre')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"capaciteMax": {
"name": "capaciteMax",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 12
},
"dateBlocage": {
"name": "dateBlocage",
"type": "datetime",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"statut": {
"name": "statut",
"type": "enum('ouverte','bloquee','terminee')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'ouverte'"
},
"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": {
"sequences_id": {
"name": "sequences_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'"
},
"isActive": {
"name": "isActive",
"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())"
},
"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

@@ -64,6 +64,13 @@
"when": 1763416801277,
"tag": "0008_jittery_fenris",
"breakpoints": true
},
{
"idx": 9,
"version": "5",
"when": 1763448844826,
"tag": "0009_polite_senator_kelly",
"breakpoints": true
}
]
}

View File

@@ -171,3 +171,30 @@ export const emailTemplates = mysqlTable("emailTemplates", {
export type EmailTemplate = typeof emailTemplates.$inferSelect;
export type InsertEmailTemplate = typeof emailTemplates.$inferInsert;
/**
* Table de configuration SMTP/Email
* Stocke la configuration pour l'envoi d'emails (Resend, etc.)
*/
export const emailConfig = mysqlTable("emailConfig", {
id: int("id").autoincrement().primaryKey(),
/** Service d'envoi (resend, smtp) */
provider: varchar("provider", { length: 50 }).default("resend").notNull(),
/** Clé API Resend (chiffrée) */
apiKey: text("apiKey"),
/** Email expéditeur */
fromEmail: varchar("fromEmail", { length: 320 }).notNull(),
/** Nom de l'expéditeur */
fromName: varchar("fromName", { length: 255 }).default("Formation Manager Itinova").notNull(),
/** Mode: simulation ou production */
mode: mysqlEnum("mode", ["simulation", "production"]).default("simulation").notNull(),
/** Domaine vérifié (pour Resend) */
domainVerified: boolean("domainVerified").default(false).notNull(),
/** Actif ou non */
active: boolean("active").default(true).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type EmailConfig = typeof emailConfig.$inferSelect;
export type InsertEmailConfig = typeof emailConfig.$inferInsert;