Compare commits

...

3 commits

Author SHA1 Message Date
ffdeacc0c4 Merge pull request #1: Portainer stack and deployment doc for Nox
Some checks are pending
Build / build (push) Waiting to run
Lint / eslint (push) Waiting to run
2026-09-02 13:10:56 -06:00
Claude
788afd6e8e
docs: chown the data directory before downloading into it
Some checks failed
Build / build (push) Has been cancelled
Lint / eslint (push) Has been cancelled
Build / build (pull_request) Has been cancelled
Lint / eslint (pull_request) Has been cancelled
The procedure created /opt/mythica-armory with sudo but then ran the curl
downloads as the normal user, so every one failed with "Permission denied /
Failure writing output to destination". Adds the chown and notes that user
ownership is fine, since the container reads the bind mount as root.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NW2ooBP2KPqQVzdZZDMvZd
2026-09-02 17:31:50 +00:00
Claude
52a3af6642
Add a Portainer stack and deployment doc for Nox
Some checks failed
Build / build (push) Waiting to run
Lint / eslint (push) Waiting to run
Build / build (pull_request) Has been cancelled
Lint / eslint (pull_request) Has been cancelled
Deploys this fork alongside the Mythica web page, reading the AzerothCore
databases over the LAN. Nothing runs on the game server itself.

docker-compose.mythica.yml differs from upstream's compose because Portainer
deploys from a git checkout it manages:

- Configuration comes from Portainer stack environment variables rather than
  a .env file in the repo, which git stacks do not provide.
- Only the four large model-data directories are bind-mounted from the host,
  so the DBC csv files stay versioned with the code in the image and a repo
  re-clone cannot wipe the 2 GB of model data.
- The unused ac-network block is dropped.

The doc also records why the model data is a manual step: upstream's fetchdata
tool no longer works, because Zam reorganised the CDN and every character path
now 404s. A run appears to succeed while producing no character data, and the
app then fails at startup on meta/charactercustomization2/1_0.json. The v1.0.0
snapshot mirrored into this fork's release is the only remaining source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NW2ooBP2KPqQVzdZZDMvZd
2026-09-02 17:21:45 +00:00
2 changed files with 213 additions and 0 deletions

View file

