feat: Web Push 비활성 운영 설정을 준비한다

VAPID private key를 Compose secret으로 주입하고 Web Push를 기본 비활성으로 전달한다. 활성화 입력과 Compose 모델 검증, 운영 README를 함께 추가한다.
This commit is contained in:
2026-08-23 13:16:21 +00:00
parent 0c2b9632eb
commit 663e66b8f9
10 changed files with 129 additions and 2 deletions
+18
View File
@@ -20,6 +20,8 @@ const safeRuntime = {
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',
WEB_PUSH_ENABLED: 'false',
WEB_PUSH_VAPID_PRIVATE_KEY_FILE: '/run/secrets/web_push_vapid_private_key',
FRONTEND_SERVE_MODE: 'static',
FRONTEND_SHARED_ASSET_PUBLIC_PATH: '/gateway/profile-assets',
RELEASE_BUILDER_URL: 'http://builder:15100',
@@ -45,6 +47,12 @@ const safeRuntime = {
read_only: true,
},
],
secrets: [
{
source: 'web_push_vapid_private_key',
target: '/run/secrets/web_push_vapid_private_key',
},
],
restart: 'unless-stopped',
mem_limit: String(4 * 1024 * 1024 * 1024),
memswap_limit: String(4 * 1024 * 1024 * 1024),
@@ -149,3 +157,13 @@ test('rejects missing or excessive build memory and parallelism limits', () => {
assert.ok(errors.some((error) => error.includes('turn daemon Node heap limit')));
assert.ok(errors.some((error) => error.includes('Rayon thread count')));
});
test('requires Web Push activation to keep the private key in a mounted secret', () => {
const services = safeServices();
services.runtime.environment.WEB_PUSH_ENABLED = 'true';
services.runtime.secrets = [];
const errors = validateComposeModel({ services }, 'production');
assert.ok(errors.some((error) => error.includes('VAPID private key')));
assert.ok(errors.some((error) => error.includes('WEB_PUSH_VAPID_SUBJECT')));
assert.ok(errors.some((error) => error.includes('WEB_PUSH_VAPID_PUBLIC_KEY')));
});
+29
View File
@@ -75,3 +75,32 @@ test('rejects credentials embedded in repository URLs and mixed auth modes', ()
assert.ok(errors.some((error) => error.includes('must not embed credentials')));
assert.ok(errors.some((error) => error.includes('only one Core repository authentication mode')));
});
test('keeps Web Push disabled by default and validates activation inputs', () => {
assert.deepEqual(validateEnvironment({ ...validEnv, WEB_PUSH_ENABLED: 'false' }), []);
const errors = validateEnvironment({ ...validEnv, WEB_PUSH_ENABLED: 'true' });
assert.ok(errors.some((error) => error.includes('WEB_PUSH_VAPID_SUBJECT')));
assert.ok(errors.some((error) => error.includes('WEB_PUSH_VAPID_PUBLIC_KEY')));
assert.ok(errors.some((error) => error.includes('WEB_PUSH_VAPID_PRIVATE_KEY_FILE')));
assert.ok(
validateEnvironment({
...validEnv,
WEB_PUSH_ENABLED: 'true',
WEB_PUSH_VAPID_SUBJECT: 'mailto:admin@test.invalid',
WEB_PUSH_VAPID_PUBLIC_KEY: 'test-public-key',
WEB_PUSH_VAPID_PRIVATE_KEY_FILE: './secrets/web_push_vapid_private_key.example',
}).some((error) => error.includes('WEB_PUSH_VAPID_PRIVATE_KEY_FILE')),
);
assert.deepEqual(
validateEnvironment({
...validEnv,
WEB_PUSH_ENABLED: 'true',
WEB_PUSH_VAPID_SUBJECT: 'mailto:admin@test.invalid',
WEB_PUSH_VAPID_PUBLIC_KEY: 'test-public-key',
WEB_PUSH_VAPID_PRIVATE_KEY_FILE: './secrets/web-push-private-key',
}),
[],
);
});