Skip to content

Commit

Permalink
feat: http webhooks
Browse files Browse the repository at this point in the history
  • Loading branch information
diced committed Jun 14, 2024
1 parent 080a3a2 commit ca766bb
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 34 deletions.
14 changes: 7 additions & 7 deletions src/lib/api/upload/partialUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { FastifyRequest } from 'fastify';
import { writeFile } from 'fs/promises';
import { extname, join } from 'path';
import { Worker } from 'worker_threads';
import { config } from '../../config';
import { hashPassword } from '../../crypto';
import { prisma } from '../../db';
import { log } from '../../logger';
import { guess } from '../../mimes';
import { formatFileName } from '../../uploader/formatFileName';
import { UploadHeaders, UploadOptions } from '../../uploader/parseHeaders';
import { config } from '@/lib/config';
import { hashPassword } from '@/lib/crypto';
import { prisma } from '@/lib/db';
import { log } from '@/lib/logger';
import { guess } from '@/lib/mimes';
import { formatFileName } from '@/lib/uploader/formatFileName';
import { UploadHeaders, UploadOptions } from '@/lib/uploader/parseHeaders';

const logger = log('api').c('upload');
export async function handlePartialUpload({
Expand Down
26 changes: 13 additions & 13 deletions src/lib/api/upload/upload.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { ApiUploadResponse, MultipartFileBuffer } from '@/server/routes/api/upload';
import { FastifyRequest } from 'fastify';
import { extname } from 'path';
import { bytes } from '../../bytes';
import { compress } from '../../compress';
import { config } from '../../config';
import { hashPassword } from '../../crypto';
import { datasource } from '../../datasource';
import { prisma } from '../../db';
import { fileSelect } from '../../db/models/file';
import { onUpload } from '../../discord';
import { removeGps } from '../../gps';
import { log } from '../../logger';
import { guess } from '../../mimes';
import { formatFileName } from '../../uploader/formatFileName';
import { UploadHeaders, UploadOptions } from '../../uploader/parseHeaders';
import { bytes } from '@/lib/bytes';
import { compress } from '@/lib/compress';
import { config } from '@/lib/config';
import { hashPassword } from '@/lib/crypto';
import { datasource } from '@/lib/datasource';
import { prisma } from '@/lib/db';
import { fileSelect } from '@/lib/db/models/file';
import { onUpload } from '@/lib/webhooks';
import { removeGps } from '@/lib/gps';
import { log } from '@/lib/logger';
import { guess } from '@/lib/mimes';
import { formatFileName } from '@/lib/uploader/formatFileName';
import { UploadHeaders, UploadOptions } from '@/lib/uploader/parseHeaders';

const logger = log('api').c('upload');

Expand Down
10 changes: 10 additions & 0 deletions src/lib/config/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ export const rawConfig: any = {
adminBypass: undefined,
allowList: undefined,
},
httpWebhook: {
onUpload: undefined,
onShorten: undefined,
},
};

export const PROP_TO_ENV = {
Expand Down Expand Up @@ -239,6 +243,9 @@ export const PROP_TO_ENV = {
'ratelimit.window': 'RATELIMIT_WINDOW',
'ratelimit.adminBypass': 'RATELIMIT_ADMIN_BYPASS',
'ratelimit.allowList': 'RATELIMIT_ALLOW_LIST',

'httpWebhook.onUpload': 'HTTP_WEBHOOK_ONUPLOAD',
'httpWebhook.onShorten': 'HTTP_WEBHOOK_ONSHORTEN',
};

const logger = log('config').c('read');
Expand Down Expand Up @@ -359,6 +366,9 @@ export function readEnv() {
env('ratelimit.window', 'ms'),
env('ratelimit.adminBypass', 'boolean'),
env('ratelimit.allowList', 'string[]'),

env('httpWebhook.onUpload', 'string'),
env('httpWebhook.onShorten', 'string'),
];

// clone raw
Expand Down
7 changes: 5 additions & 2 deletions src/lib/config/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { config } from '.';
import enabled from '../oauth/enabled';
import { Config } from './validate';

export type SafeConfig = Omit<Config, 'oauth' | 'datasource' | 'core'> & {
export type SafeConfig = Omit<
Config,
'oauth' | 'datasource' | 'core' | 'discord' | 'httpWebhook' | 'ratelimit'
> & {
oauthEnabled: ReturnType<typeof enabled>;
oauth: {
bypassLocalLogin: boolean;
Expand All @@ -11,7 +14,7 @@ export type SafeConfig = Omit<Config, 'oauth' | 'datasource' | 'core'> & {
};

export function safeConfig(): SafeConfig {
const { datasource: _d, core: _c, oauth, ...rest } = config;
const { datasource: _d, core: _c, oauth, discord: _di, ratelimit: _r, httpWebhook: _h, ...rest } = config;

(rest as SafeConfig).oauthEnabled = enabled(config);
(rest as SafeConfig).oauth = {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/config/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ export const schema = z.object({
adminBypass: z.boolean().default(true),
allowList: z.array(z.string()).default([]),
}),
httpWebhook: z.object({
onUpload: z.string().url().nullable().default(null),
onShorten: z.string().url().nullable().default(null),
}),
});

export type Config = z.infer<typeof schema>;
Expand Down
20 changes: 10 additions & 10 deletions src/lib/discord.ts → src/lib/webhooks/discord.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { z } from 'zod';
import { discordContent } from './config/validate';
import { ParseValue, parseString } from './parser';
import { config } from './config';
import { File } from './db/models/file';
import { User } from './db/models/user';
import { log } from './logger';
import { Url } from './db/models/url';
import { parserMetrics } from './parser/metrics';

const logger = log('discord');
import { discordContent } from '../config/validate';
import { ParseValue, parseString } from '../parser';
import { config } from '../config';
import { File } from '../db/models/file';
import { User } from '../db/models/user';
import { log } from '../logger';
import { Url } from '../db/models/url';
import { parserMetrics } from '../parser/metrics';

const logger = log('webhooks').c('discord');

export type DiscordContent = z.infer<typeof discordContent>;
export type WebhooksExecuteBody = {
Expand Down
87 changes: 87 additions & 0 deletions src/lib/webhooks/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { config } from '../config';
import { log } from '../logger';
import { onUpload as discordOnUpload, onShorten as discordOnShorten } from './discord';

const logger = log('webhooks').c('http');

export async function onUpload({ user, file, link }: Parameters<typeof discordOnUpload>[0]) {
if (!config.httpWebhook.onUpload) return;
if (!URL.canParse(config.httpWebhook.onUpload)) {
logger.debug('invalid url for http onUpload');
return;
}

delete (<any>user).oauthProviders;
delete user.passkeys;
delete user.token;
delete user.password;
delete user.totpSecret;
delete (<any>file).password;

const payload = {
type: 'upload',
data: {
user,
file,
link,
},
};

const res = await fetch(config.httpWebhook.onUpload, {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json',
'x-zipline-webhook': 'true',
'x-zipline-webhook-type': 'upload',
},
});

if (!res.ok) {
const text = await res.text();
logger.error('webhook failed', { response: text, status: res.status });
}

return;
}

export async function onShorten({ user, url, link }: Parameters<typeof discordOnShorten>[0]) {
if (!config.httpWebhook.onShorten) return;
if (!URL.canParse(config.httpWebhook.onShorten)) {
logger.debug('invalid url for http onShorten');
return;
}

delete (<any>user).oauthProviders;
delete user.passkeys;
delete user.token;
delete user.password;
delete user.totpSecret;
delete (<any>url).password;

const payload = {
type: 'shorten',
data: {
user,
url,
link,
},
};

const res = await fetch(config.httpWebhook.onShorten, {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json',
'x-zipline-webhook': 'true',
'x-zipline-webhook-type': 'shorten',
},
});

if (!res.ok) {
const text = await res.text();
logger.error('webhook failed', { response: text, status: res.status });
}

return;
}
14 changes: 14 additions & 0 deletions src/lib/webhooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { onUpload as discordOnUpload, onShorten as discordOnShorten } from './discord';
import { onUpload as httpOnUpload, onShorten as httpOnShorten } from './http';

export async function onUpload(args: Parameters<typeof discordOnUpload>[0]) {
Promise.all([discordOnUpload(args), httpOnUpload(args)]);

return;
}

export async function onShorten(args: Parameters<typeof discordOnShorten>[0]) {
Promise.all([discordOnShorten(args), httpOnShorten(args)]);

return;
}
2 changes: 1 addition & 1 deletion src/offload/partial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { config } from '@/lib/config';
import { prisma } from '@/lib/db';
import { fileSelect } from '@/lib/db/models/file';
import { userSelect } from '@/lib/db/models/user';
import { onUpload } from '@/lib/discord';
import { onUpload } from '@/lib/webhooks';
import { log } from '@/lib/logger';
import { UploadOptions } from '@/lib/uploader/parseHeaders';
import { open, readFile, readdir, rm } from 'fs/promises';
Expand Down
2 changes: 1 addition & 1 deletion src/server/routes/api/user/urls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Url } from '@/lib/db/models/url';
import { log } from '@/lib/logger';
import { z } from 'zod';
import { Prisma } from '@prisma/client';
import { onShorten } from '@/lib/discord';
import { onShorten } from '@/lib/webhooks';
import fastifyPlugin from 'fastify-plugin';
import { userMiddleware } from '@/server/middleware/user';

Expand Down

0 comments on commit ca766bb

Please sign in to comment.