This commit is contained in:
diced-tomato 2020-10-26 13:52:05 -07:00
parent e6efff28dd
commit d7c7c9da18
27 changed files with 244 additions and 232 deletions

View file

@ -1,25 +1,25 @@
module.exports = {
env: {
es2021: true,
node: true,
node: true
},
settings: {
react: {
version: 'detect',
},
version: 'detect'
}
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
jsx: true
},
ecmaVersion: 12,
sourceType: 'module',
sourceType: 'module'
},
plugins: ['react', '@typescript-eslint'],
rules: {
@ -29,5 +29,6 @@ module.exports = {
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always'],
},
'comma-dangle': ['error', 'never']
}
};

View file

@ -2,5 +2,7 @@
"$schema": "http://json.schemastore.org/prettierrc",
"jsxSingleQuote": true,
"singleQuote": true,
"arrowParens": "avoid"
"arrowParens": "avoid",
"trailingComma": "none",
"endOfLine": "lf"
}

View file

@ -1,16 +1,18 @@
![](https://raw.githubusercontent.com/ZiplineProject/zipline/next/public/zipline.png)
---
## ![](https://raw.githubusercontent.com/ZiplineProject/zipline/next/public/zipline.png)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/29a3d02f0df447acadd721d93229d072)](https://app.codacy.com/gh/ZiplineProject/zipline?utm_source=github.com&utm_medium=referral&utm_content=ZiplineProject/zipline&utm_campaign=Badge_Grade)
# ZiplineNext
Speed & reliable
- Configurable
- Fast (API)
- Built with Next.js & React
- Support for multible database types (mongo soon)
- Support for multible database types (mongo soon)
# Documentation
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._

View file

@ -10,7 +10,7 @@ const base = {
description: 'My Zipline Server',
thumbnail:
'https://github.githubassets.com/images/modules/open_graph/github-mark.png',
color: '#128377',
color: '#128377'
},
core: { secret: 'my-secret', port: 3000 },
uploader: {
@ -18,9 +18,9 @@ const base = {
route: '/u',
length: 6,
original: false,
blacklisted: [],
blacklisted: []
},
urls: { route: '/s', length: 4, vanity: false },
urls: { route: '/s', length: 4, vanity: false }
};
(async () => {
@ -37,34 +37,34 @@ const base = {
{ name: 'mssql' },
{ name: 'sqlite' },
{ name: 'sqlite3' },
{ name: 'mongodb', extra: 'No support yet' },
],
{ name: 'mongodb', extra: 'No support yet' }
]
},
{
type: 'input',
name: 'host',
message: 'Database Host',
message: 'Database Host'
},
{
type: 'number',
name: 'port',
message: 'Database Port',
message: 'Database Port'
},
{
type: 'input',
name: 'database',
message: 'Database Name',
message: 'Database Name'
},
{
type: 'input',
name: 'username',
message: 'Database User',
message: 'Database User'
},
{
type: 'password',
name: 'password',
message: 'Database Password',
},
message: 'Database Password'
}
]);
console.log('\nCore\n');
@ -73,13 +73,13 @@ const base = {
{
type: 'input',
name: 'secret',
message: 'Secret (this must be secure)',
message: 'Secret (this must be secure)'
},
{
type: 'number',
name: 'port',
message: 'Serve on Port',
},
message: 'Serve on Port'
}
]);
console.log('\nUploader\n');
@ -88,13 +88,13 @@ const base = {
{
type: 'input',
name: 'directory',
message: 'Uploads Directory',
message: 'Uploads Directory'
},
{
type: 'confirm',
name: 'original',
message: 'Keep Original?',
},
message: 'Keep Original?'
}
]);
console.log('\nURLs\n');
@ -103,8 +103,8 @@ const base = {
{
type: 'confirm',
name: 'vanity',
message: 'Allow vanity URLs',
},
message: 'Allow vanity URLs'
}
]);
const config = {
@ -112,7 +112,7 @@ const base = {
meta: { ...base.meta },
core: { ...base.core, ...core },
uploader: { ...base.uploader, ...uploader },
urls: { ...base.urls, ...urls },
urls: { ...base.urls, ...urls }
};
writeFileSync('Zipline.toml', stringify(config));
})();

