feat(v3.6.0-rc1): small fixes
This commit is contained in:
parent
642e8796f0
commit
a90130e8bf
14 changed files with 73 additions and 84 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "zipline",
|
||||
"version": "3.5.1",
|
||||
"version": "3.6.0-rc1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "npm-run-all build:server dev:run",
|
||||
|
@ -17,7 +17,7 @@
|
|||
"docker:run": "docker-compose up -d",
|
||||
"docker:down": "docker-compose down",
|
||||
"docker:build-dev": "docker-compose --file docker-compose.dev.yml up --build",
|
||||
"scripts:read-config": "node dist/scripts/read-config"
|
||||
"scripts:read-config": "npm-run-all build:server && node dist/scripts/read-config"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dicedtomato/mantine-data-grid": "0.0.23",
|
||||
|
@ -82,4 +82,4 @@
|
|||
"url": "https://github.com/diced/zipline.git"
|
||||
},
|
||||
"packageManager": "yarn@3.2.4"
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import {
|
|||
Image,
|
||||
Tooltip,
|
||||
Badge,
|
||||
Menu,
|
||||
} from '@mantine/core';
|
||||
import { useClipboard } from '@mantine/hooks';
|
||||
import { useModals } from '@mantine/modals';
|
||||
|
@ -194,7 +195,7 @@ export default function Layout({ children, props }) {
|
|||
|
||||
const openResetToken = () =>
|
||||
modals.openConfirmModal({
|
||||
title: 'Reset Token',
|
||||
title: <Title>Reset Token?</Title>,
|
||||
children: (
|
||||
<Text size='sm'>
|
||||
Once you reset your token, you will have to update any uploaders to use this new token.
|
||||
|
@ -227,7 +228,7 @@ export default function Layout({ children, props }) {
|
|||
|
||||
const openCopyToken = () =>
|
||||
modals.openConfirmModal({
|
||||
title: 'Copy Token',
|
||||
title: <Title>Copy Token</Title>,
|
||||
children: (
|
||||
<Text size='sm'>
|
||||
Make sure you don't share this token with anyone as they will be able to upload files on your
|
||||
|
@ -362,17 +363,10 @@ export default function Layout({ children, props }) {
|
|||
|
||||
<Popover.Dropdown p={4} mr='md' sx={{ minWidth: '200px' }}>
|
||||
<Stack spacing={2}>
|
||||
<Text
|
||||
sx={{
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark[2] : theme.colors.gray[6],
|
||||
fontWeight: 500,
|
||||
fontSize: theme.fontSizes.sm,
|
||||
padding: `${theme.spacing.xs / 2}px ${theme.spacing.sm}px`,
|
||||
cursor: 'default',
|
||||
}}
|
||||
>
|
||||
{user.username}
|
||||
</Text>
|
||||
<Menu.Label>
|
||||
{user.username}{' '}
|
||||
{user.administrator && user.username !== 'administrator' ? '(Administrator)' : ''}
|
||||
</Menu.Label>
|
||||
<MenuItemLink icon={<SettingsIcon />} href='/dashboard/manage'>
|
||||
Manage Account
|
||||
</MenuItemLink>
|
||||
|
@ -398,20 +392,10 @@ export default function Layout({ children, props }) {
|
|||
<MenuItemLink icon={<LogoutIcon />} href='/auth/logout' color='red'>
|
||||
Logout
|
||||
</MenuItemLink>
|
||||
<Divider
|
||||
variant='solid'
|
||||
my={theme.spacing.xs / 2}
|
||||
sx={(theme) => ({
|
||||
width: '110%',
|
||||
borderTopColor:
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.colors.gray[2],
|
||||
margin: `${theme.spacing.xs / 2}px -4px`,
|
||||
})}
|
||||
/>
|
||||
<Menu.Divider />
|
||||
{user.oauth ? (
|
||||
<>
|
||||
<MenuItem
|
||||
noClick
|
||||
icon={
|
||||
user.oauthProvider === 'discord' ? (
|
||||
<DiscordIcon size={18} />
|
||||
|
@ -424,16 +408,7 @@ export default function Layout({ children, props }) {
|
|||
<span style={{ textTransform: 'capitalize' }}>{user.oauthProvider}</span>
|
||||
</MenuItem>
|
||||
|
||||
<Divider
|
||||
variant='solid'
|
||||
my={theme.spacing.xs / 2}
|
||||
sx={(theme) => ({
|
||||
width: '110%',
|
||||
borderTopColor:
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.colors.gray[2],
|
||||
margin: `${theme.spacing.xs / 2}px -4px`,
|
||||
})}
|
||||
/>
|
||||
<Menu.Divider />
|
||||
</>
|
||||
) : null}
|
||||
<MenuItem icon={<PencilIcon />}>
|
||||
|
|
|
@ -100,6 +100,11 @@ export default function ZiplineTheming({ Component, pageProps, ...props }) {
|
|||
overlayColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : 'white',
|
||||
},
|
||||
},
|
||||
Loader: {
|
||||
defaultProps: {
|
||||
variant: 'dots',
|
||||
},
|
||||
},
|
||||
Card: {
|
||||
styles: (t) => ({
|
||||
root: {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Table, Tooltip, Badge, useMantineTheme } from '@mantine/core';
|
||||
import { Table, Tooltip, Badge, HoverCard, Text, useMantineTheme, Group } from '@mantine/core';
|
||||
import Type from 'components/Type';
|
||||
|
||||
export function FilePreview({ file }: { file: File }) {
|
||||
|
@ -21,10 +21,12 @@ export default function FileDropzone({ file }: { file: File }) {
|
|||
const theme = useMantineTheme();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
position='top'
|
||||
label={
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<HoverCard shadow='md'>
|
||||
<HoverCard.Target>
|
||||
<Badge size='lg'>{file.name}</Badge>
|
||||
</HoverCard.Target>
|
||||
<HoverCard.Dropdown>
|
||||
<Group grow>
|
||||
<FilePreview file={file} />
|
||||
|
||||
<Table sx={{ color: theme.colorScheme === 'dark' ? 'white' : 'white' }} ml='md'>
|
||||
|
@ -43,10 +45,8 @@ export default function FileDropzone({ file }: { file: File }) {
|
|||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Badge size='lg'>{file.name}</Badge>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
</HoverCard.Dropdown>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export function GeneratorModal({ opened, onClose, title, onSubmit, ...other }) {
|
|||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={<Title order={3}>{title}</Title>} size='lg'>
|
||||
{other.desc && <Text>{other.desc}</Text>}
|
||||
{other.desc && <Text mb='md'>{other.desc}</Text>}
|
||||
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
|
||||
<Select
|
||||
label='Select file name format'
|
||||
|
|
|
@ -283,21 +283,28 @@ export default function Manage() {
|
|||
<Link href='https://zipline.diced.tech/docs/guides/variables'>the docs</Link> for variables
|
||||
</MutedText>
|
||||
<form onSubmit={form.onSubmit((v) => onSubmit(v))}>
|
||||
<TextInput id='username' label='Username' {...form.getInputProps('username')} />
|
||||
<TextInput id='username' label='Username' my='sm' {...form.getInputProps('username')} />
|
||||
<PasswordInput
|
||||
id='password'
|
||||
label='Password'
|
||||
description='Leave blank to keep your old password'
|
||||
my='sm'
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<TextInput id='embedTitle' label='Embed Title' {...form.getInputProps('embedTitle')} />
|
||||
<ColorInput id='embedColor' label='Embed Color' {...form.getInputProps('embedColor')} />
|
||||
<TextInput id='embedSiteName' label='Embed Site Name' {...form.getInputProps('embedSiteName')} />
|
||||
<TextInput id='embedTitle' label='Embed Title' my='sm' {...form.getInputProps('embedTitle')} />
|
||||
<ColorInput id='embedColor' label='Embed Color' my='sm' {...form.getInputProps('embedColor')} />
|
||||
<TextInput
|
||||
id='embedSiteName'
|
||||
label='Embed Site Name'
|
||||
my='sm'
|
||||
{...form.getInputProps('embedSiteName')}
|
||||
/>
|
||||
<TextInput
|
||||
id='domains'
|
||||
label='Domains'
|
||||
description='A list of domains separated by commas. These domains will be used to randomly output a domain when uploading. This is optional.'
|
||||
placeholder='https://example.com, https://example2.com'
|
||||
my='sm'
|
||||
{...form.getInputProps('domains')}
|
||||
/>
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ export default function Upload() {
|
|||
dropdownPosition='top'
|
||||
data={Object.keys(exts).map((x) => ({ value: x, label: exts[x] }))}
|
||||
icon={<TypeIcon />}
|
||||
searchable
|
||||
/>
|
||||
<Button
|
||||
leftIcon={<UploadIcon />}
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||
import type { CookieSerializeOptions } from 'cookie';
|
||||
|
||||
import { serialize } from 'cookie';
|
||||
import { sign64, unsign64 } from 'lib/util';
|
||||
import { sign64, unsign64 } from 'lib/utils/crypto';
|
||||
import config from 'lib/config';
|
||||
import prisma from 'lib/prisma';
|
||||
import { User } from '@prisma/client';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createHmac, randomBytes, timingSafeEqual } from 'crypto';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { hash, verify } from 'argon2';
|
||||
import { readdir, stat } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
|
@ -25,31 +25,6 @@ export function createToken() {
|
|||
return randomChars(24) + '.' + Buffer.from(Date.now().toString()).toString('base64url');
|
||||
}
|
||||
|
||||
export function sign(value: string, secret: string): string {
|
||||
const signed = value + ':' + createHmac('sha256', secret).update(value).digest('base64').replace(/=+$/, '');
|
||||
|
||||
return signed;
|
||||
}
|
||||
|
||||
export function unsign(value: string, secret: string): string {
|
||||
const str = value.slice(0, value.lastIndexOf(':'));
|
||||
|
||||
const mac = sign(str, secret);
|
||||
|
||||
const macBuffer = Buffer.from(mac);
|
||||
const valBuffer = Buffer.from(value);
|
||||
|
||||
return timingSafeEqual(macBuffer, valBuffer) ? str : null;
|
||||
}
|
||||
|
||||
export function sign64(value: string, secret: string): string {
|
||||
return Buffer.from(sign(value, secret)).toString('base64');
|
||||
}
|
||||
|
||||
export function unsign64(value: string, secret: string): string {
|
||||
return unsign(Buffer.from(value, 'base64').toString(), secret);
|
||||
}
|
||||
|
||||
export function chunk<T>(arr: T[], size: number): Array<T[]> {
|
||||
const result = [];
|
||||
const L = arr.length;
|
||||
|
|
26
src/lib/utils/crypto.ts
Normal file
26
src/lib/utils/crypto.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { createHmac, timingSafeEqual } from 'crypto';
|
||||
|
||||
export function sign(value: string, secret: string): string {
|
||||
const signed = value + ':' + createHmac('sha256', secret).update(value).digest('base64').replace(/=+$/, '');
|
||||
|
||||
return signed;
|
||||
}
|
||||
|
||||
export function unsign(value: string, secret: string): string {
|
||||
const str = value.slice(0, value.lastIndexOf(':'));
|
||||
|
||||
const mac = sign(str, secret);
|
||||
|
||||
const macBuffer = Buffer.from(mac);
|
||||
const valBuffer = Buffer.from(value);
|
||||
|
||||
return timingSafeEqual(macBuffer, valBuffer) ? str : null;
|
||||
}
|
||||
|
||||
export function sign64(value: string, secret: string): string {
|
||||
return Buffer.from(sign(value, secret)).toString('base64');
|
||||
}
|
||||
|
||||
export function unsign64(value: string, secret: string): string {
|
||||
return unsign(Buffer.from(value, 'base64').toString(), secret);
|
||||
}
|
|
@ -7,12 +7,12 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
const user = await req.user();
|
||||
if (!user) return res.forbid('not logged in');
|
||||
|
||||
let amount = typeof req.query.amount === 'string' ? parseInt(req.query.amount) : 2;
|
||||
let amount = typeof req.query.amount === 'string' ? Number(req.query.amount) : 2;
|
||||
if (isNaN(amount)) return res.bad('invalid amount');
|
||||
|
||||
// get stats per day
|
||||
|
||||
var stats = await prisma.$queryRaw<Stats[]>`
|
||||
let stats: Stats[] = await prisma.$queryRaw`
|
||||
SELECT *
|
||||
FROM "Stats" as t JOIN
|
||||
(SELECT MAX(t2."created_at") as max_timestamp
|
||||
|
|
|
@ -24,7 +24,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
},
|
||||
});
|
||||
|
||||
if (!user) return res.forbid('authorization incorect');
|
||||
if (!user) return res.forbid('authorization incorrect');
|
||||
if (user.ratelimit) {
|
||||
const remaining = user.ratelimit.getTime() - Date.now();
|
||||
if (remaining <= 0) {
|
||||
|
|
|
@ -87,7 +87,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
Logger.get('user').info(`Export for ${user.username} (${user.id}) has started`);
|
||||
for (let i = 0; i !== files.length; ++i) {
|
||||
const file = files[i];
|
||||
const stream = await datasource.get(file.file);
|
||||
const stream = datasource.get(file.file);
|
||||
if (stream) {
|
||||
const def = new ZipPassThrough(file.file);
|
||||
zip.add(def);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import config from 'lib/config';
|
||||
import config from '../lib/config';
|
||||
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
|
Loading…
Reference in a new issue