Cardy
Discount card storage. Specification: development specification-v2.md.
| Package | Port | Public URL | Proxy auth |
|---|---|---|---|
server-fe — admin UI (Vite) |
10001 | https://dev.aggtaa.com/ |
Authelia |
server-be — admin API |
10002 | https://dev.aggtaa.com/api/admin/* → /admin/* |
Authelia, passes Remote-Email |
server-be — client API |
10002 | https://dev.aggtaa.com/api/* → /* |
none |
mobile-client — PWA |
10003 | https://dev.aggtaa.com/m/* → /* |
none |
The proxy strips /api and /m and passes them in X-Forwarded-Prefix.
Setup
pnpm install
cp server-be/.env.example server-be/.env # fill in KEY_ENCRYPTION_SECRET, SUPERADMIN_EMAIL
cp server-fe/.env.example server-fe/.env
Run
pnpm --filter @cardy/server-be dev # API on 127.0.0.1:10002 (tsx watch)
pnpm --filter @cardy/server-fe dev # admin UI on :10001
pnpm --filter @cardy/mobile-client start # PWA: build + vite preview on :10003 (service worker needs a build)
Production-like: pnpm --filter @cardy/server-be build && pnpm --filter @cardy/server-be start,
pnpm --filter @cardy/server-fe start (build + vite preview on :10001).
The mobile client dev server (pnpm --filter @cardy/mobile-client dev) works locally at
http://localhost:10003/m/; behind the proxy use start.
Android app
pnpm --filter @cardy/mobile-client build:android # web build for Capacitor + cap sync
Then open mobile-client/android in Android Studio (or gradlew assembleDebug there) to build the APK.
The app registers cardy://login?... links (key QR codes) and asks for camera access when scanning.
server-be listens on 127.0.0.1 by default because /admin/* trusts Remote-Email.
If the proxy runs on another machine, set HOST and ADMIN_PROXY_SECRET, and make the proxy add
X-Cardy-Proxy-Secret on the /api/admin route.
Without the proxy (local development), server-fe emulates it: forwards /api/* to server-be
with /api stripped; DEV_REMOTE_EMAIL in server-fe/.env fakes the Authelia user.
Tests
pnpm test
pnpm typecheck