Files
image/README.md
T

127 lines
5.0 KiB
Markdown

# 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/걸그룹/` or `icons/롤시나리오/`.
- Root-level numeric files such as `icons/1047.jpg` are 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
```sh
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:
```sh
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:
```text
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.
Legacy HTTP mutation is disabled by default. An emergency PHP rollback must
first stop `image-hook`, then create the ignored `hook/legacy-enabled` sentinel
in the legacy checkout before restoring its Caddy/Gitea route. Remove the
sentinel before Node is started again. CLI execution of `hook/git_pull.php`
from the `hook/` directory remains available for local recovery without
exposing the HTTP endpoint.
### Start and verify
```sh
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:
```sh
./deploy/scripts/admin-deploy.sh <branch> [expected-commit]
```
The administration route is not proxied through Nginx or Caddy.
### Tests and rollback
```sh
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.