refactor: renamed from 'draconic' to 'void'

This commit is contained in:
AlphaNecron 2021-10-01 15:14:44 +07:00
parent 76c19674a0
commit 6daa54cc87
31 changed files with 160 additions and 115 deletions

2
.gitignore vendored
View File

@ -36,7 +36,7 @@ package.lock.json
# vercel
.vercel
# draconic
# void
.vscode/
.idea/
config.toml

View File

@ -1,5 +1,5 @@
{
"name": "draconic",
"name": "void",
"version": "0.2.2",
"private": true,
"engines": {

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "User" ALTER COLUMN "embedSiteName" SET DEFAULT E'Void';

View File

@ -14,7 +14,7 @@ model User {
token String @unique
isAdmin Boolean @default(false)
useEmbed Boolean? @default(false)
embedSiteName String? @default("Draconic")
embedSiteName String? @default("Void")
embedTitle String?
embedColor String @default("#B794F4")
embedDesc String?

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@ -1,12 +1,12 @@
<div align="center">
<img src="https://raw.githubusercontent.com/AlphaNecron/Draconic/v0/public/banner.png"/>
<img src="https://raw.githubusercontent.com/AlphaNecron/Void/v0/public/banner.png"/>
A self-hosted file hosting service based on Zipline with many features.
![Build stable](https://img.shields.io/github/workflow/status/AlphaNecron/Draconic/CI:%20Build/v0?color=%2368D391&label=stable&logo=github&style=for-the-badge)
![Build stable](https://img.shields.io/github/workflow/status/AlphaNecron/Draconic/CI:%20Build/dev?color=%2368D391&label=dev&logo=github&style=for-the-badge)
![Stars](https://img.shields.io/github/stars/AlphaNecron/Draconic?color=%23B794F4&logo=github&style=for-the-badge)
![Version](https://img.shields.io/github/package-json/v/AlphaNecron/Draconic/v0?color=%23B794F4&label=latest&logo=react&logoColor=ffffff&style=for-the-badge)
![Last commit](https://img.shields.io/github/last-commit/AlphaNecron/Draconic/dev?color=%234FD1C5&logo=github&style=for-the-badge)
![Build stable](https://img.shields.io/github/workflow/status/AlphaNecron/Void/CI:%20Build/v0?color=%2368D391&label=stable&logo=github&style=for-the-badge)
![Build stable](https://img.shields.io/github/workflow/status/AlphaNecron/Void/CI:%20Build/dev?color=%2368D391&label=dev&logo=github&style=for-the-badge)
![Stars](https://img.shields.io/github/stars/AlphaNecron/Void?color=%23B794F4&logo=github&style=for-the-badge)
![Version](https://img.shields.io/github/package-json/v/AlphaNecron/Void/v0?color=%23B794F4&label=latest&logo=react&logoColor=ffffff&style=for-the-badge)
![Last commit](https://img.shields.io/github/last-commit/AlphaNecron/Void/dev?color=%234FD1C5&logo=github&style=for-the-badge)
</div>
### Requirements
@ -16,8 +16,8 @@ A self-hosted file hosting service based on Zipline with many features.
### Installation / Deployment
```sh
git clone https://github.com/AlphaNecron/Draconic.git
cd Draconic
git clone https://github.com/AlphaNecron/Void.git
cd Void
yarn install # or npm install
cp config.example.toml config.toml
nano config.toml # edit the config file
@ -50,8 +50,8 @@ A self-hosted file hosting service based on Zipline with many features.
[core]
secure = false # Whether to use https or not
secret = 'supersecretpassphrase' # The secret used to sign cookie
host = '0.0.0.0' # The host Draconic should run on
port = 3000 # The port Draconic should run on
host = '0.0.0.0' # The host Void should run on
port = 3000 # The port Void should run on
database_url = 'postgres://username:password@localhost:5432/db_name' # PostgreSQL database url
[bot]

View File

@ -11,7 +11,7 @@ const mimes = require('../src/lib/mimetype');
const deployDb = require('../scripts/deployDb');
const { join } = require('path');
info('SERVER', 'Starting Draconic server');
info('SERVER', 'Starting Void server');
const dev = process.env.NODE_ENV === 'development';

View File

@ -4,7 +4,7 @@ import { Download, X } from 'react-feather';
export default function ShareXDialog({ open, onClose, token }) {
const ref = React.useRef();
const [name, setName] = useState('Draconic');
const [name, setName] = useState('Void');
const [generator, setGenerator] = useState('random');
const [preserveFileName, setPreserveFileName] = useState(false);
const generateConfig = shortener => {
@ -64,7 +64,7 @@ export default function ShareXDialog({ open, onClose, token }) {
<Input
value={name}
onChange={n => setName(n.target.value)}
placeholder='Draconic'
placeholder='Void'
size='sm'
/>
<Heading mt={2} mb={1} size='sm'>URL generator</Heading>

View File

@ -1,4 +1,4 @@
import { Box, Button, Checkbox, Flex, Heading, HStack, Select, Text, useColorModeValue, useToast, VStack } from '@chakra-ui/react';
import { Button, Center, Checkbox, Heading, HStack, Select, Text, useColorModeValue, useToast, VStack } from '@chakra-ui/react';
import copy from 'copy-to-clipboard';
import { useStoreSelector } from 'lib/redux/store';
import React, { useState } from 'react';
@ -55,48 +55,42 @@ export default function Upload() {
const bg = useColorModeValue('gray.100', 'gray.700');
const shadow = useColorModeValue('outline', 'dark-lg');
return (
<Flex minHeight='92vh' width='full' align='center' justifyContent='center'>
<Box
<Center h='92vh'>
<VStack
px={2}
boxShadow='xl'
bg={bg}
fg={fg}
justify='center'
align='center'
p={2}
borderRadius={4}
textAlign='left'
shadow={shadow}
>
<VStack>
<Heading fontSize='lg' m={1} align='left'>Upload a file</Heading>
<Button m={2} variant='ghost' width='385' height='200'>
<Dropzone disabled={busy} onDrop={acceptedFiles => setFile(acceptedFiles[0])}>
{({ getRootProps, getInputProps, isDragActive }) => (
<VStack {...getRootProps()}>
<input {...getInputProps()}/>
<UploadIcon size={56}/>
{isDragActive ? (
<Text fontSize='xl'>Drop the file here</Text>
) : (
<Text fontSize='xl'>Drag a file here or click to upload one</Text>
)}
<Text fontSize='lg' colorScheme='yellow' isTruncated maxWidth='350'>{file && file.name}</Text>
</VStack>
)}
</Dropzone>
</Button>
<HStack justify='stretch' width='385'>
<Checkbox width='160' isChecked={preserve} colorScheme='purple' onChange={p => setPreserve(p.target.checked)}>Preserve filename</Checkbox>
<Select size='sm' variant='filled' width='110' value={generator} onChange={selection => setGenerator(selection.target.value)}>
<option value='random'>Random</option>
<option value='zws'>Invisible</option>
<option value='emoji'>Emoji</option>
</Select>
<Button size='sm' width='100' isDisabled={busy || !file} isLoading={busy} loadingText='Uploading' onClick={handleFileUpload} colorScheme='purple' leftIcon={<UploadIcon size={16}/>}>Upload</Button>
</HStack>
</VStack>
</Box>
</Flex>
shadow={shadow}>
<Heading fontSize='lg' m={1} align='left'>Upload a file</Heading>
<Button m={2} variant='ghost' width='385' height='200'>
<Dropzone disabled={busy} onDrop={acceptedFiles => setFile(acceptedFiles[0])}>
{({ getRootProps, getInputProps, isDragActive }) => (
<VStack {...getRootProps()}>
<input {...getInputProps()}/>
<UploadIcon size={56}/>
{isDragActive ? (
<Text fontSize='xl'>Drop the file here</Text>
) : (
<Text fontSize='xl'>Drag a file here or click to upload one</Text>
)}
<Text fontSize='lg' colorScheme='yellow' isTruncated maxWidth='350'>{file && file.name}</Text>
</VStack>
)}
</Dropzone>
</Button>
<HStack justify='stretch' width='385'>
<Checkbox width='160' isChecked={preserve} colorScheme='purple' onChange={p => setPreserve(p.target.checked)}>Preserve filename</Checkbox>
<Select size='sm' variant='filled' width='110' value={generator} onChange={selection => setGenerator(selection.target.value)}>
<option value='random'>Random</option>
<option value='zws'>Invisible</option>
<option value='emoji'>Emoji</option>
</Select>
<Button size='sm' width='100' isDisabled={busy || !file} isLoading={busy} loadingText='Uploading' onClick={handleFileUpload} colorScheme='purple' leftIcon={<UploadIcon size={16}/>}>Upload</Button>
</HStack>
</VStack>
</Center>
);
}

View File

@ -40,7 +40,7 @@ export type NextApiRes = NextApiResponse & {
setCookie: (name: string, value: unknown, options: CookieSerializeOptions) => void;
}
export const withDraconic = (handler: (req: NextApiRequest, res: NextApiResponse) => unknown) => (req: NextApiReq, res: NextApiRes) => {
export const withVoid = (handler: (req: NextApiRequest, res: NextApiResponse) => unknown) => (req: NextApiReq, res: NextApiRes) => {
res.error = (message: string) => {
res.setHeader('Content-Type', 'application/json');
res.status(400);

View File

@ -17,24 +17,15 @@ export default function Embed({ file, embed, username, content = undefined, misc
a.href = misc.src;
a.click();
};
const replace = text => {
const time = new Date(file.uploadedAt);
return (text ?? '').replace(/{size}/ig, misc.size)
.replace(/{filename}/ig, file.fileName)
.replace(/{orig}/ig, file.origFileName)
.replace(/{date}/ig, time.toLocaleDateString())
.replace(/{time}/ig, time.toLocaleTimeString())
.replace(/{author}/ig, username);
};
return (
<>
<Head>
<>
{embed.enabled && (
<>
<meta property='og:site_name' content={replace(embed.siteName)}/>
<meta property='og:title' content={replace(embed.title)}/>
<meta property='og:description' content={replace(embed.desc)}/>
<meta property='og:site_name' content={embed.siteName}/>
<meta property='og:title' content={embed.title}/>
<meta property='og:description' content={embed.desc}/>
<meta property='theme-color' content={embed.color}/>
</>
)}
@ -58,11 +49,10 @@ export default function Embed({ file, embed, username, content = undefined, misc
<title>Uploaded by {username}</title>
</>
</Head>
<Center>
<Center h='100vh'>
<Box
m={4}
boxShadow='xl'
flexDirection='column'
bg={useColorModeValue('gray.100', 'gray.700')}
fg={useColorModeValue('gray.800', 'white')}
p={1}
@ -150,54 +140,63 @@ export const getServerSideProps: GetServerSideProps = async context => {
embedDesc: true
}
});
const embed = {
enabled,
siteName,
title,
color,
desc
};
const ext = file.fileName.split('.').pop();
const type = file.mimetype.split('/').shift();
const src = `${config.uploader.raw_route}/${file.fileName}`;
const url = `http${config.core.secure ? 's' : ''}://${context.req.headers.host}/${config.uploader.raw_route}`;
const isCode = Object.keys(languages).some(name => languages[name] === ext);
const replace = size => {
const time = new Date(file.uploadedAt);
const replace = text => {
return (text ?? '').replace(/{size}/ig, size)
.replace(/{filename}/ig, file.fileName)
.replace(/{orig}/ig, file.origFileName)
.replace(/{date}/ig, time.toLocaleDateString())
.replace(/{time}/ig, time.toLocaleTimeString())
.replace(/{author}/ig, username);
};
['siteName', 'title', 'desc'].forEach(prop => embed[prop] = replace(embed[prop]));
};
if (file.mimetype.startsWith('text') || isCode) {
const res = await fetch(`${url}/${file.fileName}`);
if (!res.ok) return { notFound: true };
const content = await res.text();
const size = bytesToHr(res.headers.get('content-length'));
replace(size);
delete file.uploadedAt;
return {
props: {
file,
embed: {
enabled,
siteName,
title,
color,
desc
},
embed,
username,
misc: {
ext,
type,
language: isCode ? ext : 'text',
size
language: isCode ? ext : 'text'
},
content
}
};
};
const size = bytesToHr((await fetch(`${url}/${file.fileName}`)).headers.get('content-length'));
replace(size);
delete file.uploadedAt;
return {
props: {
file,
embed: {
enabled,
siteName,
title,
color,
desc
},
embed,
username,
misc: {
ext,
type,
src,
size
src
}
}
};

View File

@ -4,7 +4,7 @@ import Head from 'next/head';
import React from 'react';
import { Provider } from 'react-redux';
export default function Draconic({ Component, pageProps }) {
export default function Void({ Component, pageProps }) {
const store = useStore();
return (
<Provider store={store}>

50
src/pages/_error.tsx Normal file
View File

@ -0,0 +1,50 @@
import { Button, Center, Heading, VStack } from '@chakra-ui/react';
import React from 'react';
import Link from 'next/link';
import { ArrowLeftCircle } from 'react-feather';
export default function Error({ code }) {
const errors = {
'400': 'Bad Request',
'401': 'Unauthorized',
'402': 'Payment Required',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed',
'406': 'Not Acceptable',
'407': 'Proxy Authentication Required',
'408': 'Request Timeout',
'409': 'Conflict',
'410': 'Gone',
'411': 'Length Required',
'412': 'Precondition Required',
'413': 'Request Entry Too Large',
'414': 'Request-URI Too Long',
'415': 'Unsupported Media Type',
'416': 'Requested Range Not Satisfiable',
'417': 'Expectation Failed',
'418': 'I\'m a teapot',
'429': 'Too Many Requests',
'500': 'Internal Server Error',
'501': 'Not Implemented',
'502': 'Bad Gateway',
'503': 'Service Unavailable',
'504': 'Gateway Timeout',
'505': 'HTTP Version Not Supported'
};
return (
<Center h='100vh'>
<VStack>
<Heading>{code}: {errors[String(code)]}</Heading>
<Link href='/dash' passHref>
<Button justifyContent='flex-start' colorScheme='purple' leftIcon={<ArrowLeftCircle/>}>Go back</Button>
</Link>
</VStack>
</Center>
);
}
Error.getInitialProps = ({ res, err }) => {
const code = res ? res.statusCode : err ? err.statusCode : 404;
return { code };
};

View File

@ -1,7 +1,7 @@
import { info } from 'lib/logger';
import prisma from 'lib/prisma';
import { checkPassword, createToken, hashPassword } from 'lib/utils';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
async function handler(req: NextApiReq, res: NextApiRes) {
if (req.method !== 'POST') return res.status(405).end();
@ -11,12 +11,12 @@ async function handler(req: NextApiReq, res: NextApiRes) {
const user = await prisma.user.create({
data: {
username: 'admin',
password: await hashPassword('draconicuser'),
password: await hashPassword('voiduser'),
token: createToken(),
isAdmin: true
}
});
info('SEED', `Created default user with username "${user.username}" and password "draconicuser"`);
info('SEED', `Created default user with username "${user.username}" and password "voiduser"`);
}
const user = await prisma.user.findFirst({
where: {
@ -31,4 +31,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
return res.json({ success: true });
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -1,5 +1,5 @@
import { info } from 'lib/logger';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user();
@ -9,4 +9,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
return res.json({ success: true });
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -2,7 +2,7 @@ import { rm } from 'fs/promises';
import { join } from 'path';
import cfg from '../../lib/config';
import { info } from '../../lib/logger';
import { NextApiReq, NextApiRes, withDraconic } from '../../lib/middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from '../../lib/middleware/withVoid';
import prisma from '../../lib/prisma';
async function handler(req: NextApiReq, res: NextApiRes) {
@ -47,4 +47,4 @@ export const config = {
},
};
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -1,7 +1,7 @@
import { default as cfg, default as config } from 'lib/config';
import generate from 'lib/generators';
import { info } from 'lib/logger';
import { NextApiReq, NextApiRes, withDraconic } from 'lib/middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'lib/middleware/withVoid';
import prisma from 'lib/prisma';
async function handler(req: NextApiReq, res: NextApiRes) {
@ -38,4 +38,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
});
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -1,7 +1,7 @@
import config from 'lib/config';
import prisma from 'lib/prisma';
import { bytesToHr, sizeOfDir } from 'lib/utils';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
import { join } from 'path';
async function handler(req: NextApiReq, res: NextApiRes) {
@ -69,4 +69,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
});
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -2,7 +2,7 @@ import { writeFile } from 'fs/promises';
import cfg from 'lib/config';
import generate, { emoji, zws } from 'lib/generators';
import { info } from 'lib/logger';
import { NextApiReq, NextApiRes, withDraconic } from 'lib/middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'lib/middleware/withVoid';
import mimetypes from 'lib/mimetype';
import prisma from 'lib/prisma';
import multer from 'multer';
@ -82,7 +82,7 @@ function run(middleware: any) {
export default async function handlers(req, res) {
await run(uploader.single('file'))(req, res);
return withDraconic(handler)(req, res);
return withVoid(handler)(req, res);
};
export const config = {

View File

@ -1,6 +1,6 @@
import config from 'lib/config';
import prisma from 'lib/prisma';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user();
@ -35,4 +35,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -1,7 +1,7 @@
import { info } from 'lib/logger';
import prisma from 'lib/prisma';
import { hashPassword } from 'lib/utils';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user();
@ -74,4 +74,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -1,7 +1,7 @@
import { info } from 'lib/logger';
import prisma from 'lib/prisma';
import { createToken } from 'lib/utils';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user();
@ -23,4 +23,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -1,7 +1,7 @@
import config from 'lib/config';
import { info } from 'lib/logger';
import prisma from 'lib/prisma';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user();
@ -37,4 +37,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -1,7 +1,7 @@
import { info } from 'lib/logger';
import prisma from 'lib/prisma';
import { createToken, hashPassword } from 'lib/utils';
import { NextApiReq, NextApiRes, withDraconic } from 'middleware/withDraconic';
import { NextApiReq, NextApiRes, withVoid } from 'middleware/withVoid';
async function handler(req: NextApiReq, res: NextApiRes) {
const user = await req.user();
@ -62,4 +62,4 @@ async function handler(req: NextApiReq, res: NextApiRes) {
}
}
export default withDraconic(handler);
export default withVoid(handler);

View File

@ -67,7 +67,7 @@ export default function Login() {
{props => (
<Form>
<VStack>
<Heading fontSize='xl' mb={2} align='center'>Draconic</Heading>
<Heading fontSize='xl' mb={2} align='center'>Void</Heading>
<Field name='username'>
{({ field, form }) => (
<FormControl isInvalid={form.errors.username && form.touched.username} isRequired mb={4}>

View File

@ -9,7 +9,7 @@ export default function Index() {
}, [router]);
return (
<Head>
<meta property='og:title' content='Draconic'/>
<meta property='og:title' content='Void'/>
<meta property='og:description' content='Free and open source file hosting service.'/>
</Head>
);

View File

@ -36,7 +36,7 @@
},
"include": [
"next-env.d.ts",
"draconic-env.d.ts",
"void-env.d.ts",
"**/*.ts",
"**/*.tsx",
"twilight/**/*.ts"

View File