Serve blog media via nginx static path instead of the API + prod config fixes #473

Closed
opened 2026-07-11 05:12:55 +00:00 by spikerj · 1 comment
Owner

Follow-ups from a mount-point audit of the SERVER /mnt/fusionio/spikersoft share vs our docker-stack files.

1. Blog media should be served by nginx, not the API

Blog media currently gets a /blog-pictures/{username}/{file} URL, which the SPA prefixes with https://api.spikersoft.com, so every image/video request goes through the API's static-files middleware. The Angular nginx container already bind-mounts the whole share at /usr/share/nginx/html/spikersoft, so the final blogs folder is directly servable at https://learn.spikersoft.com/spikersoft/blogs/... with zero API involvement.

Change: BlogMediaMoveConsumer now builds the public URL from a BlogMedia:PublicUrlPrefix config value. Production sets it to https://learn.spikersoft.com/spikersoft/blogs (absolute URL — the SPA passes absolute URLs through untouched). The default stays /blog-pictures so Development (no nginx in front) and existing posts with legacy relative URLs keep being served by the API's /blog-pictures static route, which remains in place.

2. Typo broke the UploadCoordinator's ebooks final directory in Production

SpikerSoft.EventHandlers.UploadCoordinator/appsettings.Production.json had "Ebooks": "yes/app/ebooks" — a relative path that would resolve against the container CWD instead of the /app/ebooks bind mount. Fixed to /app/ebooks.

3. Stale duplicate stack file removed

spikersoft-backend/deploy/docker-stack.yml was an out-of-date copy of spikersoft-infrastructure/spikersoft-backend/docker-stack.yml (missing the lesson-videos mount, among other drift). CI deploys from /mnt/infrastructure/spikersoft-backend, and nothing referenced the copy, so it's deleted.

Ops prerequisites (already done)

  • uploads/assets created on SERVER (was missing; the coordinator's WatchDirectories.Assets points at it).

Known remaining item (separate ticket-worthy)

  • The code bind mounts in file-movement/upload-coordinator point /app/code at host uploads/code, making the watch dir and final dir the same host directory (move-to-final is a no-op). The top-level /mnt/fusionio/spikersoft/code folder is mounted by nothing and looks like the intended final target.
  • nginx serves the entire share publicly, including quarantine/ and unscanned uploads/ — should be narrowed or deny-ruled.
Follow-ups from a mount-point audit of the SERVER `/mnt/fusionio/spikersoft` share vs our docker-stack files. ## 1. Blog media should be served by nginx, not the API Blog media currently gets a `/blog-pictures/{username}/{file}` URL, which the SPA prefixes with `https://api.spikersoft.com`, so every image/video request goes through the API's static-files middleware. The Angular nginx container already bind-mounts the whole share at `/usr/share/nginx/html/spikersoft`, so the final blogs folder is directly servable at `https://learn.spikersoft.com/spikersoft/blogs/...` with zero API involvement. **Change:** `BlogMediaMoveConsumer` now builds the public URL from a `BlogMedia:PublicUrlPrefix` config value. Production sets it to `https://learn.spikersoft.com/spikersoft/blogs` (absolute URL — the SPA passes absolute URLs through untouched). The default stays `/blog-pictures` so Development (no nginx in front) and existing posts with legacy relative URLs keep being served by the API's `/blog-pictures` static route, which remains in place. ## 2. Typo broke the UploadCoordinator's ebooks final directory in Production `SpikerSoft.EventHandlers.UploadCoordinator/appsettings.Production.json` had `"Ebooks": "yes/app/ebooks"` — a relative path that would resolve against the container CWD instead of the `/app/ebooks` bind mount. Fixed to `/app/ebooks`. ## 3. Stale duplicate stack file removed `spikersoft-backend/deploy/docker-stack.yml` was an out-of-date copy of `spikersoft-infrastructure/spikersoft-backend/docker-stack.yml` (missing the `lesson-videos` mount, among other drift). CI deploys from `/mnt/infrastructure/spikersoft-backend`, and nothing referenced the copy, so it's deleted. ## Ops prerequisites (already done) - `uploads/assets` created on SERVER (was missing; the coordinator's `WatchDirectories.Assets` points at it). ## Known remaining item (separate ticket-worthy) - The `code` bind mounts in file-movement/upload-coordinator point `/app/code` at host `uploads/code`, making the watch dir and final dir the same host directory (move-to-final is a no-op). The top-level `/mnt/fusionio/spikersoft/code` folder is mounted by nothing and looks like the intended final target. - nginx serves the entire share publicly, including `quarantine/` and unscanned `uploads/` — should be narrowed or deny-ruled.
Author
Owner

Resolved in spikersoft-backend PR #201 (merged): blog media served via nginx static path, UploadCoordinator ebooks final-directory typo fixed, stale duplicate stack file removed; plus spikersoft-infrastructure commit e28e968 (blog-media-processor joins the clamav overlay). The 'known remaining item' flagged in the ticket (code bind-mount target + nginx serving quarantine/ publicly) was called separate-ticket-worthy — file it separately if still wanted. Closing.

Resolved in spikersoft-backend PR #201 (merged): blog media served via nginx static path, UploadCoordinator ebooks final-directory typo fixed, stale duplicate stack file removed; plus spikersoft-infrastructure commit e28e968 (blog-media-processor joins the clamav overlay). The 'known remaining item' flagged in the ticket (code bind-mount target + nginx serving quarantine/ publicly) was called separate-ticket-worthy — file it separately if still wanted. Closing.
Sign in to join this conversation.