aboutsummaryrefslogtreecommitdiff
path: root/worker/scripts/coolify_webhook.ts
blob: d1b4eb0c3b19f514e19ca7fc7167314f6595d4c3 (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!/usr/bin/env node

import {
    Either,
    getRequiredEnvVars,
    LogTraceable,
    LogMetricTraceable,
    Metric,
    TraceUtil,
} from '@emprespresso/pengueno';
import { Infisical, type SecureNote } from '@emprespresso/ci_worker';
import { CoolifyWebhookJob } from '@emprespresso/ci_model';

const eitherJob = getRequiredEnvVars(['webhookUrl']).mapRight(
    (baseArgs) =>
        <CoolifyWebhookJob>{
            type: 'coolify_webhook.js',
            arguments: baseArgs,
        },
);

const eitherVault = Infisical.getConfigFromEnvironment().mapRight((config) => new Infisical(config));

const webhookMetric = Metric.fromName('coolify_webhook.trigger').asResult();
const _logJob = LogTraceable.of(eitherJob).flatMap(TraceUtil.withTrace('coolify_webhook'));
await LogMetricTraceable.ofLogTraceable(_logJob)
    .flatMap(TraceUtil.withMetricTrace(webhookMetric))
    .peek((tEitherJob) => tEitherJob.trace.trace('starting coolify webhook trigger! (⑅˘꒳˘)'))
    .map((tEitherJob) =>
        tEitherJob.get().flatMapAsync(async (job) => {
            tEitherJob.trace.trace('checking vault configuration...');
            return eitherVault.flatMapAsync(async (vault) => {
                tEitherJob.trace.trace('unlocking vault...');
                const eitherKey = await vault.unlock(tEitherJob);
                eitherKey.fold(
                    (err) => tEitherJob.trace.trace(`failed to unlock vault: ${err.message || err}`),
                    () => tEitherJob.trace.trace('unlocked vault :3'),
                );
                return eitherKey.mapRight((key) => ({ job, key, vault }));
            });
        }),
    )
    .map(async (tEitherJobVault) => {
        const eitherJobVault = await tEitherJobVault.get();
        return eitherJobVault.flatMapAsync(async ({ job, key, vault }) => {
            tEitherJobVault.trace.trace('fetching coolify secret from vault...');
            const eitherSecret = await vault.fetchSecret<SecureNote>(tEitherJobVault, key, 'coolify');
            await vault.lock(tEitherJobVault, key);

            eitherSecret.fold(
                (err: Error) => tEitherJobVault.trace.trace(`failed to fetch coolify secret: ${err.message || err}`),
                () => tEitherJobVault.trace.trace('successfully retrieved coolify webhook secret'),
            );

            return eitherSecret.mapRight(({ notes }: SecureNote) => ({ job, webhookSecret: notes }));
        });
    })
    .map(async (tEitherJobAndSecret) => {
        const eitherJobAndSecret = await tEitherJobAndSecret.get();
        return eitherJobAndSecret.flatMapAsync(async ({ job, webhookSecret }) => {
            tEitherJobAndSecret.trace.trace(`triggering webhook at ${job.arguments.webhookUrl} (◕ᴗ◕✿)`);

            return Either.fromFailableAsync<Error, string>(() =>
                fetch(job.arguments.webhookUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        Authorization: `Bearer ${webhookSecret}`,
                    },
                    body: JSON.stringify({}),
                }).then(async (response) => {
                    if (!response.ok) {
                        const errorText = await response.text();
                        tEitherJobAndSecret.trace.trace(
                            `webhook failed with status ${response.status}: ${errorText}`,
                        );
                        throw new Error(`webhook request failed with status ${response.status}: ${errorText}`);
                    }
                    return response.text();
                }),
            ).then((e) => {
                e.fold(
                    (err) => tEitherJobAndSecret.trace.trace(`webhook error: ${err.message || err}`),
                    (responseText) => tEitherJobAndSecret.trace.trace(`webhook response: ${responseText}`),
                );
                return e;
            });
        });
    })
    .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(webhookMetric)))
    .peek(
        TraceUtil.promiseify((tEitherResult) =>
            tEitherResult.get().fold(
                (err) => tEitherResult.trace.trace(`oh nyoo webhook failed :(( ${err.message || err}`),
                () => tEitherResult.trace.trace('webhook triggered successfully! (◕ᴗ◕✿)'),
            ),
        ),
    )
    .map(async (tEitherJob) => {
        const eitherJob = await tEitherJob.get();
        return eitherJob.fold(
            (e) => Promise.reject(e),
            () => Promise.resolve(0),
        );
    })
    .get();