mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-19 21:18:25 +02:00
feat(mails): send supplier document requests over each store's own SMTP
Replaces the mailto: link with a server-side send, so the message carries the store's signature and logo instead of depending on each workstation's Outlook. Store record (groups): - address, phone and logo (data URI, 200 KB cap) feed the mail signature - per-store SMTP settings: host, port, SSL/STARTTLS, credentials, sender address and display name, with an enable switch - "test connection" button verifies the server without sending anything - the empty-form literal, previously repeated five times, becomes one constant Sending: - nodemailer transport built per store from its own settings - multipart mail: plain-text alternative plus HTML whose signature embeds the logo as an inline CID attachment, which Outlook renders without the remote image blocking that a data: URI would hit - delivery details are HTML-escaped - Reply-To set to the store address; the row shows a spinner and refuses a second click while a send is in flight Credentials: - the SMTP password is never returned to the client; a response-layer sanitizer strips it from every /api payload and replaces it with a smtpPasswordSet flag, covering the ten-plus queries that join full group rows into deliveries, orders and user relations - an empty password field on save keeps the stored one rather than clearing it Verified end-to-end against a local SMTP server: transport, auth, From/Reply-To, multipart structure and the inline logo attachment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
This commit is contained in:
14 files changed
+994
-209
No files matched your search
@@ -61,6 +61,19 @@ export const groups = pgTable("groups", {
|
||||
nocodbSupplierColumnName: varchar("nocodb_supplier_column_name"), // Nom de la colonne fournisseur dans leur table
|
||||
nocodbDueDateColumnName: varchar("nocodb_due_date_column_name"), // Nom de la colonne date d'échéance dans leur table
|
||||
webhookUrl: varchar("webhook_url", { length: 500 }), // URL de webhook pour notifications par magasin
|
||||
// Coordonnées du magasin, reprises dans la signature des mails fournisseurs
|
||||
address: text("address"), // Adresse postale
|
||||
phone: varchar("phone", { length: 50 }), // Téléphone
|
||||
logo: text("logo"), // Logo en data URI (data:image/png;base64,...), intégré aux mails
|
||||
// Configuration SMTP propre au magasin (envoi des mails fournisseurs)
|
||||
smtpEnabled: boolean("smtp_enabled").default(false),
|
||||
smtpHost: varchar("smtp_host", { length: 255 }),
|
||||
smtpPort: integer("smtp_port"),
|
||||
smtpSecure: boolean("smtp_secure").default(false), // true = SSL/TLS direct (465), false = STARTTLS
|
||||
smtpUser: varchar("smtp_user", { length: 255 }),
|
||||
smtpPassword: varchar("smtp_password", { length: 255 }), // jamais renvoyé au client
|
||||
smtpSenderEmail: varchar("smtp_sender_email", { length: 255 }), // adresse expéditeur "De :"
|
||||
smtpSenderName: varchar("smtp_sender_name", { length: 255 }), // nom affiché, par défaut le nom du magasin
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
updatedAt: timestamp("updated_at").defaultNow(),
|
||||
});
|
||||
@@ -554,6 +567,17 @@ export const insertGroupSchema = createInsertSchema(groups).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
}).extend({
|
||||
// Champs optionnels : le formulaire envoie "" quand ils ne sont pas renseignés
|
||||
address: z.string().optional().nullable(),
|
||||
phone: z.string().optional().nullable(),
|
||||
logo: z.string().optional().nullable(),
|
||||
smtpHost: z.string().optional().nullable(),
|
||||
smtpPort: z.coerce.number().int().min(1).max(65535).optional().nullable(),
|
||||
smtpUser: z.string().optional().nullable(),
|
||||
smtpPassword: z.string().optional().nullable(),
|
||||
smtpSenderEmail: z.union([z.string().email(), z.literal("")]).optional().nullable(),
|
||||
smtpSenderName: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export const insertSupplierSchema = createInsertSchema(suppliers).omit({
|
||||
|
||||
Reference in new issue
Block a user