From f624b34bf31ee13b83be6c435d75ef80011d4879 Mon Sep 17 00:00:00 2001 From: Natalie Rose Date: Mon, 7 Sep 2026 11:22:41 +1000 Subject: [PATCH] Simpler structure --- .env.example | 1 + .gitignore | 1 + CLAUDE.md | 100 +++++++++ .../Dockerfile => Dockerfile.logrotate | 2 +- .../nginx/Dockerfile => Dockerfile.nginx | 0 README.md | 23 ++ components/app/docker-compose.yml | 8 - components/beanstalk/docker-compose.yml | 14 -- components/logrotate/docker-compose.yml | 13 -- components/memcached/docker-compose.yml | 9 - components/mysql/docker-compose.yml | 11 - components/nginx/docker-compose.yml | 24 --- components/phpmyadmin/docker-compose.yml | 12 -- components/server/Dockerfile | 76 ------- components/server/docker-compose.yml | 80 ------- components/super-ui/docker-compose.yml | 8 - components/v2-api/Dockerfile | 6 - components/v2-api/docker-compose.yml | 15 -- docker-compose.yml | 201 ++++++++++++++---- .../entrypoint.sh => entrypoint-logrotate.sh | 8 +- lacuna-prod.conf => etc/lacuna.example.conf | 14 +- log4perl.conf => etc/log4perl.conf | 0 lacuna.logrotate | 34 +++ {components/nginx => nginx}/nginx.conf | 0 .../server/schedule.ini => schedule.ini | 0 setup.sql | 6 + 26 files changed, 341 insertions(+), 325 deletions(-) create mode 100644 CLAUDE.md rename components/logrotate/Dockerfile => Dockerfile.logrotate (85%) rename components/nginx/Dockerfile => Dockerfile.nginx (100%) delete mode 100644 components/app/docker-compose.yml delete mode 100644 components/beanstalk/docker-compose.yml delete mode 100644 components/logrotate/docker-compose.yml delete mode 100644 components/memcached/docker-compose.yml delete mode 100644 components/mysql/docker-compose.yml delete mode 100644 components/nginx/docker-compose.yml delete mode 100644 components/phpmyadmin/docker-compose.yml delete mode 100644 components/server/Dockerfile delete mode 100644 components/server/docker-compose.yml delete mode 100644 components/super-ui/docker-compose.yml delete mode 100644 components/v2-api/Dockerfile delete mode 100644 components/v2-api/docker-compose.yml rename components/logrotate/entrypoint.sh => entrypoint-logrotate.sh (70%) rename lacuna-prod.conf => etc/lacuna.example.conf (89%) rename log4perl.conf => etc/log4perl.conf (100%) create mode 100644 lacuna.logrotate rename {components/nginx => nginx}/nginx.conf (100%) rename components/server/schedule.ini => schedule.ini (100%) create mode 100644 setup.sql diff --git a/.env.example b/.env.example index d9c2d07..ed66870 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ CONTAINER_REGISTRY_URL= +LACUNA_MYSQL_ROOT_PASSWORD= diff --git a/.gitignore b/.gitignore index 4c49bd7..686f6ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +etc/lacuna.conf diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d4cf5a0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,100 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this repo is + +Docker Compose orchestration for deploying **The Lacuna Expanse** (TLE), a browser MMO. +It contains **no application source** — every service except `nginx` and `logrotate` runs a +prebuilt image pulled by `:latest` tag from the private registry at `$CONTAINER_REGISTRY_URL` +(a Gitea instance). To change server, frontend, or v2 API behaviour you work in the sibling +repos (`../server` Perl backend, `../app` and `../client` frontends, `../v2-api`) and publish +new images; this repo only wires them together and supplies runtime config. + +The initial commit used a per-service `components//docker-compose.yml` layout. That has +been collapsed into the single root `docker-compose.yml` (uncommitted working tree). Some +Dockerfile comments still reference `docker/docker-compose.homelab.yml` — that path is from +the `../server` repo, not this one; the canonical compose file here is `./docker-compose.yml`. + +## Common commands + +```bash +docker compose pull # fetch prebuilt images from the registry +docker compose build # build the two local images (nginx, logrotate) +docker compose up -d +docker compose logs -f server +docker compose run --rm server bash # shell inside the Perl server image +``` + +First-time bring-up (inside `docker compose run --rm server bash`): + +```bash +mysql # client is preconfigured for the mysql container +mysql> source setup.sql # creates the `lacuna` db + user +mysql> exit +cd setup +perl init_lacuna.pl # game world initialisation +perl generate_captcha.pl +``` + +## Architecture + +All services share the `lacuna` Docker network; the compose project name is also `lacuna`. + +**Request path.** `nginx` (built from `Dockerfile.nginx`, config `nginx/nginx.conf`) is the +front proxy. It serves static files from the shared `./var/www/public` tree, proxies `/v2/*` +to `v2-api:5999`, serves `/captcha/*` from `./captcha`, and sends everything else to +`server:5000`. `nginx` is the **public internet-facing edge**: it is exposed directly on the +open web (no Tailscale), so it must publish host ports 80/443. TLS terminates at `nginx` +itself or at a thin TLS-only host proxy directly in front of it — nothing else sits between +`nginx` and the internet. `server_url` in `etc/lacuna.conf` is the public domain name. + +> The working-tree `docker-compose.yml` still has no `ports:` entry on the `nginx` service — +> add `80:80` (and `443:443` if TLS terminates in the container) for direct exposure. + +**Backends.** +- `server` — Perl/Plack v1 JSON-RPC backend (`start_lacuna.sh`), image `tlecommunity/server`, + listens on 5000 (host 3050). `TLE_NO_MIDDLEWARE=1` disables the prod size-limit/profiling + middleware; `LACUNA_LOG_DIR` sets the log root. +- `v2-api` — Node v2 REST backend (`node index.ts`), image `tlecommunity/v2-api`, port 80 + (host 5999). +- `app` / `super-ui` — Caddy-served frontend SPA and admin UI images; no published ports. + +**Data + infra.** `mysql` 5.5 (host 3306, root password from `LACUNA_MYSQL_ROOT_PASSWORD`), +`phpmyadmin` (host 8888), `memcached` (sessions/cache, persisted to a file under +`./data/memcached`), `beanstalk` job queue (schickling/beanstalkd, 11300) with +`beanstalk-console` (2080). + +**Schedulers** — all reuse the `tlecommunity/server` image: +- `server-building-scheduler`, `server-ship-scheduler`, `server-captcha-scheduler` each run + one long-lived `run_scheduler.sh schedule_*.pl` daemon. +- `server-script-scheduler` runs Ofelia (`mcuadros/ofelia`), which reads `schedule.ini` and + executes the `run_{hourly,two_hourly,four_hourly,daily,weekly}.sh` batch scripts *inside* + the `server` container via the mounted `/var/run/docker.sock`. + +**logrotate** — Alpine sidecar (`Dockerfile.logrotate`, `entrypoint-logrotate.sh`) that runs +`logrotate` once a day against the shared `./log` tree using the policy in `lacuna.logrotate` +(`copytruncate`, since producers in other containers hold log handles open with no reopen +signal). + +## Configuration + +Gitignored, copy from the `*.example` sibling and fill in: +- `.env` (from `.env.example`) — `CONTAINER_REGISTRY_URL`, `LACUNA_MYSQL_ROOT_PASSWORD`. +- `etc/lacuna.conf` (from `etc/lacuna.example.conf`) — the main game config: JSON-with-`#`-comments. + Covers db DSN, beanstalk/memcached endpoints, captcha fonts, map size and neutral/starter + zones, payment gateways, `server_url`/`assets_url`. + +Tracked config: `etc/log4perl.conf` (Perl logging — INFO to stderr + `log/server/lacuna.log`), +`nginx/nginx.conf`, `schedule.ini` (Ofelia cron), `setup.sql` (db + user bootstrap). + +## Host-mounted trees + +Created on the deploy host, mostly not in git: +- `./var` — shared runtime tree between `server` and `nginx` (generated static assets under + `var/www/public`, captcha fonts under `var/fonts`). +- `./log` — unified log tree (`server/`, `scheduler/`, `cron/`, `deploy/`, `nginx/`), consumed + by the `logrotate` sidecar. +- `./tmp`, `./captcha` — server scratch and captcha image output, shared with `nginx`. +- `./data/*` — persistent volumes for `mysql`, `memcached`, and the Caddy data/config dirs of + `app` and `super-ui`. diff --git a/components/logrotate/Dockerfile b/Dockerfile.logrotate similarity index 85% rename from components/logrotate/Dockerfile rename to Dockerfile.logrotate index 2935d0e..56b2d45 100644 --- a/components/logrotate/Dockerfile +++ b/Dockerfile.logrotate @@ -4,7 +4,7 @@ FROM alpine:3.20 RUN apk add --no-cache logrotate tzdata -COPY entrypoint.sh /entrypoint.sh +COPY entrypoint-logrotate.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] diff --git a/components/nginx/Dockerfile b/Dockerfile.nginx similarity index 100% rename from components/nginx/Dockerfile rename to Dockerfile.nginx diff --git a/README.md b/README.md index 952c62c..c116247 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ # Deployment Docker files for TLE deployment + +# Getting Started + +Assuming you have `.env` and `etc/lacuna.conf` configured and ready to go! + +```bash +docker compose pull +docker compose build + +# Open a bash session inside the server container +docker compose run --rm server bash + +# Setup database +mysql + +mysql> source setup.sql +mysql> exit + +# Run the game initiation scripts +cd setup +perl init_lacuna.pl +perl generate_captcha.pl +``` diff --git a/components/app/docker-compose.yml b/components/app/docker-compose.yml deleted file mode 100644 index a4ecf49..0000000 --- a/components/app/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - app: - image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/app:latest - container_name: app - restart: unless-stopped - volumes: - - ./caddy-data:/data - - ./caddy-config:/config diff --git a/components/beanstalk/docker-compose.yml b/components/beanstalk/docker-compose.yml deleted file mode 100644 index 53036c2..0000000 --- a/components/beanstalk/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - beanstalk-console: - container_name: beanstalk-console - depends_on: - - beanstalk - image: schickling/beanstalkd-console - ports: - - 2080:2080 - - beanstalk: - container_name: beanstalk - image: schickling/beanstalkd - ports: - - 11300:11300 diff --git a/components/logrotate/docker-compose.yml b/components/logrotate/docker-compose.yml deleted file mode 100644 index 6d799a6..0000000 --- a/components/logrotate/docker-compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - # Rotates the shared ./log tree daily (copytruncate, 14 days, gzip). - # Prod only -- dev/test churn the stack often enough and rely on the - # json-file size caps plus manual cleanup. - logrotate: - build: - context: ./logrotate - dockerfile: Dockerfile - container_name: logrotate - restart: unless-stopped - volumes: - - ./../log:/home/lacuna/server/log - - ./../bin/lacuna.logrotate:/etc/logrotate.d/lacuna:ro diff --git a/components/memcached/docker-compose.yml b/components/memcached/docker-compose.yml deleted file mode 100644 index 86737e9..0000000 --- a/components/memcached/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -services: - memcached: - container_name: memcached - image: memcached - command: - # Persist cache in a file to save sessions (and other data) between restarts of the docker container - - --memory-file=/tempfs_mount/memory_file - volumes: - - ./data:/tempfs_mount diff --git a/components/mysql/docker-compose.yml b/components/mysql/docker-compose.yml deleted file mode 100644 index 12e20ab..0000000 --- a/components/mysql/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -services: - mysql: - container_name: mysql - platform: linux/amd64 - environment: - MYSQL_ROOT_PASSWORD: lacuna - image: mysql:5.5 - volumes: - - ./data:/var/lib/mysql - ports: - - 3306:3306 diff --git a/components/nginx/docker-compose.yml b/components/nginx/docker-compose.yml deleted file mode 100644 index 9136d0d..0000000 --- a/components/nginx/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -services: - nginx: - build: - context: ./../../ - dockerfile: ./docker/nginx/Dockerfile - container_name: nginx - # Bound the json-file log (YAML anchors don't cross files, so this is spelled - # out here rather than sharing docker/server/docker-compose.yml's anchor). - logging: - driver: json-file - options: - max-size: "10m" - max-file: "5" - depends_on: - server: - condition: service_started - v2-api: - condition: service_started - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - # - ./../../var:/home/lacuna/server/var - - ./../../log/nginx:/etc/nginx/logs - # - ./../../captcha:/home/lacuna/server/captcha - # - ./../../var/www/public/api/api.css:/home/lacuna/server/var/www/public/api/api.css diff --git a/components/phpmyadmin/docker-compose.yml b/components/phpmyadmin/docker-compose.yml deleted file mode 100644 index 5a9f281..0000000 --- a/components/phpmyadmin/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - phpmyadmin: - container_name: phpmyadmin - image: phpmyadmin:5.2-apache - environment: - PMA_HOST: mysql - ports: - - 8888:80 - depends_on: - - mysql - links: - - mysql diff --git a/components/server/Dockerfile b/components/server/Dockerfile deleted file mode 100644 index 2ac4588..0000000 --- a/components/server/Dockerfile +++ /dev/null @@ -1,76 +0,0 @@ -# The Lacuna game server (Perl backend). -# -# Perl 5.40 on the official image (Debian bookworm). Replaces the old -# hand-rolled source build of perl-5.24.0 on almalinux:8. -# -# Dependencies come from /home/lacuna/server/cpanfile + cpanfile.snapshot via -# Carton. To change a dependency: edit cpanfile at the repo root, run -# `carton install` to refresh cpanfile.snapshot, commit both. -# -# Application source (lib/, bin/, t/, etc/) is bind-mounted at runtime by -# docker-compose — it is NOT copied into this image. - -FROM perl:5.40-bookworm - -# System libraries the XS dependencies link against (Debian package names). -RUN apt-get update && apt-get install -y --no-install-recommends \ - build-essential \ - pkg-config \ - git \ - curl \ - ca-certificates \ - default-libmysqlclient-dev \ - libgd-dev \ - libexpat1-dev \ - libxml2-dev \ - libssl-dev \ - zlib1g-dev \ - libncurses-dev \ - libmemcached-dev \ - && rm -rf /var/lib/apt/lists/* - -# -f: no-op if the group already exists (it does on Debian; it did not on AlmaLinux). -RUN groupadd -f nogroup - -ENV ANYEVENT_WEBSOCKET_TEST_SKIP_SSL=1 - -WORKDIR /home/lacuna/server - -# Dependency manifest + lockfile. Kept in its own layer so a source change -# doesn't reinstall ~100 CPAN modules. -COPY cpanfile cpanfile.snapshot* ./ - -RUN cpanm --notest Carton - -# Memcached::libmemcached compiles a bundled libmemcached from source (a full -# autotools project) and overruns cpanm's default build-phase timeout. Carton -# wipes PERL_CPANM_OPT, so it can't pass the longer timeout through — pre-install -# this one module into ./local with an explicit timeout (version pinned to match -# the snapshot); carton then finds it already satisfied and skips it. -RUN cpanm --notest -l local --build-timeout=3600 --configure-timeout=1800 \ - Memcached::libmemcached@1.001801 - -# With a committed cpanfile.snapshot, install strictly from it (reproducible). -# On the very first build (no snapshot yet), resolve freely and generate one — -# extract it with `docker cp` and commit it, then all later builds are pinned. -RUN if [ -f cpanfile.snapshot ]; then carton install --deployment; else carton install; fi \ - && rm -rf /root/.cpanm /root/.perl-cpm - -# Carton installs into ./local — put its binaries and libs on the default -# search paths so `plackup` / `perl` in bin/startdev.sh and the compose -# `command:` lines resolve without needing `carton exec`. -ENV PATH="/home/lacuna/server/local/bin:${PATH}" \ - PERL5LIB="/home/lacuna/server/local/lib/perl5" \ - PERLLIB="/home/lacuna/server/lib" \ - TERM=xterm - -# Log tree (mirrors docker/nginx/Dockerfile's `mkdir /etc/nginx/logs`). Compose -# bind-mounts host ./log over this at run time; creating it here means the -# subdirs still exist when the mount is absent (bare-metal, or the test overlay -# which redirects LACUNA_LOG_DIR elsewhere). -RUN mkdir -p /home/lacuna/server/log/server \ - /home/lacuna/server/log/scheduler \ - /home/lacuna/server/log/cron \ - /home/lacuna/server/log/deploy - -WORKDIR /home/lacuna/server/bin diff --git a/components/server/docker-compose.yml b/components/server/docker-compose.yml deleted file mode 100644 index 40759c7..0000000 --- a/components/server/docker-compose.yml +++ /dev/null @@ -1,80 +0,0 @@ -# Bound every container's json-file log so `docker logs` / the host daemon's -# disk cannot grow without limit. `extends` (used by the dev/test/homelab -# overlays) copies the *resolved* logging map, so the anchor need only live here. -x-logging: &default-logging - driver: json-file - options: - max-size: '10m' - max-file: '5' - -services: - server: - image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest - command: '/bin/bash start_lacuna.sh' - container_name: server - depends_on: - - mysql - - memcached - - beanstalk - environment: - # Root of the host-mounted log tree (see the ./../../log mount below). - # Scripts fall back to this same path when the var is unset. - LACUNA_LOG_DIR: /home/lacuna/server/log - logging: *default-logging - ports: - - 3050:5000 - volumes: - - ./../../captcha:/home/lacuna/server/captcha - # - ./../../missions:/home/lacuna/server/missions - - ./../../tmp:/tmp - - ./../../log:/home/lacuna/server/log - - server-building-scheduler: - image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest - command: '/bin/bash run_scheduler.sh schedule_building.pl --noquiet --nodaemonize --initialize' - container_name: server-building-scheduler - depends_on: - - server - environment: - LACUNA_LOG_DIR: /home/lacuna/server/log - logging: *default-logging - volumes: - - ./../../tmp:/tmp - - ./../../log:/home/lacuna/server/log - - server-ship-scheduler: - image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest - command: '/bin/bash run_scheduler.sh schedule_ship_arrival.pl --noquiet --nodaemonize --initialize' - container_name: server-ship-scheduler - depends_on: - - server - environment: - LACUNA_LOG_DIR: /home/lacuna/server/log - logging: *default-logging - volumes: - - ./../../tmp:/tmp - - ./../../log:/home/lacuna/server/log - - server-captcha-scheduler: - image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest - command: '/bin/bash run_scheduler.sh schedule_captcha.pl --noquiet --nodaemonize' - container_name: server-captcha-scheduler - depends_on: - - server - environment: - LACUNA_LOG_DIR: /home/lacuna/server/log - logging: *default-logging - volumes: - - ./../../tmp:/tmp - - ./../../log:/home/lacuna/server/log - - server-script-scheduler: - container_name: server-script-scheduler - image: mcuadros/ofelia:latest - depends_on: - - server - command: daemon - logging: *default-logging - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - ./schedule.ini:/etc/ofelia.conf diff --git a/components/super-ui/docker-compose.yml b/components/super-ui/docker-compose.yml deleted file mode 100644 index 3dd6348..0000000 --- a/components/super-ui/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - super-ui: - image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/super-ui:latest - container_name: super-ui - restart: unless-stopped - volumes: - - ./caddy-data:/data - - ./caddy-config:/config diff --git a/components/v2-api/Dockerfile b/components/v2-api/Dockerfile deleted file mode 100644 index 82cf7a1..0000000 --- a/components/v2-api/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM node:24 - -WORKDIR /usr/src/v2-api - -COPY ./v2-api/package.json ./v2-api/package-lock.json ./ -RUN npm ci diff --git a/components/v2-api/docker-compose.yml b/components/v2-api/docker-compose.yml deleted file mode 100644 index 7512ec4..0000000 --- a/components/v2-api/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -volumes: - v2-api-node_modules: - -services: - v2-api: - build: - context: ./../../ - dockerfile: ./docker/v2-api/Dockerfile - container_name: v2-api - command: node index.ts - ports: - - 5999:5999 - volumes: - - v2-api-node_modules:/usr/src/v2-api/node_modules - - ./../../v2-api:/usr/src/v2-api diff --git a/docker-compose.yml b/docker-compose.yml index c6ae213..97ecf3d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,64 +1,193 @@ - -# Overlays that customise a service pull it in via `extends` (not `include`): -# Compose rejects a top-level override of a service that was brought in through -# `include` ("conflicts with imported resource"). Services used as-is stay in -# `include` below. -include: - - ./components/beanstalk/docker-compose.yml - - ./components/memcached/docker-compose.yml - - ./components/mysql/docker-compose.yml - - ./components/super-ui/docker-compose.yml - - ./components/app/docker-compose.yml - - ./components/phpmyadmin/docker-compose.yml - - ./components/nginx/docker-compose.yml - # - ./components/logrotate/docker-compose.yml - # - ./components/v2-api/docker-compose.yml +x-logging: &default-logging + driver: json-file + options: + max-size: '10m' + max-file: '5' services: + app: + image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/app:latest + container_name: app + restart: unless-stopped + volumes: + - ./data/app-caddy-data:/data + - ./data/app-caddy-config:/config + + beanstalk-console: + container_name: beanstalk-console + depends_on: + - beanstalk + image: schickling/beanstalkd-console + ports: + - 2080:2080 + + beanstalk: + container_name: beanstalk + image: schickling/beanstalkd + ports: + - 11300:11300 + + logrotate: + build: + context: . + dockerfile: Dockerfile.logrotate + container_name: logrotate + restart: unless-stopped + volumes: + - ./log:/home/lacuna/server/log + - ./lacuna.logrotate:/etc/logrotate.d/lacuna:ro + + memcached: + container_name: memcached + image: memcached + command: + # Persist cache in a file to save sessions (and other data) between restarts of the docker container + - --memory-file=/tempfs_mount/memory_file + volumes: + - ./data/memcached:/tempfs_mount + + mysql: + container_name: mysql + platform: linux/amd64 + environment: + MYSQL_ROOT_PASSWORD: ${LACUNA_MYSQL_ROOT_PASSWORD:?error} + image: mysql:5.5 + volumes: + - ./data/mysql:/var/lib/mysql + ports: + - 3306:3306 + + nginx: + build: + context: . + dockerfile: Dockerfile.nginx + container_name: nginx + logging: *default-logging + depends_on: + server: + condition: service_started + v2-api: + condition: service_started + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - ./var:/home/lacuna/server/var + - ./log/nginx:/etc/nginx/logs + - ./captcha:/home/lacuna/server/captcha + - ./var/www/public/api/api.css:/home/lacuna/server/var/www/public/api/api.css + + phpmyadmin: + container_name: phpmyadmin + image: phpmyadmin:5.2-apache + environment: + PMA_HOST: mysql + ports: + - 8888:80 + depends_on: + - mysql + links: + - mysql + server: - extends: - file: ./components/server/docker-compose.yml - service: server + image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest + command: '/bin/bash start_lacuna.sh' + container_name: server restart: unless-stopped + depends_on: + - mysql + - memcached + - beanstalk environment: + # Root of the host-mounted log tree (see the ./../../log mount below). + # Scripts fall back to this same path when the var is unset. + LACUNA_LOG_DIR: /home/lacuna/server/log # Disables the prod-only size-limit / profiling middleware (bin/lacuna.psgi). TLE_NO_MIDDLEWARE: 1 + logging: *default-logging + ports: + - 3050:5000 volumes: - # - ./server/etc-homelab:/home/lacuna/server/etc + - ./etc:/home/lacuna/server/etc + - ./captcha:/home/lacuna/server/captcha + - ./tmp:/tmp + - ./log:/home/lacuna/server/log + - ./var:/home/lacuna/server/var server-building-scheduler: - extends: - file: ./components/server/docker-compose.yml - service: server-building-scheduler + image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest + command: '/bin/bash run_scheduler.sh schedule_building.pl --noquiet --nodaemonize --initialize' + container_name: server-building-scheduler restart: unless-stopped + depends_on: + - server + environment: + LACUNA_LOG_DIR: /home/lacuna/server/log + logging: *default-logging volumes: - # - ./server/etc-homelab:/home/lacuna/server/etc + - ./tmp:/tmp + - ./log:/home/lacuna/server/log + - ./etc:/home/lacuna/server/etc server-ship-scheduler: - extends: - file: ./components/server/docker-compose.yml - service: server-ship-scheduler + image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest + command: '/bin/bash run_scheduler.sh schedule_ship_arrival.pl --noquiet --nodaemonize --initialize' + container_name: server-ship-scheduler restart: unless-stopped + depends_on: + - server + environment: + LACUNA_LOG_DIR: /home/lacuna/server/log + logging: *default-logging volumes: - # - ./server/etc-homelab:/home/lacuna/server/etc + - ./tmp:/tmp + - ./log:/home/lacuna/server/log + - ./etc:/home/lacuna/server/etc server-captcha-scheduler: - extends: - file: ./components/server/docker-compose.yml - service: server-captcha-scheduler + image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/server:latest + command: '/bin/bash run_scheduler.sh schedule_captcha.pl --noquiet --nodaemonize' + container_name: server-captcha-scheduler restart: unless-stopped + depends_on: + - server + environment: + LACUNA_LOG_DIR: /home/lacuna/server/log + logging: *default-logging volumes: - # - ./server/etc-homelab:/home/lacuna/server/etc + - ./tmp:/tmp + - ./log:/home/lacuna/server/log + - ./etc:/home/lacuna/server/etc server-script-scheduler: - extends: - file: ./components/server/docker-compose.yml - service: server-script-scheduler + container_name: server-script-scheduler + image: mcuadros/ofelia:latest + restart: unless-stopped + depends_on: + - server + command: daemon + logging: *default-logging + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./schedule.ini:/etc/ofelia.conf + + super-ui: + image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/super-ui:latest + container_name: super-ui restart: unless-stopped + volumes: + - ./data/super-ui-caddy-data:/data + - ./data/super-ui-caddy-config:/config + v2-api: + image: ${CONTAINER_REGISTRY_URL:?error}/tlecommunity/v2-api:latest + container_name: v2-api + command: node index.ts + ports: + - 5999:80 + environment: + PORT: 80 networks: default: - name: lacuna-homelab + name: lacuna -name: lacuna-homelab +name: lacuna diff --git a/components/logrotate/entrypoint.sh b/entrypoint-logrotate.sh similarity index 70% rename from components/logrotate/entrypoint.sh rename to entrypoint-logrotate.sh index 6ceb949..17045b0 100644 --- a/components/logrotate/entrypoint.sh +++ b/entrypoint-logrotate.sh @@ -1,8 +1,10 @@ #!/bin/sh + # # Run logrotate once a day against the bind-mounted config. `|| true` so a # transient error (e.g. a log file mid-rename) doesn't kill the container. # + set -eu CONF=/etc/logrotate.d/lacuna @@ -11,7 +13,7 @@ STATE=/var/lib/logrotate/lacuna.status mkdir -p /var/lib/logrotate while true; do - echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ') running logrotate" - logrotate -v -s "$STATE" "$CONF" || true - sleep 86400 + echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ') running logrotate" + logrotate -v -s "$STATE" "$CONF" || true + sleep 86400 done diff --git a/lacuna-prod.conf b/etc/lacuna.example.conf similarity index 89% rename from lacuna-prod.conf rename to etc/lacuna.example.conf index cbb1dce..685e465 100644 --- a/lacuna-prod.conf +++ b/etc/lacuna.example.conf @@ -104,8 +104,8 @@ "server_keys" : [ "XXXX" ], - "server_url" : "https://lacuna.allosaurus-chromatic.ts.net/", - "assets_url": "https://lacuna.allosaurus-chromatic.ts.net/assets/", + "server_url" : "https://meanjin-one.tlecommunity.com", + "assets_url": "https://meanjin-one.tlecommunity.com/assets", "starter_zone" : { # If zone is set to a valid zone, x & y are confined to within that zone. # Coord set to 1 will further narrow area. @@ -128,16 +128,12 @@ }, "voting_sites" : [ { - "name" : "Rose Harbour", + "name" : "Natalie Rose", "url" : "https://nataliethistime.com/" }, { - "name" : "YouTube Channel", - "url" : "https://www.youtube.com/@nataliethistime" - }, - { - "name" : "TLE Community GitHub", - "url" : "https://github.com/tlecommunity" + "name" : "TLE Community", + "url" : "https://tlecommunity.com/" } ], "test" : { diff --git a/log4perl.conf b/etc/log4perl.conf similarity index 100% rename from log4perl.conf rename to etc/log4perl.conf diff --git a/lacuna.logrotate b/lacuna.logrotate new file mode 100644 index 0000000..0e47136 --- /dev/null +++ b/lacuna.logrotate @@ -0,0 +1,34 @@ +# logrotate configuration for The Lacuna Expanse server. +# +# Consumed by the logrotate sidecar defined in docker/docker-compose.homelab.yml, +# which bind-mounts host ./log at /home/lacuna/server/log and this file at +# /etc/logrotate.d/lacuna. (It can equally be installed as /etc/logrotate.d/lacuna +# on a bare-metal host.) The paths below are the in-container log tree, which is +# also the host ./log tree via the bind mount. +# +# Every producer now writes into this tree: +# server/ - the plack app (Log4perl FILE appender) + plackup framework noise +# scheduler/ - schedule_{building,ship_arrival,captcha}.pl (via bin/run_scheduler.sh) +# cron/ - the ofelia-driven bin/run_*.sh batch scripts +# deploy/ - bin/deploy.pl +# nginx/ - nginx access.log + error.log +# +# copytruncate: plackup, the scheduler daemons and nginx hold their log handles +# open and have no cross-container reopen signal; truncating in place on the +# shared bind mount needs none. + +/home/lacuna/server/log/server/*.log +/home/lacuna/server/log/scheduler/*.log +/home/lacuna/server/log/cron/*.log +/home/lacuna/server/log/deploy/*.log +/home/lacuna/server/log/nginx/*.log +{ + daily + rotate 14 + missingok + notifempty + compress + delaycompress + copytruncate + sharedscripts +} diff --git a/components/nginx/nginx.conf b/nginx/nginx.conf similarity index 100% rename from components/nginx/nginx.conf rename to nginx/nginx.conf diff --git a/components/server/schedule.ini b/schedule.ini similarity index 100% rename from components/server/schedule.ini rename to schedule.ini diff --git a/setup.sql b/setup.sql new file mode 100644 index 0000000..4d86e1c --- /dev/null +++ b/setup.sql @@ -0,0 +1,6 @@ +create database lacuna; +create user 'lacuna'@'%' identified by 'lacuna'; +grant all privileges on lacuna.* to 'lacuna'@'%'; +flush privileges; + + -- 2.51.2