Classify is organized as a pnpm monorepo containing a React 19 frontend, an Express 5 TypeScript backend, and firmware for an ESP32-C3 hardware device — all sharing a single repository root. The Supabase platform provides authentication, a PostgreSQL database, and Row-Level Security policies that enforce data access rules at the database layer, independent of application logic.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Taykl12/Classify/llms.txt
Use this file to discover all available pages before exploring further.
Repository Layout
The directory tree below shows the top-level structure of the monorepo. The React source lives insrc/, the Express server in server/src/, Supabase migration SQL files in supabase/migrations/, and the ESP32 firmware in esp32/.
Frontend — React + Vite
The frontend is a single-page application bootstrapped with Vite 6 and written in React 19 + TypeScript. Routing is handled byreact-router-dom; the full set of route path strings is centralised in src/routes.ts as the ROUTES constant, preventing hard-coded strings from spreading across the codebase.
Styling uses vanilla CSS with a variables.css design-token file. There is no CSS framework or utility-class library — all component styles reference custom properties (e.g. --color-primary, --spacing-md) defined in that token file.
Global providers are composed in App.tsx in the following order:
AuthContext exposes the current user, the raw JWT, and the logout helper. ThemeContext manages the light/dark theme preference persisted to localStorage.
API Client and the Vite Proxy
All HTTP calls from the frontend go throughsrc/lib/api.ts, which exposes two functions:
apiFetch(path, options?)— attaches theAuthorization: Bearer <token>header fromlocalStorage.classify_access_token, callsfetch, and triggers an automatic logout + redirect to/loginon a401response.apiFetchWithRetry(path, options?, retries = 3)— wrapsapiFetchand re-attempts the request up to three times when the server returns502,503, or504(gateway/upstream errors that are transient in nature).
vite.config.ts declares a proxy rule that rewrites any request starting with /api to http://localhost:3001. This means the React app always calls relative URLs like /api/auth/login, and Vite silently forwards them to Express — no CORS headers are involved in the local dev flow.
Backend — Express 5
The Express application is defined inserver/src/app.ts and started in server/src/index.ts. All configuration is loaded from environment variables via server/src/config.ts, which exports typed constants (SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, PORT, APP_ORIGIN, ESP32_DEVICE_TOKEN).
Middleware Chain
Most incoming requests pass through the following layers in order:- CORS —
APP_ORIGINis the only allowed origin; credentials are permitted. /api/device/esp32— the ESP32 device router is mounted here, beforeexpress.json(), so it handles raw device payloads independently.express.json()— parses JSON request bodies for all other routes.- Route-specific middleware — applied per-router, not globally:
auth.ts— verifies theAuthorization: BearerJWT with Supabase and attaches the decoded user toreq.user.admin.ts— rejects requests wherereq.user.role !== 1.professor.ts— rejects requests wherereq.user.role > 2.
Route Modules
| Module | Mount path | Protection |
|---|---|---|
auth.ts | /api/auth | Public |
dashboard.ts | /api/dashboard | Auth required |
projects.ts | /api/projects | Auth required |
tasks.ts | /api/tasks | Auth required |
calendar.ts | /api/calendar | Auth required |
professor.ts | /api/professor | Professor + Admin |
admin.ts | /api/admin | Admin only |
esp32Device.ts | /api/device/esp32 | Device token |
users.ts | /api/users | Auth required |
profile.ts | /api/profile | Auth required |
adminProjects.ts | /api/admin/proyectos | Admin only |
ESP32 Device Layer
Theesp32Device.ts route and esp32State.ts library handle communication with the ESP32-C3 SuperMini hardware. The device authenticates using the ESP32_DEVICE_TOKEN shared secret (passed as a header), polls for pending attendance commands, and posts back scan results. esp32State.ts maintains in-memory state on the server representing the device’s last-known status and pending operations.
Database — Supabase
Classify uses Supabase as both its authentication provider and its PostgreSQL database host. The project reference isjgrtmokyqdvdxsldmkou.
The
supabase/migrations/ directory contains 15 SQL migration files (numbered 001 through 015). They must be applied in order to create the full schema — including tables for users, courses, subjects, projects, tasks, attendance records, and calendar events — along with all RLS policies. Use the Supabase CLI (supabase db push) or apply them manually in the SQL editor.Supabase Client Strategy
server/src/lib/supabase.ts exports two factory functions that are used throughout the server:
createUserClient(req.user.accessToken) so that Supabase RLS policies see the correct auth.uid() and evaluate row visibility accordingly. Admin routes that need to bypass RLS use a service-role client initialized with SUPABASE_SERVICE_ROLE_KEY.
Row-Level Security
All database tables have RLS enabled. Policies are expressed in SQL usingauth.uid() and auth.jwt() ->> 'role' to restrict SELECT, INSERT, UPDATE, and DELETE operations. A student, for example, can only select rows in the attendance table where their user ID matches — the restriction lives in the database, not only in Express middleware.
Role System
Roles are stored as integer values in theusers table and carried inside the JWT issued by Supabase on login.
| Role name | Integer | Express middleware | Accessible routes |
|---|---|---|---|
admin | 1 | admin.ts | All routes |
profesor | 2 | professor.ts | All except admin |
alumno | 3 | (auth only) | Student-facing routes |
server/src/lib/roles.ts exports helpers for checking role membership, and the admin.ts / professor.ts middleware files use those helpers to short-circuit unauthorized requests with a 403 Forbidden response before the route handler is ever invoked.
Authentication Flow
The sequence below describes what happens from the moment a user submits the login form to the moment a protected API response is returned.401 at any point, apiFetch on the client side removes classify_access_token from localStorage and redirects the user to /login, ensuring stale or revoked tokens never accumulate silently.