@ -0,0 +1,80 @@
# Portainer stack for the Mythica armory.
#
# Differs from the upstream docker-compose.yml in three ways, all of them
# because Portainer deploys from a git checkout it manages itself:
#
# 1. Configuration comes from environment variables set in the Portainer
# stack UI, not from a .env file in the repo (upstream uses env_file,
# which Portainer git stacks do not provide).
# 2. Only the four large model-data directories are bind-mounted from the
# host. The DBC .csv files stay in the image so they stay versioned with
# the code, and Portainer re-cloning the repo cannot wipe the 2 GB of
# model data.
# 3. The unused ac-network block is dropped.
#
# See docs/mythica-deploy.md for the full procedure.
services:
armory:
build:
context: .
dockerfile: Dockerfile
container_name: mythica-armory
restart: unless-stopped
environment:
# --- site identity -------------------------------------------------
ACORE_ARMORY_WEBSITE_NAME: ${ARMORY_WEBSITE_NAME:-Mythica}
ACORE_ARMORY_WEBSITE_URL: ${ARMORY_WEBSITE_URL:-http://192.168.1.77:48733}
ACORE_ARMORY_WEBSITE_ROOT: ${ARMORY_WEBSITE_ROOT:-}
# Item tooltips and item icons are loaded from this AoWoW instance.
# The default is a public third-party site: point this at your own
# AoWoW if you do not want that dependency.
ACORE_ARMORY_AOWOW_URL: ${ARMORY_AOWOW_URL:-https://wowgaming.altervista.org/aowow}
# --- embedding -----------------------------------------------------
# Set both when embedding the armory in a page on the main site.
ACORE_ARMORY_IFRAME_MODE__ENABLED: ${ARMORY_IFRAME_ENABLED:-0}
ACORE_ARMORY_IFRAME_MODE__URL: ${ARMORY_IFRAME_URL:-}
# --- behaviour -----------------------------------------------------
ACORE_ARMORY_LOAD_DBCS: ${ARMORY_LOAD_DBCS:-1}
ACORE_ARMORY_HIDE_GAME_MASTERS: ${ARMORY_HIDE_GMS:-1}
ACORE_ARMORY_TRANSMOG_MODULE: ${ARMORY_TRANSMOG:-0}
# 0 = serve 3D model assets from the local data directory.
# Leave it at 0: the upstream fetch tool can no longer rebuild this
# data from the Zam CDN, so the local copy is the only source.
ACORE_ARMORY_USE_ZAM_CDN: ${ARMORY_USE_ZAM_CDN:-0}
# --- realm ---------------------------------------------------------
ACORE_ARMORY_REALMS__0__NAME: ${ARMORY_REALM_NAME:-Mythica}
ACORE_ARMORY_REALMS__0__REALM_ID: ${ARMORY_REALM_ID:-1}
ACORE_ARMORY_REALMS__0__AUTH_DATABASE: ${ARMORY_AUTH_DB:-acore_auth}
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__HOST: ${ARMORY_DB_HOST:-192.168.1.60}
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__PORT: ${ARMORY_DB_PORT:-3306}
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__USER: ${ARMORY_DB_USER:?set ARMORY_DB_USER}
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__PASSWORD: ${ARMORY_DB_PASS:?set ARMORY_DB_PASS}
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__DATABASE: ${ARMORY_CHARACTERS_DB:-acore_characters}
# --- world database -------------------------------------------------
ACORE_ARMORY_WORLD_DATABASE__HOST: ${ARMORY_DB_HOST:-192.168.1.60}
ACORE_ARMORY_WORLD_DATABASE__PORT: ${ARMORY_DB_PORT:-3306}
ACORE_ARMORY_WORLD_DATABASE__USER: ${ARMORY_DB_USER:?set ARMORY_DB_USER}
ACORE_ARMORY_WORLD_DATABASE__PASSWORD: ${ARMORY_DB_PASS:?set ARMORY_DB_PASS}
ACORE_ARMORY_WORLD_DATABASE__DATABASE: ${ARMORY_WORLD_DB:-acore_world}
ACORE_ARMORY_DB_QUERY_TIMEOUT: ${ARMORY_DB_TIMEOUT:-10000}
volumes:
# The 3D model payload, extracted from the release archive. These
# four directories are exactly the top level of data.tar.gz.
- ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/meta:/data/meta
- ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/bone:/data/bone
- ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/mo3:/data/mo3
- ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/textures:/data/textures
- ${ARMORY_LOG_DIR:-/opt/mythica-armory/logs}:/logs
ports:
- "${ARMORY_PORT:-48733}:48733"

133
docs/mythica-deploy.md Normal file
View file

@ -0,0 +1,133 @@
# Deploying the armory on Nox
This fork of [r-o-b-o-t-o/azerothcore-armory](https://github.com/r-o-b-o-t-o/azerothcore-armory)
(MIT) is set up to run as a Portainer stack on Nox, alongside the Mythica web
page, reading the AzerothCore databases on the game server over the LAN.
**It must not run on the AzerothCore VM (192.168.1.60).** That box is tuned for
the playerbot load; anything extra there means redoing that balancing. Nothing
in this procedure touches the game server — the armory only reads its databases.
---
## Why the model data is a manual step
The armory needs about 2 GB of processed 3D model data — bone sets, mo3 meshes,
textures, and the per-race character descriptors.
Upstream ships a `fetchdata` tool that downloads this from Zam's CDN. **It no
longer works.** Zam reorganised that part of the CDN, and every character path
now 404s (`meta/character/*`, `meta/charactercustomization2/*` — checked against
the `live`, `wrath` and `classic` prefixes and several filename variants). The
armor and item paths still resolve, so a run appears to succeed while silently
producing no character data, and the app then fails at startup on
`data/meta/charactercustomization2/1_0.json`.
The only remaining source is the snapshot in upstream's v1.0.0 release. That
snapshot is mirrored into this fork's own release so the fork does not depend on
GitHub staying up for the one piece that cannot be regenerated.
The mirrored copy is **split into five parts** because Nginx Proxy Manager in
front of the forge rejects request bodies at that size (500 MB uploads fine,
2 GB returns 413). Raising `client_max_body_size` on the forge's proxy host
would let it be stored as a single file, if that is ever worth doing.
---
## 1. Put the model data on Nox
Once. Redeploying the stack does not disturb it.
```bash
sudo mkdir -p /opt/mythica-armory/data /opt/mythica-armory/logs
sudo chown -R "$USER:$USER" /opt/mythica-armory # else the downloads below fail
cd /opt/mythica-armory/data
BASE=https://gitlab.hallsworth.ca/yrtria/azerothcore-armory/releases/download/v1.0.0
for i in 0 1 2 3 4; do curl -LO $BASE/data.tar.gz.part-$i; done
curl -LO $BASE/SHA256SUMS.txt
cat data.tar.gz.part-* > data.tar.gz
sha256sum -c --ignore-missing SHA256SUMS.txt # data.tar.gz must say OK
tar xzf data.tar.gz
rm -f data.tar.gz data.tar.gz.part-*
```
Afterwards `/opt/mythica-armory/data` holds four directories — `meta`, `bone`,
`mo3`, `textures` — totalling roughly 2.5 GB unpacked.
Ownership by your own user is fine — the container reads these as root either
way, and it keeps the download and extract steps out of `sudo`.
The DBC `.csv` files are **not** part of this. They are committed to the repo and
travel in the image, so they stay in step with the code.
> `data.tar.gz.UPSTREAM-GITHUB-LINK.txt` on the same release is not the archive.
> It is the original external reference Forgejo recorded during migration — a
> short-lived signed GitHub URL, kept only for provenance.
## 2. Create the Portainer stack
Repository stack, same pattern as `wow-web-page`:
| Field | Value |
|---|---|
| Repository URL | `https://gitlab.hallsworth.ca/yrtria/azerothcore-armory.git` |
| Reference | `refs/heads/master` |
| Compose path | `docker-compose.mythica.yml` |
Environment variables — `ARMORY_DB_USER` and `ARMORY_DB_PASS` are required and
the stack refuses to start without them:
| Variable | Value |
|---|---|
| `ARMORY_DB_USER` | database user (see §4) |
| `ARMORY_DB_PASS` | its password |
| `ARMORY_DB_HOST` | `192.168.1.60` |
| `ARMORY_WEBSITE_URL` | `https://armory.dionysismedia.ca` once proxied, otherwise `http://192.168.1.77:48733` |
| `ARMORY_WEBSITE_NAME` | `Mythica` |
| `ARMORY_REALM_ID` | `1` |
| `ARMORY_DATA_DIR` | `/opt/mythica-armory/data` |
| `ARMORY_LOG_DIR` | `/opt/mythica-armory/logs` |
The first build compiles TypeScript, so it takes a few minutes. It listens on
48733.
## 3. Reverse proxy
Add a proxy host in NPM pointing at `192.168.1.77:48733`.
To embed it in the main site instead of giving it its own subdomain, set
`ARMORY_IFRAME_ENABLED=1` and `ARMORY_IFRAME_URL` to the page that hosts the
iframe, then use the snippet in the upstream README. That path keeps the site's
own header and navigation around it.
## 4. Database access
`acore` is already granted from `192.168.1.%` and MySQL on the game server binds
`0.0.0.0`, so this works with no changes. But the armory only ever reads, and it
is public-facing, so a dedicated read-only user is worth the two minutes:
```sql
CREATE USER 'armory'@'192.168.1.%' IDENTIFIED BY '<password>';
GRANT SELECT ON acore_characters.* TO 'armory'@'192.168.1.%';
GRANT SELECT ON acore_world.* TO 'armory'@'192.168.1.%';
GRANT SELECT ON acore_auth.* TO 'armory'@'192.168.1.%';
FLUSH PRIVILEGES;
```
## 5. Things to know
- **Item icons and tooltips come from a third-party AoWoW instance**
(`wowgaming.altervista.org` by default) — they are hotlinked, so they break if
that site moves or goes down. Point `ARMORY_AOWOW_URL` at your own AoWoW to
remove that dependency. This is the one remaining external asset dependency;
the model data is now self-hosted.
- **The armory has its own talent view** with dual-spec support, which overlaps
the talent page already in `WoW-Web-Page`. Decide which one owns the character
page rather than shipping both.
- **The Dockerfile builds from `node:16`**, which is end-of-life. It builds and
runs fine; bumping it is a separate change from getting this deployed.
- **`hideGameMasters` defaults to on** here, so GM characters are hidden from
search and return 404.