Record live test results and drop outdated screenshots
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>
This commit is contained in:
parent
f620cd96d4
commit
b29c0a022d
14
README.md
14
README.md
@ -4,8 +4,6 @@ A help desk dashboard for ManageEngine ServiceDesk Plus (SDP) Cloud. It shows ea
|
|||||||
|
|
||||||
Everyone signs in with their own Zoho account, and every query runs as that person. It's built as a Docker image (`v0idf1sh/sdp-dashboard`) to run behind Nginx Proxy Manager, which limits access to MCCS network addresses.
|
Everyone signs in with their own Zoho account, and every query runs as that person. It's built as a Docker image (`v0idf1sh/sdp-dashboard`) to run behind Nginx Proxy Manager, which limits access to MCCS network addresses.
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
### 1. Zoho API client
|
### 1. Zoho API client
|
||||||
@ -60,6 +58,15 @@ npm run app
|
|||||||
|
|
||||||
`npm run app` runs `node src/server.js` from the project root. Then open <http://localhost:3000>. Leave `PUBLIC_URL` unset (or set it to `http://localhost:3000`) and register `http://localhost:3000/auth/callback` with the Zoho client.
|
`npm run app` runs `node src/server.js` from the project root. Then open <http://localhost:3000>. Leave `PUBLIC_URL` unset (or set it to `http://localhost:3000`) and register `http://localhost:3000/auth/callback` with the Zoho client.
|
||||||
|
|
||||||
|
### Testing on the server without Docker
|
||||||
|
|
||||||
|
To try a branch on the VPS before building an image, run it directly with Node behind a separate test subdomain:
|
||||||
|
|
||||||
|
- Use Node 24 to match the image, run `npm ci`, and keep a **test** `.env` in the repo root. Give it its own `SESSION_SECRET`, and set `PUBLIC_URL` to the test subdomain. Register that subdomain's `/auth/callback` with the Zoho client.
|
||||||
|
- Port 3000 is taken by Gitea on the VPS, so set `PORT` (for example `PORT=3001`) in the test `.env` only. **Leave `PORT` out of the production `.env`**: the container must listen on 3000, which its health check and the NPM host expect.
|
||||||
|
- NPM runs in Docker, so `localhost` in a proxy host means NPM's own container. Forward the test host to the gateway of NPM's network instead. Find it with `docker network inspect npm_default --format '{{(index .IPAM.Config 0).Gateway}}'`. If ufw is on, allow that network's subnet to the test port (`sudo ufw allow from <subnet> to any port 3001 proto tcp`).
|
||||||
|
- Attach the same access list as production.
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
### Image
|
### Image
|
||||||
@ -88,7 +95,8 @@ The compose file publishes no ports. Docker's port publishing bypasses the host
|
|||||||
|
|
||||||
- **Proxy host:** the dashboard's subdomain, forwarding to `http://sdp-dashboard:3000`.
|
- **Proxy host:** the dashboard's subdomain, forwarding to `http://sdp-dashboard:3000`.
|
||||||
- **SSL:** request a certificate and turn on **Force SSL**. Sign-in cookies are secure-only when `PUBLIC_URL` is `https://`.
|
- **SSL:** request a certificate and turn on **Force SSL**. Sign-in cookies are secure-only when `PUBLIC_URL` is `https://`.
|
||||||
- **Access list:** allow MCCS' public IP blocks and deny everything else. Attach it to the proxy host.
|
- **Access list:** allow MCCS' public IP blocks and deny everything else. Attach it to the proxy host. To check it, load the site from a phone on mobile data; it should return 403 Forbidden.
|
||||||
|
- **Cloudflare:** if the DNS record is proxied (orange cloud), NPM may see Cloudflare's addresses instead of the visitor's. Then the access list blocks everyone, MCCS included. If allowed addresses get 403s, either switch the record to DNS only, or add `real_ip_header CF-Connecting-IP;` in the proxy host's **Advanced** tab. With the proxy on, use Cloudflare SSL mode **Full (strict)**; Flexible combined with Force SSL causes a redirect loop.
|
||||||
|
|
||||||
## Signing in
|
## Signing in
|
||||||
|
|
||||||
|
|||||||
21
SESSION.md
21
SESSION.md
@ -6,7 +6,7 @@ A Claude Code session that turned the closed-ticket board into a dashboard, then
|
|||||||
|
|
||||||
The dashboard is now a stateless server meant to run as the Docker image `v0idf1sh/sdp-dashboard` on the RackNerd VPS, behind Nginx Proxy Manager. NPM's access list limits the site to MCCS' public IP blocks. Every user signs in with their own Zoho account. Filtering is still the central region from `.env`.
|
The dashboard is now a stateless server meant to run as the Docker image `v0idf1sh/sdp-dashboard` on the RackNerd VPS, behind Nginx Proxy Manager. NPM's access list limits the site to MCCS' public IP blocks. Every user signs in with their own Zoho account. Filtering is still the central region from `.env`.
|
||||||
|
|
||||||
All of this is uncommitted on branch `v1.1.0`. **None of it has been run against live Zoho or built with Docker yet.** It was verified with stubbed Zoho and SDP calls only (see "Verified" below).
|
It's committed and pushed on branch `v2.0.0`. **Tested live on 2026-10-15** as a bare-metal run on the VPS (Node, `PORT=3001`) behind `dash-test` in NPM: Zoho sign-in, live SDP data, Sign out, and the IP access list (mobile data gets 403 Forbidden) all work. Still to check: hourly token renewal, and the first Docker image build and deploy.
|
||||||
|
|
||||||
## Auth rebuild and hosting (this session's last piece of work)
|
## Auth rebuild and hosting (this session's last piece of work)
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ All of this is uncommitted on branch `v1.1.0`. **None of it has been run against
|
|||||||
|
|
||||||
- End to end against the real `server.js` over a unix socket, with axios stubbed: login redirect and state cookie; callback with a forged or missing state, a refused sign-in and a bad code; cookie flags and encryption; tokens never reach the page; a tampered cookie; three concurrent calls with an expired token renewing once; a revoked refresh token; a session past `SESSION_DAYS`; an SDP 401 signing the user out; sign-out revoking the token.
|
- End to end against the real `server.js` over a unix socket, with axios stubbed: login redirect and state cookie; callback with a forged or missing state, a refused sign-in and a bad code; cookie flags and encryption; tokens never reach the page; a tampered cookie; three concurrent calls with an expired token renewing once; a revoked refresh token; a session past `SESSION_DAYS`; an SDP 401 signing the user out; sign-out revoking the token.
|
||||||
- The page script with stubbed DOM and fetch: the signed-out panel, `?signin=` messages, first-load backfill with no ding, a ding for a new close, and signing out mid-session.
|
- The page script with stubbed DOM and fetch: the signed-out panel, `?signin=` messages, first-load backfill with no ding, a ding for a new close, and signing out mid-session.
|
||||||
- **Not verified:** a real Zoho sign-in, the `user/info` response shape, the Docker build, the Gitea workflow, NPM.
|
- **Live, bare metal behind NPM (2026-10-15):** Zoho sign-in, SDP data, Sign out, and the access list blocking mobile data.
|
||||||
|
- **Not verified yet:** token renewal after an hour, the Docker build, the Gitea workflow, the production container behind `sdp-dashboard:3000`.
|
||||||
|
|
||||||
## Earlier the same day (dashboard work)
|
## Earlier the same day (dashboard work)
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ All of this is uncommitted on branch `v1.1.0`. **None of it has been run against
|
|||||||
- The "partial" note now names which query was cut off.
|
- The "partial" note now names which query was cut off.
|
||||||
- **Verified fixed:** the "partial" note is gone.
|
- **Verified fixed:** the "partial" note is gone.
|
||||||
8. **Small fixes:** `[hidden]` CSS bug (Refresh now showed when disconnected); a misplaced comment in `server.js`.
|
8. **Small fixes:** `[hidden]` CSS bug (Refresh now showed when disconnected); a misplaced comment in `server.js`.
|
||||||
9. **README rewritten**, with new screenshots in `images/`. `1.png` and `2.png` are rendered with sample data; `4.png` is from the live server; `3.png` is unchanged.
|
9. **README rewritten.** The screenshots it had then were removed after the auth rebuild; new ones are due.
|
||||||
10. **Credits:** page footer, README, `package.json` contributors, `server.js` header. Isaac Godin keeps the design credit.
|
10. **Credits:** page footer, README, `package.json` contributors, `server.js` header. Isaac Godin keeps the design credit.
|
||||||
11. **License:** MIT (LICENSE copyright Skylar Grant; package.json now says MIT, not ISC).
|
11. **License:** MIT (LICENSE copyright Skylar Grant; package.json now says MIT, not ISC).
|
||||||
12. **Repo structure for working with Claude:** `CLAUDE.md`, `docs/ROADMAP.md`, `docs/NOTES.md`.
|
12. **Repo structure for working with Claude:** `CLAUDE.md`, `docs/ROADMAP.md`, `docs/NOTES.md`.
|
||||||
@ -49,20 +50,22 @@ New: `PUBLIC_URL`, `SESSION_SECRET`, `SESSION_DAYS` (optional), `TRUST_PROXY` (o
|
|||||||
## Known issues
|
## Known issues
|
||||||
|
|
||||||
- On a typical screen the status table scrolls sideways ("Need Info" gets cut off), because the page is capped at 1,320px.
|
- On a typical screen the status table scrolls sideways ("Need Info" gets cut off), because the page is capped at 1,320px.
|
||||||
- `images/1.png` and `images/2.png` show the old Connect screens and are no longer used in the README.
|
- No screenshots in the README for now (`images/` removed); Skylar is retaking them.
|
||||||
- The README's reference links for Zoho's device flow were removed; links for the server-based (web) flow haven't been added.
|
- The README's reference links for Zoho's device flow were removed; links for the server-based (web) flow haven't been added.
|
||||||
- Each open dashboard polls SDP with its own token. That's fine for a few viewers; watch SDP rate limits if wallboards multiply.
|
- Each open dashboard polls SDP with its own token. That's fine for a few viewers; watch SDP rate limits if wallboards multiply.
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
1. **Zoho client:** create the Server-based Application with the production redirect URI (and localhost for development). Put its ID and secret, `PUBLIC_URL` and a new `SESSION_SECRET` into the VPS `.env`.
|
Already done: Zoho Server-based client, NPM proxy hosts (`dash` and `dash-test`) with the MCCS access list, Cloudflare DNS, a wildcard certificate, and the bare-metal test.
|
||||||
2. **Gitea and Docker Hub:** add the `DHUB_UNAME` and `DHUB_PASS` secrets, make sure the runner has the `self-hosted` label and Docker, then push a `v*-prod*` tag.
|
|
||||||
3. **VPS:** check NPM's network name, then `docker compose up -d`. Set up the NPM proxy host (forward to `sdp-dashboard:3000`, SSL, Force SSL) and the MCCS access list.
|
1. **Token renewal:** keep a tab open for more than an hour and confirm it stays signed in.
|
||||||
4. **Check live:** sign in; go more than an hour without being asked to sign in again; sign out; confirm an outside IP is blocked.
|
2. **First image:** check the `DHUB_UNAME` and `DHUB_PASS` secrets and the runner's `self-hosted` label, then push a `v*-prod*` tag and watch the workflow.
|
||||||
|
3. **Production container:** use a production `.env` with **no `PORT` line** and `PUBLIC_URL=https://` the production subdomain, check NPM's network name, then `docker compose up -d`. Confirm `docker ps` shows it healthy and the production subdomain signs in.
|
||||||
|
4. **Screenshots** for the README.
|
||||||
5. **Then:** per-browser settings, as designed in `docs/ROADMAP.md` (not started).
|
5. **Then:** per-browser settings, as designed in `docs/ROADMAP.md` (not started).
|
||||||
|
|
||||||
## To pick up
|
## To pick up
|
||||||
|
|
||||||
Start a Claude Code session in this repo and say something like:
|
Start a Claude Code session in this repo and say something like:
|
||||||
|
|
||||||
*"Read SESSION.md and docs/ROADMAP.md. The per-user Zoho sign-in and Docker setup are built but not yet tested live. Help me get it deployed and checked, then we'll plan the per-browser settings panel."*
|
*"Read SESSION.md and docs/ROADMAP.md. The per-user sign-in works live on bare metal; next is the first Docker deploy. Help me check the production container, then we'll plan the per-browser settings panel."*
|
||||||
|
|||||||
@ -20,6 +20,12 @@ Docs: <https://www.manageengine.com/products/service-desk/sdpod-v3-api/getting-s
|
|||||||
- **Errors come back as HTTP 200:** a bad code or refresh token returns `{ "error": "invalid_code" }` with status 200. `auth.js` checks for `access_token` instead of relying on axios throwing.
|
- **Errors come back as HTTP 200:** a bad code or refresh token returns `{ "error": "invalid_code" }` with status 200. `auth.js` checks for `access_token` instead of relying on axios throwing.
|
||||||
- **Data centre:** everything points at `accounts.zoho.com` (US). The callback also gets `location` and `accounts-server` parameters, which would matter only for users in another Zoho DC.
|
- **Data centre:** everything points at `accounts.zoho.com` (US). The callback also gets `location` and `accounts-server` parameters, which would matter only for users in another Zoho DC.
|
||||||
|
|
||||||
|
## Deployment gotchas
|
||||||
|
|
||||||
|
- **NPM runs in Docker**, so `localhost` in a proxy host is NPM's own container. For a bare-metal test run, forward to the gateway of NPM's Docker network and allow that subnet through ufw. The production container is reached by name (`sdp-dashboard:3000`) because it joins NPM's network.
|
||||||
|
- **Port 3000 is Gitea's on the VPS.** Bare-metal test runs use `PORT=3001`. Never put `PORT` in the production `.env`; the container listens on 3000, and its health check and the NPM host expect that.
|
||||||
|
- **Cloudflare proxy and the NPM access list:** a proxied record can make NPM see Cloudflare's IPs, so the allow list blocks everyone. The fix is DNS only, or `real_ip_header CF-Connecting-IP;` in the proxy host's Advanced config. The live test on 2026-10-15 worked with the access list (an MCCS-listed IP got in, mobile data got a 403).
|
||||||
|
|
||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
- **2026-10-15:** **MIT license.** Copyright Skylar Grant.
|
- **2026-10-15:** **MIT license.** Copyright Skylar Grant.
|
||||||
@ -35,4 +41,4 @@ Docs: <https://www.manageengine.com/products/service-desk/sdpod-v3-api/getting-s
|
|||||||
|
|
||||||
- The status table scrolls sideways once there are more statuses than fit, because the page is capped at 1,320px. Consider widening the page or narrowing the sidebar.
|
- The status table scrolls sideways once there are more statuses than fit, because the page is capped at 1,320px. Consider widening the page or narrowing the sidebar.
|
||||||
- All statuses not in `STATUS_NAMES` count as open, including "Need Information" and "On Hold". This is intentional for now; revisit with per-browser settings.
|
- All statuses not in `STATUS_NAMES` count as open, including "Need Information" and "On Hold". This is intentional for now; revisit with per-browser settings.
|
||||||
- `images/1.png` and `images/2.png` show the old device-code Connect screens. The README no longer uses them. Retake them for the sign-in page or delete them.
|
- No screenshots: `images/` was removed on 2026-10-15 and new ones are due.
|
||||||
|
|||||||
@ -14,7 +14,7 @@ A hosted dashboard for MCCS help desk staff. Everyone signs in with their own Zo
|
|||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
- [ ] **Check the rebuild live:** create the Zoho Server-based client, deploy with a `v*-prod*` tag, sign in, and confirm the token renews after an hour (no sign-in prompt), that Sign out revokes it, and that NPM's access list blocks an outside address.
|
- [ ] **Finish checking the rebuild live.** Done on 2026-10-15 with a bare-metal test run behind `dash-test`: Zoho sign-in, live SDP data, Sign out, and the NPM access list (403 from mobile data). Still to check: the token renews after an hour without a sign-in prompt, and the first `v*-prod*` image builds, starts healthy and serves the production subdomain.
|
||||||
- [ ] **Per-browser settings** (planned, not built). A settings panel, or a setup prompt on first launch, that saves choices in the browser (`localStorage`): sites, groups, technicians, which statuses count as closed, unassigned exclusions, sound, hidden columns. Design notes:
|
- [ ] **Per-browser settings** (planned, not built). A settings panel, or a setup prompt on first launch, that saves choices in the browser (`localStorage`): sites, groups, technicians, which statuses count as closed, unassigned exclusions, sound, hidden columns. Design notes:
|
||||||
- `/api/session` already returns the `.env` region as `settings`. The panel starts from those defaults, and "reset" goes back to them.
|
- `/api/session` already returns the `.env` region as `settings`. The panel starts from those defaults, and "reset" goes back to them.
|
||||||
- The page would send its settings with each `/api/closed` and `/api/workload` call (query string or a JSON header). `routes.js` currently passes the server's `settings` object to `tickets.js`. That becomes "defaults merged with the validated request", and `tickets.js` needs no change because it already takes these as arguments.
|
- The page would send its settings with each `/api/closed` and `/api/workload` call (query string or a JSON header). `routes.js` currently passes the server's `settings` object to `tickets.js`. That becomes "defaults merged with the validated request", and `tickets.js` needs no change because it already takes these as arguments.
|
||||||
@ -39,3 +39,4 @@ A hosted dashboard for MCCS help desk staff. Everyone signs in with their own Zo
|
|||||||
- Fixed SDP paging and the date filter (see `NOTES.md`).
|
- Fixed SDP paging and the date filter (see `NOTES.md`).
|
||||||
- Server-based auth rebuild (2026-10-15): per-user Zoho web sign-in, sessions in an encrypted browser cookie, a stateless server, and the browser running the checks. This replaced the shared device-code token, the `ADMIN_KEY` and `data/token.json` from the short-lived phase 1.
|
- Server-based auth rebuild (2026-10-15): per-user Zoho web sign-in, sessions in an encrypted browser cookie, a stateless server, and the browser running the checks. This replaced the shared device-code token, the `ADMIN_KEY` and `data/token.json` from the short-lived phase 1.
|
||||||
- Docker image `v0idf1sh/sdp-dashboard`, an example `docker-compose.yml`, and a Gitea Actions build on `v*-prod*` tags.
|
- Docker image `v0idf1sh/sdp-dashboard`, an example `docker-compose.yml`, and a Gitea Actions build on `v*-prod*` tags.
|
||||||
|
- Live test on 2026-10-15: per-user sign-in, data, Sign out and the IP access list all work behind NPM.
|
||||||
|
|||||||
BIN
images/1.png
BIN
images/1.png
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
BIN
images/2.png
BIN
images/2.png
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB |
BIN
images/3.png
BIN
images/3.png
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
BIN
images/4.png
BIN
images/4.png
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB |
Loading…
Reference in New Issue
Block a user