View file

@ -14,11 +14,11 @@ import { useDispatch } from 'react-redux';
const useStyles = makeStyles({
field: {
width: '100%',
width: '100%'
},
padding: {
padding: '10px',
},
padding: '10px'
}
});
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
@ -40,7 +40,7 @@ export default function Login() {
await fetch('/api/user/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
body: JSON.stringify({ username, password })
})
).json();
if (!d.error) {
@ -55,7 +55,7 @@ export default function Login() {
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
horizontal: 'center'
}}
open={open}
autoHideDuration={6000}

View file

@ -14,17 +14,17 @@ import { useDispatch } from 'react-redux';
const useStyles = makeStyles({
margin: {
margin: '5px',
margin: '5px'
},
padding: {
padding: '10px',
padding: '10px'
},
field: {
width: '100%',
width: '100%'
},
button: {
marginLeft: 'auto',
},
marginLeft: 'auto'
}
});
export default function ManageUser() {
@ -40,7 +40,7 @@ export default function ManageUser() {
await fetch('/api/user', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
body: JSON.stringify({ username, password })
})
).json();
if (!d.error) {
@ -53,7 +53,7 @@ export default function ManageUser() {
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
horizontal: 'center'
}}
open={alertOpen}
autoHideDuration={6000}
@ -96,4 +96,4 @@ export default function ManageUser() {
</Card>
</React.Fragment>
);
}
}

View file

