[dev-ex/Mac] No turnkey local full-app browse — frontend dev expects API :33333, Mac-native API binds :5290/5291 #421

Closed
opened 2026-07-05 22:01:12 +00:00 by spikerj · 3 comments
Owner

Verified finding

There's no turnkey way to browse the full app locally on a Mac (https://localhost:4200 → local API) because the frontend's expected API port and the only Mac-runnable API path disagree.

Verified specifics

  • Frontend (dev): libraries/spikersoft-environment/src/lib/environment.development.tsbaseUrl: "https://localhost:33333". pnpm nx serve spikersoft (HTTPS :4200) sends all API calls to localhost:33333 (only /spikersoft/ebooks is proxied). So the frontend is hard-wired to a local API on :33333.
  • The :33333 mapping is Windows-only: spikersoft-backend/docker-compose.yml's spikersoft-backend (API) service maps 33333:8081, but it's Visual-Studio/Windows-configured — C:\temp\spikersoft-api-local\... volume mounts, docker-compose.override.yml adds ${APPDATA}/Microsoft/UserSecrets, and it needs the spikersoft-api image built.
  • docker-compose.mac.yml overrides only infra (rabbitmq/seq/redis/redisinsight) — it does not adapt the API service for Mac. So docker compose … up on a Mac brings up infra (mongo cluster/redis/rabbitmq/jaeger/seq — currently 23 containers) but no working API container.
  • The Mac-native API path (docs/mac-local-stack.md §2, dotnet run + UseTestAuthentication) binds Kestrel to 5290/5291, not 33333. That runbook is ArtPipe-smoke-focused, not full-app-browse-focused.

Net

Frontend wants :33333; the only Mac-runnable API answers on :5291. Right now nothing is on :33333, so https://localhost:4200 can't reach a local API. Relevant because this Mac is the dev + school-demo rig (per the ProArt epic topology).

Fix options (pick one; low effort)

  1. Documented Mac full-app profile (recommended): run the native API with Kestrel__Endpoints__Https__Url=https://localhost:33333 against the already-up infra + pnpm nx serve spikersoft; add it to mac-local-stack.md as a "browse the full app" section. Decide auth: UseTestAuthentication (quick, role-less) vs. point the local API at real ids.spikersoft.com (faithful roles).
  2. Mac-ready API compose: add a spikersoft-backend override in docker-compose.mac.yml with Mac volume paths (no C:\temp/${APPDATA}) + build config, so docker compose … up yields a :33333 API container.
  3. Align ports: point environment.development.ts at :5291 (the native port) — smallest change, but bakes the ArtPipe-runbook port into the app dev config.

Severity: Low (dev-experience; no prod impact). Surfaced live while wiring up a local browse.

## Verified finding There's no turnkey way to browse the full app locally on a Mac (`https://localhost:4200` → local API) because the frontend's expected API port and the only Mac-runnable API path disagree. ### Verified specifics - **Frontend (dev):** `libraries/spikersoft-environment/src/lib/environment.development.ts` → `baseUrl: "https://localhost:33333"`. `pnpm nx serve spikersoft` (HTTPS `:4200`) sends all API calls to `localhost:33333` (only `/spikersoft/ebooks` is proxied). So the frontend is hard-wired to a local API on **:33333**. - **The `:33333` mapping is Windows-only:** `spikersoft-backend/docker-compose.yml`'s `spikersoft-backend` (API) service maps `33333:8081`, but it's Visual-Studio/Windows-configured — `C:\temp\spikersoft-api-local\...` volume mounts, `docker-compose.override.yml` adds `${APPDATA}/Microsoft/UserSecrets`, and it needs the `spikersoft-api` image built. - **`docker-compose.mac.yml` overrides only infra** (rabbitmq/seq/redis/redisinsight) — it does **not** adapt the API service for Mac. So `docker compose … up` on a Mac brings up infra (mongo cluster/redis/rabbitmq/jaeger/seq — currently 23 containers) but **no working API container**. - **The Mac-native API path** (`docs/mac-local-stack.md §2`, `dotnet run` + `UseTestAuthentication`) binds Kestrel to **`5290/5291`**, not `33333`. That runbook is ArtPipe-smoke-focused, not full-app-browse-focused. ### Net Frontend wants `:33333`; the only Mac-runnable API answers on `:5291`. Right now nothing is on `:33333`, so `https://localhost:4200` can't reach a local API. Relevant because this Mac is the **dev + school-demo rig** (per the ProArt epic topology). ## Fix options (pick one; low effort) 1. **Documented Mac full-app profile (recommended):** run the native API with `Kestrel__Endpoints__Https__Url=https://localhost:33333` against the already-up infra + `pnpm nx serve spikersoft`; add it to `mac-local-stack.md` as a "browse the full app" section. Decide auth: `UseTestAuthentication` (quick, role-less) vs. point the local API at real `ids.spikersoft.com` (faithful roles). 2. **Mac-ready API compose:** add a `spikersoft-backend` override in `docker-compose.mac.yml` with Mac volume paths (no `C:\temp`/`${APPDATA}`) + build config, so `docker compose … up` yields a `:33333` API container. 3. **Align ports:** point `environment.development.ts` at `:5291` (the native port) — smallest change, but bakes the ArtPipe-runbook port into the app dev config. **Severity:** Low (dev-experience; no prod impact). Surfaced live while wiring up a local browse.
Author
Owner

