Merge pull request #55 from R0m1k3/dev

Dev
This commit is contained in:
LogiFlow authored and GitHub committed 2025-10-10 11:37:41 +02:00
commit 3ca7932d9e
3 files changed
+49 -5

No files matched your search

+8
View File
@@ -14,6 +14,10 @@ run = ["npm", "run", "start"]
localPort = 5000 localPort = 5000
externalPort = 80 externalPort = 80
[[ports]]
localPort = 33821
externalPort = 8099
[[ports]] [[ports]]
localPort = 34045 localPort = 34045
externalPort = 3003 externalPort = 3003
@@ -46,6 +50,10 @@ externalPort = 8081
localPort = 40537 localPort = 40537
externalPort = 3000 externalPort = 3000
[[ports]]
localPort = 41803
externalPort = 8008
[[ports]] [[ports]]
localPort = 42161 localPort = 42161
externalPort = 6000 externalPort = 6000
+3
View File
@@ -11,6 +11,9 @@ Preferred communication style: Simple, everyday language.
## Recent Changes ## Recent Changes
### October 10, 2025 ### October 10, 2025
- **Calendar Permissions System**: Implemented comprehensive page-based permissions for calendar access. Admin users see all scheduled posts with full edit/delete rights. Standard users see only posts from their assigned pages via `user_page_permissions` table, but can only edit/delete their own posts (ownership check via `post.userId`). GET /api/scheduled-posts filters posts by accessible pages (admin bypass). DELETE and PATCH /api/scheduled-posts enforce ownership validation (admin can modify any post, users limited to own posts).
- **Scheduled Posts Data Sync Fix**: Fixed critical bug where editing scheduled post time via PATCH /api/scheduled-posts/:id updated `scheduled_posts.scheduledAt` but not `posts.scheduledFor`, causing calendar/database inconsistency. Now both tables are synchronized: updating scheduledAt automatically updates scheduledFor via `storage.updatePost()`.
- **Scheduled Posts Validation**: Added validation to POST /api/posts requiring `pageIds` when `scheduledFor` is provided. Prevents orphaned scheduled posts that would be invisible in calendar. Returns 400 error: "Les posts programmés nécessitent au moins une page cible".
- **AI Assistant Admin-Only Access**: Restricted access to AI Assistant to administrators only. Route `/ai` now requires admin role with `adminOnly` prop. Sidebar moved "Assistant IA" link to Administration section (visible only to admins). Backend endpoints (`/api/ai/models`, `/api/ai/generate`, `/api/ai/generations`) protected with `requireAdmin` middleware. Standard users attempting to access AI features see "Accès refusé" message and receive 403 responses. - **AI Assistant Admin-Only Access**: Restricted access to AI Assistant to administrators only. Route `/ai` now requires admin role with `adminOnly` prop. Sidebar moved "Assistant IA" link to Administration section (visible only to admins). Backend endpoints (`/api/ai/models`, `/api/ai/generate`, `/api/ai/generations`) protected with `requireAdmin` middleware. Standard users attempting to access AI features see "Accès refusé" message and receive 403 responses.
### October 9, 2025 ### October 9, 2025
+38 -5
View File
@@ -611,6 +611,11 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Set status to "scheduled" if scheduledFor is provided, otherwise "draft" // Set status to "scheduled" if scheduledFor is provided, otherwise "draft"
if (postFields.scheduledFor) { if (postFields.scheduledFor) {
postFields.status = "scheduled"; postFields.status = "scheduled";
// Validate that scheduled posts require at least one page
if (!pageIds || !Array.isArray(pageIds) || pageIds.length === 0) {
return res.status(400).json({ error: "Les posts programmés nécessitent au moins une page cible" });
}
} }
// Create the post // Create the post
@@ -758,7 +763,23 @@ export async function registerRoutes(app: Express): Promise<Server> {
const start = startDate ? new Date(startDate as string) : undefined; const start = startDate ? new Date(startDate as string) : undefined;
const end = endDate ? new Date(endDate as string) : undefined; const end = endDate ? new Date(endDate as string) : undefined;
const scheduledPosts = await storage.getScheduledPosts(userId, start, end); 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();
} else {
// User voit uniquement les posts des pages qui lui sont attribuées
const accessiblePages = await storage.getUserAccessiblePages(userId);
const accessiblePageIds = accessiblePages.map(p => p.id);
const userScheduledPosts = await storage.getScheduledPosts(userId, start, end);
scheduledPosts = userScheduledPosts.filter(sp => accessiblePageIds.includes(sp.pageId));
}
res.json(scheduledPosts); res.json(scheduledPosts);
} catch (error) { } catch (error) {
console.error("Error fetching scheduled posts:", error); console.error("Error fetching scheduled posts:", error);
@@ -772,14 +793,19 @@ export async function registerRoutes(app: Express): Promise<Server> {
const userId = user.id; const userId = user.id;
const { id } = req.params; const { id } = req.params;
// Verify the scheduled post belongs to the user before deleting // Verify the scheduled post exists
const scheduledPost = await storage.getScheduledPost(id); const scheduledPost = await storage.getScheduledPost(id);
if (!scheduledPost) { if (!scheduledPost) {
return res.status(404).json({ error: "Scheduled post not found" }); return res.status(404).json({ error: "Scheduled post not found" });
} }
const post = await storage.getPost(scheduledPost.postId); const post = await storage.getPost(scheduledPost.postId);
if (!post || post.userId !== userId) { if (!post) {
return res.status(404).json({ error: "Post not found" });
}
// Admin peut tout supprimer, user peut supprimer uniquement ses propres posts
if (user.role !== 'admin' && post.userId !== userId) {
return res.status(403).json({ error: "Unauthorized" }); return res.status(403).json({ error: "Unauthorized" });
} }
@@ -797,14 +823,19 @@ export async function registerRoutes(app: Express): Promise<Server> {
const userId = user.id; const userId = user.id;
const { id } = req.params; const { id } = req.params;
// Verify the scheduled post belongs to the user before updating // Verify the scheduled post exists
const scheduledPost = await storage.getScheduledPost(id); const scheduledPost = await storage.getScheduledPost(id);
if (!scheduledPost) { if (!scheduledPost) {
return res.status(404).json({ error: "Scheduled post not found" }); return res.status(404).json({ error: "Scheduled post not found" });
} }
const post = await storage.getPost(scheduledPost.postId); const post = await storage.getPost(scheduledPost.postId);
if (!post || post.userId !== userId) { if (!post) {
return res.status(404).json({ error: "Post not found" });
}
// Admin peut tout modifier, user peut modifier uniquement ses propres posts
if (user.role !== 'admin' && post.userId !== userId) {
return res.status(403).json({ error: "Unauthorized" }); return res.status(403).json({ error: "Unauthorized" });
} }
@@ -814,6 +845,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
if (scheduledAt) { if (scheduledAt) {
updateData.scheduledAt = new Date(scheduledAt); updateData.scheduledAt = new Date(scheduledAt);
// Synchroniser avec la table posts
await storage.updatePost(scheduledPost.postId, { scheduledFor: new Date(scheduledAt) });
} }
if (pageId) { if (pageId) {