1
Fork 0
mirror of https://github.com/diced/zipline.git synced 2025-04-11 23:31:17 -05:00

Different EXT system. Locked vals now warn instead

This commit is contained in:
Dissssy 2025-03-13 10:43:27 -04:00
parent e8ab4dec00
commit 000c22da30
No known key found for this signature in database
GPG key ID: 3B9C786B50FEBE32
20 changed files with 1349 additions and 1580 deletions

View file

@ -1,5 +1,5 @@
import { Response } from '@/lib/api/response';
import { Group, SimpleGrid, Skeleton, Stack, Title } from '@mantine/core';
import { Group, SimpleGrid, Skeleton, Stack, Title, Tooltip } from '@mantine/core';
import useSWR from 'swr';
import dynamic from 'next/dynamic';
@ -93,3 +93,42 @@ export default function DashboardSettings() {
</>
);
}
export function EnvTooltip(
props: React.PropsWithChildren<{
envVar: string;
data: any;
varKey: string;
}>,
) {
const state = checkPropSafe(props);
const enabled = state !== false;
return (
<Tooltip
label={
enabled
? `WARNING: The ${props.envVar} environment variable takes priority over this value. Currently "${state}"`
: ''
}
color='red'
events={{
hover: enabled,
focus: false,
touch: false,
}}
>
<div>{props.children}</div>
</Tooltip>
);
}
function checkPropSafe(props: any): boolean | string {
const data = props.data;
if (data === undefined) return false;
const locked = data.locked;
if (locked === undefined) return false;
const val = locked[props.varKey];
if (val === undefined) return false;
return val;
}

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsChunks({
swr: { data, isLoading },
@ -39,39 +40,38 @@ export default function ServerSettingsChunks({
<Title order={2}>Chunks</Title>
<form onSubmit={form.onSubmit(onSubmit)}>
<Switch
mt='md'
label='Enable Chunks'
description='Enable chunked uploads.'
disabled={data?.locked['chunksEnabled'] ? true : false}
{...form.getInputProps('chunksEnabled', { type: 'checkbox' })}
/>
<EnvTooltip envVar='CHUNKS_ENABLED' data={data} varKey='chunksEnabled'>
<Switch
mt='md'
label='Enable Chunks'
description='Enable chunked uploads.'
{...form.getInputProps('chunksEnabled', { type: 'checkbox' })}
/>
</EnvTooltip>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Max Chunk Size'
description='Maximum size of an upload before it is split into chunks.'
placeholder='95mb'
disabled={!form.values.chunksEnabled || data?.locked['chunksMax']}
{...form.getInputProps('chunksMax')}
/>
<EnvTooltip envVar='CHUNKS_MAX' data={data} varKey='chunksMax'>
<TextInput
label={'Max Chunk Size'}
description='Maximum size of an upload before it is split into chunks.'
placeholder='95mb'
disabled={!form.values.chunksEnabled}
{...form.getInputProps('chunksMax')}
/>
</EnvTooltip>
<TextInput
label='Chunk Size'
description='Size of each chunk.'
placeholder='25mb'
disabled={!form.values.chunksEnabled || data?.locked['chunksSize']}
{...form.getInputProps('chunksSize')}
/>
<EnvTooltip envVar='CHUNKS_SIZE' data={data} varKey='chunksSize'>
<TextInput
label='Chunk Size'
description='Size of each chunk.'
placeholder='25mb'
disabled={!form.values.chunksEnabled}
{...form.getInputProps('chunksSize')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={data?.locked['chunksEnabled'] && data?.locked['chunksMax'] && data?.locked['chunksSize']}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsCore({
swr: { data, isLoading },
@ -49,43 +50,36 @@ export default function ServerSettingsCore({
<Title order={2}>Core</Title>
<form onSubmit={form.onSubmit(onSubmit)}>
<Switch
mt='md'
label='Return HTTPS URLs'
description='Return URLs with HTTPS protocol.'
disabled={data?.locked['coreReturnHttpsUrls'] ? true : false}
{...form.getInputProps('coreReturnHttpsUrls', { type: 'checkbox' })}
/>
<EnvTooltip envVar='CORE_RETURN_HTTPS_URLS' data={data} varKey='coreReturnHttpsUrls'>
<Switch
mt='md'
label='Return HTTPS URLs'
description='Return URLs with HTTPS protocol.'
{...form.getInputProps('coreReturnHttpsUrls', { type: 'checkbox' })}
/>
</EnvTooltip>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Default Domain'
description='The domain to use when generating URLs. This value should not include the protocol.'
placeholder='example.com'
disabled={data?.locked['coreDefaultDomain']}
{...form.getInputProps('coreDefaultDomain')}
/>
<EnvTooltip envVar='CORE_DEFAULT_DOMAIN' data={data} varKey='coreDefaultDomain'>
<TextInput
label='Default Domain'
description='The domain to use when generating URLs. This value should not include the protocol.'
placeholder='example.com'
{...form.getInputProps('coreDefaultDomain')}
/>
</EnvTooltip>
<TextInput
label='Temporary Directory'
description='The directory to store temporary files. If the path is invalid, certain functions may break. Requires a server restart.'
placeholder='/tmp/zipline'
disabled={data?.locked['coreTempDirectory']}
{...form.getInputProps('coreTempDirectory')}
/>
<EnvTooltip envVar='CORE_TEMP_DIRECTORY' data={data} varKey='coreTempDirectory'>
<TextInput
label='Temporary Directory'
description='The directory to store temporary files. If the path is invalid, certain functions may break. Requires a server restart.'
placeholder='/tmp/zipline'
{...form.getInputProps('coreTempDirectory')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['coreReturnHttpsUrls'] &&
data?.locked['coreDefaultDomain'] &&
data?.locked['coreTempDirectory']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -16,6 +16,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
type DiscordEmbed = Record<string, any>;
@ -169,43 +170,36 @@ export default function ServerSettingsDiscord({
<Title order={2}>Discord Webhook</Title>
<form onSubmit={formMain.onSubmit(onSubmitMain)}>
<TextInput
mt='md'
label='Webhook URL'
description='The Discord webhook URL to send notifications to'
placeholder='https://discord.com/api/webhooks/...'
disabled={data?.locked['discordWebhookUrl']}
{...formMain.getInputProps('discordWebhookUrl')}
/>
<EnvTooltip envVar='DISCORD_WEBHOOK_URL' data={data} varKey='discordWebhookUrl'>
<TextInput
mt='md'
label='Webhook URL'
description='The Discord webhook URL to send notifications to'
placeholder='https://discord.com/api/webhooks/...'
{...formMain.getInputProps('discordWebhookUrl')}
/>
</EnvTooltip>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Username'
description='The username to send notifications as'
disabled={data?.locked['discordUsername']}
{...formMain.getInputProps('discordUsername')}
/>
<EnvTooltip envVar='DISCORD_USERNAME' data={data} varKey='discordUsername'>
<TextInput
label='Username'
description='The username to send notifications as'
{...formMain.getInputProps('discordUsername')}
/>
</EnvTooltip>
<TextInput
label='Avatar URL'
description='The avatar for the webhook'
placeholder='https://example.com/avatar.png'
disabled={data?.locked['discordAvatarUrl']}
{...formMain.getInputProps('discordAvatarUrl')}
/>
<EnvTooltip envVar='DISCORD_AVATAR_URL' data={data} varKey='discordAvatarUrl'>
<TextInput
label='Avatar URL'
description='The avatar for the webhook'
placeholder='https://example.com/avatar.png'
{...formMain.getInputProps('discordAvatarUrl')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['discordWebhookUrl'] &&
data?.locked['discordUsername'] &&
data?.locked['discordAvatarUrl']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>
@ -215,119 +209,112 @@ export default function ServerSettingsDiscord({
<Title order={3}>On Upload</Title>
<form onSubmit={formOnUpload.onSubmit(onSubmitNotif('upload'))}>
<TextInput
mt='md'
label='Webhook URL'
description='The Discord webhook URL to send notifications to. If this is left blank, the main webhook url will be used'
placeholder='https://discord.com/api/webhooks/...'
disabled={data?.locked['discordOnUploadWebhookUrl']}
{...formOnUpload.getInputProps('discordOnUploadWebhookUrl')}
/>
<EnvTooltip envVar='DISCORD_ON_UPLOAD_WEBHOOK_URL' data={data} varKey='discordOnUploadWebhookUrl'>
<TextInput
mt='md'
label='Webhook URL'
description='The Discord webhook URL to send notifications to. If this is left blank, the main webhook url will be used'
placeholder='https://discord.com/api/webhooks/...'
{...formOnUpload.getInputProps('discordOnUploadWebhookUrl')}
/>
</EnvTooltip>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Username'
description='The username to send notifications as. If this is left blank, the main username will be used'
disabled={data?.locked['discordOnUploadUsername']}
{...formOnUpload.getInputProps('discordOnUploadUsername')}
/>
<EnvTooltip envVar='DISCORD_ON_UPLOAD_USERNAME' data={data} varKey='discordOnUploadUsername'>
<TextInput
label='Username'
description='The username to send notifications as. If this is left blank, the main username will be used'
{...formOnUpload.getInputProps('discordOnUploadUsername')}
/>
</EnvTooltip>
<TextInput
label='Avatar URL'
description='The avatar for the webhook. If this is left blank, the main avatar will be used'
placeholder='https://example.com/avatar.png'
disabled={data?.locked['discordOnUploadAvatarUrl']}
{...formOnUpload.getInputProps('discordOnUploadAvatarUrl')}
/>
<EnvTooltip envVar='DISCORD_ON_UPLOAD_AVATAR_URL' data={data} varKey='discordOnUploadAvatarUrl'>
<TextInput
label='Avatar URL'
description='The avatar for the webhook. If this is left blank, the main avatar will be used'
placeholder='https://example.com/avatar.png'
{...formOnUpload.getInputProps('discordOnUploadAvatarUrl')}
/>
</EnvTooltip>
</SimpleGrid>
<Textarea
mt='md'
label='Content'
description='The content of the notification. This can be blank, but at least one of the content or embed fields must be filled out'
minRows={1}
maxRows={7}
disabled={data?.locked['discordOnUploadContent']}
{...formOnUpload.getInputProps('discordOnUploadContent')}
/>
<EnvTooltip envVar='DISCORD_ON_UPLOAD_CONTENT' data={data} varKey='discordOnUploadContent'>
<Textarea
mt='md'
label='Content'
description='The content of the notification. This can be blank, but at least one of the content or embed fields must be filled out'
minRows={1}
maxRows={7}
{...formOnUpload.getInputProps('discordOnUploadContent')}
/>
</EnvTooltip>
<Switch
mt='md'
label='Embed'
description='Send the notification as an embed. This will allow for more customization below.'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbed', { type: 'checkbox' })}
/>
<EnvTooltip envVar='DISCORD_ON_UPLOAD_EMBED' data={data} varKey='discordOnUploadEmbed'>
<Switch
mt='md'
label='Embed'
description='Send the notification as an embed. This will allow for more customization below.'
{...formOnUpload.getInputProps('discordOnUploadEmbed', { type: 'checkbox' })}
/>
<Collapse in={formOnUpload.values.discordOnUploadEmbed}>
<Paper withBorder p='sm' mt='md'>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Title'
description='The title of the embed'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedTitle')}
/>
<Collapse in={formOnUpload.values.discordOnUploadEmbed}>
<Paper withBorder p='sm' mt='md'>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Title'
description='The title of the embed'
{...formOnUpload.getInputProps('discordOnUploadEmbedTitle')}
/>
<TextInput
label='Description'
description='The description of the embed'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedDescription')}
/>
<TextInput
label='Description'
description='The description of the embed'
{...formOnUpload.getInputProps('discordOnUploadEmbedDescription')}
/>
<TextInput
label='Footer'
description='The footer of the embed'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedFooter')}
/>
<TextInput
label='Footer'
description='The footer of the embed'
{...formOnUpload.getInputProps('discordOnUploadEmbedFooter')}
/>
<ColorInput
label='Color'
description='The color of the embed'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedColor')}
/>
<ColorInput
label='Color'
description='The color of the embed'
{...formOnUpload.getInputProps('discordOnUploadEmbedColor')}
/>
<Switch
label='Thumbnail'
description="Show the thumbnail (it will show the file if it's an image) in the embed"
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedThumbnail', { type: 'checkbox' })}
/>
<Switch
label='Thumbnail'
description="Show the thumbnail (it will show the file if it's an image) in the embed"
{...formOnUpload.getInputProps('discordOnUploadEmbedThumbnail', { type: 'checkbox' })}
/>
<Switch
label='Image/Video'
description='Show the image or video in the embed'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedImageOrVideo', { type: 'checkbox' })}
/>
<Switch
label='Image/Video'
description='Show the image or video in the embed'
{...formOnUpload.getInputProps('discordOnUploadEmbedImageOrVideo', {
type: 'checkbox',
})}
/>
<Switch
label='Timestamp'
description='Show the timestamp in the embed'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedTimestamp', { type: 'checkbox' })}
/>
<Switch
label='Timestamp'
description='Show the timestamp in the embed'
{...formOnUpload.getInputProps('discordOnUploadEmbedTimestamp', { type: 'checkbox' })}
/>
<Switch
label='URL'
description='Makes the title clickable and links to the URL of the file'
disabled={data?.locked['discordOnUploadEmbed']}
{...formOnUpload.getInputProps('discordOnUploadEmbedUrl', { type: 'checkbox' })}
/>
</SimpleGrid>
</Paper>
</Collapse>
<Switch
label='URL'
description='Makes the title clickable and links to the URL of the file'
{...formOnUpload.getInputProps('discordOnUploadEmbedUrl', { type: 'checkbox' })}
/>
</SimpleGrid>
</Paper>
</Collapse>
</EnvTooltip>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={data?.locked['discordOnUploadEmbed']}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>
@ -337,105 +324,106 @@ export default function ServerSettingsDiscord({
<Title order={3}>On Shorten</Title>
<form onSubmit={formOnShorten.onSubmit(onSubmitNotif('shorten'))}>
<TextInput
mt='md'
label='Webhook URL'
description='The Discord webhook URL to send notifications to. If this is left blank, the main webhook url will be used'
placeholder='https://discord.com/api/webhooks/...'
disabled={data?.locked['discordOnShortenWebhookUrl']}
{...formOnShorten.getInputProps('discordOnShortenWebhookUrl')}
/>
<EnvTooltip
envVar='DISCORD_ON_SHORTEN_WEBHOOK_URL'
data={data}
varKey='discordOnShortenWebhookUrl'
>
<TextInput
mt='md'
label='Webhook URL'
description='The Discord webhook URL to send notifications to. If this is left blank, the main webhook url will be used'
placeholder='https://discord.com/api/webhooks/...'
{...formOnShorten.getInputProps('discordOnShortenWebhookUrl')}
/>
</EnvTooltip>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Username'
description='The username to send notifications as. If this is left blank, the main username will be used'
disabled={data?.locked['discordOnShortenUsername']}
{...formOnShorten.getInputProps('discordOnShortenUsername')}
/>
<EnvTooltip envVar='DISCORD_ON_SHORTEN_USERNAME' data={data} varKey='discordOnShortenUsername'>
<TextInput
label='Username'
description='The username to send notifications as. If this is left blank, the main username will be used'
{...formOnShorten.getInputProps('discordOnShortenUsername')}
/>
</EnvTooltip>
<TextInput
label='Avatar URL'
description='The avatar for the webhook. If this is left blank, the main avatar will be used'
placeholder='https://example.com/avatar.png'
disabled={data?.locked['discordOnShortenAvatarUrl']}
{...formOnShorten.getInputProps('discordOnShortenAvatarUrl')}
/>
<EnvTooltip
envVar='DISCORD_ON_SHORTEN_AVATAR_URL'
data={data}
varKey='discordOnShortenAvatarUrl'
>
<TextInput
label='Avatar URL'
description='The avatar for the webhook. If this is left blank, the main avatar will be used'
placeholder='https://example.com/avatar.png'
{...formOnShorten.getInputProps('discordOnShortenAvatarUrl')}
/>
</EnvTooltip>
</SimpleGrid>
<Textarea
mt='md'
label='Content'
description='The content of the notification. This can be blank, but at least one of the content or embed fields must be filled out'
minRows={1}
maxRows={7}
disabled={data?.locked['discordOnShortenContent']}
{...formOnShorten.getInputProps('discordOnShortenContent')}
/>
<EnvTooltip envVar='DISCORD_ON_SHORTEN_CONTENT' data={data} varKey='discordOnShortenContent'>
<Textarea
mt='md'
label='Content'
description='The content of the notification. This can be blank, but at least one of the content or embed fields must be filled out'
minRows={1}
maxRows={7}
{...formOnShorten.getInputProps('discordOnShortenContent')}
/>
</EnvTooltip>
<Switch
mt='md'
label='Embed'
description='Send the notification as an embed. This will allow for more customization below.'
disabled={data?.locked['discordOnShortenEmbed']}
{...formOnShorten.getInputProps('discordOnShortenEmbed', { type: 'checkbox' })}
/>
<EnvTooltip envVar='DISCORD_ON_SHORTEN_EMBED' data={data} varKey='discordOnShortenEmbed'>
<Switch
mt='md'
label='Embed'
description='Send the notification as an embed. This will allow for more customization below.'
{...formOnShorten.getInputProps('discordOnShortenEmbed', { type: 'checkbox' })}
/>
<Collapse in={formOnShorten.values.discordOnShortenEmbed}>
<Paper withBorder p='sm' mt='md'>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Title'
description='The title of the embed'
disabled={data?.locked['discordOnShortenEmbed']}
{...formOnShorten.getInputProps('discordOnShortenEmbedTitle')}
/>
<Collapse in={formOnShorten.values.discordOnShortenEmbed}>
<Paper withBorder p='sm' mt='md'>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Title'
description='The title of the embed'
{...formOnShorten.getInputProps('discordOnShortenEmbedTitle')}
/>
<TextInput
label='Description'
description='The description of the embed'
disabled={data?.locked['discordOnShortenEmbed']}
{...formOnShorten.getInputProps('discordOnShortenEmbedDescription')}
/>
<TextInput
label='Description'
description='The description of the embed'
{...formOnShorten.getInputProps('discordOnShortenEmbedDescription')}
/>
<TextInput
label='Footer'
description='The footer of the embed'
disabled={data?.locked['discordOnShortenEmbed']}
{...formOnShorten.getInputProps('discordOnShortenEmbedFooter')}
/>
<TextInput
label='Footer'
description='The footer of the embed'
{...formOnShorten.getInputProps('discordOnShortenEmbedFooter')}
/>
<ColorInput
label='Color'
description='The color of the embed'
disabled={data?.locked['discordOnShortenEmbed']}
{...formOnShorten.getInputProps('discordOnShortenEmbedColor')}
/>
<ColorInput
label='Color'
description='The color of the embed'
{...formOnShorten.getInputProps('discordOnShortenEmbedColor')}
/>
<Switch
label='Timestamp'
description='Show the timestamp in the embed'
disabled={data?.locked['discordOnShortenEmbed']}
{...formOnShorten.getInputProps('discordOnShortenEmbedTimestamp', { type: 'checkbox' })}
/>
<Switch
label='Timestamp'
description='Show the timestamp in the embed'
{...formOnShorten.getInputProps('discordOnShortenEmbedTimestamp', { type: 'checkbox' })}
/>
<Switch
label='URL'
description='Makes the title clickable and links to the URL of the file'
disabled={data?.locked['discordOnShortenEmbed']}
{...formOnShorten.getInputProps('discordOnShortenEmbedUrl', { type: 'checkbox' })}
/>
</SimpleGrid>
</Paper>
</Collapse>
<Switch
label='URL'
description='Makes the title clickable and links to the URL of the file'
{...formOnShorten.getInputProps('discordOnShortenEmbedUrl', { type: 'checkbox' })}
/>
</SimpleGrid>
</Paper>
</Collapse>
</EnvTooltip>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={data?.locked['discordOnShortenEmbed']}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsFeatures({
swr: { data, isLoading },
@ -54,106 +55,107 @@ export default function ServerSettingsFeatures({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<Switch
label='Image Compression'
description='Allows the ability for users to compress images.'
disabled={data?.locked['featuresImageCompression']}
{...form.getInputProps('featuresImageCompression', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_IMAGE_COMPRESSION' data={data} varKey='featuresImageCompression'>
<Switch
label='Image Compression'
description='Allows the ability for users to compress images.'
{...form.getInputProps('featuresImageCompression', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='/robots.txt'
description='Enables a robots.txt file for search engine optimization. Requires a server restart.'
disabled={data?.locked['featuresRobotsTxt']}
{...form.getInputProps('featuresRobotsTxt', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_ROBOTS_TXT' data={data} varKey='featuresRobotsTxt'>
<Switch
label='/robots.txt'
description='Enables a robots.txt file for search engine optimization. Requires a server restart.'
{...form.getInputProps('featuresRobotsTxt', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Healthcheck'
description='Enables a healthcheck route for uptime monitoring. Requires a server restart.'
disabled={data?.locked['featuresHealthcheck']}
{...form.getInputProps('featuresHealthcheck', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_HEALTHCHECK' data={data} varKey='featuresHealthcheck'>
<Switch
label='Healthcheck'
description='Enables a healthcheck route for uptime monitoring. Requires a server restart.'
{...form.getInputProps('featuresHealthcheck', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='User Registration'
description='Allows users to register an account on the server.'
disabled={data?.locked['featuresUserRegistration']}
{...form.getInputProps('featuresUserRegistration', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_USER_REGISTRATION' data={data} varKey='featuresUserRegistration'>
<Switch
label='User Registration'
description='Allows users to register an account on the server.'
{...form.getInputProps('featuresUserRegistration', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='OAuth Registration'
description='Allows users to register an account using OAuth providers.'
disabled={data?.locked['featuresOauthRegistration']}
{...form.getInputProps('featuresOauthRegistration', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_OAUTH_REGISTRATION' data={data} varKey='featuresOauthRegistration'>
<Switch
label='OAuth Registration'
description='Allows users to register an account using OAuth providers.'
{...form.getInputProps('featuresOauthRegistration', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Delete on Max Views'
description='Automatically deletes files/urls after they reach the maximum view count. Requires a server restart.'
disabled={data?.locked['featuresDeleteOnMaxViews']}
{...form.getInputProps('featuresDeleteOnMaxViews', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_DELETE_ON_MAX_VIEWS' data={data} varKey='featuresDeleteOnMaxViews'>
<Switch
label='Delete on Max Views'
description='Automatically deletes files/urls after they reach the maximum view count. Requires a server restart.'
{...form.getInputProps('featuresDeleteOnMaxViews', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Enable Metrics'
description='Enables metrics for the server. Requires a server restart.'
disabled={data?.locked['featuresMetricsEnabled']}
{...form.getInputProps('featuresMetricsEnabled', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_METRICS_ENABLED' data={data} varKey='featuresMetricsEnabled'>
<Switch
label='Enable Metrics'
description='Enables metrics for the server. Requires a server restart.'
{...form.getInputProps('featuresMetricsEnabled', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Admin Only Metrics'
description='Requires an administrator to view metrics.'
disabled={data?.locked['featuresMetricsAdminOnly']}
{...form.getInputProps('featuresMetricsAdminOnly', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_METRICS_ADMIN_ONLY' data={data} varKey='featuresMetricsAdminOnly'>
<Switch
label='Admin Only Metrics'
description='Requires an administrator to view metrics.'
{...form.getInputProps('featuresMetricsAdminOnly', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Show User Specific Metrics'
description='Shows metrics specific to each user, for all users.'
disabled={data?.locked['featuresMetricsShowUserSpecific']}
{...form.getInputProps('featuresMetricsShowUserSpecific', { type: 'checkbox' })}
/>
<EnvTooltip
envVar='FEATURES_METRICS_SHOW_USER_SPECIFIC'
data={data}
varKey='featuresMetricsShowUserSpecific'
>
<Switch
label='Show User Specific Metrics'
description='Shows metrics specific to each user, for all users.'
{...form.getInputProps('featuresMetricsShowUserSpecific', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Enable Thumbnails'
description='Enables thumbnail generation for images. Requires a server restart.'
disabled={data?.locked['featuresThumbnailsEnabled']}
{...form.getInputProps('featuresThumbnailsEnabled', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FEATURES_THUMBNAILS_ENABLED' data={data} varKey='featuresThumbnailsEnabled'>
<Switch
label='Enable Thumbnails'
description='Enables thumbnail generation for images. Requires a server restart.'
{...form.getInputProps('featuresThumbnailsEnabled', { type: 'checkbox' })}
/>
</EnvTooltip>
<NumberInput
label='Thumbnails Number Threads'
description='Number of threads to use for thumbnail generation, usually the number of CPU threads. Requires a server restart.'
placeholder='Enter a number...'
min={1}
max={16}
disabled={data?.locked['featuresThumbnailsNumberThreads']}
{...form.getInputProps('featuresThumbnailsNumberThreads')}
/>
<EnvTooltip
envVar='FEATURES_THUMBNAILS_NUMBER_THREADS'
data={data}
varKey='featuresThumbnailsNumberThreads'
>
<NumberInput
label='Thumbnails Number Threads'
description='Number of threads to use for thumbnail generation, usually the number of CPU threads. Requires a server restart.'
placeholder='Enter a number...'
min={1}
max={16}
{...form.getInputProps('featuresThumbnailsNumberThreads')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['featuresImageCompression'] &&
data?.locked['featuresRobotsTxt'] &&
data?.locked['featuresHealthcheck'] &&
data?.locked['featuresUserRegistration'] &&
data?.locked['featuresOauthRegistration'] &&
data?.locked['featuresDeleteOnMaxViews'] &&
data?.locked['featuresThumbnailsEnabled'] &&
data?.locked['featuresThumbnailsNumberThreads'] &&
data?.locked['featuresMetricsEnabled'] &&
data?.locked['featuresMetricsAdminOnly'] &&
data?.locked['featuresMetricsShowUserSpecific']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -15,6 +15,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsFiles({
swr: { data, isLoading },
@ -103,115 +104,112 @@ export default function ServerSettingsFiles({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Route'
description='The route to use for file uploads. Requires a server restart.'
placeholder='/u'
disabled={data?.locked['filesRoute']}
{...form.getInputProps('filesRoute')}
/>
<EnvTooltip envVar='FILES_ROUTE' data={data} varKey='filesRoute'>
<TextInput
label='Route'
description='The route to use for file uploads. Requires a server restart.'
placeholder='/u'
{...form.getInputProps('filesRoute')}
/>
</EnvTooltip>
<NumberInput
label='Length'
description='The length of the file name (for randomly generated names).'
min={1}
max={64}
disabled={data?.locked['filesLength']}
{...form.getInputProps('filesLength')}
/>
<EnvTooltip envVar='FILES_LENGTH' data={data} varKey='filesLength'>
<NumberInput
label='Length'
description='The length of the file name (for randomly generated names).'
min={1}
max={64}
{...form.getInputProps('filesLength')}
/>
</EnvTooltip>
<Switch
label='Assume Mimetypes'
description='Assume the mimetype of a file for its extension.'
disabled={data?.locked['filesAssumeMimetypes']}
{...form.getInputProps('filesAssumeMimetypes', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FILES_ASSUME_MIMETYPES' data={data} varKey='filesAssumeMimetypes'>
<Switch
label='Assume Mimetypes'
description='Assume the mimetype of a file for its extension.'
{...form.getInputProps('filesAssumeMimetypes', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Remove GPS Metadata'
description='Remove GPS metadata from files.'
disabled={data?.locked['filesRemoveGpsMetadata']}
{...form.getInputProps('filesRemoveGpsMetadata', { type: 'checkbox' })}
/>
<EnvTooltip envVar='FILES_REMOVE_GPS_METADATA' data={data} varKey='filesRemoveGpsMetadata'>
<Switch
label='Remove GPS Metadata'
description='Remove GPS metadata from files.'
{...form.getInputProps('filesRemoveGpsMetadata', { type: 'checkbox' })}
/>
</EnvTooltip>
<Select
label='Default Format'
description='The default format to use for file names.'
placeholder='random'
data={['random', 'date', 'uuid', 'name', 'gfycat']}
disabled={data?.locked['filesDefaultFormat']}
{...form.getInputProps('filesDefaultFormat')}
/>
<EnvTooltip envVar='FILES_DEFAULT_FORMAT' data={data} varKey='filesDefaultFormat'>
<Select
label='Default Format'
description='The default format to use for file names.'
placeholder='random'
data={['random', 'date', 'uuid', 'name', 'gfycat']}
{...form.getInputProps('filesDefaultFormat')}
/>
</EnvTooltip>
<TextInput
label='Disabled Extensions'
description='Extensions to disable, separated by commas.'
placeholder='exe, bat, sh'
disabled={data?.locked['filesDisabledExtensions']}
{...form.getInputProps('filesDisabledExtensions')}
/>
<EnvTooltip envVar='FILES_DISABLED_EXTENSIONS' data={data} varKey='filesDisabledExtensions'>
<TextInput
label='Disabled Extensions'
description='Extensions to disable, separated by commas.'
placeholder='exe, bat, sh'
{...form.getInputProps('filesDisabledExtensions')}
/>
</EnvTooltip>
<TextInput
label='Max File Size'
description='The maximum file size allowed.'
placeholder='100mb'
disabled={data?.locked['filesMaxFileSize']}
{...form.getInputProps('filesMaxFileSize')}
/>
<EnvTooltip envVar='FILES_MAX_FILE_SIZE' data={data} varKey='filesMaxFileSize'>
<TextInput
label='Max File Size'
description='The maximum file size allowed.'
placeholder='100mb'
{...form.getInputProps('filesMaxFileSize')}
/>
</EnvTooltip>
<TextInput
label='Default Expiration'
description='The default expiration time for files.'
placeholder='30d'
disabled={data?.locked['filesDefaultExpiration']}
{...form.getInputProps('filesDefaultExpiration')}
/>
<EnvTooltip envVar='FILES_DEFAULT_EXPIRATION' data={data} varKey='filesDefaultExpiration'>
<TextInput
label='Default Expiration'
description='The default expiration time for files.'
placeholder='30d'
{...form.getInputProps('filesDefaultExpiration')}
/>
</EnvTooltip>
<TextInput
label='Default Date Format'
description='The default date format to use.'
placeholder='YYYY-MM-DD_HH:mm:ss'
disabled={data?.locked['filesDefaultDateFormat']}
{...form.getInputProps('filesDefaultDateFormat')}
/>
<EnvTooltip envVar='FILES_DEFAULT_DATE_FORMAT' data={data} varKey='filesDefaultDateFormat'>
<TextInput
label='Default Date Format'
description='The default date format to use.'
placeholder='YYYY-MM-DD_HH:mm:ss'
{...form.getInputProps('filesDefaultDateFormat')}
/>
</EnvTooltip>
<NumberInput
label='Random Words Num Adjectives'
description='The number of adjectives to use for the random-words/gfycat format.'
min={1}
max={10}
disabled={data?.locked['filesRandomWordsNumAdjectives']}
{...form.getInputProps('filesRandomWordsNumAdjectives')}
/>
<EnvTooltip
envVar='FILES_RANDOM_WORDS_NUM_ADJECTIVES'
data={data}
varKey='filesRandomWordsNumAdjectives'
>
<NumberInput
label='Random Words Num Adjectives'
description='The number of adjectives to use for the random-words/gfycat format.'
min={1}
max={10}
{...form.getInputProps('filesRandomWordsNumAdjectives')}
/>
</EnvTooltip>
<TextInput
label='Random Words Separator'
description='The separator to use for the random-words/gfycat format.'
placeholder='-'
disabled={data?.locked['filesRandomWordsSeparator']}
{...form.getInputProps('filesRandomWordsSeparator')}
/>
<EnvTooltip envVar='FILES_RANDOM_WORDS_SEPARATOR' data={data} varKey='filesRandomWordsSeparator'>
<TextInput
label='Random Words Separator'
description='The separator to use for the random-words/gfycat format.'
placeholder='-'
{...form.getInputProps('filesRandomWordsSeparator')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['filesRoute'] &&
data?.locked['filesLength'] &&
data?.locked['filesDefaultFormat'] &&
data?.locked['filesDisabledExtensions'] &&
data?.locked['filesMaxFileSize'] &&
data?.locked['filesDefaultExpiration'] &&
data?.locked['filesAssumeMimetypes'] &&
data?.locked['filesDefaultDateFormat'] &&
data?.locked['filesRemoveGpsMetadata'] &&
data?.locked['filesRandomWordsNumAdjectives'] &&
data?.locked['filesRandomWordsSeparator']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsHttpWebhook({
swr: { data, isLoading },
@ -50,30 +51,26 @@ export default function ServerSettingsHttpWebhook({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='On Upload'
description='The URL to send a POST request to when a file is uploaded.'
placeholder='https://example.com/upload'
disabled={data?.locked['httpWebhookOnUpload']}
{...form.getInputProps('httpWebhookOnUpload')}
/>
<EnvTooltip envVar='HTTP_WEBHOOK_ON_UPLOAD' data={data} varKey='httpWebhookOnUpload'>
<TextInput
label='On Upload'
description='The URL to send a POST request to when a file is uploaded.'
placeholder='https://example.com/upload'
{...form.getInputProps('httpWebhookOnUpload')}
/>
</EnvTooltip>
<TextInput
label='On Shorten'
description='The URL to send a POST request to when a URL is shortened.'
placeholder='https://example.com/shorten'
disabled={data?.locked['httpWebhookOnShorten']}
{...form.getInputProps('httpWebhookOnShorten')}
/>
<EnvTooltip envVar='HTTP_WEBHOOK_ON_SHORTEN' data={data} varKey='httpWebhookOnShorten'>
<TextInput
label='On Shorten'
description='The URL to send a POST request to when a URL is shortened.'
placeholder='https://example.com/shorten'
{...form.getInputProps('httpWebhookOnShorten')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={data?.locked['httpWebhookOnUpload'] && data?.locked['httpWebhookOnShorten']}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsInvites({
swr: { data, isLoading },
@ -38,31 +39,28 @@ export default function ServerSettingsInvites({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<Switch
label='Enable Invites'
description='Enable the use of invite links to register new users.'
disabled={data?.locked['invitesEnabled']}
{...form.getInputProps('invitesEnabled', { type: 'checkbox' })}
/>
<EnvTooltip envVar='INVITES_ENABLED' data={data} varKey='invitesEnabled'>
<Switch
label='Enable Invites'
description='Enable the use of invite links to register new users.'
{...form.getInputProps('invitesEnabled', { type: 'checkbox' })}
/>
</EnvTooltip>
<NumberInput
label='Length'
description='The length of the invite code.'
placeholder='6'
min={1}
max={64}
disabled={!form.values.invitesEnabled || data?.locked['invitesLength']}
{...form.getInputProps('invitesLength')}
/>
<EnvTooltip envVar='INVITES_LENGTH' data={data} varKey='invitesLength'>
<NumberInput
label='Length'
description='The length of the invite code.'
placeholder='6'
min={1}
max={64}
disabled={!form.values.invitesEnabled}
{...form.getInputProps('invitesLength')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={data?.locked['invitesEnabled'] && data?.locked['invitesLength']}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsMfa({
swr: { data, isLoading },
@ -40,37 +41,33 @@ export default function ServerSettingsMfa({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<Switch
label='Passkeys'
description='Enable the use of passwordless login with the use of WebAuthn passkeys like your phone, security keys, etc.'
disabled={data?.locked['mfaPasskeys']}
{...form.getInputProps('mfaPasskeys', { type: 'checkbox' })}
/>
<EnvTooltip envVar='MFA_TOTP_ENABLED' data={data} varKey='mfaTotpEnabled'>
<Switch
label='Passkeys'
description='Enable the use of passwordless login with the use of WebAuthn passkeys like your phone, security keys, etc.'
{...form.getInputProps('mfaPasskeys', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Enable TOTP'
description='Enable Time-based One-Time Passwords with the use of an authenticator app.'
disabled={data?.locked['mfaTotpEnabled']}
{...form.getInputProps('mfaTotpEnabled', { type: 'checkbox' })}
/>
<TextInput
label='Issuer'
description='The issuer to use for the TOTP token.'
placeholder='Zipline'
disabled={data?.locked['mfaTotpIssuer']}
{...form.getInputProps('mfaTotpIssuer')}
/>
<EnvTooltip envVar='MFA_TOTP_ENABLED' data={data} varKey='mfaTotpEnabled'>
<Switch
label='Enable TOTP'
description='Enable Time-based One-Time Passwords with the use of an authenticator app.'
{...form.getInputProps('mfaTotpEnabled', { type: 'checkbox' })}
/>
</EnvTooltip>
<EnvTooltip envVar='MFA_TOTP_ISSUER' data={data} varKey='mfaTotpIssuer'>
<TextInput
label='Issuer'
description='The issuer to use for the TOTP token.'
placeholder='Zipline'
{...form.getInputProps('mfaTotpIssuer')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['mfaTotpEnabled'] && data?.locked['mfaTotpIssuer'] && data?.locked['mfaPasskeys']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -15,6 +15,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsOauth({
swr: { data, isLoading },
@ -107,19 +108,21 @@ export default function ServerSettingsOauth({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<Switch
label='Bypass Local Login'
description='Skips the local login page and redirects to the OAuth provider, this only works with one provider enabled.'
disabled={data?.locked['oauthBypassLocalLogin']}
{...form.getInputProps('oauthBypassLocalLogin', { type: 'checkbox' })}
/>
<EnvTooltip envVar='OAUTH_BYPASS_LOCAL_LOGIN' data={data} varKey='oauthBypassLocalLogin'>
<Switch
label='Bypass Local Login'
description='Skips the local login page and redirects to the OAuth provider, this only works with one provider enabled.'
{...form.getInputProps('oauthBypassLocalLogin', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Login Only'
description='Disables registration and only allows login with OAuth, existing users can link providers for example.'
disabled={data?.locked['oauthLoginOnly']}
{...form.getInputProps('oauthLoginOnly', { type: 'checkbox' })}
/>
<EnvTooltip envVar='OAUTH_LOGIN_ONLY' data={data} varKey='oauthLoginOnly'>
<Switch
label='Login Only'
description='Disables registration and only allows login with OAuth, existing users can link providers for example.'
{...form.getInputProps('oauthLoginOnly', { type: 'checkbox' })}
/>
</EnvTooltip>
</SimpleGrid>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<Paper withBorder p='sm'>
@ -129,22 +132,21 @@ export default function ServerSettingsOauth({
</Title>
</Anchor>
<TextInput
label='Discord Client ID'
disabled={data?.locked['oauthDiscordClientId']}
{...form.getInputProps('oauthDiscordClientId')}
/>
<TextInput
label='Discord Client Secret'
disabled={data?.locked['oauthDiscordClientSecret']}
{...form.getInputProps('oauthDiscordClientSecret')}
/>
<TextInput
label='Discord Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
disabled={data?.locked['oauthDiscordRedirectUri']}
{...form.getInputProps('oauthDiscordRedirectUri')}
/>
<EnvTooltip envVar='OAUTH_DISCORD_CLIENT_ID' data={data} varKey='oauthDiscordClientId'>
<TextInput label='Discord Client ID' {...form.getInputProps('oauthDiscordClientId')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_DISCORD_CLIENT_SECRET' data={data} varKey='oauthDiscordClientSecret'>
<TextInput label='Discord Client Secret' {...form.getInputProps('oauthDiscordClientSecret')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_DISCORD_REDIRECT_URI' data={data} varKey='oauthDiscordRedirectUri'>
<TextInput
label='Discord Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
{...form.getInputProps('oauthDiscordRedirectUri')}
/>
</EnvTooltip>
</Paper>
<Paper withBorder p='sm'>
<Anchor href='https://console.developers.google.com/' target='_blank'>
@ -153,22 +155,21 @@ export default function ServerSettingsOauth({
</Title>
</Anchor>
<TextInput
label='Google Client ID'
disabled={data?.locked['oauthGoogleClientId']}
{...form.getInputProps('oauthGoogleClientId')}
/>
<TextInput
label='Google Client Secret'
disabled={data?.locked['oauthGoogleClientSecret']}
{...form.getInputProps('oauthGoogleClientSecret')}
/>
<TextInput
label='Google Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
disabled={data?.locked['oauthGoogleRedirectUri']}
{...form.getInputProps('oauthGoogleRedirectUri')}
/>
<EnvTooltip envVar='OAUTH_GOOGLE_CLIENT_ID' data={data} varKey='oauthGoogleClientId'>
<TextInput label='Google Client ID' {...form.getInputProps('oauthGoogleClientId')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_GOOGLE_CLIENT_SECRET' data={data} varKey='oauthGoogleClientSecret'>
<TextInput label='Google Client Secret' {...form.getInputProps('oauthGoogleClientSecret')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_GOOGLE_REDIRECT_URI' data={data} varKey='oauthGoogleRedirectUri'>
<TextInput
label='Google Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
{...form.getInputProps('oauthGoogleRedirectUri')}
/>
</EnvTooltip>
</Paper>
</SimpleGrid>
@ -180,22 +181,21 @@ export default function ServerSettingsOauth({
</Anchor>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='GitHub Client ID'
disabled={data?.locked['oauthGithubClientId']}
{...form.getInputProps('oauthGithubClientId')}
/>
<TextInput
label='GitHub Client Secret'
disabled={data?.locked['oauthGithubClientSecret']}
{...form.getInputProps('oauthGithubClientSecret')}
/>
<TextInput
label='GitHub Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
disabled={data?.locked['oauthGithubRedirectUri']}
{...form.getInputProps('oauthGithubRedirectUri')}
/>
<EnvTooltip envVar='OAUTH_GITHUB_CLIENT_ID' data={data} varKey='oauthGithubClientId'>
<TextInput label='GitHub Client ID' {...form.getInputProps('oauthGithubClientId')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_GITHUB_CLIENT_SECRET' data={data} varKey='oauthGithubClientSecret'>
<TextInput label='GitHub Client Secret' {...form.getInputProps('oauthGithubClientSecret')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_GITHUB_REDIRECT_URI' data={data} varKey='oauthGithubRedirectUri'>
<TextInput
label='GitHub Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
{...form.getInputProps('oauthGithubRedirectUri')}
/>
</EnvTooltip>
</SimpleGrid>
</Paper>
@ -203,65 +203,37 @@ export default function ServerSettingsOauth({
<Title order={4}>OpenID Connect</Title>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='OIDC Client ID'
disabled={data?.locked['oauthOidcClientId']}
{...form.getInputProps('oauthOidcClientId')}
/>
<TextInput
label='OIDC Client Secret'
disabled={data?.locked['oauthOidcClientSecret']}
{...form.getInputProps('oauthOidcClientSecret')}
/>
<TextInput
label='OIDC Authorize URL'
disabled={data?.locked['oauthOidcAuthorizeUrl']}
{...form.getInputProps('oauthOidcAuthorizeUrl')}
/>
<TextInput
label='OIDC Token URL'
disabled={data?.locked['oauthOidcTokenUrl']}
{...form.getInputProps('oauthOidcTokenUrl')}
/>
<TextInput
label='OIDC Userinfo URL'
disabled={data?.locked['oauthOidcUserinfoUrl']}
{...form.getInputProps('oauthOidcUserinfoUrl')}
/>
<TextInput
label='OIDC Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
disabled={data?.locked['oauthOidcRedirectUri']}
{...form.getInputProps('oauthOidcRedirectUri')}
/>
<EnvTooltip envVar='OAUTH_OIDC_CLIENT_ID' data={data} varKey='oauthOidcClientId'>
<TextInput label='OIDC Client ID' {...form.getInputProps('oauthOidcClientId')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_OIDC_CLIENT_SECRET' data={data} varKey='oauthOidcClientSecret'>
<TextInput label='OIDC Client Secret' {...form.getInputProps('oauthOidcClientSecret')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_OIDC_AUTHORIZE_URL' data={data} varKey='oauthOidcAuthorizeUrl'>
<TextInput label='OIDC Authorize URL' {...form.getInputProps('oauthOidcAuthorizeUrl')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_OIDC_TOKEN_URL' data={data} varKey='oauthOidcTokenUrl'>
<TextInput label='OIDC Token URL' {...form.getInputProps('oauthOidcTokenUrl')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_OIDC_USERINFO_URL' data={data} varKey='oauthOidcUserinfoUrl'>
<TextInput label='OIDC Userinfo URL' {...form.getInputProps('oauthOidcUserinfoUrl')} />
</EnvTooltip>
<EnvTooltip envVar='OAUTH_OIDC_REDIRECT_URI' data={data} varKey='oauthOidcRedirectUri'>
<TextInput
label='OIDC Redirect URL'
description='The redirect URL to use instead of the host when logging in. This is not required if the URL generated by Zipline works as intended.'
{...form.getInputProps('oauthOidcRedirectUri')}
/>
</EnvTooltip>
</SimpleGrid>
</Paper>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['oauthBypassLocalLogin'] &&
data?.locked['oauthLoginOnly'] &&
data?.locked['oauthDiscordClientId'] &&
data?.locked['oauthDiscordClientSecret'] &&
data?.locked['oauthDiscordRedirectUri'] &&
data?.locked['oauthGoogleClientId'] &&
data?.locked['oauthGoogleClientSecret'] &&
data?.locked['oauthGoogleRedirectUri'] &&
data?.locked['oauthGithubClientId'] &&
data?.locked['oauthGithubClientSecret'] &&
data?.locked['oauthGithubRedirectUri'] &&
data?.locked['oauthOidcClientId'] &&
data?.locked['oauthOidcClientSecret'] &&
data?.locked['oauthOidcAuthorizeUrl'] &&
data?.locked['oauthOidcTokenUrl'] &&
data?.locked['oauthOidcUserinfoUrl'] &&
data?.locked['oauthOidcRedirectUri']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -16,6 +16,7 @@ import { IconDeviceFloppy, IconRefresh } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsPWA({
swr: { data, isLoading },
@ -74,70 +75,69 @@ export default function ServerSettingsPWA({
</Text>
<form onSubmit={form.onSubmit(onSubmit)}>
<Switch
mt='md'
label='PWA Enabled'
description='Allow users to install the Zipline PWA on their devices.'
disabled={data?.locked['pwaEnabled']}
{...form.getInputProps('pwaEnabled', { type: 'checkbox' })}
/>
<EnvTooltip envVar='PWA_ENABLED' data={data} varKey='pwaEnabled'>
<Switch
mt='md'
label='PWA Enabled'
description='Allow users to install the Zipline PWA on their devices.'
{...form.getInputProps('pwaEnabled', { type: 'checkbox' })}
/>
</EnvTooltip>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Title'
description='The title for the PWA'
placeholder='Zipline'
disabled={!form.values.pwaEnabled || data?.locked['pwaTitle']}
{...form.getInputProps('pwaTitle')}
/>
<EnvTooltip envVar='PWA_TITLE' data={data} varKey='pwaTitle'>
<TextInput
label='Title'
description='The title for the PWA'
placeholder='Zipline'
disabled={!form.values.pwaEnabled}
{...form.getInputProps('pwaTitle')}
/>
</EnvTooltip>
<TextInput
label='Short Name'
description='The short name for the PWA'
placeholder='Zipline'
disabled={!form.values.pwaEnabled || data?.locked['pwaShortName']}
{...form.getInputProps('pwaShortName')}
/>
<EnvTooltip envVar='PWA_SHORT_NAME' data={data} varKey='pwaShortName'>
<TextInput
label='Short Name'
description='The short name for the PWA'
placeholder='Zipline'
disabled={!form.values.pwaEnabled}
{...form.getInputProps('pwaShortName')}
/>
</EnvTooltip>
<TextInput
label='Description'
description='The description for the PWA'
placeholder='Zipline'
disabled={!form.values.pwaEnabled || data?.locked['pwaDescription']}
{...form.getInputProps('pwaDescription')}
/>
<EnvTooltip envVar='PWA_DESCRIPTION' data={data} varKey='pwaDescription'>
<TextInput
label='Description'
description='The description for the PWA'
placeholder='Zipline'
disabled={!form.values.pwaEnabled}
{...form.getInputProps('pwaDescription')}
/>
</EnvTooltip>
<ColorInput
label='Theme Color'
description='The theme color for the PWA'
placeholder='#000000'
disabled={!form.values.pwaEnabled || data?.locked['pwaThemeColor']}
{...form.getInputProps('pwaThemeColor')}
/>
<EnvTooltip envVar='PWA_THEME_COLOR' data={data} varKey='pwaThemeColor'>
<ColorInput
label='Theme Color'
description='The theme color for the PWA'
placeholder='#000000'
disabled={!form.values.pwaEnabled}
{...form.getInputProps('pwaThemeColor')}
/>
</EnvTooltip>
<ColorInput
label='Background Color'
description='The background color for the PWA'
placeholder='#ffffff'
disabled={!form.values.pwaEnabled || data?.locked['pwaBackgroundColor']}
{...form.getInputProps('pwaBackgroundColor')}
/>
<EnvTooltip envVar='PWA_BACKGROUND_COLOR' data={data} varKey='pwaBackgroundColor'>
<ColorInput
label='Background Color'
description='The background color for the PWA'
placeholder='#ffffff'
disabled={!form.values.pwaEnabled}
{...form.getInputProps('pwaBackgroundColor')}
/>
</EnvTooltip>
</SimpleGrid>
<Group mt='md'>
<Button
type='submit'
loading={isLoading}
disabled={
data?.locked['pwaEnabled'] &&
data?.locked['pwaTitle'] &&
data?.locked['pwaShortName'] &&
data?.locked['pwaDescription'] &&
data?.locked['pwaThemeColor'] &&
data?.locked['pwaBackgroundColor']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
<Button onClick={() => router.reload()} leftSection={<IconRefresh size='1rem' />}>

View file

@ -15,6 +15,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsRatelimit({
swr: { data, isLoading },
@ -82,45 +83,54 @@ export default function ServerSettingsRatelimit({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<Switch
label='Enable Ratelimit'
description='Enable ratelimiting for the server.'
disabled={data?.locked['ratelimitEnabled']}
{...form.getInputProps('ratelimitEnabled', { type: 'checkbox' })}
/>
<EnvTooltip envVar='RATELIMIT_ENABLED' data={data} varKey='ratelimitEnabled'>
<Switch
label='Enable Ratelimit'
description='Enable ratelimiting for the server.'
{...form.getInputProps('ratelimitEnabled', { type: 'checkbox' })}
/>
</EnvTooltip>
<Switch
label='Admin Bypass'
description='Allow admins to bypass the ratelimit.'
disabled={!form.values.ratelimitEnabled || data?.locked['ratelimitAdminBypass']}
{...form.getInputProps('ratelimitAdminBypass', { type: 'checkbox' })}
/>
<EnvTooltip envVar='RATELIMIT_ADMIN_BYPASS' data={data} varKey='ratelimitAdminBypass'>
<Switch
label='Admin Bypass'
description='Allow admins to bypass the ratelimit.'
disabled={!form.values.ratelimitEnabled}
{...form.getInputProps('ratelimitAdminBypass', { type: 'checkbox' })}
/>
</EnvTooltip>
<NumberInput
label='Max Requests'
description='The maximum number of requests allowed within the window. If no window is set, this is the maximum number of requests until it reaches the limit.'
placeholder='10'
min={1}
disabled={!form.values.ratelimitEnabled || data?.locked['ratelimitMax']}
{...form.getInputProps('ratelimitMax')}
/>
<EnvTooltip envVar='RATELIMIT_MAX' data={data} varKey='ratelimitMax'>
<NumberInput
label='Max Requests'
description='The maximum number of requests allowed within the window. If no window is set, this is the maximum number of requests until it reaches the limit.'
placeholder='10'
min={1}
disabled={!form.values.ratelimitEnabled}
{...form.getInputProps('ratelimitMax')}
/>
</EnvTooltip>
<NumberInput
label='Window'
description='The window in seconds to allow the max requests.'
placeholder='60'
min={1}
disabled={!form.values.ratelimitEnabled || data?.locked['ratelimitWindow']}
{...form.getInputProps('ratelimitWindow')}
/>
<EnvTooltip envVar='RATELIMIT_WINDOW' data={data} varKey='ratelimitWindow'>
<NumberInput
label='Window'
description='The window in seconds to allow the max requests.'
placeholder='60'
min={1}
disabled={!form.values.ratelimitEnabled}
{...form.getInputProps('ratelimitWindow')}
/>
</EnvTooltip>
<TextInput
label='Allow List'
description='A comma-separated list of IP addresses to bypass the ratelimit.'
placeholder='1.1.1.1, 8.8.8.8'
disabled={!form.values.ratelimitEnabled || data?.locked['ratelimitAllowList']}
{...form.getInputProps('ratelimitAllowList')}
/>
<EnvTooltip envVar='RATELIMIT_ALLOW_LIST' data={data} varKey='ratelimitAllowList'>
<TextInput
label='Allow List'
description='A comma-separated list of IP addresses to bypass the ratelimit.'
placeholder='1.1.1.1, 8.8.8.8'
disabled={!form.values.ratelimitEnabled}
{...form.getInputProps('ratelimitAllowList')}
/>
</EnvTooltip>
</SimpleGrid>
<Button

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsTasks({
swr: { data, isLoading },
@ -48,51 +49,44 @@ export default function ServerSettingsTasks({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Delete Files Interval'
description='How often to check and delete expired files.'
placeholder='30m'
disabled={data?.locked['tasksDeleteInterval']}
{...form.getInputProps('tasksDeleteInterval')}
/>
<EnvTooltip envVar='TASKS_DELETE_INTERVAL' data={data} varKey='tasksDeleteInterval'>
<TextInput
label='Delete Files Interval'
description='How often to check and delete expired files.'
placeholder='30m'
{...form.getInputProps('tasksDeleteInterval')}
/>
</EnvTooltip>
<TextInput
label='Clear Invites Interval'
description='How often to check and clear expired/used invites.'
placeholder='30m'
disabled={data?.locked['tasksClearInvitesInterval']}
{...form.getInputProps('tasksClearInvitesInterval')}
/>
<EnvTooltip envVar='TASKS_METRICS_INTERVAL' data={data} varKey='tasksMetricsInterval'>
<TextInput
label='Clear Invites Interval'
description='How often to check and clear expired/used invites.'
placeholder='30m'
{...form.getInputProps('tasksClearInvitesInterval')}
/>
</EnvTooltip>
<TextInput
label='Max Views Interval'
description='How often to check and delete files that have reached max views.'
placeholder='30m'
disabled={data?.locked['tasksMaxViewsInterval']}
{...form.getInputProps('tasksMaxViewsInterval')}
/>
<EnvTooltip envVar='TASKS_MAX_VIEWS_INTERVAL' data={data} varKey='tasksMaxViewsInterval'>
<TextInput
label='Max Views Interval'
description='How often to check and delete files that have reached max views.'
placeholder='30m'
{...form.getInputProps('tasksMaxViewsInterval')}
/>
</EnvTooltip>
<TextInput
label='Thumbnails Interval'
description='How often to check and generate thumbnails for video files.'
placeholder='30m'
disabled={data?.locked['tasksThumbnailsInterval']}
{...form.getInputProps('tasksThumbnailsInterval')}
/>
<EnvTooltip envVar='TASKS_THUMBNAILS_INTERVAL' data={data} varKey='tasksThumbnailsInterval'>
<TextInput
label='Thumbnails Interval'
description='How often to check and generate thumbnails for video files.'
placeholder='30m'
{...form.getInputProps('tasksThumbnailsInterval')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['tasksDeleteInterval'] &&
data?.locked['tasksClearInvitesInterval'] &&
data?.locked['tasksMaxViewsInterval'] &&
data?.locked['tasksThumbnailsInterval']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
export default function ServerSettingsUrls({
swr: { data, isLoading },
@ -38,32 +39,28 @@ export default function ServerSettingsUrls({
<form onSubmit={form.onSubmit(onSubmit)}>
<SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'>
<TextInput
label='Route'
description='The route to use for short URLs. Requires a server restart.'
placeholder='/go'
disabled={data?.locked['urlsRoute']}
{...form.getInputProps('urlsRoute')}
/>
<EnvTooltip envVar='URLS_ROUTE' data={data} varKey='urlsRoute'>
<TextInput
label='Route'
description='The route to use for short URLs. Requires a server restart.'
placeholder='/go'
{...form.getInputProps('urlsRoute')}
/>
</EnvTooltip>
<NumberInput
label='Length'
description='The length of the short URL (for randomly generated names).'
placeholder='6'
min={1}
max={64}
disabled={data?.locked['urlsLength']}
{...form.getInputProps('urlsLength')}
/>
<EnvTooltip envVar='URLS_LENGTH' data={data} varKey='urlsLength'>
<NumberInput
label='Length'
description='The length of the short URL (for randomly generated names).'
placeholder='6'
min={1}
max={64}
{...form.getInputProps('urlsLength')}
/>
</EnvTooltip>
</SimpleGrid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={data?.locked['urlsRoute'] && data?.locked['urlsLength']}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -5,6 +5,7 @@ import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { settingsOnSubmit } from '../settingsOnSubmit';
import { EnvTooltip } from '..';
const defaultExternalLinks = [
{
@ -97,127 +98,126 @@ export default function ServerSettingsWebsite({
{/* <SimpleGrid mt='md' cols={{ base: 1, md: 2 }} spacing='lg'> */}
<Grid>
<Grid.Col span={{ base: 12, md: 6 }}>
<TextInput
label='Title'
description='The title of the website in browser tabs and at the top.'
placeholder='Zipline'
disabled={data?.locked['websiteTitle']}
{...form.getInputProps('websiteTitle')}
/>
<EnvTooltip envVar='WEBSITE_TITLE' data={data} varKey='websiteTitle'>
<TextInput
label='Title'
description='The title of the website in browser tabs and at the top.'
placeholder='Zipline'
{...form.getInputProps('websiteTitle')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6 }}>
<TextInput
label='Title Logo'
description='The URL to use for the title logo. This is placed to the left of the title.'
placeholder='https://example.com/logo.png'
disabled={data?.locked['websiteTitleLogo']}
{...form.getInputProps('websiteTitleLogo')}
/>
<EnvTooltip envVar='WEBSITE_TITLE_LOGO' data={data} varKey='websiteTitleLogo'>
<TextInput
label='Title Logo'
description='The URL to use for the title logo. This is placed to the left of the title.'
placeholder='https://example.com/logo.png'
{...form.getInputProps('websiteTitleLogo')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={12}>
<JsonInput
label='External Links'
description='The external links to show in the footer. This must be valid JSON.'
formatOnBlur
minRows={1}
maxRows={7}
autosize
placeholder={JSON.stringify(defaultExternalLinks, null, 2)}
disabled={data?.locked['websiteExternalLinks']}
{...form.getInputProps('websiteExternalLinks')}
/>
<EnvTooltip envVar='WEBSITE_EXTERNAL_LINKS' data={data} varKey='websiteExternalLinks'>
<JsonInput
label='External Links'
description='The external links to show in the footer. This must be valid JSON.'
formatOnBlur
minRows={1}
maxRows={7}
autosize
placeholder={JSON.stringify(defaultExternalLinks, null, 2)}
{...form.getInputProps('websiteExternalLinks')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6 }}>
<TextInput
label='Login Background'
description='The URL to use for the login background.'
placeholder='https://example.com/background.png'
disabled={data?.locked['websiteLoginBackground']}
{...form.getInputProps('websiteLoginBackground')}
/>
<EnvTooltip envVar='WEBSITE_LOGIN_BACKGROUND' data={data} varKey='websiteLoginBackground'>
<TextInput
label='Login Background'
description='The URL to use for the login background.'
placeholder='https://example.com/background.png'
{...form.getInputProps('websiteLoginBackground')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6 }}>
<Switch
label='Login Background Blur'
description='Whether to blur the login background.'
disabled={data?.locked['websiteLoginBackgroundBlur']}
{...form.getInputProps('websiteLoginBackgroundBlur', { type: 'checkbox' })}
/>
<EnvTooltip
envVar='WEBSITE_LOGIN_BACKGROUND_BLUR'
data={data}
varKey='websiteLoginBackgroundBlur'
>
<Switch
label='Login Background Blur'
description='Whether to blur the login background.'
{...form.getInputProps('websiteLoginBackgroundBlur', { type: 'checkbox' })}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6 }}>
<TextInput
label='Default Avatar'
description='The path to use for the default avatar. This must be a path to an image, not a URL.'
placeholder='/zipline/avatar.png'
disabled={data?.locked['websiteDefaultAvatar']}
{...form.getInputProps('websiteDefaultAvatar')}
/>
<EnvTooltip envVar='WEBSITE_DEFAULT_AVATAR' data={data} varKey='websiteDefaultAvatar'>
<TextInput
label='Default Avatar'
description='The path to use for the default avatar. This must be a path to an image, not a URL.'
placeholder='/zipline/avatar.png'
{...form.getInputProps('websiteDefaultAvatar')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6 }}>
<TextInput
label='Terms of Service'
description='Path to a Markdown (.md) file to use for the terms of service.'
placeholder='/zipline/TOS.md'
disabled={data?.locked['websiteTos']}
{...form.getInputProps('websiteTos')}
/>
<EnvTooltip envVar='WEBSITE_TOS' data={data} varKey='websiteTos'>
<TextInput
label='Terms of Service'
description='Path to a Markdown (.md) file to use for the terms of service.'
placeholder='/zipline/TOS.md'
{...form.getInputProps('websiteTos')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={12}>
<TextInput
label='Default Theme'
description='The default theme to use for the website.'
placeholder='system'
disabled={data?.locked['websiteThemeDefault']}
{...form.getInputProps('websiteThemeDefault')}
/>
<EnvTooltip envVar='WEBSITE_THEME_DEFAULT' data={data} varKey='websiteThemeDefault'>
<TextInput
label='Default Theme'
description='The default theme to use for the website.'
placeholder='system'
{...form.getInputProps('websiteThemeDefault')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6 }}>
<TextInput
label='Dark Theme'
description='The dark theme to use for the website when the default theme is "system".'
placeholder='builtin:dark_gray'
disabled={form.values.websiteThemeDefault !== 'system' || data?.locked['websiteThemeDark']}
{...form.getInputProps('websiteThemeDark')}
/>
<EnvTooltip envVar='WEBSITE_THEME_DARK' data={data} varKey='websiteThemeDark'>
<TextInput
label='Dark Theme'
description='The dark theme to use for the website when the default theme is "system".'
placeholder='builtin:dark_gray'
disabled={form.values.websiteThemeDefault !== 'system'}
{...form.getInputProps('websiteThemeDark')}
/>
</EnvTooltip>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6 }}>
<TextInput
label='Light Theme'
description='The light theme to use for the website when the default theme is "system".'
placeholder='builtin:light_gray'
disabled={form.values.websiteThemeDefault !== 'system' || data?.locked['websiteThemeLight']}
{...form.getInputProps('websiteThemeLight')}
/>
<EnvTooltip envVar='WEBSITE_THEME_LIGHT' data={data} varKey='websiteThemeLight'>
<TextInput
label='Light Theme'
description='The light theme to use for the website when the default theme is "system".'
placeholder='builtin:light_gray'
disabled={form.values.websiteThemeDefault !== 'system'}
{...form.getInputProps('websiteThemeLight')}
/>
</EnvTooltip>
</Grid.Col>
</Grid>
<Button
type='submit'
mt='md'
loading={isLoading}
disabled={
data?.locked['websiteTitle'] &&
data?.locked['websiteTitleLogo'] &&
data?.locked['websiteExternalLinks'] &&
data?.locked['websiteLoginBackground'] &&
data?.locked['websiteLoginBackgroundBlur'] &&
data?.locked['websiteDefaultAvatar'] &&
data?.locked['websiteTos'] &&
data?.locked['websiteThemeDefault'] &&
data?.locked['websiteThemeDark'] &&
data?.locked['websiteThemeLight']
}
leftSection={<IconDeviceFloppy size='1rem' />}
>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>
Save
</Button>
</form>

View file

@ -631,8 +631,15 @@ export function replaceDatabaseValueWithEnv<T>(
return databaseValue;
}
export function valueIsFromEnv(Key: keyof typeof DATABASE_TO_PROP): boolean {
return databaseToEnv(Key).some((key) => process.env[key] !== undefined);
export function valueIsFromEnv(Key: keyof typeof DATABASE_TO_PROP): string | undefined {
const envKeys = databaseToEnv(Key);
for (let i = 0; i !== envKeys.length; ++i) {
const value = process.env[envKeys[i]];
if (value !== undefined) return value;
}
return undefined;
}
export function databaseToEnv(key: keyof typeof DATABASE_TO_PROP): string[] {

View file

@ -3,7 +3,7 @@ import { Prisma, PrismaClient } from '@prisma/client';
import { userViewSchema } from './models/user';
import { metricDataSchema } from './models/metric';
import { metadataSchema } from './models/incompleteFile';
import { replaceDatabaseValueWithEnv } from '../config/read';
import { SettingsExtension } from './settingsExtension';
const building = !!process.env.ZIPLINE_BUILD;
@ -39,674 +39,44 @@ function getClient() {
const client = new PrismaClient({
log: process.env.ZIPLINE_DB_LOG ? parseDbLog(process.env.ZIPLINE_DB_LOG) : undefined,
}).$extends({
result: {
file: {
size: {
needs: { size: true },
compute({ size }: { size: bigint }) {
return Number(size);
})
.$extends({
result: {
file: {
size: {
needs: { size: true },
compute({ size }: { size: bigint }) {
return Number(size);
},
},
},
user: {
view: {
needs: { view: true },
compute({ view }: { view: Prisma.JsonValue }) {
return userViewSchema.parse(view);
},
},
},
metric: {
data: {
needs: { data: true },
compute({ data }: { data: Prisma.JsonValue }) {
return metricDataSchema.parse(data);
},
},
},
incompleteFile: {
metadata: {
needs: { metadata: true },
compute({ metadata }: { metadata: Prisma.JsonValue }) {
return metadataSchema.parse(metadata);
},
},
},
},
user: {
view: {
needs: { view: true },
compute({ view }: { view: Prisma.JsonValue }) {
return userViewSchema.parse(view);
},
},
},
metric: {
data: {
needs: { data: true },
compute({ data }: { data: Prisma.JsonValue }) {
return metricDataSchema.parse(data);
},
},
},
incompleteFile: {
metadata: {
needs: { metadata: true },
compute({ metadata }: { metadata: Prisma.JsonValue }) {
return metadataSchema.parse(metadata);
},
},
},
zipline: {
coreReturnHttpsUrls: {
needs: { coreReturnHttpsUrls: true },
compute({ coreReturnHttpsUrls }: { coreReturnHttpsUrls: boolean }) {
return replaceDatabaseValueWithEnv('coreReturnHttpsUrls', coreReturnHttpsUrls, 'boolean');
},
},
coreDefaultDomain: {
needs: { coreDefaultDomain: true },
compute({ coreDefaultDomain }: { coreDefaultDomain: string }) {
return replaceDatabaseValueWithEnv('coreDefaultDomain', coreDefaultDomain, 'string');
},
},
coreTempDirectory: {
needs: { coreTempDirectory: true },
compute({ coreTempDirectory }: { coreTempDirectory: string }) {
return replaceDatabaseValueWithEnv('coreTempDirectory', coreTempDirectory, 'string');
},
},
chunksMax: {
needs: { chunksMax: true },
compute({ chunksMax }: { chunksMax: string }) {
return replaceDatabaseValueWithEnv('chunksMax', chunksMax, 'string');
},
},
chunksSize: {
needs: { chunksSize: true },
compute({ chunksSize }: { chunksSize: string }) {
return replaceDatabaseValueWithEnv('chunksSize', chunksSize, 'string');
},
},
chunksEnabled: {
needs: { chunksEnabled: true },
compute({ chunksEnabled }: { chunksEnabled: boolean }) {
return replaceDatabaseValueWithEnv('chunksEnabled', chunksEnabled, 'boolean');
},
},
tasksDeleteInterval: {
needs: { tasksDeleteInterval: true },
compute({ tasksDeleteInterval }: { tasksDeleteInterval: string }) {
return replaceDatabaseValueWithEnv('tasksDeleteInterval', tasksDeleteInterval, 'ms');
},
},
tasksClearInvitesInterval: {
needs: { tasksClearInvitesInterval: true },
compute({ tasksClearInvitesInterval }: { tasksClearInvitesInterval: string }) {
return replaceDatabaseValueWithEnv('tasksClearInvitesInterval', tasksClearInvitesInterval, 'ms');
},
},
tasksMaxViewsInterval: {
needs: { tasksMaxViewsInterval: true },
compute({ tasksMaxViewsInterval }: { tasksMaxViewsInterval: string }) {
return replaceDatabaseValueWithEnv('tasksMaxViewsInterval', tasksMaxViewsInterval, 'ms');
},
},
tasksThumbnailsInterval: {
needs: { tasksThumbnailsInterval: true },
compute({ tasksThumbnailsInterval }: { tasksThumbnailsInterval: string }) {
return replaceDatabaseValueWithEnv('tasksThumbnailsInterval', tasksThumbnailsInterval, 'ms');
},
},
tasksMetricsInterval: {
needs: { tasksMetricsInterval: true },
compute({ tasksMetricsInterval }: { tasksMetricsInterval: string }) {
return replaceDatabaseValueWithEnv('tasksMetricsInterval', tasksMetricsInterval, 'ms');
},
},
filesRoute: {
needs: { filesRoute: true },
compute({ filesRoute }: { filesRoute: string }) {
return replaceDatabaseValueWithEnv('filesRoute', filesRoute, 'string');
},
},
filesLength: {
needs: { filesLength: true },
compute({ filesLength }: { filesLength: number }) {
return replaceDatabaseValueWithEnv('filesLength', filesLength, 'number');
},
},
filesDefaultFormat: {
needs: { filesDefaultFormat: true },
compute({ filesDefaultFormat }: { filesDefaultFormat: string }) {
return replaceDatabaseValueWithEnv('filesDefaultFormat', filesDefaultFormat, 'string');
},
},
filesDisabledExtensions: {
needs: { filesDisabledExtensions: true },
compute({ filesDisabledExtensions }: { filesDisabledExtensions: string[] }) {
return replaceDatabaseValueWithEnv(
'filesDisabledExtensions',
filesDisabledExtensions,
'string[]',
);
},
},
filesMaxFileSize: {
needs: { filesMaxFileSize: true },
compute({ filesMaxFileSize }: { filesMaxFileSize: string }) {
return replaceDatabaseValueWithEnv('filesMaxFileSize', filesMaxFileSize, 'byte');
},
},
filesDefaultExpiration: {
needs: { filesDefaultExpiration: true },
compute({ filesDefaultExpiration }: { filesDefaultExpiration: string }) {
return replaceDatabaseValueWithEnv('filesDefaultExpiration', filesDefaultExpiration, 'string');
},
},
filesAssumeMimetypes: {
needs: { filesAssumeMimetypes: true },
compute({ filesAssumeMimetypes }: { filesAssumeMimetypes: boolean }) {
return replaceDatabaseValueWithEnv('filesAssumeMimetypes', filesAssumeMimetypes, 'boolean');
},
},
filesDefaultDateFormat: {
needs: { filesDefaultDateFormat: true },
compute({ filesDefaultDateFormat }: { filesDefaultDateFormat: string }) {
return replaceDatabaseValueWithEnv('filesDefaultDateFormat', filesDefaultDateFormat, 'string');
},
},
filesRemoveGpsMetadata: {
needs: { filesRemoveGpsMetadata: true },
compute({ filesRemoveGpsMetadata }: { filesRemoveGpsMetadata: boolean }) {
return replaceDatabaseValueWithEnv('filesRemoveGpsMetadata', filesRemoveGpsMetadata, 'boolean');
},
},
filesRandomWordsNumAdjectives: {
needs: { filesRandomWordsNumAdjectives: true },
compute({ filesRandomWordsNumAdjectives }: { filesRandomWordsNumAdjectives: number }) {
return replaceDatabaseValueWithEnv(
'filesRandomWordsNumAdjectives',
filesRandomWordsNumAdjectives,
'number',
);
},
},
filesRandomWordsSeparator: {
needs: { filesRandomWordsSeparator: true },
compute({ filesRandomWordsSeparator }: { filesRandomWordsSeparator: string }) {
return replaceDatabaseValueWithEnv(
'filesRandomWordsSeparator',
filesRandomWordsSeparator,
'string',
);
},
},
urlsRoute: {
needs: { urlsRoute: true },
compute({ urlsRoute }: { urlsRoute: string }) {
return replaceDatabaseValueWithEnv('urlsRoute', urlsRoute, 'string');
},
},
urlsLength: {
needs: { urlsLength: true },
compute({ urlsLength }: { urlsLength: number }) {
return replaceDatabaseValueWithEnv('urlsLength', urlsLength, 'number');
},
},
featuresImageCompression: {
needs: { featuresImageCompression: true },
compute({ featuresImageCompression }: { featuresImageCompression: boolean }) {
return replaceDatabaseValueWithEnv(
'featuresImageCompression',
featuresImageCompression,
'boolean',
);
},
},
featuresRobotsTxt: {
needs: { featuresRobotsTxt: true },
compute({ featuresRobotsTxt }: { featuresRobotsTxt: boolean }) {
return replaceDatabaseValueWithEnv('featuresRobotsTxt', featuresRobotsTxt, 'boolean');
},
},
featuresHealthcheck: {
needs: { featuresHealthcheck: true },
compute({ featuresHealthcheck }: { featuresHealthcheck: boolean }) {
return replaceDatabaseValueWithEnv('featuresHealthcheck', featuresHealthcheck, 'boolean');
},
},
featuresUserRegistration: {
needs: { featuresUserRegistration: true },
compute({ featuresUserRegistration }: { featuresUserRegistration: boolean }) {
return replaceDatabaseValueWithEnv(
'featuresUserRegistration',
featuresUserRegistration,
'boolean',
);
},
},
featuresOauthRegistration: {
needs: { featuresOauthRegistration: true },
compute({ featuresOauthRegistration }: { featuresOauthRegistration: boolean }) {
return replaceDatabaseValueWithEnv(
'featuresOauthRegistration',
featuresOauthRegistration,
'boolean',
);
},
},
featuresDeleteOnMaxViews: {
needs: { featuresDeleteOnMaxViews: true },
compute({ featuresDeleteOnMaxViews }: { featuresDeleteOnMaxViews: boolean }) {
return replaceDatabaseValueWithEnv(
'featuresDeleteOnMaxViews',
featuresDeleteOnMaxViews,
'boolean',
);
},
},
featuresThumbnailsEnabled: {
needs: { featuresThumbnailsEnabled: true },
compute({ featuresThumbnailsEnabled }: { featuresThumbnailsEnabled: boolean }) {
return replaceDatabaseValueWithEnv(
'featuresThumbnailsEnabled',
featuresThumbnailsEnabled,
'boolean',
);
},
},
featuresThumbnailsNumberThreads: {
needs: { featuresThumbnailsNumberThreads: true },
compute({ featuresThumbnailsNumberThreads }: { featuresThumbnailsNumberThreads: number }) {
return replaceDatabaseValueWithEnv(
'featuresThumbnailsNumberThreads',
featuresThumbnailsNumberThreads,
'number',
);
},
},
featuresMetricsEnabled: {
needs: { featuresMetricsEnabled: true },
compute({ featuresMetricsEnabled }: { featuresMetricsEnabled: boolean }) {
return replaceDatabaseValueWithEnv('featuresMetricsEnabled', featuresMetricsEnabled, 'boolean');
},
},
featuresMetricsAdminOnly: {
needs: { featuresMetricsAdminOnly: true },
compute({ featuresMetricsAdminOnly }: { featuresMetricsAdminOnly: boolean }) {
return replaceDatabaseValueWithEnv(
'featuresMetricsAdminOnly',
featuresMetricsAdminOnly,
'boolean',
);
},
},
featuresMetricsShowUserSpecific: {
needs: { featuresMetricsShowUserSpecific: true },
compute({ featuresMetricsShowUserSpecific }: { featuresMetricsShowUserSpecific: boolean }) {
return replaceDatabaseValueWithEnv(
'featuresMetricsShowUserSpecific',
featuresMetricsShowUserSpecific,
'boolean',
);
},
},
invitesEnabled: {
needs: { invitesEnabled: true },
compute({ invitesEnabled }: { invitesEnabled: boolean }) {
return replaceDatabaseValueWithEnv('invitesEnabled', invitesEnabled, 'boolean');
},
},
invitesLength: {
needs: { invitesLength: true },
compute({ invitesLength }: { invitesLength: number }) {
return replaceDatabaseValueWithEnv('invitesLength', invitesLength, 'number');
},
},
websiteTitle: {
needs: { websiteTitle: true },
compute({ websiteTitle }: { websiteTitle: string }) {
return replaceDatabaseValueWithEnv('websiteTitle', websiteTitle, 'string');
},
},
websiteTitleLogo: {
needs: { websiteTitleLogo: true },
compute({ websiteTitleLogo }: { websiteTitleLogo: string }) {
return replaceDatabaseValueWithEnv('websiteTitleLogo', websiteTitleLogo, 'string');
},
},
websiteExternalLinks: {
needs: { websiteExternalLinks: true },
compute({ websiteExternalLinks }: { websiteExternalLinks: Prisma.JsonValue }) {
return replaceDatabaseValueWithEnv('websiteExternalLinks', websiteExternalLinks, 'json[]');
},
},
websiteLoginBackground: {
needs: { websiteLoginBackground: true },
compute({ websiteLoginBackground }: { websiteLoginBackground: string }) {
return replaceDatabaseValueWithEnv('websiteLoginBackground', websiteLoginBackground, 'string');
},
},
websiteLoginBackgroundBlur: {
needs: { websiteLoginBackgroundBlur: true },
compute({ websiteLoginBackgroundBlur }: { websiteLoginBackgroundBlur: boolean }) {
return replaceDatabaseValueWithEnv(
'websiteLoginBackgroundBlur',
websiteLoginBackgroundBlur,
'boolean',
);
},
},
websiteDefaultAvatar: {
needs: { websiteDefaultAvatar: true },
compute({ websiteDefaultAvatar }: { websiteDefaultAvatar: string }) {
return replaceDatabaseValueWithEnv('websiteDefaultAvatar', websiteDefaultAvatar, 'string');
},
},
websiteTos: {
needs: { websiteTos: true },
compute({ websiteTos }: { websiteTos: string }) {
return replaceDatabaseValueWithEnv('websiteTos', websiteTos, 'string');
},
},
websiteThemeDefault: {
needs: { websiteThemeDefault: true },
compute({ websiteThemeDefault }: { websiteThemeDefault: string }) {
return replaceDatabaseValueWithEnv('websiteThemeDefault', websiteThemeDefault, 'string');
},
},
websiteThemeDark: {
needs: { websiteThemeDark: true },
compute({ websiteThemeDark }: { websiteThemeDark: string }) {
return replaceDatabaseValueWithEnv('websiteThemeDark', websiteThemeDark, 'string');
},
},
websiteThemeLight: {
needs: { websiteThemeLight: true },
compute({ websiteThemeLight }: { websiteThemeLight: string }) {
return replaceDatabaseValueWithEnv('websiteThemeLight', websiteThemeLight, 'string');
},
},
oauthBypassLocalLogin: {
needs: { oauthBypassLocalLogin: true },
compute({ oauthBypassLocalLogin }: { oauthBypassLocalLogin: boolean }) {
return replaceDatabaseValueWithEnv('oauthBypassLocalLogin', oauthBypassLocalLogin, 'boolean');
},
},
oauthLoginOnly: {
needs: { oauthLoginOnly: true },
compute({ oauthLoginOnly }: { oauthLoginOnly: boolean }) {
return replaceDatabaseValueWithEnv('oauthLoginOnly', oauthLoginOnly, 'boolean');
},
},
oauthDiscordClientId: {
needs: { oauthDiscordClientId: true },
compute({ oauthDiscordClientId }: { oauthDiscordClientId: string }) {
return replaceDatabaseValueWithEnv('oauthDiscordClientId', oauthDiscordClientId, 'string');
},
},
oauthDiscordClientSecret: {
needs: { oauthDiscordClientSecret: true },
compute({ oauthDiscordClientSecret }: { oauthDiscordClientSecret: string }) {
return replaceDatabaseValueWithEnv(
'oauthDiscordClientSecret',
oauthDiscordClientSecret,
'string',
);
},
},
oauthDiscordRedirectUri: {
needs: { oauthDiscordRedirectUri: true },
compute({ oauthDiscordRedirectUri }: { oauthDiscordRedirectUri: string }) {
return replaceDatabaseValueWithEnv('oauthDiscordRedirectUri', oauthDiscordRedirectUri, 'string');
},
},
oauthGoogleClientId: {
needs: { oauthGoogleClientId: true },
compute({ oauthGoogleClientId }: { oauthGoogleClientId: string }) {
return replaceDatabaseValueWithEnv('oauthGoogleClientId', oauthGoogleClientId, 'string');
},
},
oauthGoogleClientSecret: {
needs: { oauthGoogleClientSecret: true },
compute({ oauthGoogleClientSecret }: { oauthGoogleClientSecret: string }) {
return replaceDatabaseValueWithEnv('oauthGoogleClientSecret', oauthGoogleClientSecret, 'string');
},
},
oauthGoogleRedirectUri: {
needs: { oauthGoogleRedirectUri: true },
compute({ oauthGoogleRedirectUri }: { oauthGoogleRedirectUri: string }) {
return replaceDatabaseValueWithEnv('oauthGoogleRedirectUri', oauthGoogleRedirectUri, 'string');
},
},
oauthGithubClientId: {
needs: { oauthGithubClientId: true },
compute({ oauthGithubClientId }: { oauthGithubClientId: string }) {
return replaceDatabaseValueWithEnv('oauthGithubClientId', oauthGithubClientId, 'string');
},
},
oauthGithubClientSecret: {
needs: { oauthGithubClientSecret: true },
compute({ oauthGithubClientSecret }: { oauthGithubClientSecret: string }) {
return replaceDatabaseValueWithEnv('oauthGithubClientSecret', oauthGithubClientSecret, 'string');
},
},
oauthGithubRedirectUri: {
needs: { oauthGithubRedirectUri: true },
compute({ oauthGithubRedirectUri }: { oauthGithubRedirectUri: string }) {
return replaceDatabaseValueWithEnv('oauthGithubRedirectUri', oauthGithubRedirectUri, 'string');
},
},
oauthOidcClientId: {
needs: { oauthOidcClientId: true },
compute({ oauthOidcClientId }: { oauthOidcClientId: string }) {
return replaceDatabaseValueWithEnv('oauthOidcClientId', oauthOidcClientId, 'string');
},
},
oauthOidcClientSecret: {
needs: { oauthOidcClientSecret: true },
compute({ oauthOidcClientSecret }: { oauthOidcClientSecret: string }) {
return replaceDatabaseValueWithEnv('oauthOidcClientSecret', oauthOidcClientSecret, 'string');
},
},
oauthOidcAuthorizeUrl: {
needs: { oauthOidcAuthorizeUrl: true },
compute({ oauthOidcAuthorizeUrl }: { oauthOidcAuthorizeUrl: string }) {
return replaceDatabaseValueWithEnv('oauthOidcAuthorizeUrl', oauthOidcAuthorizeUrl, 'string');
},
},
oauthOidcUserinfoUrl: {
needs: { oauthOidcUserinfoUrl: true },
compute({ oauthOidcUserinfoUrl }: { oauthOidcUserinfoUrl: string }) {
return replaceDatabaseValueWithEnv('oauthOidcUserinfoUrl', oauthOidcUserinfoUrl, 'string');
},
},
oauthOidcTokenUrl: {
needs: { oauthOidcTokenUrl: true },
compute({ oauthOidcTokenUrl }: { oauthOidcTokenUrl: string }) {
return replaceDatabaseValueWithEnv('oauthOidcTokenUrl', oauthOidcTokenUrl, 'string');
},
},
oauthOidcRedirectUri: {
needs: { oauthOidcRedirectUri: true },
compute({ oauthOidcRedirectUri }: { oauthOidcRedirectUri: string }) {
return replaceDatabaseValueWithEnv('oauthOidcRedirectUri', oauthOidcRedirectUri, 'string');
},
},
mfaTotpEnabled: {
needs: { mfaTotpEnabled: true },
compute({ mfaTotpEnabled }: { mfaTotpEnabled: boolean }) {
return replaceDatabaseValueWithEnv('mfaTotpEnabled', mfaTotpEnabled, 'boolean');
},
},
mfaTotpIssuer: {
needs: { mfaTotpIssuer: true },
compute({ mfaTotpIssuer }: { mfaTotpIssuer: string }) {
return replaceDatabaseValueWithEnv('mfaTotpIssuer', mfaTotpIssuer, 'string');
},
},
mfaPasskeys: {
needs: { mfaPasskeys: true },
compute({ mfaPasskeys }: { mfaPasskeys: boolean }) {
return replaceDatabaseValueWithEnv('mfaPasskeys', mfaPasskeys, 'boolean');
},
},
ratelimitEnabled: {
needs: { ratelimitEnabled: true },
compute({ ratelimitEnabled }: { ratelimitEnabled: boolean }) {
return replaceDatabaseValueWithEnv('ratelimitEnabled', ratelimitEnabled, 'boolean');
},
},
ratelimitMax: {
needs: { ratelimitMax: true },
compute({ ratelimitMax }: { ratelimitMax: number }) {
return replaceDatabaseValueWithEnv('ratelimitMax', ratelimitMax, 'number');
},
},
ratelimitWindow: {
needs: { ratelimitWindow: true },
compute({ ratelimitWindow }: { ratelimitWindow: number }) {
return replaceDatabaseValueWithEnv('ratelimitWindow', ratelimitWindow, 'number');
},
},
ratelimitAdminBypass: {
needs: { ratelimitAdminBypass: true },
compute({ ratelimitAdminBypass }: { ratelimitAdminBypass: boolean }) {
return replaceDatabaseValueWithEnv('ratelimitAdminBypass', ratelimitAdminBypass, 'boolean');
},
},
ratelimitAllowList: {
needs: { ratelimitAllowList: true },
compute({ ratelimitAllowList }: { ratelimitAllowList: string[] }) {
return replaceDatabaseValueWithEnv('ratelimitAllowList', ratelimitAllowList, 'string[]');
},
},
httpWebhookOnUpload: {
needs: { httpWebhookOnUpload: true },
compute({ httpWebhookOnUpload }: { httpWebhookOnUpload: string }) {
return replaceDatabaseValueWithEnv('httpWebhookOnUpload', httpWebhookOnUpload, 'string');
},
},
httpWebhookOnShorten: {
needs: { httpWebhookOnShorten: true },
compute({ httpWebhookOnShorten }: { httpWebhookOnShorten: string }) {
return replaceDatabaseValueWithEnv('httpWebhookOnShorten', httpWebhookOnShorten, 'string');
},
},
discordWebhookUrl: {
needs: { discordWebhookUrl: true },
compute({ discordWebhookUrl }: { discordWebhookUrl: string }) {
return replaceDatabaseValueWithEnv('discordWebhookUrl', discordWebhookUrl, 'string');
},
},
discordUsername: {
needs: { discordUsername: true },
compute({ discordUsername }: { discordUsername: string }) {
return replaceDatabaseValueWithEnv('discordUsername', discordUsername, 'string');
},
},
discordAvatarUrl: {
needs: { discordAvatarUrl: true },
compute({ discordAvatarUrl }: { discordAvatarUrl: string }) {
return replaceDatabaseValueWithEnv('discordAvatarUrl', discordAvatarUrl, 'string');
},
},
discordOnUploadWebhookUrl: {
needs: { discordOnUploadWebhookUrl: true },
compute({ discordOnUploadWebhookUrl }: { discordOnUploadWebhookUrl: string }) {
return replaceDatabaseValueWithEnv(
'discordOnUploadWebhookUrl',
discordOnUploadWebhookUrl,
'string',
);
},
},
discordOnUploadUsername: {
needs: { discordOnUploadUsername: true },
compute({ discordOnUploadUsername }: { discordOnUploadUsername: string }) {
return replaceDatabaseValueWithEnv('discordOnUploadUsername', discordOnUploadUsername, 'string');
},
},
discordOnUploadAvatarUrl: {
needs: { discordOnUploadAvatarUrl: true },
compute({ discordOnUploadAvatarUrl }: { discordOnUploadAvatarUrl: string }) {
return replaceDatabaseValueWithEnv(
'discordOnUploadAvatarUrl',
discordOnUploadAvatarUrl,
'string',
);
},
},
discordOnUploadContent: {
needs: { discordOnUploadContent: true },
compute({ discordOnUploadContent }: { discordOnUploadContent: string }) {
return replaceDatabaseValueWithEnv('discordOnUploadContent', discordOnUploadContent, 'string');
},
},
discordOnUploadEmbed: {
needs: { discordOnUploadEmbed: true },
compute({ discordOnUploadEmbed }: { discordOnUploadEmbed: Prisma.JsonValue }) {
return replaceDatabaseValueWithEnv('discordOnUploadEmbed', discordOnUploadEmbed, 'json[]');
},
},
discordOnShortenWebhookUrl: {
needs: { discordOnShortenWebhookUrl: true },
compute({ discordOnShortenWebhookUrl }: { discordOnShortenWebhookUrl: string }) {
return replaceDatabaseValueWithEnv(
'discordOnShortenWebhookUrl',
discordOnShortenWebhookUrl,
'string',
);
},
},
discordOnShortenUsername: {
needs: { discordOnShortenUsername: true },
compute({ discordOnShortenUsername }: { discordOnShortenUsername: string }) {
return replaceDatabaseValueWithEnv(
'discordOnShortenUsername',
discordOnShortenUsername,
'string',
);
},
},
discordOnShortenAvatarUrl: {
needs: { discordOnShortenAvatarUrl: true },
compute({ discordOnShortenAvatarUrl }: { discordOnShortenAvatarUrl: string }) {
return replaceDatabaseValueWithEnv(
'discordOnShortenAvatarUrl',
discordOnShortenAvatarUrl,
'string',
);
},
},
discordOnShortenContent: {
needs: { discordOnShortenContent: true },
compute({ discordOnShortenContent }: { discordOnShortenContent: string }) {
return replaceDatabaseValueWithEnv('discordOnShortenContent', discordOnShortenContent, 'string');
},
},
discordOnShortenEmbed: {
needs: { discordOnShortenEmbed: true },
compute({ discordOnShortenEmbed }: { discordOnShortenEmbed: Prisma.JsonValue }) {
return replaceDatabaseValueWithEnv('discordOnShortenEmbed', discordOnShortenEmbed, 'json[]');
},
},
pwaEnabled: {
needs: { pwaEnabled: true },
compute({ pwaEnabled }: { pwaEnabled: boolean }) {
return replaceDatabaseValueWithEnv('pwaEnabled', pwaEnabled, 'boolean');
},
},
pwaTitle: {
needs: { pwaTitle: true },
compute({ pwaTitle }: { pwaTitle: string }) {
return replaceDatabaseValueWithEnv('pwaTitle', pwaTitle, 'string');
},
},
pwaShortName: {
needs: { pwaShortName: true },
compute({ pwaShortName }: { pwaShortName: string }) {
return replaceDatabaseValueWithEnv('pwaShortName', pwaShortName, 'string');
},
},
pwaDescription: {
needs: { pwaDescription: true },
compute({ pwaDescription }: { pwaDescription: string }) {
return replaceDatabaseValueWithEnv('pwaDescription', pwaDescription, 'string');
},
},
pwaThemeColor: {
needs: { pwaThemeColor: true },
compute({ pwaThemeColor }: { pwaThemeColor: string }) {
return replaceDatabaseValueWithEnv('pwaThemeColor', pwaThemeColor, 'string');
},
},
pwaBackgroundColor: {
needs: { pwaBackgroundColor: true },
compute({ pwaBackgroundColor }: { pwaBackgroundColor: string }) {
return replaceDatabaseValueWithEnv('pwaBackgroundColor', pwaBackgroundColor, 'string');
},
},
},
},
});
})
.$extends(SettingsExtension);
client.$connect();
return client;

View file

@ -1,7 +1,7 @@
import { prisma } from '..';
export async function getZipline() {
const zipline = await prisma.zipline.findFirst();
const zipline = await prisma.zipline.getSettings();
if (!zipline) {
return prisma.zipline.create({
data: {

View file

@ -0,0 +1,401 @@
import { Prisma } from '@prisma/client';
import { prisma } from '.';
import { replaceDatabaseValueWithEnv } from '../config/read';
export const SettingsExtension = Prisma.defineExtension({
name: 'settings',
model: {
zipline: {
async getSettings(...args: any[]) {
let settings = await prisma.zipline.findFirst(...args);
if (!settings) {
return settings;
}
// I was going to do a loop, but i kept getting typescript errors. please forgive me
settings = {
id: settings.id,
createdAt: settings.createdAt,
updatedAt: settings.updatedAt,
firstSetup: settings.firstSetup,
coreReturnHttpsUrls: replaceDatabaseValueWithEnv(
'coreReturnHttpsUrls',
settings.coreReturnHttpsUrls,
'boolean',
),
coreDefaultDomain: replaceDatabaseValueWithEnv(
'coreDefaultDomain',
settings.coreDefaultDomain,
'string',
),
coreTempDirectory: replaceDatabaseValueWithEnv(
'coreTempDirectory',
settings.coreTempDirectory,
'string',
),
chunksMax: replaceDatabaseValueWithEnv('chunksMax', settings.chunksMax, 'string'),
chunksSize: replaceDatabaseValueWithEnv('chunksSize', settings.chunksSize, 'string'),
chunksEnabled: replaceDatabaseValueWithEnv('chunksEnabled', settings.chunksEnabled, 'boolean'),
tasksDeleteInterval: replaceDatabaseValueWithEnv(
'tasksDeleteInterval',
settings.tasksDeleteInterval,
'ms',
),
tasksClearInvitesInterval: replaceDatabaseValueWithEnv(
'tasksClearInvitesInterval',
settings.tasksClearInvitesInterval,
'ms',
),
tasksMaxViewsInterval: replaceDatabaseValueWithEnv(
'tasksMaxViewsInterval',
settings.tasksMaxViewsInterval,
'ms',
),
tasksThumbnailsInterval: replaceDatabaseValueWithEnv(
'tasksThumbnailsInterval',
settings.tasksThumbnailsInterval,
'ms',
),
tasksMetricsInterval: replaceDatabaseValueWithEnv(
'tasksMetricsInterval',
settings.tasksMetricsInterval,
'ms',
),
filesRoute: replaceDatabaseValueWithEnv('filesRoute', settings.filesRoute, 'string'),
filesLength: replaceDatabaseValueWithEnv('filesLength', settings.filesLength, 'number'),
filesDefaultFormat: replaceDatabaseValueWithEnv(
'filesDefaultFormat',
settings.filesDefaultFormat,
'string',
),
filesDisabledExtensions: replaceDatabaseValueWithEnv(
'filesDisabledExtensions',
settings.filesDisabledExtensions,
'string[]',
),
filesMaxFileSize: replaceDatabaseValueWithEnv(
'filesMaxFileSize',
settings.filesMaxFileSize,
'byte',
),
filesDefaultExpiration: replaceDatabaseValueWithEnv(
'filesDefaultExpiration',
settings.filesDefaultExpiration,
'string',
),
filesAssumeMimetypes: replaceDatabaseValueWithEnv(
'filesAssumeMimetypes',
settings.filesAssumeMimetypes,
'boolean',
),
filesDefaultDateFormat: replaceDatabaseValueWithEnv(
'filesDefaultDateFormat',
settings.filesDefaultDateFormat,
'string',
),
filesRemoveGpsMetadata: replaceDatabaseValueWithEnv(
'filesRemoveGpsMetadata',
settings.filesRemoveGpsMetadata,
'boolean',
),
filesRandomWordsNumAdjectives: replaceDatabaseValueWithEnv(
'filesRandomWordsNumAdjectives',
settings.filesRandomWordsNumAdjectives,
'number',
),
filesRandomWordsSeparator: replaceDatabaseValueWithEnv(
'filesRandomWordsSeparator',
settings.filesRandomWordsSeparator,
'string',
),
urlsRoute: replaceDatabaseValueWithEnv('urlsRoute', settings.urlsRoute, 'string'),
urlsLength: replaceDatabaseValueWithEnv('urlsLength', settings.urlsLength, 'number'),
featuresImageCompression: replaceDatabaseValueWithEnv(
'featuresImageCompression',
settings.featuresImageCompression,
'boolean',
),
featuresRobotsTxt: replaceDatabaseValueWithEnv(
'featuresRobotsTxt',
settings.featuresRobotsTxt,
'boolean',
),
featuresHealthcheck: replaceDatabaseValueWithEnv(
'featuresHealthcheck',
settings.featuresHealthcheck,
'boolean',
),
featuresUserRegistration: replaceDatabaseValueWithEnv(
'featuresUserRegistration',
settings.featuresUserRegistration,
'boolean',
),
featuresOauthRegistration: replaceDatabaseValueWithEnv(
'featuresOauthRegistration',
settings.featuresOauthRegistration,
'boolean',
),
featuresDeleteOnMaxViews: replaceDatabaseValueWithEnv(
'featuresDeleteOnMaxViews',
settings.featuresDeleteOnMaxViews,
'boolean',
),
featuresThumbnailsEnabled: replaceDatabaseValueWithEnv(
'featuresThumbnailsEnabled',
settings.featuresThumbnailsEnabled,
'boolean',
),
featuresThumbnailsNumberThreads: replaceDatabaseValueWithEnv(
'featuresThumbnailsNumberThreads',
settings.featuresThumbnailsNumberThreads,
'number',
),
featuresMetricsEnabled: replaceDatabaseValueWithEnv(
'featuresMetricsEnabled',
settings.featuresMetricsEnabled,
'boolean',
),
featuresMetricsAdminOnly: replaceDatabaseValueWithEnv(
'featuresMetricsAdminOnly',
settings.featuresMetricsAdminOnly,
'boolean',
),
featuresMetricsShowUserSpecific: replaceDatabaseValueWithEnv(
'featuresMetricsShowUserSpecific',
settings.featuresMetricsShowUserSpecific,
'boolean',
),
invitesEnabled: replaceDatabaseValueWithEnv('invitesEnabled', settings.invitesEnabled, 'boolean'),
invitesLength: replaceDatabaseValueWithEnv('invitesLength', settings.invitesLength, 'number'),
websiteTitle: replaceDatabaseValueWithEnv('websiteTitle', settings.websiteTitle, 'string'),
websiteTitleLogo: replaceDatabaseValueWithEnv(
'websiteTitleLogo',
settings.websiteTitleLogo,
'string',
),
websiteExternalLinks: replaceDatabaseValueWithEnv(
'websiteExternalLinks',
settings.websiteExternalLinks,
'json[]',
),
websiteLoginBackground: replaceDatabaseValueWithEnv(
'websiteLoginBackground',
settings.websiteLoginBackground,
'string',
),
websiteLoginBackgroundBlur: replaceDatabaseValueWithEnv(
'websiteLoginBackgroundBlur',
settings.websiteLoginBackgroundBlur,
'boolean',
),
websiteDefaultAvatar: replaceDatabaseValueWithEnv(
'websiteDefaultAvatar',
settings.websiteDefaultAvatar,
'string',
),
websiteTos: replaceDatabaseValueWithEnv('websiteTos', settings.websiteTos, 'string'),
websiteThemeDefault: replaceDatabaseValueWithEnv(
'websiteThemeDefault',
settings.websiteThemeDefault,
'string',
),
websiteThemeDark: replaceDatabaseValueWithEnv(
'websiteThemeDark',
settings.websiteThemeDark,
'string',
),
websiteThemeLight: replaceDatabaseValueWithEnv(
'websiteThemeLight',
settings.websiteThemeLight,
'string',
),
oauthBypassLocalLogin: replaceDatabaseValueWithEnv(
'oauthBypassLocalLogin',
settings.oauthBypassLocalLogin,
'boolean',
),
oauthLoginOnly: replaceDatabaseValueWithEnv('oauthLoginOnly', settings.oauthLoginOnly, 'boolean'),
oauthDiscordClientId: replaceDatabaseValueWithEnv(
'oauthDiscordClientId',
settings.oauthDiscordClientId,
'string',
),
oauthDiscordClientSecret: replaceDatabaseValueWithEnv(
'oauthDiscordClientSecret',
settings.oauthDiscordClientSecret,
'string',
),
oauthDiscordRedirectUri: replaceDatabaseValueWithEnv(
'oauthDiscordRedirectUri',
settings.oauthDiscordRedirectUri,
'string',
),
oauthGoogleClientId: replaceDatabaseValueWithEnv(
'oauthGoogleClientId',
settings.oauthGoogleClientId,
'string',
),
oauthGoogleClientSecret: replaceDatabaseValueWithEnv(
'oauthGoogleClientSecret',
settings.oauthGoogleClientSecret,
'string',
),
oauthGoogleRedirectUri: replaceDatabaseValueWithEnv(
'oauthGoogleRedirectUri',
settings.oauthGoogleRedirectUri,
'string',
),
oauthGithubClientId: replaceDatabaseValueWithEnv(
'oauthGithubClientId',
settings.oauthGithubClientId,
'string',
),
oauthGithubClientSecret: replaceDatabaseValueWithEnv(
'oauthGithubClientSecret',
settings.oauthGithubClientSecret,
'string',
),
oauthGithubRedirectUri: replaceDatabaseValueWithEnv(
'oauthGithubRedirectUri',
settings.oauthGithubRedirectUri,
'string',
),
oauthOidcClientId: replaceDatabaseValueWithEnv(
'oauthOidcClientId',
settings.oauthOidcClientId,
'string',
),
oauthOidcClientSecret: replaceDatabaseValueWithEnv(
'oauthOidcClientSecret',
settings.oauthOidcClientSecret,
'string',
),
oauthOidcAuthorizeUrl: replaceDatabaseValueWithEnv(
'oauthOidcAuthorizeUrl',
settings.oauthOidcAuthorizeUrl,
'string',
),
oauthOidcUserinfoUrl: replaceDatabaseValueWithEnv(
'oauthOidcUserinfoUrl',
settings.oauthOidcUserinfoUrl,
'string',
),
oauthOidcTokenUrl: replaceDatabaseValueWithEnv(
'oauthOidcTokenUrl',
settings.oauthOidcTokenUrl,
'string',
),
oauthOidcRedirectUri: replaceDatabaseValueWithEnv(
'oauthOidcRedirectUri',
settings.oauthOidcRedirectUri,
'string',
),
mfaTotpEnabled: replaceDatabaseValueWithEnv('mfaTotpEnabled', settings.mfaTotpEnabled, 'boolean'),
mfaTotpIssuer: replaceDatabaseValueWithEnv('mfaTotpIssuer', settings.mfaTotpIssuer, 'string'),
mfaPasskeys: replaceDatabaseValueWithEnv('mfaPasskeys', settings.mfaPasskeys, 'boolean'),
ratelimitEnabled: replaceDatabaseValueWithEnv(
'ratelimitEnabled',
settings.ratelimitEnabled,
'boolean',
),
ratelimitMax: replaceDatabaseValueWithEnv('ratelimitMax', settings.ratelimitMax, 'number'),
ratelimitWindow: replaceDatabaseValueWithEnv('ratelimitWindow', settings.ratelimitWindow, 'number'),
ratelimitAdminBypass: replaceDatabaseValueWithEnv(
'ratelimitAdminBypass',
settings.ratelimitAdminBypass,
'boolean',
),
ratelimitAllowList: replaceDatabaseValueWithEnv(
'ratelimitAllowList',
settings.ratelimitAllowList,
'string[]',
),
httpWebhookOnUpload: replaceDatabaseValueWithEnv(
'httpWebhookOnUpload',
settings.httpWebhookOnUpload,
'string',
),
httpWebhookOnShorten: replaceDatabaseValueWithEnv(
'httpWebhookOnShorten',
settings.httpWebhookOnShorten,
'string',
),
discordWebhookUrl: replaceDatabaseValueWithEnv(
'discordWebhookUrl',
settings.discordWebhookUrl,
'string',
),
discordUsername: replaceDatabaseValueWithEnv('discordUsername', settings.discordUsername, 'string'),
discordAvatarUrl: replaceDatabaseValueWithEnv(
'discordAvatarUrl',
settings.discordAvatarUrl,
'string',
),
discordOnUploadWebhookUrl: replaceDatabaseValueWithEnv(
'discordOnUploadWebhookUrl',
settings.discordOnUploadWebhookUrl,
'string',
),
discordOnUploadUsername: replaceDatabaseValueWithEnv(
'discordOnUploadUsername',
settings.discordOnUploadUsername,
'string',
),
discordOnUploadAvatarUrl: replaceDatabaseValueWithEnv(
'discordOnUploadAvatarUrl',
settings.discordOnUploadAvatarUrl,
'string',
),
discordOnUploadContent: replaceDatabaseValueWithEnv(
'discordOnUploadContent',
settings.discordOnUploadContent,
'string',
),
discordOnUploadEmbed: replaceDatabaseValueWithEnv(
'discordOnUploadEmbed',
settings.discordOnUploadEmbed,
'json[]',
),
discordOnShortenWebhookUrl: replaceDatabaseValueWithEnv(
'discordOnShortenWebhookUrl',
settings.discordOnShortenWebhookUrl,
'string',
),
discordOnShortenUsername: replaceDatabaseValueWithEnv(
'discordOnShortenUsername',
settings.discordOnShortenUsername,
'string',
),
discordOnShortenAvatarUrl: replaceDatabaseValueWithEnv(
'discordOnShortenAvatarUrl',
settings.discordOnShortenAvatarUrl,
'string',
),
discordOnShortenContent: replaceDatabaseValueWithEnv(
'discordOnShortenContent',
settings.discordOnShortenContent,
'string',
),
discordOnShortenEmbed: replaceDatabaseValueWithEnv(
'discordOnShortenEmbed',
settings.discordOnShortenEmbed,
'json[]',
),
pwaEnabled: replaceDatabaseValueWithEnv('pwaEnabled', settings.pwaEnabled, 'boolean'),
pwaTitle: replaceDatabaseValueWithEnv('pwaTitle', settings.pwaTitle, 'string'),
pwaShortName: replaceDatabaseValueWithEnv('pwaShortName', settings.pwaShortName, 'string'),
pwaDescription: replaceDatabaseValueWithEnv('pwaDescription', settings.pwaDescription, 'string'),
pwaThemeColor: replaceDatabaseValueWithEnv('pwaThemeColor', settings.pwaThemeColor, 'string'),
pwaBackgroundColor: replaceDatabaseValueWithEnv(
'pwaBackgroundColor',
settings.pwaBackgroundColor,
'string',
),
};
return settings;
},
async getSettingsRaw(...args: any[]) {
return await prisma.zipline.findFirst(...args);
},
},
},
});

View file

@ -1,6 +1,6 @@
import { bytes } from '@/lib/bytes';
import { reloadSettings } from '@/lib/config';
import { DATABASE_TO_PROP, readDatabaseSettings, valueIsFromEnv } from '@/lib/config/read';
import { DATABASE_TO_PROP, readDatabaseSettings, valueIsFromEnv as valueFromEnv } from '@/lib/config/read';
import { prisma } from '@/lib/db';
import { log } from '@/lib/logger';
import { readThemes } from '@/lib/theme/file';
@ -64,7 +64,7 @@ export default fastifyPlugin(
preHandler: [userMiddleware, administratorMiddleware],
},
async (_, res) => {
const settings = await prisma.zipline.findFirst({
const settings = await prisma.zipline.getSettingsRaw({
omit: {
createdAt: true,
updatedAt: true,
@ -80,12 +80,11 @@ export default fastifyPlugin(
};
for (const key in DATABASE_TO_PROP) {
if (DATABASE_TO_PROP.hasOwnProperty(key)) {
if (valueIsFromEnv(key as keyof typeof DATABASE_TO_PROP)) {
// settingsResponse.locked[key] = "This value has been set by the " + databaseToEnv(key as keyof typeof DATABASE_TO_PROP) + " environment variable and cannot be changed here.";
settingsResponse.locked[key] = true;
}
const val = valueFromEnv(key as keyof typeof DATABASE_TO_PROP);
if (val === undefined) {
continue;
}
settingsResponse.locked[key] = val;
}
return res.send(settingsResponse);
@ -98,7 +97,7 @@ export default fastifyPlugin(
preHandler: [userMiddleware, administratorMiddleware],
},
async (req, res) => {
const settings = await prisma.zipline.findFirst();
const settings = await prisma.zipline.getSettingsRaw();
if (!settings) return res.notFound('no settings table found');
const result: any = await parseSettings(req.body);
@ -114,15 +113,6 @@ export default fastifyPlugin(
});
}
// iterate through every database_to_prop object key
for (const key in DATABASE_TO_PROP) {
if (DATABASE_TO_PROP.hasOwnProperty(key)) {
if (valueIsFromEnv(key as keyof typeof DATABASE_TO_PROP)) {
result.data[key] = undefined;
}
}
}
const newSettings = await prisma.zipline.update({
where: {
id: settings.id,
@ -146,7 +136,22 @@ export default fastifyPlugin(
by: req.user.username,
});
return res.send(newSettings);
const settingsResponse: ApiServerSettingsResponse = {
...newSettings,
locked: {},
};
for (const key in DATABASE_TO_PROP) {
if (DATABASE_TO_PROP.hasOwnProperty(key)) {
const val = valueFromEnv(key as keyof typeof DATABASE_TO_PROP);
if (val === undefined) {
continue;
}
settingsResponse.locked[key] = val;
}
}
return res.send(settingsResponse);
},
);