diff --git a/.env-example b/.env-example index 921a806..33dc0ed 100644 --- a/.env-example +++ b/.env-example @@ -16,6 +16,9 @@ LOCAL_RESTURL=http://localhost:5942/v6 # END INFRASTRUCTURE SETUP +# Public base URL for apiDoc HTML (optional; used by Docker entrypoint before `npm run docs`). +#APIDOC_URL=https://api.example.com + # START ACCESS CONTROL diff --git a/README.md b/README.md index a51a7fc..dc3e8e2 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,8 @@ FULCRUM_API=http://172.17.0.1:3001/v1 SLP_INDEXER_API=http://172.17.0.1:5010 ``` +Set `APIDOC_URL` to the public base URL for your deployment (for example `https://api.example.com` or your subdomain). The container entrypoint applies this value to `apidoc.json` and the `apidoc` section of `package.json`, then runs `npm run docs` before starting the server so generated HTML matches each instance. The compose file mounts `./.env` to `/home/safeuser/psf-bch-api/.env` so it matches `dotenv.config()` in the app. + 4. Build the Docker image: `docker-compose build --no-cache` diff --git a/dev-docs/update-logs/2026-04-02.md b/dev-docs/update-logs/2026-04-02.md new file mode 100644 index 0000000..cd40bd5 --- /dev/null +++ b/dev-docs/update-logs/2026-04-02.md @@ -0,0 +1,19 @@ +# 2026-04-02 Update Log + +## Summary + +Docker deployments now run `npm run docs` at container start via an entrypoint script, and optionally apply `APIDOC_URL` from the environment so apiDoc HTML matches each deployment subdomain or public URL. + +## Changes Made + +- Added `scripts/patch-apidoc-from-env.js` to set `apidoc.json` and `package.json` `apidoc.url` from `APIDOC_URL` (dotenv loads the project `.env`, same path as `bin/server.js`). +- Added `production/docker/entrypoint.sh` to run the patch script, `npm run docs`, then `exec npm start`. +- Updated `production/docker/Dockerfile` to remove build-time `npm run docs`, copy the entrypoint and patch script from the build context, and use `ENTRYPOINT` for the shell script. +- Updated `production/docker/docker-compose.yml` to use build `context: ../..` and `dockerfile: production/docker/Dockerfile` so COPY paths resolve from the repository root. +- Compose mounts `./.env` at `/home/safeuser/psf-bch-api/.env` so the app and entrypoint share one file (not `/home/safeuser/.env`). +- Documented `APIDOC_URL` in `.env-example`, `production/docker/.env-example`, and `README.md` (Production Docker section). + +## Outcome + +- Operators can set `APIDOC_URL` in the mounted `.env` (for example `https://api.example.com`) per instance without rebuilding the image for each subdomain. +- The HTML apiDoc bundle is regenerated on every container start so it stays aligned with the configured URL. diff --git a/production/docker/.env-example b/production/docker/.env-example index 12a1d3d..e8dd3f7 100644 --- a/production/docker/.env-example +++ b/production/docker/.env-example @@ -16,6 +16,10 @@ LOCAL_RESTURL=http://172.17.0.1:5942/v6 # END INFRASTRUCTURE SETUP +# Public base URL for apiDoc HTML (set per deployment / subdomain). +# Applied at container start before `npm run docs`. Example: https://api.example.com +#APIDOC_URL=https://api.example.com + # START ACCESS CONTROL diff --git a/production/docker/Dockerfile b/production/docker/Dockerfile index b445ac5..c151d16 100644 --- a/production/docker/Dockerfile +++ b/production/docker/Dockerfile @@ -57,13 +57,15 @@ RUN git checkout ct-unstable RUN npm install RUN npm install minimal-slp-wallet -# Generate the API docs -RUN npm run docs +# Runtime entrypoint + apidoc URL patch (see production/docker/entrypoint.sh). +# API docs are generated at container start so APIDOC_URL can be set per deployment. +COPY production/docker/entrypoint.sh /home/safeuser/psf-bch-api/entrypoint.sh +COPY scripts/patch-apidoc-from-env.js /home/safeuser/psf-bch-api/scripts/patch-apidoc-from-env.js +RUN chmod +x /home/safeuser/psf-bch-api/entrypoint.sh -COPY .env .env +# Runtime `.env` is provided by docker-compose (mount at psf-bch-api/.env), not baked into the image. - -CMD ["npm", "start"] +ENTRYPOINT ["/home/safeuser/psf-bch-api/entrypoint.sh"] # Used to debug the container. #COPY temp.js temp.js diff --git a/production/docker/docker-compose.yml b/production/docker/docker-compose.yml index 0bca108..f1d8317 100644 --- a/production/docker/docker-compose.yml +++ b/production/docker/docker-compose.yml @@ -2,7 +2,9 @@ services: psf-bch-api: - build: . + build: + context: ../.. + dockerfile: production/docker/Dockerfile container_name: psf-bch-api logging: driver: 'json-file' @@ -16,7 +18,7 @@ services: - '5942:5942' # : volumes: #- ./start-rest2nostr.sh:/home/safeuser/REST2NOSTR/start-rest2nostr.sh - - ./.env:/home/safeuser/.env + - ./.env:/home/safeuser/psf-bch-api/.env - ../data:/home/safeuser/psf-bch-api/production/data - ../data/logs:/home/safeuser/psf-bch-api/logs restart: always \ No newline at end of file diff --git a/production/docker/entrypoint.sh b/production/docker/entrypoint.sh new file mode 100644 index 0000000..7040f8c --- /dev/null +++ b/production/docker/entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -euo pipefail + +cd /home/safeuser/psf-bch-api + +node scripts/patch-apidoc-from-env.js + +npm run docs + +exec npm start diff --git a/scripts/patch-apidoc-from-env.js b/scripts/patch-apidoc-from-env.js new file mode 100644 index 0000000..f14c4b2 --- /dev/null +++ b/scripts/patch-apidoc-from-env.js @@ -0,0 +1,41 @@ +/* + Applies APIDOC_URL from the environment to apidoc.json and package.json (apidoc.url). + Loads dotenv from the project root `.env` (same file as `dotenv.config()` in bin/server.js). + Exits without changes when APIDOC_URL is unset or empty. +*/ + +import dotenv from 'dotenv' +import { readFileSync, writeFileSync } from 'fs' +import { dirname, resolve } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const root = resolve(__dirname, '..') + +dotenv.config({ path: resolve(root, '.env') }) + +const url = process.env.APIDOC_URL +if (url === undefined || url === null || String(url).trim() === '') { + process.exit(0) +} + +const normalized = String(url).trim() + +function patchApidocJson () { + const path = resolve(root, 'apidoc.json') + const data = JSON.parse(readFileSync(path, 'utf8')) + data.url = normalized + data.sampleUrl = normalized + writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`) +} + +function patchPackageJson () { + const path = resolve(root, 'package.json') + const data = JSON.parse(readFileSync(path, 'utf8')) + if (!data.apidoc) data.apidoc = {} + data.apidoc.url = normalized + writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`) +} + +patchApidocJson() +patchPackageJson()