mirror of
https://github.com/diced/zipline.git
synced 2025-05-11 10:26:05 +02:00
nice
This commit is contained in:
parent
e6efff28dd
commit
d7c7c9da18
27 changed files with 244 additions and 232 deletions
15
.eslintrc.js
15
.eslintrc.js
|
@ -1,25 +1,25 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
es2021: true,
|
es2021: true,
|
||||||
node: true,
|
node: true
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
version: 'detect',
|
version: 'detect'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'plugin:react/recommended',
|
'plugin:react/recommended',
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended'
|
||||||
],
|
],
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true,
|
jsx: true
|
||||||
},
|
},
|
||||||
ecmaVersion: 12,
|
ecmaVersion: 12,
|
||||||
sourceType: 'module',
|
sourceType: 'module'
|
||||||
},
|
},
|
||||||
plugins: ['react', '@typescript-eslint'],
|
plugins: ['react', '@typescript-eslint'],
|
||||||
rules: {
|
rules: {
|
||||||
|
@ -29,5 +29,6 @@ module.exports = {
|
||||||
'linebreak-style': ['error', 'unix'],
|
'linebreak-style': ['error', 'unix'],
|
||||||
quotes: ['error', 'single'],
|
quotes: ['error', 'single'],
|
||||||
semi: ['error', 'always'],
|
semi: ['error', 'always'],
|
||||||
},
|
'comma-dangle': ['error', 'never']
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
"$schema": "http://json.schemastore.org/prettierrc",
|
"$schema": "http://json.schemastore.org/prettierrc",
|
||||||
"jsxSingleQuote": true,
|
"jsxSingleQuote": true,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"arrowParens": "avoid"
|
"arrowParens": "avoid",
|
||||||
|
"trailingComma": "none",
|
||||||
|
"endOfLine": "lf"
|
||||||
}
|
}
|
||||||
|
|
10
README.md
10
README.md
|
@ -1,16 +1,18 @@
|
||||||

