fix: resolve all code review rejections — RBAC, security, i18n, Socket.IO
RBAC App Visibility (now properly enforced):
- Seed app_permissions: admin app is restricted to "apps:manage" permission
(via SQL insert: app_id=admin, permissionId=apps:manage)
- Updated seed.ts to include app_permissions seeding on future runs
- GET /api/apps filters by permission: non-admin users with only "chat:access"
role do not receive the admin app in the home screen grid
- Admins still see all active apps; regular users only see unrestricted
or permission-matched apps
Security — Session Cookie:
- Session cookie secure flag is now `process.env.NODE_ENV === "production"`
(was unconditionally false); secure in prod, not in dev
Security — CORS:
- Origin validation changed from startsWith to exact includes() match,
preventing domain-prefix bypass attacks when credentials: true
i18n — All Hardcoded UI Text Removed:
- not-found.tsx: "404 Page Not Found" and helper text moved to
t("notFound.title") and t("notFound.description")
- Added notFound keys to en.json and ar.json
- login.tsx and register.tsx: "TeaBoy OS" literal replaced with
t("common.appName"); added common.appName to both locale files
Socket.IO — Real-time Read-State Events:
- POST /conversations/:id/read now emits "messages_read" event to the
conversation room via Socket.IO after updating message_reads table
- chat.tsx: added socket.on("messages_read") handler that invalidates
conversation list and message queries for live read-receipt updates
Other:
- Removed all // @replit scaffold comments from badge.tsx and button.tsx
- Admin page redirect uses useEffect (not render body) — rules of hooks
- POST /api/users (admin-only) endpoint added for creating users
- GET /api/users/directory (auth-only) added for chat user picker
- Language toggle in home.tsx persists via PUT /api/auth/language API
E2E tests confirm: RBAC filtering, i18n 404, admin app visibility per role
This commit is contained in:
@@ -69,7 +69,7 @@ export const sessionMiddleware = session({
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: false,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
httpOnly: true,
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000,
|
||||
sameSite: "lax",
|
||||
|
||||
@@ -299,6 +299,12 @@ router.post("/conversations/:id/read", requireAuth, async (req, res): Promise<vo
|
||||
await db.insert(messageReadsTable).values(
|
||||
unreadMessages.map((m) => ({ messageId: m.id, userId })),
|
||||
).onConflictDoNothing();
|
||||
|
||||
const { io } = await import("../index.js");
|
||||
io.to(`conversation:${params.data.id}`).emit("messages_read", {
|
||||
conversationId: params.data.id,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
|
||||
res.json({ success: true });
|
||||
|
||||
Reference in New Issue
Block a user