From ec2e6349560d1a2cace3cdc5ef2703f32142dd98 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 14 Oct 2025 13:35:03 +0000 Subject: [PATCH 1/7] Fix issue preventing users from generating text with AI Resolves a bug where the AI text generation functionality was not accessible to users. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/vZocxw2 --- ...12-45F9-819A-FF762D37D235}_1760448843187.png | Bin 0 -> 40819 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 attached_assets/{ACCB856E-9212-45F9-819A-FF762D37D235}_1760448843187.png diff --git a/attached_assets/{ACCB856E-9212-45F9-819A-FF762D37D235}_1760448843187.png b/attached_assets/{ACCB856E-9212-45F9-819A-FF762D37D235}_1760448843187.png new file mode 100644 index 0000000000000000000000000000000000000000..28aa48aa19a13e3a3d22937911ed0c84ec78adad GIT binary patch literal 40819 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 Date: Tue, 14 Oct 2025 13:58:28 +0000 Subject: [PATCH 2/7] Allow all users to generate AI text for posts Change API endpoint `/api/ai/generate` from `requireAdmin` to `requireAuth` middleware and update OpenRouterService to use shared admin configuration instead of per-user config. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/tWDOGLP --- replit.md | 3 ++- server/routes.ts | 2 +- server/services/openrouter.ts | 6 +++--- server/storage.ts | 6 ++++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/replit.md b/replit.md index b60fbc0..d077407 100644 --- a/replit.md +++ b/replit.md @@ -11,6 +11,7 @@ Preferred communication style: Simple, everyday language. ## Recent Changes ### October 14, 2025 +- **AI Generation Permissions Fix**: Enabled AI text generation for all authenticated users (previously admin-only). Changed `/api/ai/generate` endpoint from `requireAdmin` to `requireAuth` middleware. Added `getAnyOpenrouterConfig()` method in storage layer to retrieve first available OpenRouter config, enabling shared credentials across all users (same pattern as Cloudinary). Updated `OpenRouterService.generatePostText()` to use shared config instead of per-user lookup. Standard users can now generate AI-powered post text using admin's OpenRouter configuration. - **Cloudinary Upload Permissions Fix**: Fixed critical bug preventing non-admin users from uploading media. Problem: System searched for user-specific Cloudinary config (only admins can configure). Solution: Added `getAnyCloudinaryConfig()` method in storage layer to retrieve first available config, enabling shared Cloudinary credentials across all users. Updated `cloudinaryService.uploadMedia()` and `deleteMedia()` to use shared config internally while maintaining userId for media ownership. Upload and image editor endpoints now verify shared config exists before processing. Standard users can now upload media successfully using admin's Cloudinary configuration. - **Android Camera Upload Fix**: Fixed critical Android camera capture bug where uploads failed with 400 error. Android devices send captured files with invalid names (empty, 'blob', etc.) that Multer rejects. Solution: `handleCameraCapture` now detects invalid filenames and creates new File object with generated name `camera-${timestamp}.${extension}` while preserving MIME type. Applied to both new-post and media-upload components. iPhone functionality unaffected. - **Mobile Performance Optimizations**: Implemented comprehensive mobile speed optimizations for "Nouvelle publication" and "Médiathèque" pages. Changes include: (1) Adaptive initial loading - 6 media items on mobile (<768px), 12 on desktop; (2) Optimized thumbnail URLs - replaced `originalUrl` with `facebookFeedUrl` (1080x1080 Cloudinary transformed images) for 70-90% reduction in data transfer while keeping `originalUrl` for zoom/preview quality; (3) Native lazy loading - added `loading="lazy"` attribute to all `` tags for deferred off-screen image loading; (4) Responsive grid layout - 2 columns on mobile, 3 on desktop (sm:grid-cols-3) for better touch targets. Expected impact: Initial load time reduced from 3-5s to <1s on mobile devices. @@ -37,7 +38,7 @@ The platform leverages **Cloudinary** for cloud-based image and video storage an ### Authentication & Authorization -Authentication uses **Passport.js** with local strategy and `bcrypt` for password hashing. User roles (`admin`, `user`) control access, with `admin` having full access and `user` limited to publishing features. The **AI Assistant** is restricted to administrators only. Session management is via `express-session` with HTTP-only cookies and a 7-day duration. Routes are protected on both the backend (middleware `requireAuth`, `requireAdmin`) and frontend (`ProtectedRoute` component). +Authentication uses **Passport.js** with local strategy and `bcrypt` for password hashing. User roles (`admin`, `user`) control access, with `admin` having full access to configuration settings (Cloudinary, OpenRouter, user management) and `user` limited to publishing features. **AI text generation is available to all authenticated users** using shared admin OpenRouter configuration. Session management is via `express-session` with HTTP-only cookies and a 7-day duration. Routes are protected on both the backend (middleware `requireAuth`, `requireAdmin`) and frontend (`ProtectedRoute` component). ### UI/UX Decisions diff --git a/server/routes.ts b/server/routes.ts index a625994..76244fc 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -435,7 +435,7 @@ export async function registerRoutes(app: Express): Promise { }); // AI text generation - app.post("/api/ai/generate", requireAdmin, async (req, res) => { + app.post("/api/ai/generate", requireAuth, async (req, res) => { try { const user = req.user as User; const userId = user.id; diff --git a/server/services/openrouter.ts b/server/services/openrouter.ts index e1747a2..0e607bd 100644 --- a/server/services/openrouter.ts +++ b/server/services/openrouter.ts @@ -18,11 +18,11 @@ export class OpenRouterService { private baseUrl = "https://openrouter.ai/api/v1/chat/completions"; async generatePostText(productInfo: ProductInfo, userId: string, modelOverride?: string): Promise { - // Get user's OpenRouter configuration - const config = await storage.getOpenrouterConfig(userId); + // Get any available OpenRouter configuration (shared across all users) + const config = await storage.getAnyOpenrouterConfig(); if (!config) { - throw new Error('Configuration OpenRouter non trouvée. Veuillez configurer OpenRouter dans les paramètres.'); + throw new Error('Configuration OpenRouter non trouvée. Veuillez demander à un administrateur de configurer OpenRouter dans les Paramètres.'); } const prompt = this.buildPrompt(productInfo, config.systemPrompt); diff --git a/server/storage.ts b/server/storage.ts index 4141e1e..557c5e9 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -85,6 +85,7 @@ export interface IStorage { // OpenRouter Config getOpenrouterConfig(userId: string): Promise; + getAnyOpenrouterConfig(): Promise; createOpenrouterConfig(config: InsertOpenrouterConfig): Promise; updateOpenrouterConfig(userId: string, config: Partial): Promise; @@ -336,6 +337,11 @@ export class DatabaseStorage implements IStorage { return config || undefined; } + async getAnyOpenrouterConfig(): Promise { + const [config] = await db.select().from(openrouterConfig).limit(1); + return config || undefined; + } + async createOpenrouterConfig(config: InsertOpenrouterConfig): Promise { const [newConfig] = await db.insert(openrouterConfig).values(config).returning(); return newConfig; From 312a4223d05159f61d9cee6dc09fb735eaaff3e3 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 14 Oct 2025 13:58:45 +0000 Subject: [PATCH 3/7] Update authentication requirement for administrative actions Refactor access control logic from requireAdmin to requireAuth middleware. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/tWDOGLP From 49871c7b5641210fb712113d29c5128ed751d6cd Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 14 Oct 2025 14:09:58 +0000 Subject: [PATCH 4/7] Transitioned from Plan to Build mode Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/tWDOGLP From 82846ab63307b4c125d2867442dc2cf5b2f509af Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 14 Oct 2025 14:11:16 +0000 Subject: [PATCH 5/7] Ensure users can only post to pages they have access to Add access control to the posting route in server/routes.ts to verify user permissions against specified pageIds, preventing unauthorized posts. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/tWDOGLP --- server/routes.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server/routes.ts b/server/routes.ts index 76244fc..f772145 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -793,6 +793,22 @@ export async function registerRoutes(app: Express): Promise { return res.status(400).json({ error: "Les stories nécessitent au moins un média (image ou vidéo)" }); } + // Security: Verify user has access to all specified pages (unless admin) + if (user.role !== 'admin' && pageIds && Array.isArray(pageIds) && pageIds.length > 0) { + const accessiblePages = await storage.getUserAccessiblePages(userId); + const accessiblePageIds = accessiblePages.map(p => p.id); + + const hasAccessToAllPages = pageIds.every(pageId => + accessiblePageIds.includes(pageId) + ); + + if (!hasAccessToAllPages) { + return res.status(403).json({ + error: "Vous n'avez pas accès à certaines pages sélectionnées" + }); + } + } + // Convert scheduledFor string to Date if provided if (postFields.scheduledFor && typeof postFields.scheduledFor === 'string') { postFields.scheduledFor = new Date(postFields.scheduledFor); From dca629fcc668df1753af73de92887b1418e6ec97 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 14 Oct 2025 14:17:31 +0000 Subject: [PATCH 6/7] Enhance security and user permissions across the application Fixes security vulnerabilities in publishing, AI generation, and media uploads by implementing robust permission checks and shared configurations. Addresses Android camera upload issues. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/VDgoPyA --- replit.md | 1 + 1 file changed, 1 insertion(+) diff --git a/replit.md b/replit.md index d077407..aac5036 100644 --- a/replit.md +++ b/replit.md @@ -11,6 +11,7 @@ Preferred communication style: Simple, everyday language. ## Recent Changes ### October 14, 2025 +- **Publishing Permissions Security Fix**: Fixed critical security vulnerability in POST `/api/posts` endpoint where standard users could publish to ANY page without verification. Added permission check that validates non-admin users can only publish to pages in their `user_page_permissions` list via `getUserAccessiblePages()`. Admins bypass this check and retain full access. Returns 403 Forbidden if user attempts to publish to unauthorized pages. This prevents privilege escalation and ensures proper page-level access control. - **AI Generation Permissions Fix**: Enabled AI text generation for all authenticated users (previously admin-only). Changed `/api/ai/generate` endpoint from `requireAdmin` to `requireAuth` middleware. Added `getAnyOpenrouterConfig()` method in storage layer to retrieve first available OpenRouter config, enabling shared credentials across all users (same pattern as Cloudinary). Updated `OpenRouterService.generatePostText()` to use shared config instead of per-user lookup. Standard users can now generate AI-powered post text using admin's OpenRouter configuration. - **Cloudinary Upload Permissions Fix**: Fixed critical bug preventing non-admin users from uploading media. Problem: System searched for user-specific Cloudinary config (only admins can configure). Solution: Added `getAnyCloudinaryConfig()` method in storage layer to retrieve first available config, enabling shared Cloudinary credentials across all users. Updated `cloudinaryService.uploadMedia()` and `deleteMedia()` to use shared config internally while maintaining userId for media ownership. Upload and image editor endpoints now verify shared config exists before processing. Standard users can now upload media successfully using admin's Cloudinary configuration. - **Android Camera Upload Fix**: Fixed critical Android camera capture bug where uploads failed with 400 error. Android devices send captured files with invalid names (empty, 'blob', etc.) that Multer rejects. Solution: `handleCameraCapture` now detects invalid filenames and creates new File object with generated name `camera-${timestamp}.${extension}` while preserving MIME type. Applied to both new-post and media-upload components. iPhone functionality unaffected. From 1f94d76743a5d3cb2eabc963bc9d4d9ce7efb824 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 14 Oct 2025 14:17:51 +0000 Subject: [PATCH 7/7] Improve the display of product information for social media posts No changes to be committed Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/VDgoPyA