A small webapp around an existing OneDrive Excel workbook. Reads the workbook through Microsoft Graph, renders it as a table in the browser, and (in later phases) caches and edits it.
- packages/shared — TypeScript contracts + workbook parser (vitest).
- apps/api — Express + Pino API serving the parsed workbook snapshot.
- apps/web — Angular 21 standalone PWA (signals, OnPush,
@switch/@for). - scripts/test-graph-connection.mjs — MSAL device-code flow that dumps
the workbook used range to
dumps/dump-<timestamp>.json. Phase-0 tooling.
.
├── apps/
│ ├── api/ Express server reading the latest dump
│ └── web/ Angular table view
├── packages/
│ └── shared/ Types + parser shared by api and web
├── scripts/ Standalone Graph tooling (auth + dump)
├── dumps/ Workbook dumps (gitignored)
├── .env Real config (gitignored)
└── .env.example Documented config template
- Register a Microsoft Entra app — fastest path is
scripts/register-app.sh(requiresaz login). Manual portal steps are inSETUP_GRAPH.md. cp .env.example .envand fill inMICROSOFT_CLIENT_IDandONEDRIVE_WORKBOOK_URL.npm installat the repo root (npm workspaces will install all packages).
npm run dumpFirst run prints a device-code URL — sign in with the Microsoft account that
owns the OneDrive file. The refresh token is cached in .token-cache.json
so subsequent runs are non-interactive.
The dump lands in dumps/dump-<ISO-timestamp>.json.
A Dockerized variant is available too:
docker compose run --rm graph-tester # connection test
docker compose run --rm graph-tester node scripts/test-graph-connection.mjs --dumpnpm run devThis spawns the API (:4000) and Angular dev server (:4200) concurrently.
Open http://localhost:4200.
The API can run in either of two modes, controlled by EXPENSES_SOURCE in
your .env:
| Mode | Source | Sign-in required in browser? |
|---|---|---|
dump |
Latest file under dumps/ |
No |
graph (default) |
Live Microsoft Graph | Yes (Sign in with Microsoft) |
Toggle Demo mode from the in-app Settings page to swap the live data
for funny in-memory fake data (real DB and workbook are not touched).
A DEMO MODE badge appears in the header while it's on. The toggle
state persists across restarts in data/demo-mode.json; demo edits
themselves live in memory only.
In graph mode, the API exposes GET /api/config (clientId, authority,
scopes) which the Angular app fetches at bootstrap to initialize MSAL.js
(auth-code + PKCE). Every /api/* request from the browser carries a
Authorization: Bearer <user token> header; the API forwards that token
verbatim to Microsoft Graph. No tokens are stored server-side.
- Browser loads
/api/config→ initializes MSAL with the SPAclientId. - User clicks Sign in with Microsoft (popup).
- MSAL caches the access + refresh tokens in
sessionStorage. - The HTTP interceptor adds
Authorization: Bearer …to every/api/*request. On a 401, it forces a silent refresh and retries once.
| Endpoint | Auth | Notes |
|---|---|---|
GET /healthz |
No | Liveness + current source |
GET /api/config |
No | `{ source, auth: { clientId, authority, scopes } |
GET /api/expenses |
Bearer (graph mode) | Parsed WorkbookSnapshot |
GET /api/workbook/status |
Bearer (graph mode) | Workbook metadata only |
Recurring templates support three schedule shapes:
- Monthly — exact day-of-month
- Weekly — exact day-of-week
- Monthly prediction — one projected occurrence per month without an exact business day
When backing up to Excel, only exact monthly day-of-month templates are round-trippable in the current workbook format. Weekly and monthly-prediction templates are excluded from the export sheets with a warning.
| Command | Effect |
|---|---|
npm test |
Run all workspace tests (shared parser + API graph layer) |
npm run build |
Build all workspaces |
npm run test:connection |
Smoke-test the Microsoft Graph connection (no dump) |
npm --workspace @expenses/api run backup:db |
Online SQLite backup (used by deploy cron) |
Production deployment is covered in docs/deploy.md:
Docker image, Proxmox LXC bootstrap, Tailscale-served TLS, and backups.
The full plan with all 8 phases is in
LLM_IMPLEMENTATION_PLAN.md. Phases 1 and 4
(read-only table fed by either a dump or a live Graph fetch with browser
sign-in) are complete; subsequent phases add a SQLite snapshot cache,
write-through editing, and offline support.
.env, .token-cache.json, and dumps/ are gitignored. Do not commit them.
The OneDrive sharing URL contains a capability token and is treated as a
secret throughout this repo.