UNPKG

better-claude-code

Version:

CLI auxiliary tools for Claude Code

110 lines (109 loc) 3.95 kB
import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { NodeEnv } from '../../node-utils/index.js'; import { API_PREFIX, APP_DESCRIPTION, APP_NAME, createLocalHostLink, FRONTEND_PORT, OPENAPI_SPEC_PATH, SWAGGER_UI_PATH } from '../../shared/index.js'; import { serve } from '@hono/node-server'; import { serveStatic } from '@hono/node-server/serve-static'; import { swaggerUI } from '@hono/swagger-ui'; import { OpenAPIHono } from '@hono/zod-openapi'; import { cors } from 'hono/cors'; import { ENV } from '../env.js'; import { filesRouter } from '../modules/files/router.js'; import { liveSessionsRouter } from '../modules/live-sessions/router.js'; import { projectsRouter } from '../modules/projects/router.js'; import { sessionsRouter } from '../modules/sessions/router.js'; import { settingsRouter } from '../modules/settings/router.js'; function proxyToFrontendDevServer(app) { app.use('/*', async (c, next) => { const url = new URL(c.req.url); if (!url.pathname.startsWith(API_PREFIX) && !url.pathname.startsWith(SWAGGER_UI_PATH) && !url.pathname.startsWith(OPENAPI_SPEC_PATH)) { try { const frontendUrl = `${createLocalHostLink(FRONTEND_PORT)}${url.pathname}${url.search}`; const res = await fetch(frontendUrl); return new Response(res.body, { status: res.status, statusText: res.statusText, headers: res.headers }); } catch (err) { console.error(`Failed to proxy to frontend: ${err.message}`); return c.text('Frontend dev server not available', 502); } } await next(); }); } function serveStaticFrontend(app, staticPath) { app.use('/*', serveStatic({ root: staticPath })); app.get('*', (c) => { const indexPath = resolve(staticPath, 'index.html'); try { const content = readFileSync(indexPath, 'utf8'); return c.html(content); } catch (err) { console.error(`Failed to serve index.html: ${err.message}`); return c.text('Error loading page', 500); } }); } function setupSwagger(app, port) { app.doc(OPENAPI_SPEC_PATH, getSwaggerConfig(port)); app.get(SWAGGER_UI_PATH, swaggerUI({ url: OPENAPI_SPEC_PATH })); } const startTime = Date.now(); function setupHealthRoute(app) { app.get(`${API_PREFIX}`, (c) => { const uptime = Date.now() - startTime; return c.json({ name: `${APP_NAME} API`, uptime: `${Math.floor(uptime / 1000)}s`, environment: ENV.NODE_ENV, timestamp: new Date().toISOString() }); }); } export const getSwaggerConfig = (port) => ({ openapi: '3.1.0', info: { title: `${APP_NAME} API`, description: APP_DESCRIPTION, version: '1.0.0' }, servers: [ { url: createLocalHostLink(port), description: 'Local development server' } ] }); export const createServer = (port, staticPath) => { const app = new OpenAPIHono(); app.use('*', cors()); setupHealthRoute(app); app.route(`${API_PREFIX}/files`, filesRouter); app.route(`${API_PREFIX}/live-sessions`, liveSessionsRouter); app.route(`${API_PREFIX}/projects`, projectsRouter); app.route(`${API_PREFIX}/sessions`, sessionsRouter); app.route(`${API_PREFIX}/settings`, settingsRouter); if (ENV.NODE_ENV === NodeEnv.DEVELOPMENT) { setupSwagger(app, port); proxyToFrontendDevServer(app); } if (staticPath) { serveStaticFrontend(app, staticPath); } return app; }; export const startServer = (port, staticPath) => { const app = createServer(port, staticPath); return serve({ fetch: app.fetch, port: port }); };