Proven working recipe (real Keycloak, native API on :33333)

Stood the full app up locally and browsed it — https://localhost:4200 → local API :33333 → local infra + real ids.spikersoft.com. Recipe (add to mac-local-stack.md as a "browse the full app" section):

1. Infra — already the docker-compose … up set (mongo cluster :27117, redis :6380, rabbitmq :5672, seq :5341, jaeger :4317).

2. API — native dotnet run, bound to :33333, real Keycloak (the key deltas vs §2's ArtPipe recipe):

  • Omit Authentication__UseTestAuthentication → the appsettings Keycloak config (server-url: https://ids.spikersoft.com, validate-issuer: false) drives real JWT validation.
  • Kestrel__Endpoints__Https__Url=https://localhost:33333 (+ Http__Url=http://localhost:33334) — the frontend's environment.development.ts baseUrl.
  • Cert: .mac-dev/certs/backend-cert.pem / .key.
  • Redirect the container upload paths to a writable local dir (new finding — otherwise a 500): LessonVideoStagingService's constructor does Directory.CreateDirectory('/app/uploads/lesson-videos'), and Mac's / is read-only → IOException: Read-only file system : '/app'500 on /api/Lessons (and anything else injecting it). Override:
    Videos__Path, Videos__StagingPath, Books__Path, Books__StagingPath, Blogs__Path, Blogs__StagingPath, Media__Path → a writable local base (e.g. .local-api/.localdata/...). (The blogs/ebooks/media services degrade gracefully with a warning; only LessonVideoStagingService throws from its ctor.)

3. Frontendpnpm nx serve spikersoft --configuration=development (HTTPS :4200; SSL certs already at workspace-root ssl/localhost.*). CORS is fine — the API's Origins already includes https://localhost:4200.

Gotchas confirmed: browser must trust the :33333 dev cert (visit https://localhost:33333/api/status once, or dotnet dev-certs https --trust); the async worker containers aren't part of this (art-generation/code-exec enqueue but don't complete).

Sub-finding worth its own fix: LessonVideoStagingService doing filesystem CreateDirectory in its constructor is fragile (any read-only/misconfigured path takes down every endpoint that injects it, via DI). Consider lazy dir-creation or graceful-degrade like the sibling staging services. I can file that separately if you want it tracked.

### ✅ Proven working recipe (real Keycloak, native API on :33333) Stood the full app up locally and browsed it — `https://localhost:4200` → local API `:33333` → local infra + real `ids.spikersoft.com`. Recipe (add to `mac-local-stack.md` as a "browse the full app" section): **1. Infra** — already the `docker-compose … up` set (mongo cluster :27117, redis :6380, rabbitmq :5672, seq :5341, jaeger :4317). **2. API — native `dotnet run`, bound to :33333, real Keycloak** (the key deltas vs §2's ArtPipe recipe): - **Omit** `Authentication__UseTestAuthentication` → the appsettings Keycloak config (`server-url: https://ids.spikersoft.com`, `validate-issuer: false`) drives real JWT validation. - `Kestrel__Endpoints__Https__Url=https://localhost:33333` (+ `Http__Url=http://localhost:33334`) — the frontend's `environment.development.ts` baseUrl. - Cert: `.mac-dev/certs/backend-cert.pem` / `.key`. - **Redirect the container upload paths to a writable local dir** (new finding — otherwise a 500): `LessonVideoStagingService`'s **constructor** does `Directory.CreateDirectory('/app/uploads/lesson-videos')`, and Mac's `/` is read-only → `IOException: Read-only file system : '/app'` → **500 on `/api/Lessons`** (and anything else injecting it). Override: `Videos__Path`, `Videos__StagingPath`, `Books__Path`, `Books__StagingPath`, `Blogs__Path`, `Blogs__StagingPath`, `Media__Path` → a writable local base (e.g. `.local-api/.localdata/...`). (The blogs/ebooks/media services degrade gracefully with a warning; only `LessonVideoStagingService` throws from its ctor.) **3. Frontend** — `pnpm nx serve spikersoft --configuration=development` (HTTPS :4200; SSL certs already at workspace-root `ssl/localhost.*`). CORS is fine — the API's `Origins` already includes `https://localhost:4200`. **Gotchas confirmed:** browser must trust the `:33333` dev cert (visit `https://localhost:33333/api/status` once, or `dotnet dev-certs https --trust`); the async **worker containers** aren't part of this (art-generation/code-exec enqueue but don't complete). **Sub-finding worth its own fix:** `LessonVideoStagingService` doing filesystem `CreateDirectory` **in its constructor** is fragile (any read-only/misconfigured path takes down every endpoint that injects it, via DI). Consider lazy dir-creation or graceful-degrade like the sibling staging services. I can file that separately if you want it tracked.
Author
Owner

Sub-finding fixed + merged

The LessonVideoStagingService ctor-does-filesystem-I/O fragility (the 500 on /api/Lessons) is fixed on backend master — spikersoft-backend#103 (merged): removed the redundant Directory.CreateDirectory from the constructor (StageAsync already creates it lazily at point-of-use), + a regression test. So the constructor no longer takes down unrelated endpoints when the staging path is unwritable.

Note: the Videos/Books/Blogs/Media path overrides are still part of the recipe above — they're needed for actual uploads to land in a writable local dir — but they're no longer required just to keep /api/Lessons from 500'ing. #421 stays open for the turnkey Mac full-app-browse doc/config itself.

### Sub-finding fixed + merged The `LessonVideoStagingService` ctor-does-filesystem-I/O fragility (the 500 on `/api/Lessons`) is fixed on backend master — **spikersoft-backend#103** (merged): removed the redundant `Directory.CreateDirectory` from the constructor (`StageAsync` already creates it lazily at point-of-use), + a regression test. So the constructor no longer takes down unrelated endpoints when the staging path is unwritable. Note: the Videos/Books/Blogs/Media **path overrides** are still part of the recipe above — they're needed for actual uploads to land in a writable local dir — but they're no longer required just to keep `/api/Lessons` from 500'ing. #421 stays open for the turnkey Mac full-app-browse doc/config itself.
Author
Owner

Resolved in spikersoft-backend PR #209 (merged to master), implementing the ticket's recommended fix option 1: API_HTTP_PORT/API_HTTPS_PORT in scripts/mac-dev-common.sh are now env-overridable (defaults 5290/5291 unchanged), and docs/mac-local-stack.md §2b documents the browse profile — API_HTTPS_PORT=33333 API_HTTP_PORT=33332 scripts/mac-dev-up.sh, then pnpm nx serve spikersoft and browse https://localhost:4200. The open auth question resolved itself via #464's dual-scheme registration: token-less requests get the role-less test-user-id, and a real ids.spikersoft.com SPA login sends a Bearer whose realm roles apply — so anonymous and real-role browsing both work against the local API. Closing.

Resolved in spikersoft-backend PR #209 (merged to master), implementing the ticket's recommended fix option 1: API_HTTP_PORT/API_HTTPS_PORT in scripts/mac-dev-common.sh are now env-overridable (defaults 5290/5291 unchanged), and docs/mac-local-stack.md §2b documents the browse profile — API_HTTPS_PORT=33333 API_HTTP_PORT=33332 scripts/mac-dev-up.sh, then pnpm nx serve spikersoft and browse https://localhost:4200. The open auth question resolved itself via #464's dual-scheme registration: token-less requests get the role-less test-user-id, and a real ids.spikersoft.com SPA login sends a Bearer whose realm roles apply — so anonymous and real-role browsing both work against the local API. Closing.
Sign in to join this conversation.