From fc9d0e594a05cc1fe5b4729d08f3a20ea9fd0947 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 28 Oct 2025 13:15:31 +0000 Subject: [PATCH 1/6] 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/aGnsIb5 From 9e58d3be47941df0a42650f76cba894b1bb558b8 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 28 Oct 2025 13:18:59 +0000 Subject: [PATCH 2/6] Improve date and time selection interface for better user experience Adjusted DateTimePicker component by reducing scroll area height, button size, and padding for a more compact and touch-friendly interface, optimizing space usage. 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/aGnsIb5 --- client/src/components/datetime-picker.tsx | 27 ++++++++++------------- replit.md | 2 +- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/client/src/components/datetime-picker.tsx b/client/src/components/datetime-picker.tsx index 9f1f009..29f3bda 100644 --- a/client/src/components/datetime-picker.tsx +++ b/client/src/components/datetime-picker.tsx @@ -137,15 +137,15 @@ export function DateTimePicker({ value, onChange, occupiedDates = [], placeholde
- -
-
Heures
+ +
+
Heures
{hours.map((hour) => (
-
-
-
- +
+
+
+ Heures optimales (8h, 12h-13h, 18h-20h)
-

- Ces créneaux maximisent l'engagement -

diff --git a/replit.md b/replit.md index 53ec9c8..8596f85 100644 --- a/replit.md +++ b/replit.md @@ -11,7 +11,7 @@ Preferred communication style: Simple, everyday language. ## Recent Changes ### October 28, 2025 -- **Interactive Calendar with Visual Indicators**: Replaced native datetime-local input with custom DateTimePicker component featuring visual planning aids. Calendar displays occupied dates with red background (bg-red-500/20) to prevent scheduling conflicts. Time selector highlights optimal posting hours (8h, 12h-13h, 18h-20h) with green background (bg-green-500/20) and sparkle emoji to maximize audience engagement. Component includes clear button (X icon) to quickly return to immediate publishing, useEffect synchronization for external state updates, and French locale support via date-fns. Mobile calendar list still displays today's date first for quick access. +- **Interactive Calendar with Visual Indicators**: Replaced native datetime-local input with custom DateTimePicker component featuring visual planning aids. Calendar displays occupied dates with red background (bg-red-500/20) to prevent scheduling conflicts. Time selector highlights optimal posting hours (8h, 12h-13h, 18h-20h) with green background (bg-green-500/20) and sparkle emoji to maximize audience engagement. Component includes clear button (X icon) to quickly return to immediate publishing, useEffect synchronization for external state updates, and French locale support via date-fns. Time selector optimized for compactness: ScrollArea height reduced from 240px to 192px (20% smaller), button height set to 36px for touch accessibility, tighter padding and margins throughout. Mobile calendar list still displays today's date first for quick access. - **Facebook Video Story Publishing Fix**: Fixed critical bug preventing video story publication. Facebook Graph API requires a 3-phase upload process for video stories: (1) START phase - initialize upload session and get `video_id` + `upload_url`, (2) UPLOAD phase - POST video to rupload.facebook.com endpoint with `file_url` header and OAuth authorization, (3) FINISH phase - finalize and publish story with `video_id`. Previous implementation incorrectly attempted direct upload to `/video_stories` endpoint with `file_url` parameter, causing "#100 The parameter upload_phase is required" error. Fixed by implementing proper 3-phase workflow using remote URL upload method (file_url header), avoiding need for binary chunked uploads. ### October 17, 2025 From b66591fce268aefe1a7600849659894aeb64f27c Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 28 Oct 2025 13:23:52 +0000 Subject: [PATCH 3/6] Improve layout for better display on larger screens Update CSS to adjust element sizing and positioning for improved responsiveness on desktop viewports. 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/aGnsIb5 From 1a16c71a415e591dc9aba10c81aab95236c34b23 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 28 Oct 2025 15:13:44 +0000 Subject: [PATCH 4/6] 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/aGnsIb5 From 6014c4e083500f43439d5dcba3c3dade37df35c9 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 28 Oct 2025 15:20:15 +0000 Subject: [PATCH 5/6] Improve security and filtering for scheduled posts across user pages Adds a `getScheduledPostsByPages` method to storage, enabling efficient filtering of scheduled posts by authorized page IDs. Updates API routes to correctly retrieve posts based on user roles (admin vs. standard user) and accessible pages, enhancing data security and user experience. 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/77tIrOr --- replit.md | 1 + server/routes.ts | 31 +++++++++++++++++-------------- server/storage.ts | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/replit.md b/replit.md index 8596f85..c33099b 100644 --- a/replit.md +++ b/replit.md @@ -11,6 +11,7 @@ Preferred communication style: Simple, everyday language. ## Recent Changes ### October 28, 2025 +- **Scheduled Posts Page-Level Filtering Security Fix**: Fixed security vulnerability where users could see occupied dates from all pages in the DateTimePicker, including pages they don't have access to. Added new storage method `getScheduledPostsByPages()` that filters scheduled posts directly in database using SQL WHERE IN clause with authorized page IDs. Modified GET `/api/scheduled-posts` endpoint to use getUserAccessiblePages for standard users (only their assigned pages) and all pages for admins. This eliminates temporary exposure of sensitive data in memory and improves performance by querying only authorized posts from the database. - **Interactive Calendar with Visual Indicators**: Replaced native datetime-local input with custom DateTimePicker component featuring visual planning aids. Calendar displays occupied dates with red background (bg-red-500/20) to prevent scheduling conflicts. Time selector highlights optimal posting hours (8h, 12h-13h, 18h-20h) with green background (bg-green-500/20) and sparkle emoji to maximize audience engagement. Component includes clear button (X icon) to quickly return to immediate publishing, useEffect synchronization for external state updates, and French locale support via date-fns. Time selector optimized for compactness: ScrollArea height reduced from 240px to 192px (20% smaller), button height set to 36px for touch accessibility, tighter padding and margins throughout. Mobile calendar list still displays today's date first for quick access. - **Facebook Video Story Publishing Fix**: Fixed critical bug preventing video story publication. Facebook Graph API requires a 3-phase upload process for video stories: (1) START phase - initialize upload session and get `video_id` + `upload_url`, (2) UPLOAD phase - POST video to rupload.facebook.com endpoint with `file_url` header and OAuth authorization, (3) FINISH phase - finalize and publish story with `video_id`. Previous implementation incorrectly attempted direct upload to `/video_stories` endpoint with `file_url` parameter, causing "#100 The parameter upload_phase is required" error. Fixed by implementing proper 3-phase workflow using remote URL upload method (file_url header), avoiding need for binary chunked uploads. diff --git a/server/routes.ts b/server/routes.ts index f772145..00a654d 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -991,24 +991,27 @@ export async function registerRoutes(app: Express): Promise { let scheduledPosts; if (user.role === 'admin') { - // Admin voit tous les posts programmés - const allUsers = await storage.getAllUsers(); - const allPostsPromises = allUsers.map(u => storage.getScheduledPosts(u.id, start, end)); - const allPostsArrays = await Promise.all(allPostsPromises); - scheduledPosts = allPostsArrays.flat(); + // Admin voit tous les posts programmés - on récupère toutes les pages + const allPages = await storage.getAllUsers().then(users => + Promise.all(users.map(u => storage.getSocialPages(u.id))) + ).then(pagesArrays => pagesArrays.flat()); + const allPageIds = allPages.map(p => p.id); + + if (allPageIds.length > 0) { + scheduledPosts = await storage.getScheduledPostsByPages(allPageIds, start, end); + } else { + scheduledPosts = []; + } } else { - // User voit tous les posts programmés sur les pages qui lui sont attribuées (peu importe qui les a créés) + // User voit uniquement les posts programmés sur les pages qui lui sont attribuées const accessiblePages = await storage.getUserAccessiblePages(userId); const accessiblePageIds = accessiblePages.map(p => p.id); - // Récupérer tous les posts programmés de tous les utilisateurs - const allUsers = await storage.getAllUsers(); - const allPostsPromises = allUsers.map(u => storage.getScheduledPosts(u.id, start, end)); - const allPostsArrays = await Promise.all(allPostsPromises); - const allScheduledPosts = allPostsArrays.flat(); - - // Filtrer uniquement les posts des pages accessibles - scheduledPosts = allScheduledPosts.filter(sp => accessiblePageIds.includes(sp.pageId)); + if (accessiblePageIds.length > 0) { + scheduledPosts = await storage.getScheduledPostsByPages(accessiblePageIds, start, end); + } else { + scheduledPosts = []; + } } res.json(scheduledPosts); diff --git a/server/storage.ts b/server/storage.ts index 557c5e9..65166b4 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -30,7 +30,7 @@ import { type InsertUserPagePermission, } from "@shared/schema"; import { db } from "./db"; -import { eq, and, gte, lte, desc, asc, isNull } from "drizzle-orm"; +import { eq, and, gte, lte, desc, asc, isNull, inArray } from "drizzle-orm"; export interface IStorage { // Users @@ -66,6 +66,7 @@ export interface IStorage { // Scheduled Posts getScheduledPosts(userId: string, startDate?: Date, endDate?: Date): Promise; + getScheduledPostsByPages(pageIds: string[], startDate?: Date, endDate?: Date): Promise; getScheduledPost(id: string): Promise; getScheduledPostsByPost(postId: string): Promise; createScheduledPost(scheduledPost: InsertScheduledPost): Promise; @@ -260,6 +261,40 @@ export class DatabaseStorage implements IStorage { })); } + async getScheduledPostsByPages(pageIds: string[], startDate?: Date, endDate?: Date): Promise { + if (pageIds.length === 0) { + return []; + } + + let query = db + .select() + .from(scheduledPosts) + .innerJoin(posts, eq(scheduledPosts.postId, posts.id)) + .leftJoin(socialPages, eq(scheduledPosts.pageId, socialPages.id)) + .where(inArray(scheduledPosts.pageId, pageIds)); + + if (startDate && endDate) { + const results = await query; + return results + .filter(r => { + const scheduledAt = new Date(r.scheduled_posts.scheduledAt); + return scheduledAt >= startDate && scheduledAt <= endDate; + }) + .map(r => ({ + ...r.scheduled_posts, + post: r.posts, + page: r.social_pages, + })); + } + + const results = await query; + return results.map(r => ({ + ...r.scheduled_posts, + post: r.posts, + page: r.social_pages, + })); + } + async getScheduledPost(id: string): Promise { const [scheduledPost] = await db.select().from(scheduledPosts).where(eq(scheduledPosts.id, id)); return scheduledPost || undefined; From c7c8af78b5493e7ad6c273e85a809665e59f8e71 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Tue, 28 Oct 2025 15:21:08 +0000 Subject: [PATCH 6/6] Add a secure way for users to log into their accounts Implement JWT authentication and authorization 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/77tIrOr