merge: support reverse-proxy HTTP upstream

This commit is contained in:
2026-08-05 00:09:34 +00:00
5 changed files with 33 additions and 1 deletions
+2
View File
@@ -2,6 +2,8 @@
DOMAIN=game.example.com
PUBLIC_SCHEME=https
ACME_EMAIL=admin@example.com
# Set http://DOMAIN when an external reverse proxy terminates TLS.
CADDY_SITE_ADDRESS=
HTTP_PORT=80
HTTPS_PORT=443
+6
View File
@@ -23,6 +23,12 @@ DNS의 `DOMAIN` A/AAAA record가 호스트를 가리키고 80/443 TCP 및 443 UD
Core2026 clone, frozen-lockfile install, build와 Gateway migration 때문에 수 분이
걸릴 수 있습니다.
외부 reverse proxy가 TLS를 종료하고 이 stack의 HTTP port로 전달한다면
`PUBLIC_SCHEME=https`, `CADDY_SITE_ADDRESS=http://DOMAIN`을 함께 설정합니다.
예를 들어 host `14999`로 전달할 때는 `HTTP_PORT=14999`로 두고, 사용하지 않는
`HTTPS_PORT`는 충돌하지 않는 별도 host port로 지정합니다. 외부 proxy는 원래
`Host` header를 보존해야 합니다.
runtime은 기본적으로 memory/swap 각각 4 GiB, CPU 4개, PID 256개 상한을
가집니다. 호스트 용량에 맞춰 `RUNTIME_*_LIMIT`을 조정할 수 있지만 0 또는
무제한으로 두지 않습니다. PM2 process/restart 수가 예상보다 증가하면 로그 수집보다
+1 -1
View File
@@ -121,7 +121,7 @@ services:
image: caddy:2.10.2-alpine
environment:
DOMAIN: ${DOMAIN:?set DOMAIN in .env}
SITE_ADDRESS: ${DOMAIN:?set DOMAIN in .env}
SITE_ADDRESS: ${CADDY_SITE_ADDRESS:-${DOMAIN:?set DOMAIN in .env}}
ACME_EMAIL: ${ACME_EMAIL:?set ACME_EMAIL in .env}
ports:
- ${HTTP_PORT:-80}:80
+12
View File
@@ -52,6 +52,18 @@ export const validateEnvironment = (env) => {
errors.push('DOMAIN must be a hostname without a scheme, path, or port');
}
const caddySiteAddress = env.CADDY_SITE_ADDRESS?.trim() ?? '';
if (caddySiteAddress) {
try {
const parsed = new URL(caddySiteAddress);
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.hostname !== domain || parsed.pathname !== '/') {
errors.push('CADDY_SITE_ADDRESS must be http://DOMAIN or https://DOMAIN');
}
} catch {
errors.push('CADDY_SITE_ADDRESS must be http://DOMAIN or https://DOMAIN');
}
}
const email = env.ACME_EMAIL?.trim() ?? '';
if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errors.push('ACME_EMAIL must be a valid email address');
+12
View File
@@ -20,6 +20,18 @@ test('accepts a complete public repository environment', () => {
assert.deepEqual(validateEnvironment(validEnv), []);
});
test('accepts an HTTP Caddy address for external TLS termination', () => {
assert.deepEqual(
validateEnvironment({ ...validEnv, CADDY_SITE_ADDRESS: 'http://game.test.invalid' }),
[],
);
});
test('rejects a Caddy address for a different host', () => {
const errors = validateEnvironment({ ...validEnv, CADDY_SITE_ADDRESS: 'http://other.test.invalid' });
assert.ok(errors.some((error) => error.includes('CADDY_SITE_ADDRESS')));
});
test('accepts paired HTTPS credentials without embedding them in the URL', () => {
assert.deepEqual(
validateEnvironment({ ...validEnv, CORE_REPOSITORY_USERNAME: 'deploy', CORE_REPOSITORY_TOKEN: 'token' }),