mirror of
https://github.com/diced/zipline.git
synced 2025-12-12 15:50:11 -08:00
feat: http webhooks
This commit is contained in:
@@ -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({
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -117,6 +117,10 @@ export const rawConfig: any = {
|
||||
adminBypass: undefined,
|
||||
allowList: undefined,
|
||||
},
|
||||
httpWebhook: {
|
||||
onUpload: undefined,
|
||||
onShorten: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const PROP_TO_ENV = {
|
||||
@@ -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');
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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';
|
||||
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');
|
||||
const logger = log('webhooks').c('discord');
|
||||
|
||||
export type DiscordContent = z.infer<typeof discordContent>;
|
||||
export type WebhooksExecuteBody = {
|
||||
87
src/lib/webhooks/http.ts
Normal file
87
src/lib/webhooks/http.ts
Normal file
@@ -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
src/lib/webhooks/index.ts
Normal file
14
src/lib/webhooks/index.ts
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user