mirror of
https://github.com/diced/zipline.git
synced 2025-12-12 15:50:11 -08:00
feat: string bytes/ms settings so they wont overflow
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Zipline" ALTER COLUMN "chunksMax" SET DEFAULT '95mb',
|
||||||
|
ALTER COLUMN "chunksMax" SET DATA TYPE TEXT,
|
||||||
|
ALTER COLUMN "chunksSize" SET DEFAULT '25mb',
|
||||||
|
ALTER COLUMN "chunksSize" SET DATA TYPE TEXT,
|
||||||
|
ALTER COLUMN "tasksDeleteInterval" SET DEFAULT '30m',
|
||||||
|
ALTER COLUMN "tasksDeleteInterval" SET DATA TYPE TEXT,
|
||||||
|
ALTER COLUMN "tasksClearInvitesInterval" SET DEFAULT '30m',
|
||||||
|
ALTER COLUMN "tasksClearInvitesInterval" SET DATA TYPE TEXT,
|
||||||
|
ALTER COLUMN "tasksMaxViewsInterval" SET DEFAULT '30m',
|
||||||
|
ALTER COLUMN "tasksMaxViewsInterval" SET DATA TYPE TEXT,
|
||||||
|
ALTER COLUMN "tasksThumbnailsInterval" SET DEFAULT '30m',
|
||||||
|
ALTER COLUMN "tasksThumbnailsInterval" SET DATA TYPE TEXT,
|
||||||
|
ALTER COLUMN "tasksMetricsInterval" SET DEFAULT '30m',
|
||||||
|
ALTER COLUMN "tasksMetricsInterval" SET DATA TYPE TEXT,
|
||||||
|
ALTER COLUMN "filesMaxFileSize" SET DEFAULT '100mb',
|
||||||
|
ALTER COLUMN "filesMaxFileSize" SET DATA TYPE TEXT;
|
||||||
@@ -20,20 +20,20 @@ model Zipline {
|
|||||||
coreTempDirectory String // default join(tmpdir(), 'zipline')
|
coreTempDirectory String // default join(tmpdir(), 'zipline')
|
||||||
|
|
||||||
chunksEnabled Boolean @default(true)
|
chunksEnabled Boolean @default(true)
|
||||||
chunksMax Int @default(99614720)
|
chunksMax String @default("95mb")
|
||||||
chunksSize Int @default(26214400)
|
chunksSize String @default("25mb")
|
||||||
|
|
||||||
tasksDeleteInterval Int @default(1800000)
|
tasksDeleteInterval String @default("30m")
|
||||||
tasksClearInvitesInterval Int @default(1800000)
|
tasksClearInvitesInterval String @default("30m")
|
||||||
tasksMaxViewsInterval Int @default(1800000)
|
tasksMaxViewsInterval String @default("30m")
|
||||||
tasksThumbnailsInterval Int @default(1800000)
|
tasksThumbnailsInterval String @default("30m")
|
||||||
tasksMetricsInterval Int @default(1800000)
|
tasksMetricsInterval String @default("30m")
|
||||||
|
|
||||||
filesRoute String @default("/u")
|
filesRoute String @default("/u")
|
||||||
filesLength Int @default(6)
|
filesLength Int @default(6)
|
||||||
filesDefaultFormat String @default("random")
|
filesDefaultFormat String @default("random")
|
||||||
filesDisabledExtensions String[]
|
filesDisabledExtensions String[]
|
||||||
filesMaxFileSize Int @default(104857600)
|
filesMaxFileSize String @default("100mb")
|
||||||
filesDefaultExpiration String?
|
filesDefaultExpiration String?
|
||||||
filesAssumeMimetypes Boolean @default(false)
|
filesAssumeMimetypes Boolean @default(false)
|
||||||
filesDefaultDateFormat String @default("YYYY-MM-DD_HH:mm:ss")
|
filesDefaultDateFormat String @default("YYYY-MM-DD_HH:mm:ss")
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Response } from '@/lib/api/response';
|
import { Response } from '@/lib/api/response';
|
||||||
import { bytes } from '@/lib/bytes';
|
|
||||||
import { Button, LoadingOverlay, Paper, SimpleGrid, Switch, TextInput, Title } from '@mantine/core';
|
import { Button, LoadingOverlay, Paper, SimpleGrid, Switch, TextInput, Title } from '@mantine/core';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { IconDeviceFloppy } from '@tabler/icons-react';
|
import { IconDeviceFloppy } from '@tabler/icons-react';
|
||||||
@@ -28,8 +27,8 @@ export default function ServerSettingsChunks({
|
|||||||
|
|
||||||
form.setValues({
|
form.setValues({
|
||||||
chunksEnabled: data?.chunksEnabled ?? true,
|
chunksEnabled: data?.chunksEnabled ?? true,
|
||||||
chunksMax: bytes(data!.chunksMax),
|
chunksMax: data!.chunksMax ?? '',
|
||||||
chunksSize: bytes(data!.chunksSize),
|
chunksSize: data!.chunksSize ?? '',
|
||||||
});
|
});
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Response } from '@/lib/api/response';
|
import { Response } from '@/lib/api/response';
|
||||||
import { bytes } from '@/lib/bytes';
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
LoadingOverlay,
|
LoadingOverlay,
|
||||||
@@ -82,7 +81,7 @@ export default function ServerSettingsFiles({
|
|||||||
filesLength: data?.filesLength ?? 6,
|
filesLength: data?.filesLength ?? 6,
|
||||||
filesDefaultFormat: data?.filesDefaultFormat ?? 'random',
|
filesDefaultFormat: data?.filesDefaultFormat ?? 'random',
|
||||||
filesDisabledExtensions: data?.filesDisabledExtensions.join(', ') ?? '',
|
filesDisabledExtensions: data?.filesDisabledExtensions.join(', ') ?? '',
|
||||||
filesMaxFileSize: bytes(data?.filesMaxFileSize ?? 104857600),
|
filesMaxFileSize: data?.filesMaxFileSize ?? '100mb',
|
||||||
filesDefaultExpiration: data?.filesDefaultExpiration ?? '',
|
filesDefaultExpiration: data?.filesDefaultExpiration ?? '',
|
||||||
filesAssumeMimetypes: data?.filesAssumeMimetypes ?? false,
|
filesAssumeMimetypes: data?.filesAssumeMimetypes ?? false,
|
||||||
filesDefaultDateFormat: data?.filesDefaultDateFormat ?? 'YYYY-MM-DD_HH:mm:ss',
|
filesDefaultDateFormat: data?.filesDefaultDateFormat ?? 'YYYY-MM-DD_HH:mm:ss',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Response } from '@/lib/api/response';
|
|||||||
import { Button, LoadingOverlay, Paper, SimpleGrid, Text, TextInput, Title } from '@mantine/core';
|
import { Button, LoadingOverlay, Paper, SimpleGrid, Text, TextInput, Title } from '@mantine/core';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { IconDeviceFloppy } from '@tabler/icons-react';
|
import { IconDeviceFloppy } from '@tabler/icons-react';
|
||||||
import ms from 'ms';
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { settingsOnSubmit } from '../settingsOnSubmit';
|
import { settingsOnSubmit } from '../settingsOnSubmit';
|
||||||
@@ -15,11 +14,11 @@ export default function ServerSettingsTasks({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
tasksDeleteInterval: ms(1800000),
|
tasksDeleteInterval: '30m',
|
||||||
tasksClearInvitesInterval: ms(1800000),
|
tasksClearInvitesInterval: '30m',
|
||||||
tasksMaxViewsInterval: ms(1800000),
|
tasksMaxViewsInterval: '30m',
|
||||||
tasksThumbnailsInterval: ms(1800000),
|
tasksThumbnailsInterval: '30m',
|
||||||
tasksMetricsInterval: ms(1800000),
|
tasksMetricsInterval: '30m',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,11 +28,11 @@ export default function ServerSettingsTasks({
|
|||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
form.setValues({
|
form.setValues({
|
||||||
tasksDeleteInterval: ms(data?.tasksDeleteInterval ?? 1800000),
|
tasksDeleteInterval: data?.tasksDeleteInterval ?? '30m',
|
||||||
tasksClearInvitesInterval: ms(data?.tasksClearInvitesInterval ?? 1800000),
|
tasksClearInvitesInterval: data?.tasksClearInvitesInterval ?? '30m',
|
||||||
tasksMaxViewsInterval: ms(data?.tasksMaxViewsInterval ?? 1800000),
|
tasksMaxViewsInterval: data?.tasksMaxViewsInterval ?? '30m',
|
||||||
tasksThumbnailsInterval: ms(data?.tasksThumbnailsInterval ?? 1800000),
|
tasksThumbnailsInterval: data?.tasksThumbnailsInterval ?? '30m',
|
||||||
tasksMetricsInterval: ms(data?.tasksMetricsInterval ?? 1800000),
|
tasksMetricsInterval: data?.tasksMetricsInterval ?? '30m',
|
||||||
});
|
});
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default function UploadFile() {
|
|||||||
const toPartialFiles: File[] = [];
|
const toPartialFiles: File[] = [];
|
||||||
for (let i = 0; i !== files.length; ++i) {
|
for (let i = 0; i !== files.length; ++i) {
|
||||||
const file = files[i];
|
const file = files[i];
|
||||||
if (config.chunks.enabled && file.size >= config.chunks.max) {
|
if (config.chunks.enabled && file.size >= bytes(config.chunks.max)) {
|
||||||
toPartialFiles.push(file);
|
toPartialFiles.push(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ export default function UploadFile() {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const size = aggSize();
|
const size = aggSize();
|
||||||
if (size > config.files.maxFileSize && !toPartialFiles.length) {
|
if (size > bytes(config.files.maxFileSize) && !toPartialFiles.length) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Upload may fail',
|
title: 'Upload may fail',
|
||||||
color: 'yellow',
|
color: 'yellow',
|
||||||
@@ -97,7 +97,7 @@ export default function UploadFile() {
|
|||||||
<>
|
<>
|
||||||
The upload may fail because the total size of the files (that are not being partially uploaded)
|
The upload may fail because the total size of the files (that are not being partially uploaded)
|
||||||
you are trying to upload is <b>{bytes(size)}</b>, which is larger than the limit of{' '}
|
you are trying to upload is <b>{bytes(size)}</b>, which is larger than the limit of{' '}
|
||||||
<b>{bytes(config.files.maxFileSize)}</b>
|
<b>{bytes(bytes(config.files.maxFileSize))}</b>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@@ -167,7 +167,7 @@ export default function UploadFile() {
|
|||||||
Attach as many files as you like, they will show up below to review before uploading.
|
Attach as many files as you like, they will show up below to review before uploading.
|
||||||
</Text>
|
</Text>
|
||||||
<Text size='sm' c='dimmed' mt={7}>
|
<Text size='sm' c='dimmed' mt={7}>
|
||||||
<b>{bytes(config.files.maxFileSize)}</b> limit per file
|
<b>{bytes(bytes(config.files.maxFileSize))}</b> limit per file
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useConfig } from '@/components/ConfigProvider';
|
import { useConfig } from '@/components/ConfigProvider';
|
||||||
import { Response } from '@/lib/api/response';
|
import { Response } from '@/lib/api/response';
|
||||||
|
import { bytes } from '@/lib/bytes';
|
||||||
import { randomCharacters } from '@/lib/random';
|
import { randomCharacters } from '@/lib/random';
|
||||||
import { ErrorBody } from '@/lib/response';
|
import { ErrorBody } from '@/lib/response';
|
||||||
import { UploadOptionsStore } from '@/lib/store/uploadOptions';
|
import { UploadOptionsStore } from '@/lib/store/uploadOptions';
|
||||||
@@ -94,10 +95,12 @@ export async function uploadPartialFiles(
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setProgress({ percent: 0, remaining: 0, speed: 0 });
|
setProgress({ percent: 0, remaining: 0, speed: 0 });
|
||||||
|
|
||||||
|
const chunkSize = bytes(config.chunks.size);
|
||||||
|
|
||||||
for (let i = 0; i !== files.length; ++i) {
|
for (let i = 0; i !== files.length; ++i) {
|
||||||
const file = files[i];
|
const file = files[i];
|
||||||
const identifier = randomCharacters(8);
|
const identifier = randomCharacters(8);
|
||||||
const nChunks = Math.ceil(file.size / config.chunks.size);
|
const nChunks = Math.ceil(file.size / chunkSize);
|
||||||
const chunks: {
|
const chunks: {
|
||||||
blob: Blob;
|
blob: Blob;
|
||||||
start: number;
|
start: number;
|
||||||
@@ -105,8 +108,8 @@ export async function uploadPartialFiles(
|
|||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
for (let j = 0; j !== nChunks; ++j) {
|
for (let j = 0; j !== nChunks; ++j) {
|
||||||
const start = j * config.chunks.size;
|
const start = j * chunkSize;
|
||||||
const end = Math.min(start + config.chunks.size, file.size);
|
const end = Math.min(start + chunkSize, file.size);
|
||||||
chunks.push({
|
chunks.push({
|
||||||
blob: file.slice(start, end),
|
blob: file.slice(start, end),
|
||||||
start,
|
start,
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ export async function handleFile({
|
|||||||
const extension = options.overrides?.extension ?? extname(file.filename);
|
const extension = options.overrides?.extension ?? extname(file.filename);
|
||||||
if (config.files.disabledExtensions.includes(extension)) throw `File extension ${extension} is not allowed`;
|
if (config.files.disabledExtensions.includes(extension)) throw `File extension ${extension} is not allowed`;
|
||||||
|
|
||||||
if (file.file.bytesRead > config.files.maxFileSize)
|
if (file.file.bytesRead > bytes(config.files.maxFileSize))
|
||||||
throw `File size is too large. Maximum file size is ${config.files.maxFileSize} bytes`;
|
throw `File size is too large. Maximum file size is ${bytes(config.files.maxFileSize)} bytes`;
|
||||||
|
|
||||||
const format = options.format || config.files.defaultFormat;
|
const format = options.format || config.files.defaultFormat;
|
||||||
let fileName = formatFileName(format, file.filename);
|
let fileName = formatFileName(format, file.filename);
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { type ZodIssue, z } from 'zod';
|
|
||||||
import { PROP_TO_ENV, ParsedConfig } from './read';
|
|
||||||
import { log } from '../logger';
|
|
||||||
import { join, resolve } from 'path';
|
|
||||||
import { bytes } from '../bytes';
|
|
||||||
import ms from 'ms';
|
|
||||||
import { tmpdir } from 'os';
|
import { tmpdir } from 'os';
|
||||||
|
import { join, resolve } from 'path';
|
||||||
|
import { type ZodIssue, z } from 'zod';
|
||||||
|
import { log } from '../logger';
|
||||||
|
import { PROP_TO_ENV, ParsedConfig } from './read';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
@@ -75,23 +73,23 @@ export const schema = z.object({
|
|||||||
.default(join(tmpdir(), 'zipline')),
|
.default(join(tmpdir(), 'zipline')),
|
||||||
}),
|
}),
|
||||||
chunks: z.object({
|
chunks: z.object({
|
||||||
max: z.number().default(bytes('95mb')),
|
max: z.string().default('95mb'),
|
||||||
size: z.number().default(bytes('25mb')),
|
size: z.string().default('25mb'),
|
||||||
enabled: z.boolean().default(true),
|
enabled: z.boolean().default(true),
|
||||||
}),
|
}),
|
||||||
tasks: z.object({
|
tasks: z.object({
|
||||||
deleteInterval: z.number().default(ms('30min')),
|
deleteInterval: z.string().default('30min'),
|
||||||
clearInvitesInterval: z.number().default(ms('30min')),
|
clearInvitesInterval: z.string().default('30min'),
|
||||||
maxViewsInterval: z.number().default(ms('30min')),
|
maxViewsInterval: z.string().default('30min'),
|
||||||
thumbnailsInterval: z.number().default(ms('30min')),
|
thumbnailsInterval: z.string().default('30min'),
|
||||||
metricsInterval: z.number().default(ms('30min')),
|
metricsInterval: z.string().default('30min'),
|
||||||
}),
|
}),
|
||||||
files: z.object({
|
files: z.object({
|
||||||
route: z.string().startsWith('/').min(1).trim().toLowerCase().default('/u'),
|
route: z.string().startsWith('/').min(1).trim().toLowerCase().default('/u'),
|
||||||
length: z.number().default(6),
|
length: z.number().default(6),
|
||||||
defaultFormat: z.enum(['random', 'date', 'uuid', 'name', 'gfycat']).default('random'),
|
defaultFormat: z.enum(['random', 'date', 'uuid', 'name', 'gfycat']).default('random'),
|
||||||
disabledExtensions: z.array(z.string()).default([]),
|
disabledExtensions: z.array(z.string()).default([]),
|
||||||
maxFileSize: z.number().default(bytes('100mb')),
|
maxFileSize: z.string().default('100mb'),
|
||||||
defaultExpiration: z.string().nullable().default(null),
|
defaultExpiration: z.string().nullable().default(null),
|
||||||
assumeMimetypes: z.boolean().default(false),
|
assumeMimetypes: z.boolean().default(false),
|
||||||
defaultDateFormat: z.string().default('YYYY-MM-DD_HH:mm:ss'),
|
defaultDateFormat: z.string().default('YYYY-MM-DD_HH:mm:ss'),
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export function withSafeConfig<T = unknown>(
|
|||||||
const config = safeConfig(libConfig);
|
const config = safeConfig(libConfig);
|
||||||
const data = await fn(ctx, config);
|
const data = await fn(ctx, config);
|
||||||
|
|
||||||
|
console.log(config, data);
|
||||||
|
|
||||||
if ((data as any) && (data as any).notFound)
|
if ((data as any) && (data as any).notFound)
|
||||||
return {
|
return {
|
||||||
notFound: true,
|
notFound: true,
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ async function handler({ code, host, state }: OAuthQuery, _logger: Logger): Prom
|
|||||||
|
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
if (!json.access_token) return { error: 'No access token in response' };
|
if (!json.access_token) return { error: 'No access token in response' };
|
||||||
if (!json.refresh_token) return { error: 'No refresh token in response' };
|
|
||||||
|
|
||||||
const userJson = await googleAuth.user(json.access_token);
|
const userJson = await googleAuth.user(json.access_token);
|
||||||
if (!userJson) return { error: 'Failed to fetch user' };
|
if (!userJson) return { error: 'Failed to fetch user' };
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { bytes } from '@/lib/bytes';
|
||||||
import { reloadSettings } from '@/lib/config';
|
import { reloadSettings } from '@/lib/config';
|
||||||
import { getDatasource } from '@/lib/datasource';
|
import { getDatasource } from '@/lib/datasource';
|
||||||
import { prisma } from '@/lib/db';
|
import { prisma } from '@/lib/db';
|
||||||
@@ -19,6 +20,7 @@ import { fastifySensible } from '@fastify/sensible';
|
|||||||
import { fastifyStatic } from '@fastify/static';
|
import { fastifyStatic } from '@fastify/static';
|
||||||
import fastify from 'fastify';
|
import fastify from 'fastify';
|
||||||
import { mkdir } from 'fs/promises';
|
import { mkdir } from 'fs/promises';
|
||||||
|
import ms from 'ms';
|
||||||
import { parse } from 'url';
|
import { parse } from 'url';
|
||||||
import { version } from '../../package.json';
|
import { version } from '../../package.json';
|
||||||
import { checkRateLimit } from './plugins/checkRateLimit';
|
import { checkRateLimit } from './plugins/checkRateLimit';
|
||||||
@@ -84,7 +86,7 @@ async function main() {
|
|||||||
|
|
||||||
await server.register(fastifyMultipart, {
|
await server.register(fastifyMultipart, {
|
||||||
limits: {
|
limits: {
|
||||||
fileSize: config.files.maxFileSize,
|
fileSize: bytes(config.files.maxFileSize),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -203,10 +205,10 @@ async function main() {
|
|||||||
logger.info('server started', { hostname: config.core.hostname, port: config.core.port });
|
logger.info('server started', { hostname: config.core.hostname, port: config.core.port });
|
||||||
|
|
||||||
// Tasks
|
// Tasks
|
||||||
tasks.interval('deletefiles', config.tasks.deleteInterval, deleteFiles(prisma));
|
tasks.interval('deletefiles', ms(config.tasks.deleteInterval), deleteFiles(prisma));
|
||||||
tasks.interval('maxviews', config.tasks.maxViewsInterval, maxViews(prisma));
|
tasks.interval('maxviews', ms(config.tasks.maxViewsInterval), maxViews(prisma));
|
||||||
|
|
||||||
if (config.features.metrics) tasks.interval('metrics', config.tasks.metricsInterval, metrics(prisma));
|
if (config.features.metrics) tasks.interval('metrics', ms(config.tasks.metricsInterval), metrics(prisma));
|
||||||
|
|
||||||
if (config.features.thumbnails.enabled) {
|
if (config.features.thumbnails.enabled) {
|
||||||
for (let i = 0; i !== config.features.thumbnails.num_threads; ++i) {
|
for (let i = 0; i !== config.features.thumbnails.num_threads; ++i) {
|
||||||
@@ -216,8 +218,8 @@ async function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.interval('thumbnails', config.tasks.thumbnailsInterval, thumbnails(prisma));
|
tasks.interval('thumbnails', ms(config.tasks.thumbnailsInterval), thumbnails(prisma));
|
||||||
tasks.interval('clearinvites', config.tasks.clearInvitesInterval, clearInvites(prisma));
|
tasks.interval('clearinvites', ms(config.tasks.clearInvitesInterval), clearInvites(prisma));
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.start();
|
tasks.start();
|
||||||
|
|||||||
@@ -18,17 +18,8 @@ type Settings = Awaited<ReturnType<typeof readDatabaseSettings>>;
|
|||||||
export type ApiServerSettingsResponse = Settings;
|
export type ApiServerSettingsResponse = Settings;
|
||||||
type Body = Partial<Settings>;
|
type Body = Partial<Settings>;
|
||||||
|
|
||||||
const zMs = z
|
const zMs = z.string().refine((value) => ms(value) > 0, 'Value must be greater than 0');
|
||||||
.union([z.number().min(1), z.string()])
|
const zBytes = z.string().refine((value) => bytes(value) > 0, 'Value must be greater than 0');
|
||||||
.transform((value) => (typeof value === 'string' ? ms(value) : value))
|
|
||||||
.refine((value) => value > 0, 'Value must be greater than 0');
|
|
||||||
|
|
||||||
const zMsString = z.string().refine((value) => ms(value) > 0, 'Value must be greater than 0');
|
|
||||||
|
|
||||||
const zBytes = z
|
|
||||||
.union([z.number().min(1), z.string()])
|
|
||||||
.transform((value) => (typeof value === 'string' ? bytes(value) : value))
|
|
||||||
.refine((value) => value > 0, 'Value must be greater than 0');
|
|
||||||
|
|
||||||
const discordEmbed = z
|
const discordEmbed = z
|
||||||
.union([
|
.union([
|
||||||
@@ -125,7 +116,7 @@ export default fastifyPlugin(
|
|||||||
),
|
),
|
||||||
filesMaxFileSize: zBytes,
|
filesMaxFileSize: zBytes,
|
||||||
|
|
||||||
filesDefaultExpiration: zMsString.nullable(),
|
filesDefaultExpiration: zMs.nullable(),
|
||||||
filesAssumeMimetypes: z.boolean(),
|
filesAssumeMimetypes: z.boolean(),
|
||||||
filesDefaultDateFormat: z.string(),
|
filesDefaultDateFormat: z.string(),
|
||||||
filesRemoveGpsMetadata: z.boolean(),
|
filesRemoveGpsMetadata: z.boolean(),
|
||||||
|
|||||||
Reference in New Issue
Block a user