You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Focus: backend architecture, security, and performance — but with deliberate frontend work so you keep building T-shaped full-stack skills. Each item pairs a backend improvement with a frontend deliverable or uses a frontend page as the user-facing surface for a backend change.
1. Refactor the monolithic intakeForm.ctrl.ts into focused controllers and services
Servers/controllers/intakeForm.ctrl.ts is over 2,000 lines and mixes admin CRUD, public form rendering, CAPTCHA, submissions, LLM question generation, risk scoring, email notifications, and entity mapping. Split it into intakeFormAdmin.ctrl.ts, intakeFormPublic.ctrl.ts, and intakeFormLLM.ctrl.ts, and move the business rules into service modules under Servers/services/intakeForms/. Keep controllers thin: validate, call service, return response.
On the frontend, the public intake form currently has limited validation feedback. While you are moving the backend logic, update the public intake renderer in Clients/src/presentation/pages/PublicIntake to show inline field errors returned by the new validation layer and to disable submission until required fields are valid. This makes the refactor visible to end users.
Acceptance: Controller shrinks by at least 40%; new services have unit tests; public form shows backend validation errors; existing intake flows still pass.
2. Finish the tenantId to organizationId migration
Servers/middleware/auth.middleware.ts still carries an explicit TODO to remove req.tenantId once all usages are migrated. Audit the entire Servers/ tree for req.tenantId and tenantId SQL replacements, migrate them to req.organizationId, and remove the fallback assignment and the dynamic require("../tools/getTenantHash"). Also clean up deprecated helpers in Servers/utils/getTenantHash.ts, Servers/utils/security.utils.ts, and Servers/utils/cache.utils.ts once callers are gone.
On the frontend, the authenticated request layer still sends x-tenant-id in some API calls. Update Clients/src/infrastructure/api/customAxios.ts and any explicit headers to use x-organization-id consistently, and remove any tenantId references from the frontend auth types.
Acceptance: Zero tenantId references remain outside migration scripts; auth middleware uses only static imports; frontend headers use x-organization-id; all tests pass.
3. Implement refresh-token rotation with a revocation store
Servers/utils/auth.utils.ts issues a 30-day refresh-token cookie with no rotation or server-side revocation list. Implement refresh-token rotation: when a refresh token is used, issue a new access token and a new refresh token, invalidate the old one, and store the family in Redis or a dedicated database table. Add an endpoint for users to revoke all active sessions from the settings page.
On the frontend, build a "Active Sessions" card in Clients/src/presentation/pages/SettingsPage/Security that lists current sessions (device/browser/last active) and lets the user revoke them. Use the new backend endpoint and update the auth context to clear local state when the server rejects a revoked refresh token.
Acceptance: Refresh-token rotation works end-to-end; revocation store persists across restarts; active-sessions UI lists and revokes sessions; expired/revoked tokens force re-login.
4. Add Redis-backed rate limiting and a frontend status indicator
Servers/middleware/rateLimit.middleware.ts uses the default in-memory MemoryStore, which does not scale horizontally and resets on restart. Replace it with a RedisStore backed by the existing Redis connection. Add separate rate-limit tiers for public endpoints, authenticated API calls, and share-link token guesses.
On the frontend, add a small rate-limit status indicator in the global error toast system so users see a friendly "Too many requests — please wait N seconds" message when the backend returns 429. Update customAxios to read the Retry-After header and display the cooldown.
Acceptance: Rate limits are enforced from Redis; limits survive restart; frontend shows cooldown on 429; tests verify Redis-backed limiting.
5. Add route-level validation to user, project, and risk routes
user.route.ts, project.route.ts, and risks.route.ts parse bodies and parameters manually, leading to inconsistent 400 responses. Add express-validator chains (or a Zod middleware layer) for create, update, list filters, and path parameters. Centralize the validation-error handler so it always returns STATUS_CODE[400] with a field-level message array.
On the frontend, update the user, project, and risk forms to consume the field-level errors and disable submit until client-side validation passes. Use the existing Field and Select components and make sure screen readers announce server-side errors.
Acceptance: All three routes reject malformed payloads consistently; frontend forms display inline errors; no manual parseInt without radix remains; happy-path tests pass.
6. Build an object-storage abstraction for file uploads
Servers/repositories/file.repository.ts currently inserts the entire file buffer via raw SQL into the files table. This is a memory and size bottleneck. Introduce an object-storage abstraction (Servers/services/storage/storageService.ts) that supports a local filesystem adapter for dev and an S3-compatible adapter for production. Store metadata in the database and the blob in object storage, with streaming uploads and strict per-file and total-size limits.
On the frontend, update the file-manager upload flow to show upload progress, chunked retry on network failure, and clear error messages when size limits are exceeded. Use the existing MUI LinearProgress and the standardized toast system.
Acceptance: Large files stream instead of buffering in memory; local and S3 adapters tested; frontend shows progress and errors; existing file downloads still work.
7. Make ModelInventory responsive and mobile-usable
Clients/src/presentation/pages/ModelInventory/index.tsx and Clients/src/presentation/components/Sidebar/SidebarShell.tsx overflow on small viewports. Wrap the inventory table in a horizontal-scroll container, replace fixed sidebar widths with breakpoint-aware Grid/Stack, and add a mobile filter drawer. Use MUI useMediaQuery and the theme breakpoints rather than hardcoded widths.
On the backend, the model inventory list endpoint returns a flat payload that is expensive to reshape on mobile. Add optional ?fields= and ?compact=true query parameters so mobile clients can request a lighter response, and use the existing pagination helpers to cap page size.
Acceptance: No horizontal scroll on 375 px and 768 px viewports; mobile filter drawer works; backend compact endpoint tested; responsive tests added.
8. Harden the JWT middleware and remove dynamic requires
Servers/middleware/auth.middleware.ts dynamically requires getTenantHash, accepts any token payloads, and uses a hardcoded role map. Replace the dynamic require with a top-level ES import, enforce string types in Servers/utils/jwt.utils.ts, and load the role map from the database roles table at startup so new roles do not require code changes. Also pass explicit algorithms: ["HS256"] to jwt.verify.
On the frontend, update the ProtectedRoute component to use the same role map loaded from a GET /api/roles endpoint rather than a hardcoded list. Add a <RequireRole> route wrapper and remove inline role checks from pages.
Acceptance: Auth middleware has no dynamic requires; role changes in DB reflect automatically; frontend RequireRole wrapper works; JWT algorithm is explicit.
9. Move AI detection scans to a BullMQ queue with progress persistence
Servers/services/aiDetection.service.ts currently spawns executeScan inline in the web process. Long scans block API capacity and are lost if the process crashes. Move scan execution to a BullMQ queue with a dedicated worker, persist job progress in Redis, and expose a GET /api/ai-detection/scans/:id/progress endpoint.
On the frontend, update the scan detail page to poll the progress endpoint and show a real progress bar, elapsed time, and cancellation button. Replace any in-memory progress state with the server-side job state.
Acceptance: Scans run outside the web process; progress persists across restarts; frontend shows live progress and allows cancellation; worker tests pass.
10. Migrate PolicyEditorPage theme tokens and add an ESLint rule
Clients/src/presentation/pages/PolicyDashboard/PolicyEditorPage.tsx contains 72 hardcoded color literals and many magic spacing values. Migrate the top 30 repeated literals to the MUI theme palette or a shared theme.tokens.ts file. Add an ESLint rule (or use an existing MUI best-practice config) that forbids new hardcoded hex/rgb colors and inline style={{...}} blocks in Clients/src/presentation/.
On the backend, the policy save endpoint returns only a success message. Update it to return the saved policy object so the frontend can update its local state without a full refetch, and add a server-side sanitization call before persistence.
Acceptance: Hardcoded color count in PolicyEditorPage drops to zero; ESLint rule blocks new literals in CI; backend returns updated policy; no visual regressions.
Harsh — Backend Lead 2nd Task List
1. Refactor the monolithic
intakeForm.ctrl.tsinto focused controllers and servicesServers/controllers/intakeForm.ctrl.tsis over 2,000 lines and mixes admin CRUD, public form rendering, CAPTCHA, submissions, LLM question generation, risk scoring, email notifications, and entity mapping. Split it intointakeFormAdmin.ctrl.ts,intakeFormPublic.ctrl.ts, andintakeFormLLM.ctrl.ts, and move the business rules into service modules underServers/services/intakeForms/. Keep controllers thin: validate, call service, return response.On the frontend, the public intake form currently has limited validation feedback. While you are moving the backend logic, update the public intake renderer in
Clients/src/presentation/pages/PublicIntaketo show inline field errors returned by the new validation layer and to disable submission until required fields are valid. This makes the refactor visible to end users.Acceptance: Controller shrinks by at least 40%; new services have unit tests; public form shows backend validation errors; existing intake flows still pass.
2. Finish the
tenantIdtoorganizationIdmigrationServers/middleware/auth.middleware.tsstill carries an explicit TODO to removereq.tenantIdonce all usages are migrated. Audit the entireServers/tree forreq.tenantIdandtenantIdSQL replacements, migrate them toreq.organizationId, and remove the fallback assignment and the dynamicrequire("../tools/getTenantHash"). Also clean up deprecated helpers inServers/utils/getTenantHash.ts,Servers/utils/security.utils.ts, andServers/utils/cache.utils.tsonce callers are gone.On the frontend, the authenticated request layer still sends
x-tenant-idin some API calls. UpdateClients/src/infrastructure/api/customAxios.tsand any explicit headers to usex-organization-idconsistently, and remove anytenantIdreferences from the frontend auth types.Acceptance: Zero
tenantIdreferences remain outside migration scripts; auth middleware uses only static imports; frontend headers usex-organization-id; all tests pass.3. Implement refresh-token rotation with a revocation store
Servers/utils/auth.utils.tsissues a 30-day refresh-token cookie with no rotation or server-side revocation list. Implement refresh-token rotation: when a refresh token is used, issue a new access token and a new refresh token, invalidate the old one, and store the family in Redis or a dedicated database table. Add an endpoint for users to revoke all active sessions from the settings page.On the frontend, build a "Active Sessions" card in
Clients/src/presentation/pages/SettingsPage/Securitythat lists current sessions (device/browser/last active) and lets the user revoke them. Use the new backend endpoint and update the auth context to clear local state when the server rejects a revoked refresh token.Acceptance: Refresh-token rotation works end-to-end; revocation store persists across restarts; active-sessions UI lists and revokes sessions; expired/revoked tokens force re-login.
4. Add Redis-backed rate limiting and a frontend status indicator
Servers/middleware/rateLimit.middleware.tsuses the default in-memoryMemoryStore, which does not scale horizontally and resets on restart. Replace it with aRedisStorebacked by the existing Redis connection. Add separate rate-limit tiers for public endpoints, authenticated API calls, and share-link token guesses.On the frontend, add a small rate-limit status indicator in the global error toast system so users see a friendly "Too many requests — please wait N seconds" message when the backend returns
429. UpdatecustomAxiosto read theRetry-Afterheader and display the cooldown.Acceptance: Rate limits are enforced from Redis; limits survive restart; frontend shows cooldown on
429; tests verify Redis-backed limiting.5. Add route-level validation to user, project, and risk routes
user.route.ts,project.route.ts, andrisks.route.tsparse bodies and parameters manually, leading to inconsistent 400 responses. Addexpress-validatorchains (or a Zod middleware layer) for create, update, list filters, and path parameters. Centralize the validation-error handler so it always returnsSTATUS_CODE[400]with a field-level message array.On the frontend, update the user, project, and risk forms to consume the field-level errors and disable submit until client-side validation passes. Use the existing
FieldandSelectcomponents and make sure screen readers announce server-side errors.Acceptance: All three routes reject malformed payloads consistently; frontend forms display inline errors; no manual
parseIntwithout radix remains; happy-path tests pass.6. Build an object-storage abstraction for file uploads
Servers/repositories/file.repository.tscurrently inserts the entire file buffer via raw SQL into thefilestable. This is a memory and size bottleneck. Introduce an object-storage abstraction (Servers/services/storage/storageService.ts) that supports a local filesystem adapter for dev and an S3-compatible adapter for production. Store metadata in the database and the blob in object storage, with streaming uploads and strict per-file and total-size limits.On the frontend, update the file-manager upload flow to show upload progress, chunked retry on network failure, and clear error messages when size limits are exceeded. Use the existing MUI
LinearProgressand the standardized toast system.Acceptance: Large files stream instead of buffering in memory; local and S3 adapters tested; frontend shows progress and errors; existing file downloads still work.
7. Make
ModelInventoryresponsive and mobile-usableClients/src/presentation/pages/ModelInventory/index.tsxandClients/src/presentation/components/Sidebar/SidebarShell.tsxoverflow on small viewports. Wrap the inventory table in a horizontal-scroll container, replace fixed sidebar widths with breakpoint-awareGrid/Stack, and add a mobile filter drawer. Use MUIuseMediaQueryand the theme breakpoints rather than hardcoded widths.On the backend, the model inventory list endpoint returns a flat payload that is expensive to reshape on mobile. Add optional
?fields=and?compact=truequery parameters so mobile clients can request a lighter response, and use the existing pagination helpers to cap page size.Acceptance: No horizontal scroll on 375 px and 768 px viewports; mobile filter drawer works; backend compact endpoint tested; responsive tests added.
8. Harden the JWT middleware and remove dynamic requires
Servers/middleware/auth.middleware.tsdynamically requiresgetTenantHash, acceptsanytoken payloads, and uses a hardcoded role map. Replace the dynamic require with a top-level ES import, enforcestringtypes inServers/utils/jwt.utils.ts, and load the role map from the databaserolestable at startup so new roles do not require code changes. Also pass explicitalgorithms: ["HS256"]tojwt.verify.On the frontend, update the
ProtectedRoutecomponent to use the same role map loaded from aGET /api/rolesendpoint rather than a hardcoded list. Add a<RequireRole>route wrapper and remove inline role checks from pages.Acceptance: Auth middleware has no dynamic requires; role changes in DB reflect automatically; frontend
RequireRolewrapper works; JWT algorithm is explicit.9. Move AI detection scans to a BullMQ queue with progress persistence
Servers/services/aiDetection.service.tscurrently spawnsexecuteScaninline in the web process. Long scans block API capacity and are lost if the process crashes. Move scan execution to a BullMQ queue with a dedicated worker, persist job progress in Redis, and expose aGET /api/ai-detection/scans/:id/progressendpoint.On the frontend, update the scan detail page to poll the progress endpoint and show a real progress bar, elapsed time, and cancellation button. Replace any in-memory progress state with the server-side job state.
Acceptance: Scans run outside the web process; progress persists across restarts; frontend shows live progress and allows cancellation; worker tests pass.
10. Migrate
PolicyEditorPagetheme tokens and add an ESLint ruleClients/src/presentation/pages/PolicyDashboard/PolicyEditorPage.tsxcontains 72 hardcoded color literals and many magic spacing values. Migrate the top 30 repeated literals to the MUI theme palette or a sharedtheme.tokens.tsfile. Add an ESLint rule (or use an existing MUI best-practice config) that forbids new hardcoded hex/rgb colors and inlinestyle={{...}}blocks inClients/src/presentation/.On the backend, the policy save endpoint returns only a success message. Update it to return the saved policy object so the frontend can update its local state without a full refetch, and add a server-side sanitization call before persistence.
Acceptance: Hardcoded color count in
PolicyEditorPagedrops to zero; ESLint rule blocks new literals in CI; backend returns updated policy; no visual regressions.