docs/SECURITY.md covers the layers of protection, what is stored, third
parties, limits and open items to investigate. SESSION.md moves to docs/
and is rewritten as the handoff for removing ticket subjects next.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Any Zoho account can approve the client, and an outside account got a
session that returned the .env site, group and technician names.
ALLOWED_EMAIL_DOMAINS is now required: the callback refuses other
domains and revokes their token, and every API call re-checks it so
older cookies stop working. /api/names also makes the caller's token
read from SDP before returning the shared names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A gear button and a setup prompt edit the saved settings, with name
suggestions, and apply them without a reload. Panels and status columns
can be hidden; the closed watcher and ding keep running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each user's sites, groups, technicians, timing, weather location and
hidden panels/columns are saved by Zoho email in DATA_DIR, merged over
the .env defaults and validated on PUT /api/settings. /api/names
suggests names from recent tickets, and weather is cached per zip from a
fixed list of campuses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The per-user sign-in was tested live on bare metal behind NPM: Zoho
sign-in, SDP data, Sign out and the IP access list all work. Token
renewal and the first Docker deploy are still to check.
- README: section on testing on the server without Docker (Gitea holds
port 3000, NPM runs in Docker so forward to its network gateway, keep
PORT out of the production .env), plus Cloudflare proxy and access
list notes. The screenshots are removed until they're retaken.
- NOTES: deployment gotchas from the test run.
- ROADMAP and SESSION: what's verified, what's left, new resume prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README covers the Server-based Zoho client, the new .env settings,
deployment (image, compose, Nginx Proxy Manager), signing in and the
security model. NOTES records the Zoho OAuth gotchas and the decisions
behind the rebuild. ROADMAP describes the current architecture and the
planned per-browser settings panel. CLAUDE.md and SESSION.md are
brought up to date.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Dockerfile: node:24-alpine, production deps only, runs as the node
user, healthcheck on /healthz. No volume, because the server keeps
no state.
- docker-compose.yml: example for the VPS. It publishes no ports,
since Docker's port publishing bypasses ufw and would skip NPM's IP
access list. It joins NPM's network instead.
- .gitea/workflows/docker.yml: on v*-prod* tags, builds on the
self-hosted runner and pushes v0idf1sh/sdp-dashboard:<tag> and
:latest using the DHUB_UNAME and DHUB_PASS secrets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each user now signs in with their own Zoho account through the web
OAuth flow (Server-based Application client), and every SDP query runs
with that user's token. Nobody who reaches the page can see data
without signing in.
- auth.js: authorization URL, code exchange, token renewal, revoke and
user info, replacing the device-code flow.
- session.js: tokens kept in an AES-256-GCM encrypted, HttpOnly,
SameSite=Lax cookie keyed by SESSION_SECRET. The access token renews
when due, concurrent calls share one renewal, and sessions end after
SESSION_DAYS.
- routes.js: /auth/login, /auth/callback (state checked), /auth/logout
(revokes at Zoho); /api/session, /api/closed, /api/workload and
/api/weather all need a sign-in.
- The server is stateless. boardService.js, tokenStore.js, ADMIN_KEY
and data/token.json are gone. The page runs the checks itself and
keeps the closed feed, backfilling 12 hours on first load.
- tickets.js: the closed feed query pages, and SDP 401s are rethrown
so the user is signed out.
- server.js: PUBLIC_URL, trust proxy, security headers, /healthz.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>