feat: add phase 1 runnable skeleton
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import cors from '@fastify/cors';
|
||||
import Fastify from 'fastify';
|
||||
import { registerHealthRoutes } from './routes/health.js';
|
||||
|
||||
export async function buildApp() {
|
||||
const app = Fastify({ logger: true });
|
||||
|
||||
await app.register(cors, {
|
||||
origin: true,
|
||||
});
|
||||
|
||||
app.get('/', async () => ({
|
||||
service: 'hermes-mobile-companion',
|
||||
health: '/api/health',
|
||||
}));
|
||||
|
||||
await registerHealthRoutes(app);
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { buildApp } from './app.js';
|
||||
|
||||
const port = Number.parseInt(process.env.PORT ?? '8787', 10);
|
||||
const host = process.env.HOST ?? '0.0.0.0';
|
||||
|
||||
const app = await buildApp();
|
||||
|
||||
try {
|
||||
await app.listen({ port, host });
|
||||
} catch (error) {
|
||||
app.log.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import { healthResponseSchema } from '@hermes-mobile/shared';
|
||||
import { getHermesHealth } from '../system/hermesHealth.js';
|
||||
|
||||
export async function registerHealthRoutes(app: FastifyInstance): Promise<void> {
|
||||
app.get('/api/health', async () => {
|
||||
const response = {
|
||||
ok: true,
|
||||
service: 'hermes-mobile-companion' as const,
|
||||
version: process.env.npm_package_version ?? '0.1.0',
|
||||
mode: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test' ? process.env.NODE_ENV : 'development',
|
||||
uptimeSeconds: process.uptime(),
|
||||
timestamp: new Date().toISOString(),
|
||||
checks: {
|
||||
hermes: await getHermesHealth(),
|
||||
},
|
||||
};
|
||||
|
||||
return healthResponseSchema.parse(response);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { access } from 'node:fs/promises';
|
||||
import { delimiter } from 'node:path';
|
||||
import { env } from 'node:process';
|
||||
import type { HermesHealth } from '@hermes-mobile/shared';
|
||||
|
||||
async function isExecutable(path: string): Promise<boolean> {
|
||||
try {
|
||||
await access(path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getHermesHealth(): Promise<HermesHealth> {
|
||||
const pathEntries = env.PATH?.split(delimiter).filter(Boolean) ?? [];
|
||||
|
||||
for (const pathEntry of pathEntries) {
|
||||
const cliPath = `${pathEntry}/hermes`;
|
||||
if (await isExecutable(cliPath)) {
|
||||
return { cliAvailable: true, cliPath };
|
||||
}
|
||||
}
|
||||
|
||||
return { cliAvailable: false };
|
||||
}
|
||||
Reference in New Issue
Block a user