Start server when loaded by the PM2 fork container

This commit is contained in:
2026-10-09 17:56:37 +09:00
parent f09b661ac0
commit 736da09846
3 changed files with 17 additions and 3 deletions
+4 -2
View File
@@ -74,6 +74,8 @@ NODE_ENV=production HOST=127.0.0.1 PORT=3600 npm start
`deploy/arrival.env.example`은 예시입니다. 파일을 복사하는 것만으로 자동 로드되지는 않습니다. systemd의 EnvironmentFile 또는 Node의 명시적 `--env-file=/절대경로/arrival.env` 옵션을 사용하세요. 실제 설정 파일은 공개 폴더에 두지 마세요. `deploy/arrival.env.example`은 예시입니다. 파일을 복사하는 것만으로 자동 로드되지는 않습니다. systemd의 EnvironmentFile 또는 Node의 명시적 `--env-file=/절대경로/arrival.env` 옵션을 사용하세요. 실제 설정 파일은 공개 폴더에 두지 마세요.
PM2에서는 `server/index.mjs`를 직접 실행합니다. PM2가 ESM 파일을 import하는 경우에도 서버가 시작되도록 실행 진입점을 처리했습니다. nginx가 다른 장비에 있으면 `HOST`를 nginx가 접근할 앱 서버의 IP로 지정하세요. 예를 들어 nginx가 `192.168.0.250:3600`으로 전달한다면 `HOST=192.168.0.250 PORT=3600`으로 실행합니다. 기존 PM2 프로세스의 환경변수 변경은 `--update-env`로 적용합니다. PM2의 `online` 표시만으로 HTTP 서버 시작을 판단하지 말고, nginx 장비에서 `/api/arrivals/count`에 연결되는지 확인하세요.
## 3. nginx / HTTPS ## 3. nginx / HTTPS
기존 HTTPS 도메인의 요청을 같은 Fastify 프로세스로 전달합니다. 정적 화면과 `/api`가 같은 origin이어야 합니다. 기존 server 블록 안의 location 예시: 기존 HTTPS 도메인의 요청을 같은 Fastify 프로세스로 전달합니다. 정적 화면과 `/api`가 같은 origin이어야 합니다. 기존 server 블록 안의 location 예시:
@@ -120,8 +122,8 @@ DB_PATH="$HOME/behind-glass-data/analytics.sqlite" npm run --silent metrics -- -
### 현재 Windows 로컬 검증 (2026-10-09) ### 현재 Windows 로컬 검증 (2026-10-09)
- Node 24.19.0 / npm 11.17.0에서 일반 `npm ci`, SQLite 로딩, 정적 빌드 및 서버용 빌드를 확인했습니다. 최종 `dist`는 서버용 빌드입니다. - Node 24.19.0 / npm 11.17.0에서 일반 `npm ci`, SQLite 로딩, 정적 빌드 및 서버용 빌드를 확인했습니다. 최종 `dist`는 서버용 빌드입니다.
- `npm run test:arrival`은 서버 33개 + 클라이언트 73개, 총 106개 통과했습니다. - `npm run test:arrival`은 서버 34개 + 클라이언트 73개, 총 107개 통과했습니다. PM2 방식의 ESM import 시작 검증을 포함합니다.
- 기존 전체 테스트는 470개 중 467개 통과했습니다. 다음 3개 테스트는 원래 배포본에 포함되지 않은 QA 자료 때문에 실패하며, 해당 원본 자료 복원이 필요합니다. 통과로 처리하거나 임의 자료로 대체하지 않았습니다. - 기존 전체 테스트는 471개 중 468개 통과했습니다. 다음 3개 테스트는 원래 배포본에 포함되지 않은 QA 자료 때문에 실패하며, 해당 원본 자료 복원이 필요합니다. 통과로 처리하거나 임의 자료로 대체하지 않았습니다.
- `scripts/test-cutout-worker.mjs`: `docs/qa-countryside-cutout-2026-10-07/runtime/sample-320.rgba` 누락 - `scripts/test-cutout-worker.mjs`: `docs/qa-countryside-cutout-2026-10-07/runtime/sample-320.rgba` 누락
- `scripts/test-original-photo-only.mjs`: `docs/reference-assets/countryside/open-house.png` 누락 - `scripts/test-original-photo-only.mjs`: `docs/reference-assets/countryside/open-house.png` 누락
- `scripts/test-surface-reflection.mjs`: `docs/qa-surface-reflection/baseline-mosaic.frag` 누락 - `scripts/test-surface-reflection.mjs`: `docs/qa-surface-reflection/baseline-mosaic.frag` 누락
+11
View File
@@ -38,6 +38,17 @@ test('operational CLI entrypoints exist without starting an HTTP listener', () =
for (const script of ['metrics', 'backup']) assert.ok(existsSync(resolve(`server/${script}.mjs`)), `missing ${script} CLI`); for (const script of ['metrics', 'backup']) assert.ok(existsSync(resolve(`server/${script}.mjs`)), `missing ${script} CLI`);
}); });
test('PM2 fork imports execute the server entrypoint instead of silently skipping startup', () => {
const output = spawnSync(process.execPath, ['--input-type=module', '-e',
"import { pathToFileURL } from 'node:url'; process.argv[1] = 'ProcessContainerFork.js'; await import(pathToFileURL(process.env.pm_exec_path).href);",
], {
env: { ...process.env, DB_PATH: '', NODE_ENV: 'production', pm_exec_path: resolve('server/index.mjs') },
encoding: 'utf8',
});
assert.equal(output.status, 1, 'the entrypoint must attempt startup and reject the missing database configuration');
assert.match(output.stderr, /Server startup failed/);
});
test('metrics prints aggregate-only JSON from an existing read-only database', async t => { test('metrics prints aggregate-only JSON from an existing read-only database', async t => {
const { store, dbPath } = await setup(t); const { store, dbPath } = await setup(t);
const output = cli('metrics', ['--month', '2026-10'], dbPath); const output = cli('metrics', ['--month', '2026-10'], dbPath);
+2 -1
View File
@@ -28,6 +28,7 @@ export async function startServer(env = process.env) {
return app; return app;
} }
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { const entryPath = process.env.pm_exec_path || process.argv[1];
if (entryPath && import.meta.url === pathToFileURL(entryPath).href) {
startServer().catch(() => { process.stderr.write('Server startup failed; check configuration, private storage and build\n'); process.exitCode = 1; }); startServer().catch(() => { process.stderr.write('Server startup failed; check configuration, private storage and build\n'); process.exitCode = 1; });
} }