@ -41,14 +41,14 @@ const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
flexGrow: 1,
flexGrow: 1
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
flexShrink: 0
},
outlineColor: '#fff',
outlineColor: '#fff'
},
appBar: {
display: 'flex',
@ -56,31 +56,31 @@ const useStyles = makeStyles(theme => ({
color: '#fff',
[theme.breakpoints.up('sm')]: {
width: 'calc(100%)',
marginLeft: drawerWidth,
marginLeft: drawerWidth
},
borderBottom: '1px solid #1f1f1f',
borderBottom: '1px solid #1f1f1f'
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none',
},
display: 'none'
}
},
rightButton: {
marginLeft: 'auto',
marginLeft: 'auto'
},
// necessary for content to be below app bar
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
width: drawerWidth
},
content: {
flexGrow: 1,
padding: theme.spacing(1),
padding: theme.spacing(1)
},
menuIcon: {
marginRight: '10px',
},
marginRight: '10px'
}
}));
export default function UI({ children }) {
@ -175,12 +175,12 @@ export default function UI({ children }) {
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
horizontal: 'right'
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
horizontal: 'right'
}}
open={open}
onClose={() => setAnchorEl(null)}
@ -271,7 +271,7 @@ export default function UI({ children }) {
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
horizontal: 'center'
}}
open={alertOpen}
autoHideDuration={6000}
@ -349,10 +349,10 @@ export default function UI({ children }) {
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
paper: classes.drawerPaper
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
keepMounted: true // Better open performance on mobile.
}}
>
{drawer}
@ -361,7 +361,7 @@ export default function UI({ children }) {
<Hidden xsDown implementation='css'>
<Drawer
classes={{
paper: classes.drawerPaper,
paper: classes.drawerPaper
}}
variant='permanent'
open

View file

@ -15,41 +15,41 @@ import { makeStyles, useTheme } from '@material-ui/core/styles';
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
display: 'flex'
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: 240,
flexShrink: 0,
},
flexShrink: 0
}
},
appBar: {
[theme.breakpoints.up('sm')]: {
width: 'calc(100%)',
marginLeft: 240,
},
marginLeft: 240
}
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none',
},
display: 'none'
}
},
rightButton: {
marginLeft: 'auto',
marginLeft: 'auto'
},
// necessary for content to be below app bar
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: 240,
width: 240
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
padding: theme.spacing(3)
},
fullWidth: {
width: '100%',
},
width: '100%'
}
}));
export default function UIPlaceholder() {
@ -132,10 +132,10 @@ export default function UIPlaceholder() {
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
paper: classes.drawerPaper
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
keepMounted: true // Better open performance on mobile.
}}
>
{drawer}
@ -144,7 +144,7 @@ export default function UIPlaceholder() {
<Hidden xsDown implementation='css'>
<Drawer
classes={{
paper: classes.drawerPaper,
paper: classes.drawerPaper
}}
variant='permanent'
open

View file

@ -4,7 +4,7 @@ import {
FastifyInstanceToken,
Inject,
GET,
DELETE,
DELETE
} from 'fastify-decorators';
import { Repository } from 'typeorm';
import { Image } from '../entities/Image';
@ -30,8 +30,8 @@ export class ImagesController {
const images = await this.images.find({
where: {
user: readBaseCookie(req.cookies.zipline),
},
user: readBaseCookie(req.cookies.zipline)
}
});
return reply.send(images);
@ -47,21 +47,21 @@ export class ImagesController {
const image = await this.images.findOne({
where: {
user: readBaseCookie(req.cookies.zipline),
id: req.params.id,
},
id: req.params.id
}
});
if (!image) throw new Error('No image');
this.images.delete({
id: req.params.id,
id: req.params.id
});
Console.logger(Image).info(`image ${image.id} was deleted`);
if (this.webhooks.events.includes(WebhookType.DELETE_IMAGE))
WebhookHelper.sendWebhook(this.webhooks.upload.content, {
image,
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`,
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`
});
return reply.send(image);
@ -73,8 +73,8 @@ export class ImagesController {
const images = await this.images.find({
where: {
user: readBaseCookie(req.cookies.zipline),
},
user: readBaseCookie(req.cookies.zipline)
}
});
return reply.send(images.slice(1).slice(-3).reverse());
@ -86,8 +86,8 @@ export class ImagesController {
const images = await this.images.find({
where: {
user: readBaseCookie(req.cookies.zipline),
},
user: readBaseCookie(req.cookies.zipline)
}
});
function chunk(array: Image[], size: number) {

View file

@ -4,7 +4,7 @@ import {
POST,
FastifyInstanceToken,
Inject,
GET,
GET
} from 'fastify-decorators';
import { Multipart } from 'fastify-multipart';
import { createWriteStream, existsSync, mkdirSync } from 'fs';
@ -62,7 +62,7 @@ export class RootController {
for (const user of users) {
const usersImages = await this.images.find({
where: { user: user.id },
where: { user: user.id }
});
lb.push({
@ -70,14 +70,14 @@ export class RootController {
images: usersImages.length,
views: usersImages
.map(x => x.views)
.reduce((a, b) => Number(a) + Number(b), 0),
.reduce((a, b) => Number(a) + Number(b), 0)
});
}
return reply.send({
images: images.length,
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({
where: {
token: req.headers.authorization,
},
token: req.headers.authorization
}
});
if (!user) return new AuthError('Incorrect token!');
@ -125,7 +125,7 @@ export class RootController {
if (this.webhooks.events.includes(WebhookType.UPLOAD))
WebhookHelper.sendWebhook(this.webhooks.upload.content, {
image,
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`,
host: `${req.protocol}://${req.hostname}${config.uploader.route}/`
});
reply.send(

View file

@ -5,7 +5,7 @@ import {
Inject,
GET,
DELETE,
POST,
POST
} from 'fastify-decorators';
import { Repository } from 'typeorm';
import { URL } from '../entities/URL';
@ -34,8 +34,8 @@ export class URLSController {
const all = await this.urls.find({
where: {
user: readBaseCookie(req.cookies.zipline),
},
user: readBaseCookie(req.cookies.zipline)
}
});
return reply.send(all);
@ -51,22 +51,22 @@ export class URLSController {
const url = await this.urls.findOne({
where: {
user: readBaseCookie(req.cookies.zipline),
id: req.params.id,
},
id: req.params.id
}
});
if (!url) throw new Error('No url');
this.logger.verbose(`attempting to delete url ${url.id}`);
this.urls.delete({
id: req.params.id,
id: req.params.id
});
this.logger.info(`url ${url.id} was deleted`);
if (this.webhooks.events.includes(WebhookType.DELETE_URL))
WebhookHelper.sendWebhook(this.webhooks.delete_url.content, {
url,
host: `${req.protocol}://${req.hostname}${config.urls.route}/`,
host: `${req.protocol}://${req.hostname}${config.urls.route}/`
});
return reply.send(url);
@ -82,16 +82,16 @@ export class URLSController {
if (config.urls.vanity && req.body.vanity) {
const existingVanity = await this.urls.findOne({
where: {
vanity: req.body.vanity,
},
vanity: req.body.vanity
}
});
if (existingVanity) throw new Error('There is an existing vanity!');
}
const user = await this.users.findOne({
where: {
id: readBaseCookie(req.cookies.zipline),
},
id: readBaseCookie(req.cookies.zipline)
}
});
if (!user) throw new LoginError('No user');
@ -107,7 +107,7 @@ export class URLSController {
if (this.webhooks.events.includes(WebhookType.SHORTEN))
WebhookHelper.sendWebhook(this.webhooks.shorten.content, {
url,
host: `${req.protocol}://${req.hostname}${config.urls.route}/`,
host: `${req.protocol}://${req.hostname}${config.urls.route}/`
});
return reply.send(url);

View file

@ -6,7 +6,7 @@ import {
PATCH,
FastifyInstanceToken,
Inject,
DELETE,
DELETE
} from 'fastify-decorators';
import { Repository } from 'typeorm';
import { User } from '../entities/User';
@ -14,7 +14,7 @@ import {
UserNotFoundError,
MissingBodyData,
LoginError,
UserExistsError,
UserExistsError
} from '../lib/api/APIErrors';
import { Configuration, ConfigWebhooks } from '../lib/Config';
import { Console } from '../lib/logger';
@ -23,7 +23,7 @@ import {
createBaseCookie,
createToken,
encryptPassword,
readBaseCookie,
readBaseCookie
} from '../lib/Util';
import { WebhookType, WebhookHelper } from '../lib/Webhooks';
@ -41,7 +41,7 @@ export class UserController {
@GET('/login-status')
async loginStatus(req: FastifyRequest, reply: FastifyReply) {
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.');
const user = await this.users.findOne({
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;
return reply.send(user);
}
@ -67,10 +68,11 @@ export class UserController {
const user = await this.users.findOne({
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})`);
user.username = req.body.username;
@ -80,7 +82,7 @@ export class UserController {
this.logger.info(`saved ${user.username} (${user.id})`);
if (this.webhooks.events.includes(WebhookType.USER_EDIT))
WebhookHelper.sendWebhook(this.webhooks.user_edit.content, {
user,
user
});
delete user.password;
@ -98,8 +100,8 @@ export class UserController {
const user = await this.users.findOne({
where: {
username: req.body.username,
},
username: req.body.username
}
});
if (!user)
@ -115,13 +117,13 @@ export class UserController {
this.logger.verbose(`set cookie for ${user.username} (${user.id})`);
reply.setCookie('zipline', createBaseCookie(user.id), {
path: '/',
maxAge: 1036800000,
maxAge: 1036800000
});
this.logger.info(`${user.username} (${user.id}) logged in`);
if (this.webhooks.events.includes(WebhookType.LOGIN))
WebhookHelper.sendWebhook(this.webhooks.login.content, {
user,
user
});
return reply.send(user);
@ -144,8 +146,8 @@ export class UserController {
const user = await this.users.findOne({
where: {
id: readBaseCookie(req.cookies.zipline),
},
id: readBaseCookie(req.cookies.zipline)
}
});
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})`);
if (this.webhooks.events.includes(WebhookType.TOKEN_RESET))
WebhookHelper.sendWebhook(this.webhooks.token_reset.content, {
user,
user
});
return reply.send({ updated: true });
@ -176,7 +178,7 @@ export class UserController {
if (!req.body.password) throw new MissingBodyData('Missing uassword.');
const existing = await this.users.findOne({
where: { username: req.body.username },
where: { username: req.body.username }
});
if (existing) throw new UserExistsError('User exists already');
@ -193,7 +195,7 @@ export class UserController {
this.logger.info(`created user ${user.username} (${user.id})`);
if (this.webhooks.events.includes(WebhookType.CREATE_USER))
WebhookHelper.sendWebhook(this.webhooks.create_user.content, {
user,
user
});
delete user.password;
@ -213,7 +215,7 @@ export class UserController {
reply: FastifyReply
) {
const existing = await this.users.findOne({
where: { id: req.params.id },
where: { id: req.params.id }
});
if (!existing) throw new UserExistsError('User doesnt exist');
@ -222,13 +224,13 @@ export class UserController {
`attempting to delete ${existing.username} (${existing.id})`
);
await this.users.delete({
id: existing.id,
id: existing.id
});
this.logger.info(`deleted ${existing.username} (${existing.id})`);
if (this.webhooks.events.includes(WebhookType.USER_DELETE))
WebhookHelper.sendWebhook(this.webhooks.user_delete.content, {
user: existing,
user: existing
});
return reply.send({ ok: true });

View file

@ -29,7 +29,6 @@ Mode : ${bold(dev ? red('dev') : green('production'))}
Verbose : ${bold(process.env.VERBOSE ? red('yes') : green('no'))}
`);
Console.logger(Configuration).verbose('searching for config...');
const config = Configuration.readConfig();
if (!config) {
@ -71,14 +70,14 @@ server.get(`${config.urls.route}/:id`, async function (
const urlId = await urls.findOne({
where: {
id: req.params.id,
},
id: req.params.id
}
});
const urlVanity = await urls.findOne({
where: {
vanity: req.params.id,
},
vanity: req.params.id
}
});
if (config.urls.vanity && urlVanity) return reply.redirect(urlVanity.url);
@ -95,7 +94,7 @@ server.register(fastifyTypeorm, {
...config.database,
entities: [dev ? './src/entities/**/*.ts' : './dist/entities/**/*.js'],
synchronize: true,
logging: false,
logging: false
});
server.register(bootstrap, {
@ -103,23 +102,23 @@ server.register(bootstrap, {
UserController,
RootController,
ImagesController,
URLSController,
],
URLSController
]
});
server.register(fastifyCookies, {
secret: config.core.secret,
secret: config.core.secret
});
server.register(fastifyStatic, {
root: join(process.cwd(), config.uploader.directory),
prefix: config.uploader.route,
prefix: config.uploader.route
});
server.register(fastifyStatic, {
root: join(process.cwd(), 'public'),
prefix: '/public',
decorateReply: false,
decorateReply: false
});
server.register(fastifyFavicon);
@ -136,8 +135,11 @@ server.listen(config.core.port, err => {
});
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);
return (reply.sent = true);
}
});
});

View file

@ -67,7 +67,8 @@ export class Configuration {
try {
const data = readFileSync(resolve(process.cwd(), 'Zipline.toml'), 'utf8');
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;
} catch (e) {
return null;

View file

@ -25,7 +25,7 @@ export enum WebhookParseTokens {
USER_ADMIN = '{user_admin}',
URL_ID = '{url_id}',
URL_URL = '{url}',
URL_VANITY = '{url_vanity}',
URL_VANITY = '{url_vanity}'
}
export interface WebhookData {
@ -77,12 +77,12 @@ export class WebhookHelper {
await fetch(config.webhooks.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: config.webhooks.username,
content: WebhookHelper.parseContent(content, data),
}),
content: WebhookHelper.parseContent(content, data)
})
});
} catch (e) {
Console.logger(WebhookHelper).error(e);

View file

@ -16,7 +16,7 @@ export enum ConsoleLevel {
ERROR,
INFO,
TRACE,
VERBOSE,
VERBOSE
}
export class Console {

View file

@ -1,12 +1,5 @@
import { ConsoleLevel } from '.';
import {
blue,
red,
reset,
white,
yellow
} from '@dicedtomato/colors';
import { blue, red, reset, white, yellow } from '@dicedtomato/colors';
export interface Formatter {
format(
@ -24,7 +17,7 @@ export class DefaultFormatter implements Formatter {
1: red('error') + ':',
2: blue('info') + ':',
3: white('trace') + ':',
4: yellow('verbose') + ':',
4: yellow('verbose') + ':'
}[level];
}
@ -38,4 +31,4 @@ export class DefaultFormatter implements Formatter {
level
)} ${reset(message)}`;
}
}
}

View file

@ -4,30 +4,30 @@ const darkTheme = createMuiTheme({
palette: {
type: 'dark',
primary: {
main: '#fff',
main: '#fff'
},
secondary: {
main: '#4a5bb0',
main: '#4a5bb0'
},
background: {
default: '#111111',
paper: '#000000',
},
paper: '#000000'
}
},
overrides: {
MuiListItem: {
root: {
'&$selected': {
backgroundColor: '#1F1F1F',
},
},
backgroundColor: '#1F1F1F'
}
}
},
MuiCard: {
root: {
backgroundColor: '#080808',
},
},
},
backgroundColor: '#080808'
}
}
}
});
export default darkTheme;

View file

@ -36,7 +36,7 @@ function MyApp({ Component, pageProps }) {
MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object.isRequired,
pageProps: PropTypes.object.isRequired
};
export default MyApp;

View file

@ -44,9 +44,10 @@ MyDocument.getInitialProps = async ctx => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () => originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />),
});
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />)
});
const initialProps = await Document.getInitialProps(ctx);
return {
@ -54,7 +55,7 @@ MyDocument.getInitialProps = async ctx => {
config: Configuration.readConfig(),
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
sheets.getStyleElement()
]
};
};
};

