Manager API Overview
The manager-api-node is the backend REST API for the Cheeko platform, built with Node.js/Express. All endpoints share the base path /toy.
Tech Stack
| Component | Technology |
|---|---|
| Runtime | Node.js 20+ |
| Framework | Express.js |
| Database | DigitalOcean Managed PostgreSQL via Prisma ORM |
| Auth (admin) | Custom token lookup (sys_user_token table, Prisma) |
| Auth (mobile) | Firebase ID tokens (firebase-admin SDK) |
| Base path | /toy |
| Port | 8002 |
| API docs | /toy/doc.html (Swagger UI) |
Middleware Stack
Middleware is applied in the following order in src/app.js:
| Middleware | File | Purpose |
|---|---|---|
helmet | express | Sets secure HTTP headers (CORS resource policy: cross-origin) |
cors | express | Allows configurable origins; defaults to localhost:8080 and localhost:3000. Reads CORS_ORIGINS env var. |
trust proxy | express | Enables correct IP identification behind nginx/load balancer |
| Rate limiter | express-rate-limit | 5000 req / 15 min window per IP; returns 429 with { code: 429, msg: "..." } |
express.json | express | Parses JSON bodies up to 10 MB |
express.urlencoded | express | Parses URL-encoded bodies up to 10 MB |
| XSS filter | src/middleware/xssFilter.js | Sanitizes request body/query to strip XSS payloads |
| Request ID | src/middleware/requestId.js | Attaches a unique X-Request-ID to every request |
| Morgan logger | morgan | HTTP access logging; dev format in development, compact format in production |
| Routes | src/routes/index.js | All API routes under /toy |
| 404 handler | src/middleware/errorHandler.js | Returns { code: 404, msg: "..." } for unknown routes |
| Global error handler | src/middleware/errorHandler.js | Returns { code: 500, msg: "..." } for unhandled errors |
Authentication
Two dedicated middleware files handle authentication:
src/middleware/auth.js — Custom token + service key auth
Tokens are stored in the sys_user_token database table (Prisma). Exported middleware:
| Export | Behavior |
|---|---|
requireAuth | Verifies Bearer <token> against sys_user_token. Attaches req.user. |
requireAdmin | Like requireAuth but also checks user.role === 'admin' or super_admin === 1. Accepts service key as bypass. |
requireServiceKey | Verifies X-Service-Key header equals SERVICE_SECRET_KEY env var. Sets req.isServiceAuth = true. |
requireDualAuth | Accepts either a valid Bearer token or a valid service key. |
optionalAuth | Tries both methods; attaches req.user or req.isServiceAuth if valid, but never rejects. |
requireSuperAdmin | Must be chained after requireAuth; checks super_admin === 1. |
src/middleware/flexAuth.js — Firebase + custom token (dual mobile/admin)
Used on routes that serve both the Flutter mobile app and the web admin dashboard.
| Client | Token type | Flow |
|---|---|---|
| Flutter app | Firebase ID token | admin.auth().verifyIdToken(token) → finds or creates sys_user row → sets req.firebaseUser, req.mobileUser, req.user |
| Admin web dashboard | Custom JWT | Falls back to verifyCustomToken → sets req.user |
If neither check succeeds, returns 401 Unauthorized.
Route Overview
All routes are mounted under the /toy context path.
| Mount path | Module file | Description |
|---|---|---|
/toy/user | auth.routes.js | Login, registration, user management |
/toy/device | device.routes.js | ESP32 device registration, binding, mode control, OTA |
/toy/agent | agent.routes.js | AI agent config, prompts, character switching, chat history |
/toy/content | content.routes.js | Music, stories, content library, file upload |
/toy/admin/rfid | rfid.routes.js | RFID card mappings, packs, RAG lookup |
/toy/api/mobile | mobile.routes.js, profile.routes.js | Firebase-backed mobile endpoints (kid profiles, etc.) |
/toy/models | model.routes.js | AI model management |
/toy/analytics | analytics.routes.js | Game sessions, media playback, usage stats |
/toy/system | system.routes.js | System settings |
/toy/admin | admin.routes.js | Admin utilities |
/toy/config | config.routes.js | Runtime configuration |
/toy/usage | usage.routes.js | Usage tracking |
/toy/quota | quota.routes.js | Question, token, time, and game-session quota endpoints |
/toy/subscription | subscription.routes.js | Subscription plans, unified quota checks, and AI Card time quota |
/toy/ota | ota.routes.js | OTA firmware check (device-facing) |
/toy/otaMag | otaMag.routes.js | OTA firmware management (admin) |
/toy/admin/server | server.routes.js | Server management |
/toy/admin/params | params.routes.js | System parameters |
/toy/admin/dict | dict.routes.js | Data dictionary |
/toy/ttsVoice | ttsVoice.routes.js | TTS voice configuration |
/toy/admin/email-reports | emailReport.routes.js | Email report scheduling |
Subscription and AI Card Quota
The aicard_subscription branch adds a prepaid AI Card time quota system under /toy/subscription. AI Card quota is keyed by physical RFID card (rfid_uid) rather than user or device, and active voice sessions consume connected seconds from the card's monthly balance.
See AI Card Subscription for the endpoint reference, quota flow, database objects, MQTT messages, and admin dashboard behavior.
Health Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /health | None | App health (uptime, timestamp) |
GET | /toy/health | None | API health with environment info |
GET | /toy/health/db | None | Tests Prisma → DigitalOcean PostgreSQL connection |
GET | /toy/pub-config | None | Public feature flags (rfid, analytics, rag, memory) |
Standard Response Envelope
All API responses use the same envelope:
{
"code": 0,
"msg": "success",
"data": { ... }
}
Error responses set code to a non-zero value (e.g. 400, 401, 403, 404, 500) and data to null.
Running the API
cd main/manager-api-node
npm install
# Development (auto-reload)
npm run dev
# Production
npm start
# Tests
npm test
npm run test:coverage
Required Environment Variables
PORT=8002
NODE_ENV=development
# Primary database
DATABASE_URL=postgresql://user:pass@host:5432/dbname?sslmode=require
# Supabase (legacy admin auth only)
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Service-to-service auth (LiveKit agents, MQTT gateway)
SERVICE_SECRET_KEY=your-service-secret
# Firebase (mobile app auth)
FIREBASE_SERVICE_ACCOUNT_KEY=<base64 or path>
# Vector search
QDRANT_URL=https://your-cluster.qdrant.io
QDRANT_API_KEY=your-qdrant-api-key
# Memory/personalization
MEM0_API_KEY=your-mem0-api-key