DM Portal App Code Review — v1
Scope: apps/dm/src/ — middleware, lib, server actions, API routes, layouts, all pages, components
Date: 2026-04-17
Reviewer: Claude Sonnet 4.6
Status: 12/14 findings resolved — 2 deferred (require backend changes)
Open — Deferred
H-3 — MediaLibrary Uploads Files as Base64 JSON
File: src/components/media/MediaLibrary.tsx:102–130
The base64 JSON upload approach is still in place. Fixing it requires the API server’s media endpoint to accept multipart/form-data instead of JSON. The client-side fix is straightforward once the backend supports it:
async function handleUpload(file: File) {
const form = new FormData();
form.append("file", file);
form.append("tenantId", tenantId);
const res = await fetch(apiBaseUrl, { method: "POST", body: form });
}Update the proxy route to forward FormData using request.formData() instead of request.json().
M-1 — Socket Token Exposed as Plaintext JSON
File: src/app/api/socket-token/route.ts
The /api/socket-token route still returns the dm_access_token JWT in the response body, making it readable by any XSS that can call fetch("/api/socket-token").
The preferred fix is to authenticate the Socket.IO handshake server-side by forwarding the httpOnly cookie directly:
// socket.ts — pass credentials instead of a bearer token
socket = io(SOCKET_URL + "/tenant", {
withCredentials: true,
autoConnect: false,
});Requires changes to the socket server’s auth middleware in apps/api to read and verify dm_access_token from socket.handshake.headers.cookie. Remove GET /api/socket-token once done.
Strengths
- Auth architecture is solid:
auth-cookies.tscentralises all cookie security settings server-auth.tsrequireAuth()is a clean single-source-of-truth- Role-based access enforced at the layout level
- Server Component data-fetching pattern is correct
- Proxy API routes are consistent
setAuthCookiesshared helper- Tenant switching is designed safely
DmPayloadSchemaZod validation- Deliverable plan editing is fully optimistic
- Test coverage is well-structured