View file

@ -20,24 +20,22 @@ import { ConfigUploader } from '../lib/Config';
const useStyles = makeStyles(theme => ({
margin: {
margin: '5px',
margin: '5px'
},
padding: {
border: '1px solid #1f1f1f',
padding: '10px',
padding: '10px'
},
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
color: '#fff'
},
gridList: {
width: theme.zIndex.drawer + 1,
height: 450,
},
height: 450
}
}));
export default function Images({ config }: { config: ConfigUploader }) {
const classes = useStyles();
const router = useRouter();
@ -89,7 +87,7 @@ export default function Images({ config }: { config: ConfigUploader }) {
if (!selectedImage) return;
const d = await (
await fetch(`/api/images/${selectedImage.id}`, {
method: 'DELETE',
method: 'DELETE'
})
).json();
if (!d.error) {
@ -138,11 +136,11 @@ export default function Images({ config }: { config: ConfigUploader }) {
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'center',
horizontal: 'center',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'center',
horizontal: 'center',
horizontal: 'center'
}}
onClose={() => setAnchorEl(null)}
disableRestoreFocus

View file

@ -16,16 +16,16 @@ import { ConfigUploader } from '../lib/Config';
const useStyles = makeStyles(theme => ({
margin: {
margin: '5px',
margin: '5px'
},
padding: {
border: '1px solid #1f1f1f',
padding: '10px',
padding: '10px'
},
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
},
color: '#fff'
}
}));
export default function Index({ config }: { config: ConfigUploader }) {
@ -86,9 +86,8 @@ export default function Index({ config }: { config: ConfigUploader }) {
})}
</Grid>
</Paper>
) : null
}
</UI >
) : null}
</UI>
);
}
return <UIPlaceholder />;

