160 lines
6.1 KiB
Markdown
160 lines
6.1 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. Caddy, Nginx, HAProxy, or another proxy
|
|
can use the same port and path contract.
|
|
|
|
For a native reverse proxy running on the same host as Docker, publish port
|
|
8191 on loopback only and proxy to `127.0.0.1:8191`:
|
|
|
|
```dotenv
|
|
IMAGE_BIND_ADDRESS=127.0.0.1
|
|
IMAGE_PORT=8191
|
|
TRUSTED_PROXY_CIDRS=172.24.0.1/32
|
|
```
|
|
|
|
These two addresses intentionally describe different hops. The native proxy
|
|
connects to the host loopback address, while the Nginx process inside
|
|
`image-web` normally observes the connection as coming from that Compose
|
|
network's Docker bridge gateway (`172.24.0.1` in the example). Use the source
|
|
shown in the `image-web` access/error log, then recreate `image-web` whenever
|
|
`TRUSTED_PROXY_CIDRS` changes. Do not replace it with a Cloudflare CIDR: the
|
|
direct peer of `image-web` remains the host-native reverse proxy path.
|
|
|
|
If the reverse proxy is on another host, bind an explicit private interface
|
|
instead of loopback and apply the host `DOCKER-USER` (or equivalent) firewall
|
|
rule. Keep every source CIDR observed by `image-web` in the untracked
|
|
`TRUSTED_PROXY_CIDRS` value, separated by commas or spaces. Avoid `0.0.0.0`
|
|
unless the firewall has already been verified.
|
|
|
|
### Prepare
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
./deploy/scripts/init-secrets.sh
|
|
docker compose config --quiet
|
|
docker compose build
|
|
```
|
|
|
|
Only for a proxy on another host, apply and inspect the dedicated Docker
|
|
ingress chain with root privileges:
|
|
|
|
```sh
|
|
sudo env TRUSTED_PROXY_CIDRS=192.0.2.10/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.
|
|
|
|
For the same-host native-proxy case, point the proxy upstream at loopback. A
|
|
minimal Caddy site is:
|
|
|
|
```caddyfile
|
|
sam-image.hided.net {
|
|
reverse_proxy 127.0.0.1:8191
|
|
}
|
|
```
|
|
|
|
The equivalent Nginx/HAProxy configuration should use the same upstream. The
|
|
application does not depend on Caddy-specific request behavior.
|
|
|
|
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 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.
|