- 프론트엔드+백엔드 단일 컨테이너로 통합 (Dockerfile) - Fastify 정적 파일 서빙 + SPA fallback - @fastify/static 추가 - Caddy 프록시 대상 변경 (traeon-frontend → traeon) - 체크박스 w-5/w-0 CSS 충돌 수정 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
61 lines
1.5 KiB
JavaScript
61 lines
1.5 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import Fastify from 'fastify';
|
|
import fastifyCors from '@fastify/cors';
|
|
import fastifyStatic from '@fastify/static';
|
|
import config from './config/index.js';
|
|
import dbPlugin from './plugins/db.js';
|
|
import trackerPlugin from './plugins/tracker.js';
|
|
import routes from './routes/index.js';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
export async function buildApp(opts = {}) {
|
|
const fastify = Fastify({
|
|
logger: {
|
|
level: opts.logLevel || 'info',
|
|
},
|
|
...opts,
|
|
});
|
|
|
|
// config 데코레이터 등록
|
|
fastify.decorate('config', config);
|
|
|
|
// CORS
|
|
await fastify.register(fastifyCors, {
|
|
origin: true,
|
|
});
|
|
|
|
// 플러그인
|
|
await fastify.register(dbPlugin);
|
|
await fastify.register(trackerPlugin);
|
|
|
|
// 라우트
|
|
await fastify.register(routes, { prefix: '/api' });
|
|
|
|
// 헬스 체크
|
|
fastify.get('/api/health', async () => {
|
|
return { status: 'ok', timestamp: new Date().toISOString() };
|
|
});
|
|
|
|
// 정적 파일 서빙 (프론트엔드 빌드 결과물)
|
|
const distPath = path.join(__dirname, '../dist');
|
|
if (fs.existsSync(distPath)) {
|
|
await fastify.register(fastifyStatic, {
|
|
root: distPath,
|
|
prefix: '/',
|
|
});
|
|
|
|
// SPA fallback
|
|
fastify.setNotFoundHandler((request, reply) => {
|
|
if (request.url.startsWith('/api/')) {
|
|
return reply.code(404).send({ error: 'Not found' });
|
|
}
|
|
return reply.sendFile('index.html');
|
|
});
|
|
}
|
|
|
|
return fastify;
|
|
}
|