154 lines
6.4 KiB
Markdown
154 lines
6.4 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 service is reverse-proxy agnostic. Its current reverse proxy happens to
|
|
reach this server from `172.30.1.75`, but Caddy, Nginx, HAProxy, or another
|
|
proxy can be used. Keep every observed proxy source CIDR in the untracked
|
|
`TRUSTED_PROXY_CIDRS` value, separated by commas or spaces, and re-check the
|
|
value whenever proxy networking changes. The published port binds all server
|
|
interfaces, so production also needs a host `DOCKER-USER` (or equivalent)
|
|
firewall rule allowing those source CIDRs to TCP 8191 and rejecting other
|
|
sources. The static Nginx edge 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 TRUSTED_PROXY_CIDRS=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.
|
|
|
|
### Fallback sync callers
|
|
|
|
If Gitea webhook delivery is missed, Core and Core2026 can request a restricted
|
|
reconciliation through `POST /v1/sync`. This endpoint cannot select or change a
|
|
branch: it only fetches the current active branch and applies the same clean
|
|
worktree and fast-forward checks as a webhook deployment.
|
|
|
|
Each caller has a separate secret:
|
|
|
|
- `secrets/image_sync_core_secret` for legacy Core
|
|
- `secrets/image_sync_core2026_secret` for Core2026
|
|
|
|
The caller sends its name, a timestamp, a unique request ID, and an HMAC-SHA256
|
|
signature over `timestamp.request-id.<exact JSON body>`. Requests expire after
|
|
five minutes and successful request IDs are persisted for replay protection.
|
|
The body is either `{}` or `{ "commit": "<full-image-commit-sha>" }`; all other
|
|
fields are rejected. The optional commit asserts the expected remote tip and
|
|
does not grant checkout selection.
|
|
|
|
Distribute only the matching caller secret through an ignored secret file.
|
|
Never give either caller `image_admin_secret`, which also authorizes explicit
|
|
branch changes. This fallback handles webhook delivery outages; if the image
|
|
service itself is stopped, restore it and run the caller command again.
|
|
|
|
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 exposed through the public reverse proxy.
|
|
|
|
### 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.
|