Skip to content

Commit

Permalink
feat: hono package and helpers are now centralized in @u22n/hono
Browse files Browse the repository at this point in the history
  • Loading branch information
BlankParticle committed Jul 20, 2024
1 parent 8308ef6 commit b3f8965
Show file tree
Hide file tree
Showing 52 changed files with 463 additions and 581 deletions.
102 changes: 28 additions & 74 deletions apps/mail-bridge/app.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,54 @@
import './tracing';
import { env } from './env';
import { Hono } from 'hono';
import { db } from '@u22n/database';
import { serve } from '@hono/node-server';
import { trpcMailBridgeRouter } from './trpc';
import { trpcServer } from '@hono/trpc-server';
import { eventApi } from './postal-routes/events';
import { inboundApi } from './postal-routes/inbound';
import { signatureMiddleware } from './postal-routes/signature-middleware';
import { logger } from 'hono/logger';
import { otel } from '@u22n/otel/hono';
import type { Ctx, TRPCContext } from './ctx';
import {
createHonoApp,
setupErrorHandlers,
setupHealthReporting,
setupHonoListener,
setupRouteLogger,
setupRuntime,
setupTrpcHandler
} from '@u22n/hono';

const processCleanup: Array<() => Promise<void>> = [];

if (env.MAILBRIDGE_MODE === 'dual' || env.MAILBRIDGE_MODE === 'handler') {
const app = new Hono<Ctx>();
const app = createHonoApp<Ctx>();
app.use(otel());

// Logger middleware
if (env.NODE_ENV === 'development') {
app.use(logger());
}
setupRouteLogger(app, process.env.NODE_ENV === 'development');

// Health check endpoint
app.get('/health', (c) => {
const memoryUsage = process.memoryUsage();
const uptime = process.uptime();
return c.json({
service: `Mail Bridge [${env.MAILBRIDGE_MODE === 'handler' ? 'Handler' : 'Dual'}]`,
memoryUsage,
uptime
});
setupHealthReporting(app, {
service: `Mail Bridge [${env.MAILBRIDGE_MODE === 'handler' ? 'Handler' : 'Dual'}]`
});

// TRPC handler
app.use(
'/trpc/*',
trpcServer({
router: trpcMailBridgeRouter,
createContext: (_, c) => {
const authToken = c.req.header('Authorization');
const isServiceAuthenticated = authToken === env.MAILBRIDGE_KEY;
return {
auth: isServiceAuthenticated,
db,
config: env,
context: c
} satisfies TRPCContext;
}
})
);
setupTrpcHandler(app, trpcMailBridgeRouter, (_, c) => {
const authToken = c.req.header('Authorization');
const isServiceAuthenticated = authToken === env.MAILBRIDGE_KEY;
return {
auth: isServiceAuthenticated,
db,
config: env,
context: c
} satisfies TRPCContext;
});

setupErrorHandlers(app);

// Postal endpoints
app.use('/postal/*', signatureMiddleware);
app.route('/postal', eventApi);
app.route('/postal', inboundApi);

// 404 handler
app.notFound((c) => c.json({ message: 'Not Found' }, 404));

// Global error handler
app.onError((err, c) => {
console.error(err);
return c.json({ message: 'Something went wrong' }, 500);
});

// Start server
const server = serve(
{
fetch: app.fetch,
port: env.PORT
},
() =>
console.info(`Starting mail-bridge handler server on port ${env.PORT}`)
);

processCleanup.push(
() =>
new Promise<void>((resolve) => {
server.close(() => {
console.info('Shutting down mail-bridge handler server');
resolve();
});
})
);
const cleanup = setupHonoListener(app, { port: env.PORT });
processCleanup.push(cleanup);
}

if (env.MAILBRIDGE_MODE === 'dual' || env.MAILBRIDGE_MODE === 'worker') {
Expand All @@ -100,14 +64,4 @@ if (env.MAILBRIDGE_MODE === 'dual' || env.MAILBRIDGE_MODE === 'worker') {
});
}

// Handle uncaught errors
process.on('unhandledRejection', (err) => console.error(err));
process.on('uncaughtException', (err) => console.error(err));

const handleExit = async () => {
await Promise.allSettled(processCleanup.map((fn) => fn()));
process.exit();
};

process.on('SIGINT', handleExit);
process.on('SIGTERM', handleExit);
setupRuntime(processCleanup);
11 changes: 5 additions & 6 deletions apps/mail-bridge/ctx.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type { db } from '@u22n/database';
import type { Otel } from '@u22n/otel/hono';
import type { env } from './env';
import type { Context } from 'hono';
import type { Context } from '@u22n/hono/helpers';
import type { HonoContext } from '@u22n/hono';

export type Ctx = {
Variables: {
otel: Otel;
};
};
export type Ctx = HonoContext<{
otel: Otel;
}>;

export type TRPCContext = {
auth: boolean;
Expand Down
7 changes: 2 additions & 5 deletions apps/mail-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@
}
},
"dependencies": {
"@hono/node-server": "^1.12.0",
"@hono/trpc-server": "^0.3.2",
"@hono/zod-validator": "^0.2.2",
"@t3-oss/env-core": "^0.10.1",
"@trpc/client": "11.0.0-rc.413",
"@trpc/server": "11.0.0-rc.413",
"@types/mailparser": "^3.4.4",
"@u22n/database": "workspace:*",
"@u22n/hono": "workspace:^",
"@u22n/mailtools": "^0.1.2",
"@u22n/otel": "workspace:^",
"@u22n/realtime": "workspace:^",
Expand All @@ -31,7 +28,6 @@
"bullmq": "^5.8.7",
"dompurify": "^3.1.6",
"drizzle-orm": "^0.32.0",
"hono": "^4.4.13",
"jsdom": "^24.1.0",
"mailauth": "^4.6.8",
"mailparser": "^3.7.1",
Expand All @@ -45,6 +41,7 @@
"devDependencies": {
"@types/dompurify": "^3.0.5",
"@types/jsdom": "^21.1.7",
"@types/mailparser": "^3.4.4",
"@types/node": "^20.14.10",
"@types/nodemailer": "^6.4.15",
"tsup": "^8.1.2",
Expand Down
11 changes: 7 additions & 4 deletions apps/mail-bridge/postal-routes/events.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Hono } from 'hono';
import { createHonoApp } from '@u22n/hono';

export const eventApi = new Hono().post('/events/:params{.+}', async (c) => {
return c.json({ error: 'Not implemented' }, 400);
});
export const eventApi = createHonoApp().post(
'/events/:params{.+}',
async (c) => {
return c.json({ error: 'Not implemented' }, 400);
}
);
6 changes: 3 additions & 3 deletions apps/mail-bridge/postal-routes/inbound.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { zValidator } from '@hono/zod-validator';
import {
postalMessageSchema,
mailParamsSchema
} from '../queue/mail-processor/schemas';
import type { Ctx } from '../ctx';
import { Hono } from 'hono';
import { mailProcessorQueue } from '../queue/mail-processor';
import { createHonoApp } from '@u22n/hono';
import { zValidator } from '@u22n/hono/helpers';

export const inboundApi = new Hono<Ctx>();
export const inboundApi = createHonoApp<Ctx>();

inboundApi.post(
'/mail/inbound/:orgId/:mailserverId',
Expand Down
2 changes: 1 addition & 1 deletion apps/mail-bridge/postal-routes/signature-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { env } from '../env';
import { createMiddleware } from 'hono/factory';
import { validatePostalWebhookSignature } from '../utils/validatePostalWebhookSignature';
import type { Ctx } from '../ctx';
import { createMiddleware } from '@u22n/hono/helpers';

export const signatureMiddleware = createMiddleware<Ctx>(async (c, next) =>
c
Expand Down
109 changes: 30 additions & 79 deletions apps/platform/app.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,55 @@
import './tracing';
import type { Ctx, TrpcContext } from './ctx';
import { env } from './env';
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { cors } from 'hono/cors';
import {
createHonoApp,
setupCors,
setupErrorHandlers,
setupHealthReporting,
setupHonoListener,
setupRouteLogger,
setupRuntime,
setupTrpcHandler
} from '@u22n/hono';
import { authApi } from './routes/auth';
import { realtimeApi } from './routes/realtime';
import { trpcPlatformRouter } from './trpc';
import { db } from '@u22n/database';
import { trpcServer } from '@hono/trpc-server';
import { authMiddleware, serviceMiddleware } from './middlewares';
import { otel } from '@u22n/otel/hono';
import { logger } from 'hono/logger';
import { servicesApi } from './routes/services';

const app = new Hono<Ctx>();
app.use(otel());
const app = createHonoApp<Ctx>();

// Logger middleware
if (env.NODE_ENV === 'development') {
app.use(logger());
}
app.use(otel());

// CORS middleware
app.use(
'*',
cors({
origin: env.WEBAPP_URL,
credentials: true,
exposeHeaders: ['Location']
})
);
setupRouteLogger(app, env.NODE_ENV === 'development');
setupCors(app, { origin: [env.WEBAPP_URL], exposeHeaders: ['Location'] });
setupHealthReporting(app, { service: 'Platform' });
setupErrorHandlers(app);

// Auth middleware
app.use('*', authMiddleware);

// Health check endpoint
app.get('/health', (c) => {
const memoryUsage = process.memoryUsage();
const uptime = process.uptime();
return c.json({
service: `Platform`,
memoryUsage,
uptime
});
});
setupTrpcHandler(
app,
trpcPlatformRouter,
(_, c) =>
({
db,
account: c.get('account'),
org: null,
event: c,
selfHosted: !env.EE_LICENSE_KEY
}) satisfies TrpcContext
);

// Routes
app.route('/auth', authApi);
app.route('/realtime', realtimeApi);

// Service Endpoints
app.use('/services/*', serviceMiddleware);
app.route('/services', servicesApi);

// TRPC handler
app.use(
'/trpc/*',
trpcServer({
router: trpcPlatformRouter,
createContext: (_, c) =>
({
db,
account: c.get('account'),
org: null,
event: c,
selfHosted: !env.EE_LICENSE_KEY
}) satisfies TrpcContext
})
);

// 404 handler
app.notFound((c) => c.json({ message: 'Not Found' }, 404));

// Global error handler
app.onError((err, c) => {
console.error(err);
return c.json({ message: 'Something went wrong' }, 500);
});

// Handle uncaught errors
process.on('unhandledRejection', (err) => console.error(err));
process.on('uncaughtException', (err) => console.error(err));

// Start server
const server = serve(
{
fetch: app.fetch,
port: env.PORT
},
() => console.info(`Server listening on port ${env.PORT}`)
);

// Clean Exit
const handleExit = () => {
server.close(() => {
console.info('Shutting down...');
process.exit();
});
};

process.on('SIGINT', handleExit);
process.on('SIGTERM', handleExit);
const cleanup = setupHonoListener(app, { port: env.PORT });
setupRuntime([cleanup]);
15 changes: 6 additions & 9 deletions apps/platform/ctx.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import type { HttpBindings } from '@hono/node-server';
import type { DBType } from '@u22n/database';
import type { HonoContext } from '@u22n/hono';
import type { Otel } from '@u22n/otel/hono';
import type { Context } from 'hono';
import type { Context } from '@u22n/hono/helpers';
import type { DatabaseSession } from 'lucia';

export type Ctx = {
Bindings: HttpBindings;
Variables: {
account: AccountContext;
otel: Otel;
};
};
export type Ctx = HonoContext<{
account: AccountContext;
otel: Otel;
}>;

export type OrgContext = {
id: number;
Expand Down
3 changes: 1 addition & 2 deletions apps/platform/middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getCookie } from 'hono/cookie';
import { createMiddleware, getCookie } from '@u22n/hono/helpers';
import { storage } from './storage';
import { createMiddleware } from 'hono/factory';
import type { Ctx } from './ctx';
import { env } from './env';

Expand Down
Loading

0 comments on commit b3f8965

Please sign in to comment.