View file

@ -17,19 +17,19 @@ import { store } from '../store';
const useStyles = makeStyles(theme => ({
margin: {
margin: '5px',
margin: '5px'
},
padding: {
border: '1px solid #1f1f1f',
padding: '10px',
padding: '10px'
},
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
color: '#fff'
},
tableBorder: {
borderColor: '#130929',
},
borderColor: '#130929'
}
}));
export default function Index() {
@ -68,16 +68,10 @@ export default function Index() {
<TableCell className={classes.tableBorder}>
User
</TableCell>
<TableCell
className={classes.tableBorder}
align='right'
>
<TableCell className={classes.tableBorder} align='right'>
Images
</TableCell>
<TableCell
className={classes.tableBorder}
align='right'
>
<TableCell className={classes.tableBorder} align='right'>
Views
</TableCell>
</TableRow>

View file

@ -20,16 +20,16 @@ import { ConfigUploader } from '../lib/Config';
const useStyles = makeStyles(theme => ({
margin: {
margin: '5px',
margin: '5px'
},
padding: {
border: '1px solid #1f1f1f',
padding: '10px',
padding: '10px'
},
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
},
color: '#fff'
}
}));
export default function Urls({ config }: { config: ConfigUploader }) {
@ -67,7 +67,7 @@ export default function Urls({ config }: { config: ConfigUploader }) {
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
horizontal: 'center'
}}
open={alertOpen}
autoHideDuration={6000}

View file

@ -27,19 +27,19 @@ import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
margin: {
margin: '5px',
margin: '5px'
},
padding: {
border: '1px solid #1f1f1f',
padding: '10px',
padding: '10px'
},
field: {
width: '100%',
width: '100%'
},
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
},
color: '#fff'
}
}));
export default function Index() {
@ -82,7 +82,9 @@ export default function Index() {
};
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) {
setDeleteOpen(false);
setAlertOpen(true);
@ -92,13 +94,17 @@ export default function Index() {
};
const createUserThenClose = async () => {
const d = await (await fetch('/api/user/create', {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify({
username, password, administrator
const d = await (
await fetch('/api/user/create', {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify({
username,
password,
administrator
})
})
})).json();
).json();
if (!d.error) {
setCreateOpen(false);
setAlertOpen(true);
@ -115,7 +121,7 @@ export default function Index() {
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
horizontal: 'center'
}}
open={alertOpen}
autoHideDuration={6000}
@ -167,8 +173,14 @@ export default function Index() {
onChange={e => setPassword(e.target.value)}
/>
<FormControlLabel
control={<Switch checked={administrator} onChange={() => setAdministrator(!administrator)} name="admin" />}
label="Administrator"
control={
<Switch
checked={administrator}
onChange={() => setAdministrator(!administrator)}
name='admin'
/>
}
label='Administrator'
/>
</DialogContentText>
</DialogContent>
@ -185,7 +197,10 @@ export default function Index() {
<Paper elevation={3} className={classes.padding}>
<Typography variant='h5'>
User
<IconButton aria-label='Create User' onClick={() => setCreateOpen(true)}>
<IconButton
aria-label='Create User'
onClick={() => setCreateOpen(true)}
>
<AddIcon />
</IconButton>
</Typography>
@ -203,7 +218,9 @@ export default function Index() {
</IconButton>
}
title={`${u.username} (${u.id})`}
subheader={`${u.administrator ? 'Administrator' : 'User'}`}
subheader={`${
u.administrator ? 'Administrator' : 'User'
}`}
/>
</Card>
</Grid>

View file

@ -16,7 +16,7 @@ export interface State {
const initialState: State = {
loggedIn: false,
user: null,
loading: true,
loading: true
};
export function reducer(state: State = initialState, action) {

View file

@ -7,7 +7,7 @@ import { reducer } from './reducer';
const persistedReducer = persistReducer(
{
key: 'root',
storage,
storage
},
reducer
);