merge: 런타임 공통 메뉴 영속 설정 반영

This commit is contained in:
2026-08-19 11:16:09 +00:00
4 changed files with 29 additions and 0 deletions
+6
View File
@@ -105,6 +105,12 @@ mode 0600 secret 파일에만 둡니다.
- PostgreSQL, Redis, Core clone/worktree, PM2 상태와 Caddy 인증서는 named
volume에 보존됩니다. 새 user icon은 외부 이미지 서비스가 보존하며 runtime의
user icon volume은 기존 설치 호환용입니다.
- Gateway와 게임 공통 메뉴는 runtime volume의 `/srv/data/navigation.json`
읽습니다. 최초 기동 때만 Core의 `resources/navigation.json`을 복사하며, 이후
배포와 container 재생성은 운영자가 편집한 파일을 덮어쓰지 않습니다. JSON을
유효하게 저장한 뒤 브라우저를 새로 열거나 새로고침하면 재빌드 없이 반영됩니다.
잘못된 스키마나 `javascript:` URL은 API가 거부하므로 변경 전에 Core 문서의
메뉴 설정 검증 명령을 실행합니다.
- `/image/*`는 앱 artifact가 아니며 `data/image/`의 별도 운영 자산을 제공합니다.
- 일반 `docker compose down`은 volume을 보존합니다. `down --volumes`는 DB와
release 상태를 삭제하는 파괴적 명령이므로 backup 없이 실행하지 않습니다.
+1
View File
@@ -90,6 +90,7 @@ services:
GATEWAY_API_PORT: '15001'
GATEWAY_FRONTEND_PORT: '15000'
GATEWAY_TRPC_PATH: /gateway/api/trpc
CORE_NAVIGATION_CONFIG_FILE: /srv/data/navigation.json
GATEWAY_INTERNAL_API_URL: http://127.0.0.1:15001
GATEWAY_REDIS_PREFIX: sammo:gateway
GATEWAY_WORKSPACE_ROOT: /srv/core/repository
+9
View File
@@ -71,6 +71,15 @@ esac
git config --global --add safe.directory "$core_root"
export GATEWAY_WORKSPACE_ROOT="$core_root"
export RELEASE_CONTROLLER_WORKSPACE_ROOT="$core_root"
navigation_config_file=${CORE_NAVIGATION_CONFIG_FILE:-/srv/data/navigation.json}
if [ ! -e "$navigation_config_file" ]; then
navigation_config_dir=$(dirname "$navigation_config_file")
mkdir -p "$navigation_config_dir"
cp "$core_root/resources/navigation.json" "$navigation_config_file"
chmod 644 "$navigation_config_file"
fi
export DATABASE_URL="$(node -e 'const u=new URL("postgresql://localhost");u.username=process.env.POSTGRES_USER||"sammo";u.password=process.env.POSTGRES_PASSWORD;u.hostname=process.env.POSTGRES_HOST||"postgres";u.port=process.env.POSTGRES_PORT||"5432";u.pathname="/"+(process.env.POSTGRES_DB||"sammo");u.searchParams.set("schema","public");process.stdout.write(u.href)')"
export GATEWAY_DATABASE_URL="$DATABASE_URL"
export REDIS_URL="$(node -e 'const u=new URL("redis://localhost/0");u.password=process.env.REDIS_PASSWORD;u.hostname=process.env.REDIS_HOST||"redis";u.port=process.env.REDIS_PORT||"6379";process.stdout.write(u.href)')"
+13
View File
@@ -0,0 +1,13 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
test('runtime menu config is seeded once in persistent data and never overwritten', async () => {
const entrypoint = await fs.readFile(new URL('../runtime/entrypoint.sh', import.meta.url), 'utf8');
const existenceGuard = entrypoint.indexOf('if [ ! -e "$navigation_config_file" ]; then');
const copy = entrypoint.indexOf('cp "$core_root/resources/navigation.json" "$navigation_config_file"');
assert.ok(existenceGuard >= 0, 'persistent navigation config must have an existence guard');
assert.ok(copy > existenceGuard, 'the default config must only be copied inside that guard');
assert.equal(entrypoint.indexOf('cp -f "$core_root/resources/navigation.json"'), -1);
});