4.6 KiB
Shared image repository
icons/ is served as /image/icons/. General portraits follow these rules:
icons/장수/<장수명>.<확장자>is the canonical shared path.- Scenario-specific collections keep a separate directory such as
icons/걸그룹/oricons/롤시나리오/. - Root-level numeric files such as
icons/1047.jpgare compatibility aliases for deployed legacy versions. Do not delete or replace them while those versions are in service. - A general name must map to one canonical file. If two records intentionally need different portraits, disambiguate the scenario name or use an explicit scenario-specific path.
The core2026 repository owns resources/general-icons.json and
tools/manage-general-icons.mjs. Use that tool with this repository as
--image-root to synchronize aliases, scenario paths, and verify that every
catalog source exists and has identical bytes.
Public URLs
The same tracked files are exposed through both URL contracts:
https://sam.hided.net/image/game/...and/image/icons/...https://sam-image.hided.net/game/...and/icons/...https://sam-image.hided.net/image/...is a compatibility alias.
Do not redirect the old /image/* URLs to the dedicated domain. Existing PHP
and core2026 clients use same-origin relative paths and can move independently.
The image server never exposes the repository root, .git, PHP sources, or
directory listings.
Node webhook deployment service
compose.yaml runs a read-only Nginx static edge and an internal Node service.
Only the edge publishes port 8191. The Node service verifies the raw Gitea
X-Gitea-Signature, accepts push events for devsam/image, fetches the exact
remote branch tip, rejects dirty or non-fast-forward updates, and serializes all
Git changes. The initial and only allowed branch is master unless
IMAGE_ALLOWED_BRANCHES is explicitly expanded.
The current Caddy host reaches this server from 172.30.1.75. Keep the observed
source in the untracked .env; re-check it whenever Caddy networking changes.
The published port binds all server interfaces, so production also needs a
host DOCKER-USER (or equivalent) firewall rule allowing that source CIDR to
TCP 8191 and rejecting other sources. Nginx applies the same source allowlist.
Prepare
cp .env.example .env
./deploy/scripts/init-secrets.sh
docker compose config --quiet
docker compose build
Apply and inspect the dedicated Docker ingress chain with root privileges:
sudo env CADDY_SOURCE_CIDR=172.30.1.75/32 IMAGE_PORT=8191 \
./deploy/scripts/firewall-8191.sh apply
sudo ./deploy/scripts/firewall-8191.sh check
The script only owns the SAM_IMAGE_INGRESS chain and its port-8191 jump from
DOCKER-USER; it does not flush shared firewall chains.
Secret values are generated under ignored secrets/ files with mode 0600 and
are never printed. Put the contents of secrets/gitea_webhook_secret into the
Gitea webhook configuration. Configure a JSON push webhook targeting:
https://sam-image.hided.net/v1/hooks/gitea
Use branch filter master. Disable the old PHP webhook before enabling the new
writer. The legacy PHP files remain in hook/ for an explicit rollback, but
PHP and Node must never mutate the checkout concurrently.
Start and verify
docker compose up -d --build
docker compose ps
curl -fsS https://sam-image.hided.net/healthz
curl -fsS https://sam-image.hided.net/v1/status
curl -fsS https://sam-image.hided.net/game/back.jpg -o /dev/null
curl -fsS https://sam-image.hided.net/image/icons/default.jpg -o /dev/null
Nginx serves only game/, icons/, hook/list.json, and
hook/inventory.v2.json. The API inventory additionally reports the deployed
branch, full commit, generation time, asset list, and both public base URLs.
Explicit branch deployment
Automatic pushes only update the active branch. Add a branch to
IMAGE_ALLOWED_BRANCHES, recreate image-hook, and switch it with the internal
signed administration command:
./deploy/scripts/admin-deploy.sh <branch> [expected-commit]
The administration route is not proxied through Nginx or Caddy.
Tests and rollback
docker build -t sam-image-hook:test node-hook
docker run --rm sam-image-hook:test npm test
docker compose exec -T image-web nginx -t -c /tmp/nginx.conf
Stopping image-hook does not remove the last checked-out files from the
static service. For a full rollback, stop Node mutation first, restore the old
Caddy/PHP webhook route, and only then enable the legacy writer. Preserve both
checkouts until public file hashes and representative browser requests have
been verified.