aboutsummaryrefslogtreecommitdiff
path: root/src/server/index.ts
diff options
context:
space:
mode:
authorElizabeth Hunt <me@liz.coffee>2025-12-14 20:36:24 -0800
committerElizabeth Hunt <me@liz.coffee>2025-12-14 20:36:24 -0800
commit6bf57766feb8321f860baf300140563cd9539053 (patch)
treed80ff78c2a7f4dbea79f9ee850542aee1b735ef4 /src/server/index.ts
downloadposthook-6bf57766feb8321f860baf300140563cd9539053.tar.gz
posthook-6bf57766feb8321f860baf300140563cd9539053.zip
Init
Diffstat (limited to 'src/server/index.ts')
-rw-r--r--src/server/index.ts84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/server/index.ts b/src/server/index.ts
new file mode 100644
index 0000000..0747680
--- /dev/null
+++ b/src/server/index.ts
@@ -0,0 +1,84 @@
+import {
+ Either,
+ FourOhFourActivityImpl,
+ HealthCheckActivityImpl,
+ type HealthChecker,
+ HealthCheckOutput,
+ type IFourOhFourActivity,
+ type IHealthCheckActivity,
+ type ITraceable,
+ PenguenoRequest,
+ Server,
+ type ServerTrace,
+} from '@emprespresso/pengueno';
+import { Storage } from '../storage/index.js';
+import {
+ ListRoutesActivityImpl,
+ RegisterRouteActivityImpl,
+ TokenGenerateActivityImpl,
+ WebhookActivityImpl,
+ type IListRoutesActivity,
+ type IRegisterRouteActivity,
+ type ITokenGenerateActivity,
+ type IWebhookActivity,
+} from '../activity/index.js';
+import { TokenSigner } from '../token/index.js';
+
+const defaultHealthCheck: HealthChecker = async (input) => {
+ void input.get();
+ return Either.right(HealthCheckOutput.YAASSSLAYQUEEN);
+};
+
+export class PosthookServer implements Server {
+ constructor(
+ storage: Storage,
+ signer: TokenSigner,
+ healthCheck: HealthChecker = defaultHealthCheck,
+ private readonly healthCheckActivity: IHealthCheckActivity = new HealthCheckActivityImpl(healthCheck),
+ private readonly registerRouteActivity: IRegisterRouteActivity = new RegisterRouteActivityImpl(storage),
+ private readonly webhookActivity: IWebhookActivity = new WebhookActivityImpl(storage, signer),
+ private readonly tokenGenerateActivity: ITokenGenerateActivity = new TokenGenerateActivityImpl(storage, signer),
+ private readonly listRoutesActivity: IListRoutesActivity = new ListRoutesActivityImpl(storage),
+ private readonly fourOhFourActivity: IFourOhFourActivity = new FourOhFourActivityImpl(),
+ ) {}
+
+ public serve(req: ITraceable<PenguenoRequest, ServerTrace>) {
+ const url = new URL(req.get().req.url);
+ const { pathname } = url;
+
+ // === Public Routes (/) ===
+
+ // Health check endpoint
+ if (pathname === '/health') {
+ return this.healthCheckActivity.checkHealth(req);
+ }
+
+ // Token generation endpoint - /hook/{routeName}/token
+ const tokenMatch = pathname.match(/^\/hook\/([^/]+)\/token$/);
+ if (tokenMatch && req.get().req.method === 'GET') {
+ const routeName = tokenMatch[1];
+ return this.tokenGenerateActivity.generateToken(routeName)(req);
+ }
+
+ // Dynamic webhook endpoints - /hook/{routeName}
+ const hookMatch = pathname.match(/^\/hook\/([^/]+)$/);
+ if (hookMatch && req.get().req.method === 'POST') {
+ const routeName = hookMatch[1];
+ return this.webhookActivity.processWebhook(routeName)(req);
+ }
+
+ // === Admin Routes (/admin) - Put behind OAuth proxy ===
+
+ // Admin endpoints for route management
+ if (pathname === '/admin/routes' && req.get().req.method === 'POST') {
+ return this.registerRouteActivity.registerRoute(req);
+ }
+
+ if (pathname === '/admin/routes' && req.get().req.method === 'GET') {
+ return this.listRoutesActivity.listRoutes(req);
+ }
+
+ // 404 for everything else
+ return this.fourOhFourActivity.fourOhFour(req);
+ }
+}