diff --git a/server/_core/index.ts b/server/_core/index.ts index 47aaafc..ad6845a 100644 --- a/server/_core/index.ts +++ b/server/_core/index.ts @@ -2,6 +2,7 @@ import "dotenv/config"; import express from "express"; import { createServer } from "http"; import net from "net"; +import path from "path"; import { createExpressMiddleware } from "@trpc/server/adapters/express"; import { registerOAuthRoutes } from "./oauth"; import { appRouter } from "../routers"; @@ -46,6 +47,8 @@ async function startServer() { app.use("/api/upload-image", uploadImageRouter); // File upload under /api/upload-file app.use("/api/upload-file", uploadFileRouter); + // Serve uploaded files + app.use("/uploads", express.static(path.join(process.cwd(), "uploads"))); // tRPC API app.use( "/api/trpc", diff --git a/server/_core/uploadFile.ts b/server/_core/uploadFile.ts index af6351a..99cf0dd 100644 --- a/server/_core/uploadFile.ts +++ b/server/_core/uploadFile.ts @@ -3,6 +3,7 @@ import multer from "multer"; import { storagePut } from "../storage"; import { randomBytes } from "crypto"; import path from "path"; +import fs from "fs/promises"; const router = Router(); @@ -37,24 +38,50 @@ router.post("/", upload.single("file"), async (req, res) => { const safeBaseName = baseName.replace(/[^a-zA-Z0-9-_]/g, "_"); const fileName = `rappels-attachments/${Date.now()}-${randomSuffix}-${safeBaseName}${ext}`; - // Upload vers S3 - const result = await storagePut( - fileName, - file.buffer, - file.mimetype - ); + // Essayer d'uploader vers S3, sinon utiliser le stockage local + try { + const result = await storagePut( + fileName, + file.buffer, + file.mimetype + ); - console.log(`[UploadFile] Fichier uploadé: ${file.originalname} (${file.size} octets)`); + console.log(`[UploadFile] Fichier uploadé vers S3: ${file.originalname} (${file.size} octets)`); - return res.json({ - success: true, - url: result.url, - key: fileName, - filename: file.originalname, - mimetype: file.mimetype, - size: file.size, - message: "Fichier uploadé avec succès", - }); + return res.json({ + success: true, + url: result.url, + key: fileName, + filename: file.originalname, + mimetype: file.mimetype, + size: file.size, + message: "Fichier uploadé avec succès", + }); + } catch (s3Error) { + console.warn(`[UploadFile] Échec S3, utilisation du stockage local:`, s3Error); + + // Fallback: sauvegarder localement + const uploadsDir = path.join(process.cwd(), 'uploads', 'rappels-attachments'); + await fs.mkdir(uploadsDir, { recursive: true }); + + const localFileName = `${Date.now()}-${randomSuffix}-${safeBaseName}${ext}`; + const localPath = path.join(uploadsDir, localFileName); + await fs.writeFile(localPath, file.buffer); + + const publicUrl = `/uploads/rappels-attachments/${localFileName}`; + + console.log(`[UploadFile] Fichier sauvegardé localement: ${file.originalname} (${file.size} octets)`); + + return res.json({ + success: true, + url: publicUrl, + key: fileName, + filename: file.originalname, + mimetype: file.mimetype, + size: file.size, + message: "Fichier uploadé avec succès (stockage local)", + }); + } } catch (error) { console.error("[UploadFile] Error:", error); return res.status(500).json({ diff --git a/todo.md b/todo.md index ff96216..b7684c3 100644 --- a/todo.md +++ b/todo.md @@ -846,3 +846,5 @@ - [x] Corriger le schéma Drizzle pour utiliser des noms de tables en minuscules (logsrappels au lieu de logsRappels) - [x] Tester que tous les types de rappels fonctionnent correctement - [x] Corriger le problème du bouton de vidage de l'historique des rappels qui ne fonctionne plus (nom de table incorrect) +- [ ] Déployer l'intégralité du projet mis à jour sur le VPS (bouton de suppression des rappels et toutes les fonctionnalités manquantes) +- [x] Configurer l'upload de fichiers pour utiliser le stockage local sur le VPS au lieu du service S3