/** * quickstart.mjs — the self-contained site deployment (D35, PLAN.md §10). * * The org lead chose a quickstart an operator can copy without leaving the page: the * Compose file and the environment file below are complete enough to boot a site, and * `/docs/getting-started/install-the-site/` renders them verbatim. * * That decision creates the artifact §1 spends its whole length warning about — a second * copy of somebody else's file, free to drift. `scripts/checkQuickstart.mjs` is the price * of it: every service, image, port, mount and variable below is re-read from `website`'s * own `docker-compose.yml` and `.env.example` on `main`, over the Gitea API, and any * disagreement fails the build. Same mechanism and same intent as `checkFacts.mjs`. * * WHAT THIS FILE IS NOT. It is not a smaller compose file that the project supports as an * alternative. It is the shipped one with the parts an operator does not need on day one * left out, and the page says so: `bot` and `ntfy` are real services, documented where * they are configured, and the reader is pointed at the full file for them. */ /** * Services the quickstart ships, and — for the check — what each one must still agree with * upstream about. `omitted` records the services deliberately left out, because a NEW * service appearing upstream should make someone decide, rather than pass silently. */ export const services = ['db', 'app']; export const omittedServices = { ntfy: 'Push notifications for the Android app. Nothing needs it to boot, and it wants a public URL a first install does not have yet.', bot: 'The Discord bot. It is configured from the admin panel once the site is up, so it is introduced on the integrations page rather than here.', }; /** * The Compose file, exactly as the page prints it. * * Three differences from upstream's, all deliberate and all asserted by the check: * - `bot` and `ntfy` are absent (above). * - `db` does not bind-mount `./server/db/schema.sql`. That mount is a checkout-relative * path, and this quickstart has no checkout; the server ensures its own schema on boot, * which is what actually creates the tables in every deployment. * - the `MODULES` comment block is reduced to one line pointing at the module page. */ export const compose = `services: db: image: mariadb:11 restart: unless-stopped environment: MARIADB_DATABASE: \${DB_NAME} MARIADB_USER: \${DB_USER} MARIADB_PASSWORD: \${DB_PASSWORD} MARIADB_ROOT_PASSWORD: \${DB_ROOT_PASSWORD} volumes: - dbdata:/var/lib/mysql healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 10s timeout: 5s retries: 10 app: image: gitea.whitlocktech.com/runicgateway/website-app:\${IMAGE_TAG:-latest} restart: unless-stopped env_file: .env environment: DB_HOST: db UPLOAD_DIR: /app/uploads LOG_DIR: /app/logs MODULES_DIR: /app/modules depends_on: db: condition: service_healthy volumes: - uploads:/app/uploads - ./logs:/app/logs - ./brand:/app/brand:ro - ./modules:/app/modules ports: - "3000:3000" volumes: dbdata: uploads: `; /** * The environment file, as the page prints it. `fill` marks the lines an operator must * change before this is a real deployment — the page highlights exactly these. */ export const env = [ { key: 'IMAGE_TAG', value: 'latest' }, { key: 'NODE_ENV', value: 'production' }, { key: 'PORT', value: '3000' }, { key: 'INTERNAL_PORT', value: '3001' }, { key: 'DB_HOST', value: 'db' }, { key: 'DB_PORT', value: '3306' }, { key: 'DB_NAME', value: 'runic_gateway' }, { key: 'DB_USER', value: 'runic' }, { key: 'DB_PASSWORD', value: 'change-me-db-password', fill: true }, { key: 'DB_ROOT_PASSWORD', value: 'change-me-root-password', fill: true }, { key: 'JWT_SECRET', value: 'change-me-to-a-long-random-string', fill: true }, { key: 'SECRET_ENC_KEY', value: 'change-me-to-another-long-random-string', fill: true }, { key: 'COOKIE_SECURE', value: 'auto' }, { key: 'TRUST_PROXY', value: '1' }, { key: 'ADMIN_USERNAME', value: 'admin', fill: true }, { key: 'ADMIN_PASSWORD', value: 'change-me-before-first-boot', fill: true }, { key: 'BOT_INTERNAL_KEY', value: 'change-me-to-a-third-long-random-string', fill: true }, ]; /** * Keys this quickstart sets that upstream's `.env.example` does not, each with the reason. * * **Empty, and that is the point.** Its one entry was `SECRET_ENC_KEY`: phase 7 booted this * exact file against the published image and the container crash-looped before it ever * listened, because `resolveKey()` in `utils/secretBox.js` throws * `SECRET_ENC_KEY must be set in production` at require time. The variable was documented in * `server/.env.example` — the file local development copies — and missing from the root * `.env.example` that Compose actually reads. * * The declaration was written so it could not outlive the defect: the check fails the moment * a declared key appears upstream. website#163 fixed `.env.example`, this repo went red on * the next run, and the entry was deleted. Keep the export — the next divergence gets an * entry here rather than passing quietly. */ export const notInUpstreamEnvExample = {}; /** * Variables upstream's `.env.example` carries that the quickstart leaves out, each with the * reason. The check requires this list plus the keys above to account for EVERY key in * `.env.example`: when website adds a variable, this repo goes red and someone decides * whether a first install needs it. That failure is the feature. */ export const envOmitted = { UPLOAD_DIR: 'set in the Compose file, where the volume that makes it meaningful is', LOG_LEVEL: 'logging defaults are fine until there is something to debug', FILE_LOG_LEVEL: 'as above', LOG_TO_FILE: 'as above', LOG_DIR: 'set in the Compose file, beside its bind mount', LOG_FILE: 'as above', BRAND_NAME: 'branding is its own admin screen and its own page', BRAND_SHORT_NAME: 'as above', BRAND_TAGLINE: 'as above', BRAND_DESCRIPTION: 'as above', BRAND_CONTACT_EMAIL: 'as above', BRAND_URL: 'as above', BRAND_ACCENT_COLOR: 'as above', BRAND_LOGO: 'as above', BRAND_HERO: 'as above', BRAND_FAVICON: 'as above', JWT_EXPIRES_IN: 'the default session length is a decision for later, not for boot', COOKIE_NAME: 'changing it logs everyone out; not a first-install decision', DEBUG_TRUST_PROXY: 'a diagnostic, and a noisy one', TOTP_CHALLENGE_TTL: 'the default is right', CLIENT_ORIGIN: 'only needed when the client is served from a different origin, which a Compose deployment does not do', BOT_INTERNAL_URL: 'points at the bot service, which this quickstart does not run', NTFY_BASE_URL: 'push notifications need the ntfy service, which this quickstart does not run', };