From 94429e86dfd3f04f4a1a0a0ced2be3b1bf0a5586 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Mon, 2 Mar 2026 13:21:05 +0100 Subject: [PATCH] feat: Initialize the core Express server with security, session management, and service integrations, and add a Docker volume for audio file persistence. --- docker-compose.yml | 3 +++ server/index.ts | 4 ++++ server/routes.ts | 38 +++++++++++++++++--------------------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ef4c29d..d19cdff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,6 +49,7 @@ services: condition: service_healthy volumes: - encryption_key:/app/.encryption-key-data + - audio_uploads:/app/uploads/audio command: npm run start # Service FFmpeg dédié @@ -70,6 +71,8 @@ volumes: driver: local encryption_key: driver: local + audio_uploads: + driver: local networks: # Réseau interne pour la communication DB <-> App diff --git a/server/index.ts b/server/index.ts index 27b03ab..2f42f58 100644 --- a/server/index.ts +++ b/server/index.ts @@ -60,6 +60,10 @@ const authLimiter = rateLimit({ app.use('/api/', globalLimiter); app.use('/api/auth/login', authLimiter); +// Serve locally stored audio files +const AUDIO_UPLOAD_DIR = process.env.AUDIO_UPLOAD_DIR || '/app/uploads/audio'; +app.use('/uploads/audio', express.static(AUDIO_UPLOAD_DIR)); + declare module 'http' { interface IncomingMessage { rawBody: unknown diff --git a/server/routes.ts b/server/routes.ts index 4658dbc..5797e12 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -56,9 +56,16 @@ const upload = multer({ }); // Setup audio upload with different filters +// Files saved directly to the persistent local folder +const AUDIO_UPLOAD_DIR = process.env.AUDIO_UPLOAD_DIR || '/app/uploads/audio'; + +// Ensure the directory exists at startup +import { mkdirSync } from 'fs'; +try { mkdirSync(AUDIO_UPLOAD_DIR, { recursive: true }); } catch { /* already exists */ } + const audioUpload = multer({ storage: multer.diskStorage({ - destination: os.tmpdir(), + destination: AUDIO_UPLOAD_DIR, filename: (req, file, cb) => { const safeName = file.originalname.replace(/[^a-zA-Z0-9.]/g, '_'); const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); @@ -67,7 +74,7 @@ const audioUpload = multer({ }), limits: { fileSize: 50 * 1024 * 1024, // 50MB max for audio - files: 5 + files: 20 }, fileFilter: (req, file, cb) => { if (ALLOWED_AUDIO_MIME_TYPES.includes(file.mimetype) || file.originalname.match(/\.(mp3|wav|ogg)$/i)) { @@ -977,7 +984,7 @@ export async function registerRoutes(app: Express): Promise { } }); - // Audio Tracks Management (Admin Only) - Multi-file upload + // Audio Tracks Management (Admin Only) - Multi-file upload (stored locally) app.post("/api/audio-tracks", requireAdmin, audioUpload.array("files", 20), async (req, res) => { try { const files = req.files as Express.Multer.File[]; @@ -990,41 +997,30 @@ export async function registerRoutes(app: Express): Promise { for (const file of files) { try { - console.log(`🎵 Processing audio upload: ${file.originalname} (${(file.size / 1024 / 1024).toFixed(2)} MB)`); + console.log(`🎵 Audio saved locally: ${file.originalname} (${(file.size / 1024 / 1024).toFixed(2)} MB)`); - // Upload to Cloudinary; audio is categorized as "video" in Cloudinary - const uploadResult = await cloudinaryService.uploadMedia( - file.path, - file.originalname, - user.id, - "video" - ); - - // Clean up temp file immediately after upload - await fs.promises.unlink(file.path).catch(err => console.error("Failed to cleanup temp audio file:", err)); - - const title = file.originalname.replace(/\.[^/.]+$/, ""); + // Build a public URL served by Express at /uploads/audio/ + const publicUrl = `/uploads/audio/${file.filename}`; const track = await storage.createAudioTrack({ userId: user.id, - title: title, + title: file.originalname.replace(/\.[^/.]+$/, ""), fileName: file.originalname, - url: uploadResult.originalUrl, + url: publicUrl, duration: 0, }); results.push({ success: true, track }); } catch (fileError) { - // Clean up on per-file error + // Remove the file if DB insert fails await fs.promises.unlink(file.path).catch(() => { }); - results.push({ success: false, fileName: file.originalname, error: fileError instanceof Error ? fileError.message : "Upload failed" }); + results.push({ success: false, fileName: file.originalname, error: fileError instanceof Error ? fileError.message : "Save failed" }); } } res.json({ results }); } catch (error) { console.error("Error uploading audio tracks:", error); - // Cleanup all remaining temp files if (req.files) { for (const file of req.files as Express.Multer.File[]) { await fs.promises.unlink(file.path).catch(() => { });