feat(v3.6.0-rc1): small fixes

This commit is contained in:
diced 2022-10-22 23:42:52 -07:00
parent 642e8796f0
commit a90130e8bf
No known key found for this signature in database
GPG key ID: 370BD1BA142842D1
14 changed files with 73 additions and 84 deletions

View file

@ -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",

View file

@ -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&apos;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 />}>

View file

@ -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: {

View file

@ -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>
);
}

View file

@ -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'

View file

@ -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')}
/>

View file

@ -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 />}

View file

@ -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';

View file

@ -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
View 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);
}

View file

@ -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

View file

@ -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) {

View file

@ -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);

View file

@ -1,3 +1,3 @@
import config from 'lib/config';
import config from '../lib/config';
console.log(JSON.stringify(config, null, 2));