|
## 
|
||||||
---
|
|
||||||
|
|
||||||
[](https://app.codacy.com/gh/ZiplineProject/zipline?utm_source=github.com&utm_medium=referral&utm_content=ZiplineProject/zipline&utm_campaign=Badge_Grade)
|
[](https://app.codacy.com/gh/ZiplineProject/zipline?utm_source=github.com&utm_medium=referral&utm_content=ZiplineProject/zipline&utm_campaign=Badge_Grade)
|
||||||
|
|
||||||
# ZiplineNext
|
# ZiplineNext
|
||||||
|
|
||||||
Speed & reliable
|
Speed & reliable
|
||||||
|
|
||||||
- Configurable
|
- Configurable
|
||||||
- Fast (API)
|
- Fast (API)
|
||||||
- Built with Next.js & React
|
- Built with Next.js & React
|
||||||
- Support for multible database types (mongo soon)
|
- Support for multible database types (mongo soon)
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
You can view current documentation [here](https://zipline.diced.wtf/)
|
You can view current documentation [here](https://zipline.diced.wtf/)
|
||||||
|
|
||||||
*Note: Topics like Migrations are not implemented, but will soon exist.*
|
_Note: Topics like Migrations are not implemented, but will soon exist._
|
||||||
|
|
40
setup.js
40
setup.js
|
@ -10,7 +10,7 @@ const base = {
|
||||||
description: 'My Zipline Server',
|
description: 'My Zipline Server',
|
||||||
thumbnail:
|
thumbnail:
|
||||||
'https://github.githubassets.com/images/modules/open_graph/github-mark.png',
|
'https://github.githubassets.com/images/modules/open_graph/github-mark.png',
|
||||||
color: '#128377',
|
color: '#128377'
|
||||||
},
|
},
|
||||||
core: { secret: 'my-secret', port: 3000 },
|
core: { secret: 'my-secret', port: 3000 },
|
||||||
uploader: {
|
uploader: {
|
||||||
|
@ -18,9 +18,9 @@ const base = {
|
||||||
route: '/u',
|
route: '/u',
|
||||||
length: 6,
|
length: 6,
|
||||||
original: false,
|
original: false,
|
||||||
blacklisted: [],
|
blacklisted: []
|
||||||
},
|
},
|
||||||
urls: { route: '/s', length: 4, vanity: false },
|
urls: { route: '/s', length: 4, vanity: false }
|
||||||
};
|
};
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -37,34 +37,34 @@ const base = {
|
||||||
{ name: 'mssql' },
|
{ name: 'mssql' },
|
||||||
{ name: 'sqlite' },
|
{ name: 'sqlite' },
|
||||||
{ name: 'sqlite3' },
|
{ name: 'sqlite3' },
|
||||||
{ name: 'mongodb', extra: 'No support yet' },
|
{ name: 'mongodb', extra: 'No support yet' }
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'host',
|
name: 'host',
|
||||||
message: 'Database Host',
|
message: 'Database Host'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'number',
|
type: 'number',
|
||||||
name: 'port',
|
name: 'port',
|
||||||
message: 'Database Port',
|
message: 'Database Port'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'database',
|
name: 'database',
|
||||||
message: 'Database Name',
|
message: 'Database Name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'username',
|
name: 'username',
|
||||||
message: 'Database User',
|
message: 'Database User'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'password',
|
type: 'password',
|
||||||
name: 'password',
|
name: 'password',
|
||||||
message: 'Database Password',
|
message: 'Database Password'
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log('\nCore\n');
|
console.log('\nCore\n');
|
||||||
|
@ -73,13 +73,13 @@ const base = {
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'secret',
|
name: 'secret',
|
||||||
message: 'Secret (this must be secure)',
|
message: 'Secret (this must be secure)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'number',
|
type: 'number',
|
||||||
name: 'port',
|
name: 'port',
|
||||||
message: 'Serve on Port',
|
message: 'Serve on Port'
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log('\nUploader\n');
|
console.log('\nUploader\n');
|
||||||
|
@ -88,13 +88,13 @@ const base = {
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'directory',
|
name: 'directory',
|
||||||
message: 'Uploads Directory',
|
message: 'Uploads Directory'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
name: 'original',
|
name: 'original',
|
||||||
message: 'Keep Original?',
|
message: 'Keep Original?'
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log('\nURLs\n');
|
console.log('\nURLs\n');
|
||||||
|
@ -103,8 +103,8 @@ const base = {
|
||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
name: 'vanity',
|
name: 'vanity',
|
||||||
message: 'Allow vanity URLs',
|
message: 'Allow vanity URLs'
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
|
@ -112,7 +112,7 @@ const base = {
|
||||||
meta: { ...base.meta },
|
meta: { ...base.meta },
|
||||||
core: { ...base.core, ...core },
|
core: { ...base.core, ...core },
|
||||||
uploader: { ...base.uploader, ...uploader },
|
uploader: { ...base.uploader, ...uploader },
|
||||||
urls: { ...base.urls, ...urls },
|
urls: { ...base.urls, ...urls }
|
||||||
};
|
};
|
||||||
writeFileSync('Zipline.toml', stringify(config));
|
writeFileSync('Zipline.toml', stringify(config));
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -14,11 +14,11 @@ import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
field: {
|
field: {
|
||||||
width: '100%',
|
width: '100%'
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
padding: '10px',
|
padding: '10px'
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
@ -40,7 +40,7 @@ export default function Login() {
|
||||||
await fetch('/api/user/login', {
|
await fetch('/api/user/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ username, password }),
|
body: JSON.stringify({ username, password })
|
||||||
})
|
})
|
||||||
).json();
|
).json();
|
||||||
if (!d.error) {
|
if (!d.error) {
|
||||||
|
@ -55,7 +55,7 @@ export default function Login() {
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'center',
|
horizontal: 'center'
|
||||||
}}
|
}}
|
||||||
open={open}
|
open={open}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
|
|
|
@ -14,17 +14,17 @@ import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
margin: {
|
margin: {
|
||||||
margin: '5px',
|
margin: '5px'
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
padding: '10px',
|
padding: '10px'
|
||||||
},
|
},
|
||||||
field: {
|
field: {
|
||||||
width: '100%',
|
width: '100%'
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto'
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function ManageUser() {
|
export default function ManageUser() {
|
||||||
|
@ -40,7 +40,7 @@ export default function ManageUser() {
|
||||||
await fetch('/api/user', {
|
await fetch('/api/user', {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ username, password }),
|
body: JSON.stringify({ username, password })
|
||||||
})
|
})
|
||||||
).json();
|
).json();
|
||||||
if (!d.error) {
|
if (!d.error) {
|
||||||
|
@ -53,7 +53,7 @@ export default function ManageUser() {
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'center',
|
horizontal: 'center'
|
||||||
}}
|
}}
|
||||||
open={alertOpen}
|
open={alertOpen}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
|
@ -96,4 +96,4 @@ export default function ManageUser() {
|
||||||
</Card>
|
</Card>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,14 @@ const drawerWidth = 240;
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
root: {
|
root: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexGrow: 1,
|
flexGrow: 1
|
||||||
},
|
},
|
||||||
drawer: {
|
drawer: {
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up('sm')]: {
|
||||||
width: drawerWidth,
|
width: drawerWidth,
|
||||||
flexShrink: 0,
|
flexShrink: 0
|
||||||
},
|
},
|
||||||
outlineColor: '#fff',
|
outlineColor: '#fff'
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -56,31 +56,31 @@ const useStyles = makeStyles(theme => ({
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up('sm')]: {
|
||||||
width: 'calc(100%)',
|
width: 'calc(100%)',
|
||||||
marginLeft: drawerWidth,
|
marginLeft: drawerWidth
|
||||||
},
|
},
|
||||||
borderBottom: '1px solid #1f1f1f',
|
borderBottom: '1px solid #1f1f1f'
|
||||||
},
|
},
|
||||||
menuButton: {
|
menuButton: {
|
||||||
marginRight: theme.spacing(2),
|
marginRight: theme.spacing(2),
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up('sm')]: {
|
||||||
display: 'none',
|
display: 'none'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
rightButton: {
|
rightButton: {
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto'
|
||||||
},
|
},
|
||||||
// necessary for content to be below app bar
|
// necessary for content to be below app bar
|
||||||
toolbar: theme.mixins.toolbar,
|
toolbar: theme.mixins.toolbar,
|
||||||
drawerPaper: {
|
drawerPaper: {
|
||||||
width: drawerWidth,
|
width: drawerWidth
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
padding: theme.spacing(1),
|
padding: theme.spacing(1)
|
||||||
},
|
},
|
||||||
menuIcon: {
|
menuIcon: {
|
||||||
marginRight: '10px',
|
marginRight: '10px'
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function UI({ children }) {
|
export default function UI({ children }) {
|
||||||
|
@ -175,12 +175,12 @@ export default function UI({ children }) {
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'right',
|
horizontal: 'right'
|
||||||
}}
|
}}
|
||||||
keepMounted
|
keepMounted
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'right',
|
horizontal: 'right'
|
||||||
}}
|
}}
|
||||||
open={open}
|
open={open}
|
||||||
onClose={() => setAnchorEl(null)}
|
onClose={() => setAnchorEl(null)}
|
||||||
|
@ -271,7 +271,7 @@ export default function UI({ children }) {
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'center',
|
horizontal: 'center'
|
||||||
}}
|
}}
|
||||||
open={alertOpen}
|
open={alertOpen}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
|
@ -349,10 +349,10 @@ export default function UI({ children }) {
|
||||||
open={mobileOpen}
|
open={mobileOpen}
|
||||||
onClose={handleDrawerToggle}
|
onClose={handleDrawerToggle}
|
||||||
classes={{
|
classes={{
|
||||||
paper: classes.drawerPaper,
|
paper: classes.drawerPaper
|
||||||
}}
|
}}
|
||||||
ModalProps={{
|
ModalProps={{
|
||||||
keepMounted: true, // Better open performance on mobile.
|
keepMounted: true // Better open performance on mobile.
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{drawer}
|
{drawer}
|
||||||
|
@ -361,7 +361,7 @@ export default function UI({ children }) {
|
||||||
<Hidden xsDown implementation='css'>
|
<Hidden xsDown implementation='css'>
|
||||||
<Drawer
|
<Drawer
|
||||||
classes={{
|
classes={{
|
||||||
paper: classes.drawerPaper,
|
paper: classes.drawerPaper
|
||||||
}}
|
}}
|
||||||
variant='permanent'
|
variant='permanent'
|
||||||
open
|
open
|
||||||
|
|
|
@ -15,41 +15,41 @@ import { makeStyles, useTheme } from '@material-ui/core/styles';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
root: {
|
root: {
|
||||||
display: 'flex',
|
display: 'flex'
|
||||||
},
|
},
|
||||||
drawer: {
|
drawer: {
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up('sm')]: {
|
||||||
width: 240,
|
width: 240,
|
||||||
flexShrink: 0,
|
flexShrink: 0
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up('sm')]: {
|
||||||
width: 'calc(100%)',
|
width: 'calc(100%)',
|
||||||
marginLeft: 240,
|
marginLeft: 240
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
menuButton: {
|
menuButton: {
|
||||||
marginRight: theme.spacing(2),
|
marginRight: theme.spacing(2),
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up('sm')]: {
|
||||||
display: 'none',
|
display: 'none'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
rightButton: {
|
rightButton: {
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto'
|
||||||
},
|
},
|
||||||
// necessary for content to be below app bar
|
// necessary for content to be below app bar
|
||||||
toolbar: theme.mixins.toolbar,
|
toolbar: theme.mixins.toolbar,
|
||||||
drawerPaper: {
|
drawerPaper: {
|
||||||
width: 240,
|
width: 240
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3)
|
||||||
},
|
},
|
||||||
fullWidth: {
|
fullWidth: {
|
||||||
width: '100%',
|
width: '100%'
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function UIPlaceholder() {
|
export default function UIPlaceholder() {
|
||||||
|
@ -132,10 +132,10 @@ export default function UIPlaceholder() {
|
||||||
open={mobileOpen}
|
open={mobileOpen}
|
||||||
onClose={handleDrawerToggle}
|
onClose={handleDrawerToggle}
|
||||||
classes={{
|
classes={{
|
||||||
paper: classes.drawerPaper,
|
paper: classes.drawerPaper
|
||||||
}}
|
}}
|
||||||
ModalProps={{
|
ModalProps={{
|
||||||
keepMounted: true, // Better open performance on mobile.
|
keepMounted: true // Better open performance on mobile.
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{drawer}
|
{drawer}
|
||||||
|
@ -144,7 +144,7 @@ export default function UIPlaceholder() {
|
||||||
<Hidden xsDown implementation='css'>
|
<Hidden xsDown implementation='css'>
|
||||||
<Drawer
|
<Drawer
|
||||||
classes={{
|
classes={{
|
||||||
paper: classes.drawerPaper,
|
paper: classes.drawerPaper
|
||||||
}}
|
}}
|
||||||
variant='permanent'
|
variant='permanent'
|
||||||
open
|
open
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
FastifyInstanceToken,
|
FastifyInstanceToken,
|
||||||
Inject,
|
Inject,
|
||||||
GET,
|
GET,
|
||||||
DELETE,
|
DELETE
|
||||||
} from 'fastify-decorators';
|
} from 'fastify-decorators';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { Image } from '../entities/Image';
|
import { Image } from '../entities/Image';
|
||||||
|
@ -30,8 +30,8 @@ export class ImagesController {
|
||||||
|
|
||||||
const images = await this.images.find({
|
const images = await this.images.find({
|
||||||
where: {
|
where: {
|
||||||
user: readBaseCookie(req.cookies.zipline),
|
user: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send(images);
|
return reply.send(images);
|
||||||
|
@ -47,21 +47,21 @@ export class ImagesController {
|
||||||
const image = await this.images.findOne({
|
const image = await this.images.findOne({
|
||||||
where: {
|
where: {
|
||||||
user: readBaseCookie(req.cookies.zipline),
|
user: readBaseCookie(req.cookies.zipline),
|
||||||
id: req.params.id,
|
id: req.params.id
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!image) throw new Error('No image');
|
if (!image) throw new Error('No image');
|
||||||
|
|
||||||
this.images.delete({
|
this.images.delete({
|
||||||
id: req.params.id,
|
id: req.params.id
|
||||||
});
|
});
|
||||||
|
|
||||||
Console.logger(Image).info(`image ${image.id} was deleted`);
|
Console.logger(Image).info(`image ${image.id} was deleted`);
|
||||||
if (this.webhooks.events.includes(WebhookType.DELETE_IMAGE))
|
if (this.webhooks.events.includes(WebhookType.DELETE_IMAGE))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.upload.content, {
|
WebhookHelper.sendWebhook(this.webhooks.upload.content, {
|
||||||
image,
|
image,
|
||||||
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`,
|
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send(image);
|
return reply.send(image);
|
||||||
|
@ -73,8 +73,8 @@ export class ImagesController {
|
||||||
|
|
||||||
const images = await this.images.find({
|
const images = await this.images.find({
|
||||||
where: {
|
where: {
|
||||||
user: readBaseCookie(req.cookies.zipline),
|
user: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send(images.slice(1).slice(-3).reverse());
|
return reply.send(images.slice(1).slice(-3).reverse());
|
||||||
|
@ -86,8 +86,8 @@ export class ImagesController {
|
||||||
|
|
||||||
const images = await this.images.find({
|
const images = await this.images.find({
|
||||||
where: {
|
where: {
|
||||||
user: readBaseCookie(req.cookies.zipline),
|
user: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function chunk(array: Image[], size: number) {
|
function chunk(array: Image[], size: number) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
POST,
|
POST,
|
||||||
FastifyInstanceToken,
|
FastifyInstanceToken,
|
||||||
Inject,
|
Inject,
|
||||||
GET,
|
GET
|
||||||
} from 'fastify-decorators';
|
} from 'fastify-decorators';
|
||||||
import { Multipart } from 'fastify-multipart';
|
import { Multipart } from 'fastify-multipart';
|
||||||
import { createWriteStream, existsSync, mkdirSync } from 'fs';
|
import { createWriteStream, existsSync, mkdirSync } from 'fs';
|
||||||
|
@ -62,7 +62,7 @@ export class RootController {
|
||||||
|
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
const usersImages = await this.images.find({
|
const usersImages = await this.images.find({
|
||||||
where: { user: user.id },
|
where: { user: user.id }
|
||||||
});
|
});
|
||||||
|
|
||||||
lb.push({
|
lb.push({
|
||||||
|
@ -70,14 +70,14 @@ export class RootController {
|
||||||
images: usersImages.length,
|
images: usersImages.length,
|
||||||
views: usersImages
|
views: usersImages
|
||||||
.map(x => x.views)
|
.map(x => x.views)
|
||||||
.reduce((a, b) => Number(a) + Number(b), 0),
|
.reduce((a, b) => Number(a) + Number(b), 0)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return reply.send({
|
return reply.send({
|
||||||
images: images.length,
|
images: images.length,
|
||||||
totalViews,
|
totalViews,
|
||||||
leaderboard: lb.sort((a, b) => b.images - a.images),
|
leaderboard: lb.sort((a, b) => b.images - a.images)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,8 +88,8 @@ export class RootController {
|
||||||
|
|
||||||
const user = await this.users.findOne({
|
const user = await this.users.findOne({
|
||||||
where: {
|
where: {
|
||||||
token: req.headers.authorization,
|
token: req.headers.authorization
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
if (!user) return new AuthError('Incorrect token!');
|
if (!user) return new AuthError('Incorrect token!');
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ export class RootController {
|
||||||
if (this.webhooks.events.includes(WebhookType.UPLOAD))
|
if (this.webhooks.events.includes(WebhookType.UPLOAD))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.upload.content, {
|
WebhookHelper.sendWebhook(this.webhooks.upload.content, {
|
||||||
image,
|
image,
|
||||||
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`,
|
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`
|
||||||
});
|
});
|
||||||
|
|
||||||
reply.send(
|
reply.send(
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
Inject,
|
Inject,
|
||||||
GET,
|
GET,
|
||||||
DELETE,
|
DELETE,
|
||||||
POST,
|
POST
|
||||||
} from 'fastify-decorators';
|
} from 'fastify-decorators';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { URL } from '../entities/URL';
|
import { URL } from '../entities/URL';
|
||||||
|
@ -34,8 +34,8 @@ export class URLSController {
|
||||||
|
|
||||||
const all = await this.urls.find({
|
const all = await this.urls.find({
|
||||||
where: {
|
where: {
|
||||||
user: readBaseCookie(req.cookies.zipline),
|
user: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send(all);
|
return reply.send(all);
|
||||||
|
@ -51,22 +51,22 @@ export class URLSController {
|
||||||
const url = await this.urls.findOne({
|
const url = await this.urls.findOne({
|
||||||
where: {
|
where: {
|
||||||
user: readBaseCookie(req.cookies.zipline),
|
user: readBaseCookie(req.cookies.zipline),
|
||||||
id: req.params.id,
|
id: req.params.id
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!url) throw new Error('No url');
|
if (!url) throw new Error('No url');
|
||||||
|
|
||||||
this.logger.verbose(`attempting to delete url ${url.id}`);
|
this.logger.verbose(`attempting to delete url ${url.id}`);
|
||||||
this.urls.delete({
|
this.urls.delete({
|
||||||
id: req.params.id,
|
id: req.params.id
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info(`url ${url.id} was deleted`);
|
this.logger.info(`url ${url.id} was deleted`);
|
||||||
if (this.webhooks.events.includes(WebhookType.DELETE_URL))
|
if (this.webhooks.events.includes(WebhookType.DELETE_URL))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.delete_url.content, {
|
WebhookHelper.sendWebhook(this.webhooks.delete_url.content, {
|
||||||
url,
|
url,
|
||||||
host: `${req.protocol}://${req.hostname}${config.urls.route}/`,
|
host: `${req.protocol}://${req.hostname}${config.urls.route}/`
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send(url);
|
return reply.send(url);
|
||||||
|
@ -82,16 +82,16 @@ export class URLSController {
|
||||||
if (config.urls.vanity && req.body.vanity) {
|
if (config.urls.vanity && req.body.vanity) {
|
||||||
const existingVanity = await this.urls.findOne({
|
const existingVanity = await this.urls.findOne({
|
||||||
where: {
|
where: {
|
||||||
vanity: req.body.vanity,
|
vanity: req.body.vanity
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
if (existingVanity) throw new Error('There is an existing vanity!');
|
if (existingVanity) throw new Error('There is an existing vanity!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await this.users.findOne({
|
const user = await this.users.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: readBaseCookie(req.cookies.zipline),
|
id: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) throw new LoginError('No user');
|
if (!user) throw new LoginError('No user');
|
||||||
|
@ -107,7 +107,7 @@ export class URLSController {
|
||||||
if (this.webhooks.events.includes(WebhookType.SHORTEN))
|
if (this.webhooks.events.includes(WebhookType.SHORTEN))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.shorten.content, {
|
WebhookHelper.sendWebhook(this.webhooks.shorten.content, {
|
||||||
url,
|
url,
|
||||||
host: `${req.protocol}://${req.hostname}${config.urls.route}/`,
|
host: `${req.protocol}://${req.hostname}${config.urls.route}/`
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send(url);
|
return reply.send(url);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
PATCH,
|
PATCH,
|
||||||
FastifyInstanceToken,
|
FastifyInstanceToken,
|
||||||
Inject,
|
Inject,
|
||||||
DELETE,
|
DELETE
|
||||||
} from 'fastify-decorators';
|
} from 'fastify-decorators';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { User } from '../entities/User';
|
import { User } from '../entities/User';
|
||||||
|
@ -14,7 +14,7 @@ import {
|
||||||
UserNotFoundError,
|
UserNotFoundError,
|
||||||
MissingBodyData,
|
MissingBodyData,
|
||||||
LoginError,
|
LoginError,
|
||||||
UserExistsError,
|
UserExistsError
|
||||||
} from '../lib/api/APIErrors';
|
} from '../lib/api/APIErrors';
|
||||||
import { Configuration, ConfigWebhooks } from '../lib/Config';
|
import { Configuration, ConfigWebhooks } from '../lib/Config';
|
||||||
import { Console } from '../lib/logger';
|
import { Console } from '../lib/logger';
|
||||||
|
@ -23,7 +23,7 @@ import {
|
||||||
createBaseCookie,
|
createBaseCookie,
|
||||||
createToken,
|
createToken,
|
||||||
encryptPassword,
|
encryptPassword,
|
||||||
readBaseCookie,
|
readBaseCookie
|
||||||
} from '../lib/Util';
|
} from '../lib/Util';
|
||||||
import { WebhookType, WebhookHelper } from '../lib/Webhooks';
|
import { WebhookType, WebhookHelper } from '../lib/Webhooks';
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ export class UserController {
|
||||||
@GET('/login-status')
|
@GET('/login-status')
|
||||||
async loginStatus(req: FastifyRequest, reply: FastifyReply) {
|
async loginStatus(req: FastifyRequest, reply: FastifyReply) {
|
||||||
return reply.send({
|
return reply.send({
|
||||||
user: !!req.cookies.zipline,
|
user: !!req.cookies.zipline
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +50,11 @@ export class UserController {
|
||||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||||
const user = await this.users.findOne({
|
const user = await this.users.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: readBaseCookie(req.cookies.zipline),
|
id: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
if (!user) throw new UserExistsError('User doesn\'t exist');
|
// eslint-disable-next-line quotes
|
||||||
|
if (!user) throw new UserExistsError("User doesn't exist");
|
||||||
delete user.password;
|
delete user.password;
|
||||||
return reply.send(user);
|
return reply.send(user);
|
||||||
}
|
}
|
||||||
|
@ -67,10 +68,11 @@ export class UserController {
|
||||||
|
|
||||||
const user = await this.users.findOne({
|
const user = await this.users.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: readBaseCookie(req.cookies.zipline),
|
id: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
if (!user) throw new UserExistsError('User doesn\'t exist');
|
// eslint-disable-next-line quotes
|
||||||
|
if (!user) throw new UserExistsError("User doesn't exist");
|
||||||
|
|
||||||
this.logger.verbose(`attempting to save ${user.username} (${user.id})`);
|
this.logger.verbose(`attempting to save ${user.username} (${user.id})`);
|
||||||
user.username = req.body.username;
|
user.username = req.body.username;
|
||||||
|
@ -80,7 +82,7 @@ export class UserController {
|
||||||
this.logger.info(`saved ${user.username} (${user.id})`);
|
this.logger.info(`saved ${user.username} (${user.id})`);
|
||||||
if (this.webhooks.events.includes(WebhookType.USER_EDIT))
|
if (this.webhooks.events.includes(WebhookType.USER_EDIT))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.user_edit.content, {
|
WebhookHelper.sendWebhook(this.webhooks.user_edit.content, {
|
||||||
user,
|
user
|
||||||
});
|
});
|
||||||
|
|
||||||
delete user.password;
|
delete user.password;
|
||||||
|
@ -98,8 +100,8 @@ export class UserController {
|
||||||
|
|
||||||
const user = await this.users.findOne({
|
const user = await this.users.findOne({
|
||||||
where: {
|
where: {
|
||||||
username: req.body.username,
|
username: req.body.username
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
|
@ -115,13 +117,13 @@ export class UserController {
|
||||||
this.logger.verbose(`set cookie for ${user.username} (${user.id})`);
|
this.logger.verbose(`set cookie for ${user.username} (${user.id})`);
|
||||||
reply.setCookie('zipline', createBaseCookie(user.id), {
|
reply.setCookie('zipline', createBaseCookie(user.id), {
|
||||||
path: '/',
|
path: '/',
|
||||||
maxAge: 1036800000,
|
maxAge: 1036800000
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info(`${user.username} (${user.id}) logged in`);
|
this.logger.info(`${user.username} (${user.id}) logged in`);
|
||||||
if (this.webhooks.events.includes(WebhookType.LOGIN))
|
if (this.webhooks.events.includes(WebhookType.LOGIN))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.login.content, {
|
WebhookHelper.sendWebhook(this.webhooks.login.content, {
|
||||||
user,
|
user
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send(user);
|
return reply.send(user);
|
||||||
|
@ -144,8 +146,8 @@ export class UserController {
|
||||||
|
|
||||||
const user = await this.users.findOne({
|
const user = await this.users.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: readBaseCookie(req.cookies.zipline),
|
id: readBaseCookie(req.cookies.zipline)
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) throw new UserNotFoundError('User was not found.');
|
if (!user) throw new UserNotFoundError('User was not found.');
|
||||||
|
@ -159,7 +161,7 @@ export class UserController {
|
||||||
this.logger.info(`reset token ${user.username} (${user.id})`);
|
this.logger.info(`reset token ${user.username} (${user.id})`);
|
||||||
if (this.webhooks.events.includes(WebhookType.TOKEN_RESET))
|
if (this.webhooks.events.includes(WebhookType.TOKEN_RESET))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.token_reset.content, {
|
WebhookHelper.sendWebhook(this.webhooks.token_reset.content, {
|
||||||
user,
|
user
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send({ updated: true });
|
return reply.send({ updated: true });
|
||||||
|
@ -176,7 +178,7 @@ export class UserController {
|
||||||
if (!req.body.password) throw new MissingBodyData('Missing uassword.');
|
if (!req.body.password) throw new MissingBodyData('Missing uassword.');
|
||||||
|
|
||||||
const existing = await this.users.findOne({
|
const existing = await this.users.findOne({
|
||||||
where: { username: req.body.username },
|
where: { username: req.body.username }
|
||||||
});
|
});
|
||||||
if (existing) throw new UserExistsError('User exists already');
|
if (existing) throw new UserExistsError('User exists already');
|
||||||
|
|
||||||
|
@ -193,7 +195,7 @@ export class UserController {
|
||||||
this.logger.info(`created user ${user.username} (${user.id})`);
|
this.logger.info(`created user ${user.username} (${user.id})`);
|
||||||
if (this.webhooks.events.includes(WebhookType.CREATE_USER))
|
if (this.webhooks.events.includes(WebhookType.CREATE_USER))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.create_user.content, {
|
WebhookHelper.sendWebhook(this.webhooks.create_user.content, {
|
||||||
user,
|
user
|
||||||
});
|
});
|
||||||
|
|
||||||
delete user.password;
|
delete user.password;
|
||||||
|
@ -213,7 +215,7 @@ export class UserController {
|
||||||
reply: FastifyReply
|
reply: FastifyReply
|
||||||
) {
|
) {
|
||||||
const existing = await this.users.findOne({
|
const existing = await this.users.findOne({
|
||||||
where: { id: req.params.id },
|
where: { id: req.params.id }
|
||||||
});
|
});
|
||||||
if (!existing) throw new UserExistsError('User doesnt exist');
|
if (!existing) throw new UserExistsError('User doesnt exist');
|
||||||
|
|
||||||
|
@ -222,13 +224,13 @@ export class UserController {
|
||||||
`attempting to delete ${existing.username} (${existing.id})`
|
`attempting to delete ${existing.username} (${existing.id})`
|
||||||
);
|
);
|
||||||
await this.users.delete({
|
await this.users.delete({
|
||||||
id: existing.id,
|
id: existing.id
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info(`deleted ${existing.username} (${existing.id})`);
|
this.logger.info(`deleted ${existing.username} (${existing.id})`);
|
||||||
if (this.webhooks.events.includes(WebhookType.USER_DELETE))
|
if (this.webhooks.events.includes(WebhookType.USER_DELETE))
|
||||||
WebhookHelper.sendWebhook(this.webhooks.user_delete.content, {
|
WebhookHelper.sendWebhook(this.webhooks.user_delete.content, {
|
||||||
user: existing,
|
user: existing
|
||||||
});
|
});
|
||||||
|
|
||||||
return reply.send({ ok: true });
|
return reply.send({ ok: true });
|
||||||
|
|
28
src/index.ts
28
src/index.ts
|
@ -29,7 +29,6 @@ Mode : ${bold(dev ? red('dev') : green('production'))}
|
||||||
Verbose : ${bold(process.env.VERBOSE ? red('yes') : green('no'))}
|
Verbose : ${bold(process.env.VERBOSE ? red('yes') : green('no'))}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
|
||||||
Console.logger(Configuration).verbose('searching for config...');
|
Console.logger(Configuration).verbose('searching for config...');
|
||||||
const config = Configuration.readConfig();
|
const config = Configuration.readConfig();
|
||||||
if (!config) {
|
if (!config) {
|
||||||
|
@ -71,14 +70,14 @@ server.get(`${config.urls.route}/:id`, async function (
|
||||||
|
|
||||||
const urlId = await urls.findOne({
|
const urlId = await urls.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: req.params.id,
|
id: req.params.id
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const urlVanity = await urls.findOne({
|
const urlVanity = await urls.findOne({
|
||||||
where: {
|
where: {
|
||||||
vanity: req.params.id,
|
vanity: req.params.id
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (config.urls.vanity && urlVanity) return reply.redirect(urlVanity.url);
|
if (config.urls.vanity && urlVanity) return reply.redirect(urlVanity.url);
|
||||||
|
@ -95,7 +94,7 @@ server.register(fastifyTypeorm, {
|
||||||
...config.database,
|
...config.database,
|
||||||
entities: [dev ? './src/entities/**/*.ts' : './dist/entities/**/*.js'],
|
entities: [dev ? './src/entities/**/*.ts' : './dist/entities/**/*.js'],
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
logging: false,
|
logging: false
|
||||||
});
|
});
|
||||||
|
|
||||||
server.register(bootstrap, {
|
server.register(bootstrap, {
|
||||||
|
@ -103,23 +102,23 @@ server.register(bootstrap, {
|
||||||
UserController,
|
UserController,
|
||||||
RootController,
|
RootController,
|
||||||
ImagesController,
|
ImagesController,
|
||||||
URLSController,
|
URLSController
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
server.register(fastifyCookies, {
|
server.register(fastifyCookies, {
|
||||||
secret: config.core.secret,
|
secret: config.core.secret
|
||||||
});
|
});
|
||||||
|
|
||||||
server.register(fastifyStatic, {
|
server.register(fastifyStatic, {
|
||||||
root: join(process.cwd(), config.uploader.directory),
|
root: join(process.cwd(), config.uploader.directory),
|
||||||
prefix: config.uploader.route,
|
prefix: config.uploader.route
|
||||||
});
|
});
|
||||||
|
|
||||||
server.register(fastifyStatic, {
|
server.register(fastifyStatic, {
|
||||||
root: join(process.cwd(), 'public'),
|
root: join(process.cwd(), 'public'),
|
||||||
prefix: '/public',
|
prefix: '/public',
|
||||||
decorateReply: false,
|
decorateReply: false
|
||||||
});
|
});
|
||||||
|
|
||||||
server.register(fastifyFavicon);
|
server.register(fastifyFavicon);
|
||||||
|
@ -136,8 +135,11 @@ server.listen(config.core.port, err => {
|
||||||
});
|
});
|
||||||
|
|
||||||
server.addHook('preHandler', async (req, reply) => {
|
server.addHook('preHandler', async (req, reply) => {
|
||||||
if (config.core.blacklisted_ips && config.core.blacklisted_ips.includes(req.ip)) {
|
if (
|
||||||
|
config.core.blacklisted_ips &&
|
||||||
|
config.core.blacklisted_ips.includes(req.ip)
|
||||||
|
) {
|
||||||
await app.render404(req.raw, reply.raw);
|
await app.render404(req.raw, reply.raw);
|
||||||
return (reply.sent = true);
|
return (reply.sent = true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -67,7 +67,8 @@ export class Configuration {
|
||||||
try {
|
try {
|
||||||
const data = readFileSync(resolve(process.cwd(), 'Zipline.toml'), 'utf8');
|
const data = readFileSync(resolve(process.cwd(), 'Zipline.toml'), 'utf8');
|
||||||
const parsed = parse(data);
|
const parsed = parse(data);
|
||||||
if (parsed.webhooks) parsed.webhooks.events = WebhookHelper.convert(parsed.webhooks.events);
|
if (parsed.webhooks)
|
||||||
|
parsed.webhooks.events = WebhookHelper.convert(parsed.webhooks.events);
|
||||||
return parsed;
|
return parsed;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -25,7 +25,7 @@ export enum WebhookParseTokens {
|
||||||
USER_ADMIN = '{user_admin}',
|
USER_ADMIN = '{user_admin}',
|
||||||
URL_ID = '{url_id}',
|
URL_ID = '{url_id}',
|
||||||
URL_URL = '{url}',
|
URL_URL = '{url}',
|
||||||
URL_VANITY = '{url_vanity}',
|
URL_VANITY = '{url_vanity}'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WebhookData {
|
export interface WebhookData {
|
||||||
|
@ -77,12 +77,12 @@ export class WebhookHelper {
|
||||||
await fetch(config.webhooks.url, {
|
await fetch(config.webhooks.url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: config.webhooks.username,
|
username: config.webhooks.username,
|
||||||
content: WebhookHelper.parseContent(content, data),
|
content: WebhookHelper.parseContent(content, data)
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Console.logger(WebhookHelper).error(e);
|
Console.logger(WebhookHelper).error(e);
|
||||||
|
|
|
@ -16,7 +16,7 @@ export enum ConsoleLevel {
|
||||||
ERROR,
|
ERROR,
|
||||||
INFO,
|
INFO,
|
||||||
TRACE,
|
TRACE,
|
||||||
VERBOSE,
|
VERBOSE
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Console {
|
export class Console {
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
import { ConsoleLevel } from '.';
|
import { ConsoleLevel } from '.';
|
||||||
import {
|
import { blue, red, reset, white, yellow } from '@dicedtomato/colors';
|
||||||
blue,
|
|
||||||
red,
|
|
||||||
reset,
|
|
||||||
white,
|
|
||||||
yellow
|
|
||||||
} from '@dicedtomato/colors';
|
|
||||||
|
|
||||||
|
|
||||||
export interface Formatter {
|
export interface Formatter {
|
||||||
format(
|
format(
|
||||||
|
@ -24,7 +17,7 @@ export class DefaultFormatter implements Formatter {
|
||||||
1: red('error') + ':',
|
1: red('error') + ':',
|
||||||
2: blue('info') + ':',
|
2: blue('info') + ':',
|
||||||
3: white('trace') + ':',
|
3: white('trace') + ':',
|
||||||
4: yellow('verbose') + ':',
|
4: yellow('verbose') + ':'
|
||||||
}[level];
|
}[level];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,4 +31,4 @@ export class DefaultFormatter implements Formatter {
|
||||||
level
|
level
|
||||||
)} ${reset(message)}`;
|
)} ${reset(message)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,30 +4,30 @@ const darkTheme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
type: 'dark',
|
type: 'dark',
|
||||||
primary: {
|
primary: {
|
||||||
main: '#fff',
|
main: '#fff'
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: '#4a5bb0',
|
main: '#4a5bb0'
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
default: '#111111',
|
default: '#111111',
|
||||||
paper: '#000000',
|
paper: '#000000'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
overrides: {
|
overrides: {
|
||||||
MuiListItem: {
|
MuiListItem: {
|
||||||
root: {
|
root: {
|
||||||
'&$selected': {
|
'&$selected': {
|
||||||
backgroundColor: '#1F1F1F',
|
backgroundColor: '#1F1F1F'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
MuiCard: {
|
MuiCard: {
|
||||||
root: {
|
root: {
|
||||||
backgroundColor: '#080808',
|
backgroundColor: '#080808'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default darkTheme;
|
export default darkTheme;
|
||||||
|
|
|
@ -36,7 +36,7 @@ function MyApp({ Component, pageProps }) {
|
||||||
|
|
||||||
MyApp.propTypes = {
|
MyApp.propTypes = {
|
||||||
Component: PropTypes.elementType.isRequired,
|
Component: PropTypes.elementType.isRequired,
|
||||||
pageProps: PropTypes.object.isRequired,
|
pageProps: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MyApp;
|
export default MyApp;
|
||||||
|
|
|
@ -44,9 +44,10 @@ MyDocument.getInitialProps = async ctx => {
|
||||||
const sheets = new ServerStyleSheets();
|
const sheets = new ServerStyleSheets();
|
||||||
const originalRenderPage = ctx.renderPage;
|
const originalRenderPage = ctx.renderPage;
|
||||||
|
|
||||||
ctx.renderPage = () => originalRenderPage({
|
ctx.renderPage = () =>
|
||||||
enhanceApp: App => props => sheets.collect(<App {...props} />),
|
originalRenderPage({
|
||||||
});
|
enhanceApp: App => props => sheets.collect(<App {...props} />)
|
||||||
|
});
|
||||||
|
|
||||||
const initialProps = await Document.getInitialProps(ctx);
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
return {
|
return {
|
||||||
|
@ -54,7 +55,7 @@ MyDocument.getInitialProps = async ctx => {
|
||||||
config: Configuration.readConfig(),
|
config: Configuration.readConfig(),
|
||||||
styles: [
|
styles: [
|
||||||
...React.Children.toArray(initialProps.styles),
|
...React.Children.toArray(initialProps.styles),
|
||||||
sheets.getStyleElement(),
|
sheets.getStyleElement()
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,24 +20,22 @@ import { ConfigUploader } from '../lib/Config';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
margin: {
|
margin: {
|
||||||
margin: '5px',
|
margin: '5px'
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
border: '1px solid #1f1f1f',
|
border: '1px solid #1f1f1f',
|
||||||
padding: '10px',
|
padding: '10px'
|
||||||
},
|
},
|
||||||
backdrop: {
|
backdrop: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
color: '#fff',
|
color: '#fff'
|
||||||
},
|
},
|
||||||
gridList: {
|
gridList: {
|
||||||
width: theme.zIndex.drawer + 1,
|
width: theme.zIndex.drawer + 1,
|
||||||
height: 450,
|
height: 450
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function Images({ config }: { config: ConfigUploader }) {
|
export default function Images({ config }: { config: ConfigUploader }) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -89,7 +87,7 @@ export default function Images({ config }: { config: ConfigUploader }) {
|
||||||
if (!selectedImage) return;
|
if (!selectedImage) return;
|
||||||
const d = await (
|
const d = await (
|
||||||
await fetch(`/api/images/${selectedImage.id}`, {
|
await fetch(`/api/images/${selectedImage.id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE'
|
||||||
})
|
})
|
||||||
).json();
|
).json();
|
||||||
if (!d.error) {
|
if (!d.error) {
|
||||||
|
@ -138,11 +136,11 @@ export default function Images({ config }: { config: ConfigUploader }) {
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'center',
|
vertical: 'center',
|
||||||
horizontal: 'center',
|
horizontal: 'center'
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: 'center',
|
vertical: 'center',
|
||||||
horizontal: 'center',
|
horizontal: 'center'
|
||||||
}}
|
}}
|
||||||
onClose={() => setAnchorEl(null)}
|
onClose={() => setAnchorEl(null)}
|
||||||
disableRestoreFocus
|
disableRestoreFocus
|
||||||
|
|
|
@ -16,16 +16,16 @@ import { ConfigUploader } from '../lib/Config';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
margin: {
|
margin: {
|
||||||
margin: '5px',
|
margin: '5px'
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
border: '1px solid #1f1f1f',
|
border: '1px solid #1f1f1f',
|
||||||
padding: '10px',
|
padding: '10px'
|
||||||
},
|
},
|
||||||
backdrop: {
|
backdrop: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
color: '#fff',
|
color: '#fff'
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function Index({ config }: { config: ConfigUploader }) {
|
export default function Index({ config }: { config: ConfigUploader }) {
|
||||||
|
@ -86,9 +86,8 @@ export default function Index({ config }: { config: ConfigUploader }) {
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
) : null
|
) : null}
|
||||||
}
|
</UI>
|
||||||
</UI >
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <UIPlaceholder />;
|
return <UIPlaceholder />;
|
||||||
|
|
|
@ -17,19 +17,19 @@ import { store } from '../store';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
margin: {
|
margin: {
|
||||||
margin: '5px',
|
margin: '5px'
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
border: '1px solid #1f1f1f',
|
border: '1px solid #1f1f1f',
|
||||||
padding: '10px',
|
padding: '10px'
|
||||||
},
|
},
|
||||||
backdrop: {
|
backdrop: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
color: '#fff',
|
color: '#fff'
|
||||||
},
|
},
|
||||||
tableBorder: {
|
tableBorder: {
|
||||||
borderColor: '#130929',
|
borderColor: '#130929'
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
|
@ -68,16 +68,10 @@ export default function Index() {
|
||||||
<TableCell className={classes.tableBorder}>
|
<TableCell className={classes.tableBorder}>
|
||||||
User
|
User
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell className={classes.tableBorder} align='right'>
|
||||||
className={classes.tableBorder}
|
|
||||||
align='right'
|
|
||||||
>
|
|
||||||
Images
|
Images
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell className={classes.tableBorder} align='right'>
|
||||||
className={classes.tableBorder}
|
|
||||||
align='right'
|
|
||||||
>
|
|
||||||
Views
|
Views
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
@ -20,16 +20,16 @@ import { ConfigUploader } from '../lib/Config';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
margin: {
|
margin: {
|
||||||
margin: '5px',
|
margin: '5px'
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
border: '1px solid #1f1f1f',
|
border: '1px solid #1f1f1f',
|
||||||
padding: '10px',
|
padding: '10px'
|
||||||
},
|
},
|
||||||
backdrop: {
|
backdrop: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
color: '#fff',
|
color: '#fff'
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function Urls({ config }: { config: ConfigUploader }) {
|
export default function Urls({ config }: { config: ConfigUploader }) {
|
||||||
|
@ -67,7 +67,7 @@ export default function Urls({ config }: { config: ConfigUploader }) {
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'center',
|
horizontal: 'center'
|
||||||
}}
|
}}
|
||||||
open={alertOpen}
|
open={alertOpen}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
|
|
|
@ -27,19 +27,19 @@ import { makeStyles } from '@material-ui/core';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
margin: {
|
margin: {
|
||||||
margin: '5px',
|
margin: '5px'
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
border: '1px solid #1f1f1f',
|
border: '1px solid #1f1f1f',
|
||||||
padding: '10px',
|
padding: '10px'
|
||||||
},
|
},
|
||||||
field: {
|
field: {
|
||||||
width: '100%',
|
width: '100%'
|
||||||
},
|
},
|
||||||
backdrop: {
|
backdrop: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
color: '#fff',
|
color: '#fff'
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
|
@ -82,7 +82,9 @@ export default function Index() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteUserThenClose = async () => {
|
const deleteUserThenClose = async () => {
|
||||||
const d = await (await fetch('/api/user/' + user.id, { method: 'DELETE' })).json();
|
const d = await (
|
||||||
|
await fetch('/api/user/' + user.id, { method: 'DELETE' })
|
||||||
|
).json();
|
||||||
if (!d.error) {
|
if (!d.error) {
|
||||||
setDeleteOpen(false);
|
setDeleteOpen(false);
|
||||||
setAlertOpen(true);
|
setAlertOpen(true);
|
||||||
|
@ -92,13 +94,17 @@ export default function Index() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const createUserThenClose = async () => {
|
const createUserThenClose = async () => {
|
||||||
const d = await (await fetch('/api/user/create', {
|
const d = await (
|
||||||
headers: { 'Content-Type': 'application/json' },
|
await fetch('/api/user/create', {
|
||||||
method: 'POST',
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
method: 'POST',
|
||||||
username, password, administrator
|
body: JSON.stringify({
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
administrator
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})).json();
|
).json();
|
||||||
if (!d.error) {
|
if (!d.error) {
|
||||||
setCreateOpen(false);
|
setCreateOpen(false);
|
||||||
setAlertOpen(true);
|
setAlertOpen(true);
|
||||||
|
@ -115,7 +121,7 @@ export default function Index() {
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'center',
|
horizontal: 'center'
|
||||||
}}
|
}}
|
||||||
open={alertOpen}
|
open={alertOpen}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
|
@ -167,8 +173,14 @@ export default function Index() {
|
||||||
onChange={e => setPassword(e.target.value)}
|
onChange={e => setPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={<Switch checked={administrator} onChange={() => setAdministrator(!administrator)} name="admin" />}
|
control={
|
||||||
label="Administrator"
|
<Switch
|
||||||
|
checked={administrator}
|
||||||
|
onChange={() => setAdministrator(!administrator)}
|
||||||
|
name='admin'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label='Administrator'
|
||||||
/>
|
/>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
@ -185,7 +197,10 @@ export default function Index() {
|
||||||
<Paper elevation={3} className={classes.padding}>
|
<Paper elevation={3} className={classes.padding}>
|
||||||
<Typography variant='h5'>
|
<Typography variant='h5'>
|
||||||
User
|
User
|
||||||
<IconButton aria-label='Create User' onClick={() => setCreateOpen(true)}>
|
<IconButton
|
||||||
|
aria-label='Create User'
|
||||||
|
onClick={() => setCreateOpen(true)}
|
||||||
|
>
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -203,7 +218,9 @@ export default function Index() {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
title={`${u.username} (${u.id})`}
|
title={`${u.username} (${u.id})`}
|
||||||
subheader={`${u.administrator ? 'Administrator' : 'User'}`}
|
subheader={`${
|
||||||
|
u.administrator ? 'Administrator' : 'User'
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -16,7 +16,7 @@ export interface State {
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
user: null,
|
user: null,
|
||||||
loading: true,
|
loading: true
|
||||||
};
|
};
|
||||||
|
|
||||||
export function reducer(state: State = initialState, action) {
|
export function reducer(state: State = initialState, action) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { reducer } from './reducer';
|
||||||
const persistedReducer = persistReducer(
|
const persistedReducer = persistReducer(
|
||||||
{
|
{
|
||||||
key: 'root',
|
key: 'root',
|
||||||
storage,
|
storage
|
||||||
},
|
},
|
||||||
reducer
|
reducer
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue