ludops-skeleton/apps/api/src/index.ts

54 lines
1.6 KiB
TypeScript

import Fastify from 'fastify';
import path from 'path';
import { fileURLToPath } from 'url';
import fastifyStatic from '@fastify/static';
import { AppStatus } from '@ludops/shared';
import { db } from './db/index.js';
import { visits } from './db/schemas.js';
import { sql } from 'drizzle-orm';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const fastify = Fastify({ logger: true });
// API Route
fastify.get('/api/status', async (): Promise<AppStatus> => {
const [result] = await db.select({ count: sql<number>`count(*)` }).from(visits);
return { status: 'OK', version: '1.0.0', database: true, totalVisits: Number(result.count) };
});
// Serve Frontend (Vite Dist)
const webDistPath = process.env.NODE_ENV === 'production'
? path.join(__dirname, '../web-dist') // Adjusted for the flattened Docker structure
: path.join(__dirname, '../../web/dist');
fastify.register(fastifyStatic, {
root: webDistPath,
prefix: '/',
});
// SPA Routing: Redirect everything else to index.html
fastify.setNotFoundHandler((req, reply) => {
if (req.url.startsWith('/api')) {
reply.code(404).send({ error: 'Not Found' });
} else {
reply.sendFile('index.html');
}
});
fastify.listen({ port: 3000, host: '0.0.0.0' });
fastify.addHook('onRequest', async (request) => {
try {
if (request.url.startsWith('/api')) {
await db.insert(visits).values({
ip: request.ip,
userAgent: request.headers['user-agent'] || '',
});
}
} catch (error) {
fastify.log.error(`Failed to log visit: ${error}`);
}
});