Files
cfw-autumn/server/src/initHono.ts

133 lines
4.5 KiB
TypeScript

import { httpInstrumentationMiddleware } from "@hono/otel";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { autumnWebhookRouter } from "./external/autumn/autumnWebhookRouter.js";
import { revenuecatWebhookRouter } from "./external/revenueCat/revenuecatWebhookRouter.js";
import { stripeWebhookRouter } from "./external/stripe/stripeWebhookRouter.js";
import { vercelTestApiRouter } from "./external/vercel/vercelTestApiRouter.js";
import { vercelWebhookRouter } from "./external/vercel/vercelWebhookRouter.js";
import { baseMiddleware } from "./honoMiddlewares/baseMiddleware.js";
import { errorMiddleware } from "./honoMiddlewares/errorMiddleware.js";
import { replicaDbMiddleware } from "./honoMiddlewares/replicaDbMiddleware.js";
import type { HonoEnv } from "./honoUtils/HonoEnv.js";
import { handleHealthCheck } from "./honoUtils/handleHealthCheck.js";
import { handleReadyCheck } from "./honoUtils/handleReadyCheck.js";
import { handleListAuthOrganizations } from "./internal/auth/handleListAuthOrganizations.js";
import { oauthRouter } from "./internal/auth/oauth/oauthRouter.js";
import { cliRouter } from "./internal/dev/cli/cliRouter.js";
import { handleRevenueCatOAuthCallback } from "./internal/orgs/handlers/revenueCatHandlers/handleRevenueCatOAuthCallback.js";
import { handleOAuthCallback } from "./internal/orgs/handlers/stripeHandlers/handleOAuthCallback.js";
import { apiRouter } from "./routers/apiRouter.js";
import { createChatProxyRouter } from "./routers/chatProxyRouter.js";
import { internalRouter } from "./routers/internalRouter.js";
import { publicRouter } from "./routers/publicRouter.js";
import { auth } from "./utils/auth.js";
import { isAllowedOrigin } from "./utils/corsOrigins.js";
const ALLOWED_HEADERS = [
"app_env",
"x-api-version",
"x-client-type",
"x-request-id",
"x-visitor-id",
"Authorization",
"Content-Type",
"Accept",
"Origin",
"X-API-Version",
"X-Requested-With",
"Access-Control-Request-Method",
"Access-Control-Request-Headers",
"Cache-Control",
"If-Match",
"If-None-Match",
"If-Modified-Since",
"If-Unmodified-Since",
"idempotency-key",
"Idempotency-Key",
"User-Agent", // Required for better-auth v1.4.0+ compatibility with Safari/Zen browser
];
export const createHonoApp = (env?: Env) => {
const app = new Hono<HonoEnv>();
app.route("", createChatProxyRouter());
// CORS configuration (must be before routes)
app.use(
"*",
cors({
origin: isAllowedOrigin,
allowHeaders: ALLOWED_HEADERS,
allowMethods: ["POST", "GET", "PUT", "DELETE", "PATCH", "OPTIONS"],
exposeHeaders: ["Content-Length"],
maxAge: 600,
credentials: true,
}),
);
app.route("", oauthRouter);
// Better Auth's joined Drizzle query defaults to 100 memberships.
app.get("/api/auth/organization/list", handleListAuthOrganizations);
app.on(["POST", "GET"], "/api/auth/*", (c) => {
return auth.handler(c.req.raw);
});
// OAuth callback (needs to be before middleware)
// Health check endpoint for AWS/ECS load balancer
app.get("/stripe/oauth_callback", handleOAuthCallback);
app.get("/revenuecat/oauth_callback", handleRevenueCatOAuthCallback);
app.get("/ready/:token", handleReadyCheck);
app.get("/", handleHealthCheck);
// Step 1: OTel HTTP span + base middleware + span enrichment
app.use(
"*",
httpInstrumentationMiddleware({
serviceName: "autumn-server",
serviceVersion: "1.0.0",
}),
);
app.use("*", baseMiddleware);
app.use("*", replicaDbMiddleware);
// CLI routes (uses Bearer token auth, not session auth)
app.route("/cli", cliRouter);
// Add Render region identifier header for load balancer verification
app.use("*", async (c, next) => {
await next();
c.header("x-region", c.env.AWS_REGION);
});
// Webhook routes
app.route("", stripeWebhookRouter);
app.route("/webhooks/autumn", autumnWebhookRouter);
app.route("/webhooks/vercel", vercelWebhookRouter);
app.route("/webhooks/revenuecat", revenuecatWebhookRouter);
// Vercel SDK test mock — mounted in dev/test, used only when
// `ctx.testOptions.mockVercelApi` points the SDK at this route.
if ((env?.NODE_ENV as string | undefined) !== "production") {
app.route("/__test/vercel/api", vercelTestApiRouter);
}
// Public routes (no auth required)
app.route("", publicRouter);
// Debug routes (no auth, dev-only guard is inside the handler)
// Debug routes (auth handled internally)
// app.route("/debug", heapSnapshotRouter);
// app.route("/v1/debug", debugRouter);
// API Middleware
app.route("/v1", apiRouter);
app.route("", internalRouter);
app.onError(errorMiddleware);
return app;
};