Architecture
edtech4good is four repositories that together deliver a learning management system: a central cloud API, a classroom edge API that runs on a Raspberry Pi or any Linux box on the school network, an Angular admin and teacher web UI, and a learner app built with Expo for Android, iOS and the web.
Repository roles
| Repository | Role |
|---|---|
| edtech-lms-api | Central NestJS API. Schools, curriculum, users, object storage for media, /sync/* to package curriculum for a classroom server, /log/import to ingest a classroom activity log (off by default), JWT auth, and POST /auth/school/login for teacher and staff login. |
| edtech-lms-ui | Angular 21 admin, teacher and student web UI. Talks to the central API via environment.API_URL. |
| edtech-lms-rpi-api | Classroom NestJS API. Local MySQL, lessons, progress and quizzes, GET /export/log, PUT /import/master to receive a curriculum package, and student and teacher auth. |
| edtech-expo | Expo app (React Native and web). Two HTTP bases: EXPO_PUBLIC_BASE_URL, usually the classroom server, and EXPO_PUBLIC_SYNC_URL, usually the central API. |
A legacy native Android app and a duplicate reporting UI were retired and are not part of the current stack.
End-to-end diagram
Runtime dependencies
| Component | Depends on |
|---|---|
| Central API | MySQL, an S3-compatible object store for media, SMTP (optional), configured via FORTYKAPICONFIG or plain env vars in src/config.ts |
| Admin/teacher/student UI | The central API reachable from the browser; API_URL and SCHEMA in environment.*.ts point at it |
| Classroom API | MySQL, configured via FORTYKAPIRPICONFIG or RPI_* env vars in src/config.ts |
| Learner app | Network reachability to the configured classroom and central hosts |
Ports and API docs
| Service | Default HTTP port | Swagger / OpenAPI UI |
|---|---|---|
| Central API | 3000 (PORT) | /docs, see src/server.ts |
| Classroom API | 3000 (RPI_PORT) | /docs, see src/server.ts |
| Admin/teacher/student UI (dev) | 4200 | none |
Both APIs default to port 3000 because they normally run on separate machines: the central API in the cloud, the classroom API on the Pi.
Both APIs mount Swagger only when NODE_ENV is development or test. In any other environment /docs is not served.
Expo environment matrix
| Variable | Typical classroom use | Typical online / sync use |
|---|---|---|
EXPO_PUBLIC_BASE_URL | Classroom server URL | Same classroom URL, or the central API if the deployment is unified |
EXPO_PUBLIC_SYNC_URL | Central API base URL | Central API base URL |
EXPO_PUBLIC_RESOURCE_URL / EXPO_PUBLIC_RESOURCE_PATH | Local or remote media location used by useResource | Same |
EXPO_PUBLIC_ACCESS_TYPE | offline or online, read by useResource and isOnlineOnly() to switch resource loading | Same |
HTTP clients live in src/services/api/Api.ts: one instance is built against EXPO_PUBLIC_BASE_URL, the other against EXPO_PUBLIC_SYNC_URL.
Sync and data flow
A. Cloud to classroom: curriculum and master content
- An admin on the central UI prepares content.
- Server-side push: the central API's
POST /sync/cloudbuilds a zip andPUTs it to the classroom server's/import/master, authenticated withSERVER_SYNC_KEY. See sync.controller.ts. - Client-side path: a client can download a zip from the central API's
GET /sync/contentandPUTit to the classroom server'sPUT /import/master; the current learner app does not call either. That route always accepts the server sync key as the rawAuthorizationheader, and accepts an admin, superadmin or teacher token only when the classroom server runs withRPI_OFFLINE=true. See import.controller.ts.
B. Classroom to cloud: student and activity logs
- On the classroom server,
GET /export/logreturns a zip of activity logs. See export.controller.ts. - A client can download that zip from the classroom server and upload it to the central API's
PUT /log/import. See log.controller.ts. This upload route is off unlessLOG_IMPORT_ENABLEDistrueor1, and the current learner app does not call it.
C. Teacher and student login
- Students on the classroom server typically sign in with
POST /auth/loginagainstEXPO_PUBLIC_BASE_URL. - Teachers and other staff sign in with
POST /auth/school/loginagainst the central API,EXPO_PUBLIC_SYNC_URL. This login (and any school-user token it issues) is for staff only; a student account is refused.
Central API configuration for classroom integration
From the central API's src/config.ts, these values control how the central API reaches a classroom server:
RPI_CLOUDis the base URL of the classroom API used whenPOST /sync/cloudpushes content, and for the other server-to-server calls the central API makes to a classroom server (student and teacher import, reports).SERVER_SYNC_KEYauthenticates that server-to-serverimport/mastercall.RPI_SECRETis the JWT secret for theRPIACCESStoken type. No route currently uses it, but outsidedevelopmentandtestthe API refuses to start while it holds the placeholder value.
SERVER_SYNC_KEY and RPI_SECRET are secrets. RPI_CLOUD is deployment configuration.