feat(runtime): publish images from dedicated origin

This commit is contained in:
2026-08-08 09:31:07 +00:00
parent b61efccd01
commit 4cdf585bc2
4 changed files with 20 additions and 4 deletions
+11 -2
View File
@@ -58,10 +58,19 @@ webhook 누락 시 `docker compose exec runtime pnpm sync:image`로 현재 이
branch의 fast-forward를 요청할 때만 사용합니다. 두 파일은 runtime에 read-only로
mount되며 원문은 환경 변수나 브라우저 bundle에 들어가지 않습니다.
`IMAGE_PUBLIC_URL`의 기본값은 `https://sam-image.hided.net`입니다. runtime은
이를 Gateway·game frontend의 공개 이미지 origin과 사용자 아이콘 URL에 함께
주입합니다. 정적 게임 이미지는 `/game`, 공용 아이콘은 `/icons`, Core2026
사용자 아이콘은 서버 발급 경로 `users/core2026/<파일>``/icons` base 아래에
붙여 읽습니다. 업로드 권한값은
URL query나 `VITE_*`가 아니라 `IMAGE_UPLOAD_CORE2026_SECRET_FILE`이 가리키는
mode 0600 secret 파일에만 둡니다.
## 데이터와 복구 경계
- PostgreSQL, Redis, Core clone/worktree, PM2 상태, Caddy 인증서와 user icon은
named volume 또는 `data/image`에 보존됩니다.
- PostgreSQL, Redis, Core clone/worktree, PM2 상태 Caddy 인증서는 named
volume에 보존됩니다. 새 user icon은 외부 이미지 서비스가 보존하며 runtime의
user icon volume은 기존 설치 호환용입니다.
- `/image/*`는 앱 artifact가 아니며 `data/image/`의 별도 운영 자산을 제공합니다.
- 일반 `docker compose down`은 volume을 보존합니다. `down --volumes`는 DB와
release 상태를 삭제하는 파괴적 명령이므로 backup 없이 실행하지 않습니다.
+3 -2
View File
@@ -72,7 +72,7 @@ services:
KAKAO_REDIRECT_URI: ${PUBLIC_SCHEME:-https}://${DOMAIN:?set DOMAIN in .env}/gateway/oauth/callback
GATEWAY_PUBLIC_URL: ${PUBLIC_SCHEME:-https}://${DOMAIN:?set DOMAIN in .env}/gateway/
GATEWAY_USER_ICON_DIR: /srv/data/user-icons
GATEWAY_USER_ICON_PUBLIC_URL: ${PUBLIC_SCHEME:-https}://${DOMAIN:?set DOMAIN in .env}/gateway/api/user-icons
GATEWAY_USER_ICON_PUBLIC_URL: ${IMAGE_PUBLIC_URL:-https://sam-image.hided.net}/icons
GATEWAY_IMAGE_UPLOAD_URL: ${IMAGE_SERVICE_URL:-https://sam-image.hided.net}
GATEWAY_IMAGE_UPLOAD_SECRET_FILE: /run/secrets/image_upload_core2026_secret
GATEWAY_SHARED_ICON_PUBLIC_URL: ${IMAGE_PUBLIC_URL:-https://sam-image.hided.net}/icons
@@ -100,7 +100,8 @@ services:
VITE_GATEWAY_API_URL: /gateway/api/trpc
VITE_GAME_API_URL_TEMPLATE: /{profile}/api/trpc
VITE_GAME_WEB_URL_TEMPLATE: /{profile}/
VITE_GATEWAY_USER_ICON_BASE_URL: /gateway/api/user-icons
VITE_IMAGE_PUBLIC_URL: ${IMAGE_PUBLIC_URL:-https://sam-image.hided.net}
VITE_GATEWAY_USER_ICON_BASE_URL: ${IMAGE_PUBLIC_URL:-https://sam-image.hided.net}/icons
VITE_PREVIEW_ALLOWED_HOSTS: ${DOMAIN:?set DOMAIN in .env}
PM2_HOME: /srv/data/pm2
TZ: ${TZ:-Asia/Seoul}
+3
View File
@@ -22,9 +22,12 @@ export const validateComposeModel = (model, mode) => {
for (const key of [
'GATEWAY_IMAGE_UPLOAD_URL',
'GATEWAY_SHARED_ICON_PUBLIC_URL',
'GATEWAY_USER_ICON_PUBLIC_URL',
'GAME_IMAGE_UPLOAD_URL',
'GAME_CONTENT_IMAGE_PUBLIC_URL',
'IMAGE_SYNC_URL',
'VITE_IMAGE_PUBLIC_URL',
'VITE_GATEWAY_USER_ICON_BASE_URL',
]) {
if (!runtime.environment?.[key]) errors.push(`${key} must be configured`);
}
+3
View File
@@ -11,11 +11,14 @@ const safeRuntime = {
GATEWAY_IMAGE_UPLOAD_URL: 'https://sam-image.hided.net',
GATEWAY_IMAGE_UPLOAD_SECRET_FILE: '/run/secrets/image_upload_core2026_secret',
GATEWAY_SHARED_ICON_PUBLIC_URL: 'https://sam-image.hided.net/icons',
GATEWAY_USER_ICON_PUBLIC_URL: 'https://sam-image.hided.net/icons',
GAME_IMAGE_UPLOAD_URL: 'https://sam-image.hided.net',
GAME_IMAGE_UPLOAD_SECRET_FILE: '/run/secrets/image_upload_core2026_secret',
GAME_CONTENT_IMAGE_PUBLIC_URL: 'https://sam-image.hided.net/uploads/core2026',
IMAGE_SYNC_URL: 'https://sam-image.hided.net',
IMAGE_SYNC_SECRET_FILE: '/run/secrets/image_sync_core2026_secret',
VITE_IMAGE_PUBLIC_URL: 'https://sam-image.hided.net',
VITE_GATEWAY_USER_ICON_BASE_URL: 'https://sam-image.hided.net/icons',
},
volumes: [
{