aboutsummaryrefslogtreecommitdiff
path: root/src/server/index.ts
blob: 07476801ccf05d8331cb2468c3f89125019c7b97 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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);
    }
}