mirror of
https://github.com/diced/zipline.git
synced 2025-05-11 10:26:05 +02:00
typeorm & formatting
This commit is contained in:
parent
21bc4eca48
commit
e028afeb3f
28 changed files with 577 additions and 396 deletions
4
.prettierignore
Normal file
4
.prettierignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.next
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
Zipline.toml
|
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
|
@ -1,2 +1,2 @@
|
||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/types/global" />
|
/// <reference types="next/types/global" />
|
||||||
|
|
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -5761,6 +5761,12 @@
|
||||||
"xtend": "^4.0.0"
|
"xtend": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"process": {
|
"process": {
|
||||||
"version": "0.11.10",
|
"version": "0.11.10",
|
||||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"@types/react-redux": "^7.1.9",
|
"@types/react-redux": "^7.1.9",
|
||||||
"mongodb": "^3.6.2",
|
"mongodb": "^3.6.2",
|
||||||
"pg": "^8.4.0",
|
"pg": "^8.4.0",
|
||||||
|
"prettier": "2.1.2",
|
||||||
"ts-node": "^9.0.0",
|
"ts-node": "^9.0.0",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,48 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import Link from 'next/link';
|
import Link from "next/link";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from "prop-types";
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from "@material-ui/core/AppBar";
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from "@material-ui/core/Divider";
|
||||||
import Drawer from '@material-ui/core/Drawer';
|
import Drawer from "@material-ui/core/Drawer";
|
||||||
import Hidden from '@material-ui/core/Hidden';
|
import Hidden from "@material-ui/core/Hidden";
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import HomeIcon from '@material-ui/icons/Home';
|
import HomeIcon from "@material-ui/icons/Home";
|
||||||
import DataUsageIcon from '@material-ui/icons/DataUsage';
|
import DataUsageIcon from "@material-ui/icons/DataUsage";
|
||||||
import PhotoIcon from '@material-ui/icons/Photo';
|
import PhotoIcon from "@material-ui/icons/Photo";
|
||||||
import LinkIcon from '@material-ui/icons/Link';
|
import LinkIcon from "@material-ui/icons/Link";
|
||||||
import List from '@material-ui/core/List';
|
import List from "@material-ui/core/List";
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from "@material-ui/core/ListItem";
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
import MailIcon from '@material-ui/icons/Mail';
|
import MailIcon from "@material-ui/icons/Mail";
|
||||||
import MenuIcon from '@material-ui/icons/Menu';
|
import MenuIcon from "@material-ui/icons/Menu";
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from "@material-ui/core/Toolbar";
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { makeStyles, useTheme } from '@material-ui/core/styles';
|
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||||
|
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
|
||||||
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: drawerWidth,
|
width: drawerWidth,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up("sm")]: {
|
||||||
width: `calc(100%)`,
|
width: `calc(100%)`,
|
||||||
marginLeft: drawerWidth,
|
marginLeft: drawerWidth,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
menuButton: {
|
menuButton: {
|
||||||
marginRight: theme.spacing(2),
|
marginRight: theme.spacing(2),
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up("sm")]: {
|
||||||
display: 'none',
|
display: "none",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// necessary for content to be below app bar
|
// necessary for content to be below app bar
|
||||||
|
@ -83,7 +82,7 @@ export default function UI(props) {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" noWrap>
|
<Typography variant="h6" noWrap>
|
||||||
Zipline
|
Zipline
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
@ -91,36 +90,44 @@ export default function UI(props) {
|
||||||
<List>
|
<List>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<ListItem button key="Home">
|
<ListItem button key="Home">
|
||||||
<ListItemIcon><HomeIcon /></ListItemIcon>
|
<ListItemIcon>
|
||||||
|
<HomeIcon />
|
||||||
|
</ListItemIcon>
|
||||||
<ListItemText primary="Home" />
|
<ListItemText primary="Home" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/statistics">
|
<Link href="/statistics">
|
||||||
<ListItem button key="Statistics">
|
<ListItem button key="Statistics">
|
||||||
<ListItemIcon><DataUsageIcon /></ListItemIcon>
|
<ListItemIcon>
|
||||||
|
<DataUsageIcon />
|
||||||
|
</ListItemIcon>
|
||||||
<ListItemText primary="Statistics" />
|
<ListItemText primary="Statistics" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/images">
|
<Link href="/images">
|
||||||
<ListItem button key="Images">
|
<ListItem button key="Images">
|
||||||
<ListItemIcon><PhotoIcon /></ListItemIcon>
|
<ListItemIcon>
|
||||||
|
<PhotoIcon />
|
||||||
|
</ListItemIcon>
|
||||||
<ListItemText primary="Images" />
|
<ListItemText primary="Images" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/urls">
|
<Link href="/urls">
|
||||||
<ListItem button key="URLs">
|
<ListItem button key="URLs">
|
||||||
<ListItemIcon><LinkIcon /></ListItemIcon>
|
<ListItemIcon>
|
||||||
|
<LinkIcon />
|
||||||
|
</ListItemIcon>
|
||||||
<ListItemText primary="URLs" />
|
<ListItemText primary="URLs" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
</List>
|
</List>
|
||||||
</div >
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const container = window !== undefined ? () => window().document.body : undefined;
|
const container =
|
||||||
|
window !== undefined ? () => window().document.body : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<AppBar position="fixed" className={classes.appBar}>
|
<AppBar position="fixed" className={classes.appBar}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
|
@ -143,7 +150,7 @@ export default function UI(props) {
|
||||||
<Drawer
|
<Drawer
|
||||||
container={container}
|
container={container}
|
||||||
variant="temporary"
|
variant="temporary"
|
||||||
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
|
anchor={theme.direction === "rtl" ? "right" : "left"}
|
||||||
open={mobileOpen}
|
open={mobileOpen}
|
||||||
onClose={handleDrawerToggle}
|
onClose={handleDrawerToggle}
|
||||||
classes={{
|
classes={{
|
||||||
|
@ -175,4 +182,4 @@ export default function UI(props) {
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,42 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import Link from 'next/link';
|
import Link from "next/link";
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from "@material-ui/core/AppBar";
|
||||||
import Drawer from '@material-ui/core/Drawer';
|
import Drawer from "@material-ui/core/Drawer";
|
||||||
import Hidden from '@material-ui/core/Hidden';
|
import Hidden from "@material-ui/core/Hidden";
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import List from '@material-ui/core/List';
|
import List from "@material-ui/core/List";
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from "@material-ui/core/ListItem";
|
||||||
import Box from '@material-ui/core/Box';
|
import Box from "@material-ui/core/Box";
|
||||||
import MenuIcon from '@material-ui/icons/Menu';
|
import MenuIcon from "@material-ui/icons/Menu";
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from "@material-ui/core/Toolbar";
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from "@material-ui/core/Typography";
|
||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
import Grid from '@material-ui/core/Grid';
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Skeleton from '@material-ui/lab/Skeleton';
|
import Skeleton from "@material-ui/lab/Skeleton";
|
||||||
import { makeStyles, useTheme } from '@material-ui/core/styles';
|
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||||
|
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
|
||||||
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: drawerWidth,
|
width: drawerWidth,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up("sm")]: {
|
||||||
width: `calc(100%)`,
|
width: `calc(100%)`,
|
||||||
marginLeft: drawerWidth,
|
marginLeft: drawerWidth,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
menuButton: {
|
menuButton: {
|
||||||
marginRight: theme.spacing(2),
|
marginRight: theme.spacing(2),
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up("sm")]: {
|
||||||
display: 'none',
|
display: "none",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// necessary for content to be below app bar
|
// necessary for content to be below app bar
|
||||||
|
@ -50,8 +49,8 @@ const useStyles = makeStyles((theme) => ({
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3),
|
||||||
},
|
},
|
||||||
fullWidth: {
|
fullWidth: {
|
||||||
width: '100%'
|
width: "100%",
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function UIPlaceholder(props) {
|
export default function UIPlaceholder(props) {
|
||||||
|
@ -80,7 +79,7 @@ export default function UIPlaceholder(props) {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" noWrap>
|
<Typography variant="h6" noWrap>
|
||||||
Zipline
|
Zipline
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
@ -99,13 +98,13 @@ export default function UIPlaceholder(props) {
|
||||||
<Skeleton animation="wave" className={classes.fullWidth} />
|
<Skeleton animation="wave" className={classes.fullWidth} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
</div >
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const container = window !== undefined ? () => window().document.body : undefined;
|
const container =
|
||||||
|
window !== undefined ? () => window().document.body : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<AppBar position="fixed" className={classes.appBar}>
|
<AppBar position="fixed" className={classes.appBar}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
|
@ -128,7 +127,7 @@ export default function UIPlaceholder(props) {
|
||||||
<Drawer
|
<Drawer
|
||||||
container={container}
|
container={container}
|
||||||
variant="temporary"
|
variant="temporary"
|
||||||
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
|
anchor={theme.direction === "rtl" ? "right" : "left"}
|
||||||
open={mobileOpen}
|
open={mobileOpen}
|
||||||
onClose={handleDrawerToggle}
|
onClose={handleDrawerToggle}
|
||||||
classes={{
|
classes={{
|
||||||
|
@ -156,7 +155,14 @@ export default function UIPlaceholder(props) {
|
||||||
|
|
||||||
<main className={classes.content}>
|
<main className={classes.content}>
|
||||||
<div className={classes.toolbar} />
|
<div className={classes.toolbar} />
|
||||||
<Grid container spacing={0} direction="column" alignItems="center" justify="center" style={{ minHeight: '80vh' }}>
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={0}
|
||||||
|
direction="column"
|
||||||
|
alignItems="center"
|
||||||
|
justify="center"
|
||||||
|
style={{ minHeight: "80vh" }}
|
||||||
|
>
|
||||||
<Grid item xs={3}>
|
<Grid item xs={3}>
|
||||||
<CircularProgress size={100} />
|
<CircularProgress size={100} />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -164,4 +170,4 @@ export default function UIPlaceholder(props) {
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import { FastifyReply, FastifyRequest, FastifyInstance } from 'fastify';
|
import { FastifyReply, FastifyRequest, FastifyInstance } from "fastify";
|
||||||
import { Controller, GET, POST, FastifyInstanceToken, Inject, Hook } from 'fastify-decorators';
|
import {
|
||||||
import { User } from '../entities/User';
|
Controller,
|
||||||
|
GET,
|
||||||
|
POST,
|
||||||
|
FastifyInstanceToken,
|
||||||
|
Inject,
|
||||||
|
Hook,
|
||||||
|
} from "fastify-decorators";
|
||||||
|
import { User } from "../entities/User";
|
||||||
|
|
||||||
@Controller('/api')
|
@Controller("/api")
|
||||||
export class RootController {
|
export class RootController {
|
||||||
@Inject(FastifyInstanceToken)
|
@Inject(FastifyInstanceToken)
|
||||||
private instance!: FastifyInstance;
|
private instance!: FastifyInstance;
|
||||||
|
|
||||||
@POST('/upload')
|
@POST("/upload")
|
||||||
async loginStatus(req: FastifyRequest, reply: FastifyReply) {
|
async loginStatus(req: FastifyRequest, reply: FastifyReply) {}
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,105 +1,150 @@
|
||||||
// import { FastifyReply, FastifyRequest, FastifyInstance } from 'fastify';
|
import { FastifyReply, FastifyRequest, FastifyInstance } from "fastify";
|
||||||
// import { Controller, GET, POST, FastifyInstanceToken, Inject, Hook } from 'fastify-decorators';
|
import {
|
||||||
// import { UserNotFoundError, MissingBodyData, LoginError, UserExistsError, NotAdministratorError } from '../lib/api/APIErrors';
|
Controller,
|
||||||
// import { User } from '../lib/Data';
|
GET,
|
||||||
// import { checkPassword, createToken, encryptPassword } from '../lib/Encryption';
|
POST,
|
||||||
|
FastifyInstanceToken,
|
||||||
|
Inject,
|
||||||
|
Hook,
|
||||||
|
} from "fastify-decorators";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { User } from "../entities/User";
|
||||||
|
import {
|
||||||
|
UserNotFoundError,
|
||||||
|
MissingBodyData,
|
||||||
|
LoginError,
|
||||||
|
UserExistsError,
|
||||||
|
NotAdministratorError,
|
||||||
|
} from "../lib/api/APIErrors";
|
||||||
|
import {
|
||||||
|
checkPassword,
|
||||||
|
createBaseCookie,
|
||||||
|
createToken,
|
||||||
|
encryptPassword,
|
||||||
|
readBaseCookie,
|
||||||
|
} from "../lib/Encryption";
|
||||||
|
|
||||||
// @Controller('/api/user')
|
@Controller("/api/user")
|
||||||
// export class UserController {
|
export class UserController {
|
||||||
// @Inject(FastifyInstanceToken)
|
@Inject(FastifyInstanceToken)
|
||||||
// private instance!: FastifyInstance;
|
private instance!: FastifyInstance;
|
||||||
|
|
||||||
// @GET('/login-status')
|
private users: Repository<User> = this.instance.orm.getRepository(User);
|
||||||
// async loginStatus(req: FastifyRequest, reply: FastifyReply) {
|
|
||||||
// return reply.send({ user: !!req.cookies.zipline });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @GET('/current')
|
@GET("/login-status")
|
||||||
// async currentUser(req: FastifyRequest, reply: FastifyReply) {
|
async loginStatus(req: FastifyRequest, reply: FastifyReply) {
|
||||||
// if (!req.cookies.zipline) throw new LoginError(`Not logged in.`);
|
return reply.send({
|
||||||
// const user = await this.instance.mongo.db.collection('zipline_users').findOne({ _id: new this.instance.mongo.ObjectId(req.cookies.zipline) });
|
user: !!req.cookies.zipline,
|
||||||
// if (!user) throw new UserExistsError(`User doesn't exist`);
|
});
|
||||||
// delete user.password;
|
}
|
||||||
// return reply.send(user);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @POST('/login')
|
@GET("/current")
|
||||||
// async login(req: FastifyRequest<{ Body: { username: string, password: string } }>, reply: FastifyReply) {
|
async currentUser(req: FastifyRequest, reply: FastifyReply) {
|
||||||
// if (req.cookies.zipline) throw new LoginError(`Already logged in.`)
|
if (!req.cookies.zipline) throw new LoginError(`Not logged in.`);
|
||||||
// if (!req.body.username) throw new MissingBodyData(`Missing username.`);
|
const user = await this.users.findOne({
|
||||||
// if (!req.body.password) throw new MissingBodyData(`Missing uassword.`);
|
where: {
|
||||||
|
id: readBaseCookie(req.cookies.zipline),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user) throw new UserExistsError(`User doesn't exist`);
|
||||||
|
delete user.password;
|
||||||
|
return reply.send(user);
|
||||||
|
}
|
||||||
|
|
||||||
// const user: User = await this.instance.mongo.db.collection('zipline_users').findOne({
|
@POST("/login")
|
||||||
// username: req.body.username
|
async login(
|
||||||
// });
|
req: FastifyRequest<{ Body: { username: string; password: string } }>,
|
||||||
|
reply: FastifyReply
|
||||||
|
) {
|
||||||
|
if (req.cookies.zipline) throw new LoginError(`Already logged in.`);
|
||||||
|
if (!req.body.username) throw new MissingBodyData(`Missing username.`);
|
||||||
|
if (!req.body.password) throw new MissingBodyData(`Missing uassword.`);
|
||||||
|
|
||||||
// if (!user) throw new UserNotFoundError(`User "${req.body.username}" was not found.`);
|
const user = await this.users.findOne({
|
||||||
// if (!checkPassword(req.body.password, user.password)) throw new LoginError(`Wrong credentials!`);
|
where: {
|
||||||
// delete user.password;
|
id: readBaseCookie(req.cookies.zipline),
|
||||||
// return reply
|
},
|
||||||
// .setCookie("zipline", user._id, { path: '/' })
|
});
|
||||||
// .send(user);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @POST('/logout')
|
if (!user)
|
||||||
// async logout(req: FastifyRequest, reply: FastifyReply) {
|
throw new UserNotFoundError(`User "${req.body.username}" was not found.`);
|
||||||
// if (!req.cookies.zipline) throw new LoginError(`Not logged in.`);
|
if (!checkPassword(req.body.password, user.password))
|
||||||
// try {
|
throw new LoginError(`Wrong credentials!`);
|
||||||
// reply.clearCookie('zipline', { path: '/' }).send({ clearStore: true })
|
delete user.password;
|
||||||
// } catch (e) {
|
|
||||||
// reply.send({ clearStore: false });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @POST('/reset-token')
|
return reply
|
||||||
// async resetToken(req: FastifyRequest, reply: FastifyReply) {
|
.setCookie("zipline", createBaseCookie(user.id), { path: "/" })
|
||||||
// if (!req.cookies.zipline) throw new LoginError(`Not logged in.`);
|
.send(user);
|
||||||
|
}
|
||||||
|
|
||||||
// const users = this.instance.mongo.db.collection('zipline_users');
|
@POST("/logout")
|
||||||
// const user: User = await users.findOne({ _id: new this.instance.mongo.ObjectId(req.cookies.zipline) });
|
async logout(req: FastifyRequest, reply: FastifyReply) {
|
||||||
// if (!user) throw new UserNotFoundError(`User was not found.`);
|
if (!req.cookies.zipline) throw new LoginError(`Not logged in.`);
|
||||||
|
try {
|
||||||
|
reply.clearCookie("zipline", { path: "/" }).send({ clearStore: true });
|
||||||
|
} catch (e) {
|
||||||
|
reply.send({ clearStore: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// users.updateOne({ _id: new this.instance.mongo.ObjectId(req.cookies.zipline) }, { $set: { token: createToken() } });
|
@POST("/reset-token")
|
||||||
// return reply.send({ updated: true });
|
async resetToken(req: FastifyRequest, reply: FastifyReply) {
|
||||||
// }
|
if (!req.cookies.zipline) throw new LoginError(`Not logged in.`);
|
||||||
|
|
||||||
// @POST('/create')
|
const user = await this.users.findOne({
|
||||||
// async create(req: FastifyRequest<{ Body: { username: string, password: string, administrator: boolean } }>, reply: FastifyReply) {
|
where: {
|
||||||
// if (!req.body.username) throw new MissingBodyData(`Missing username.`);
|
id: readBaseCookie(req.cookies.zipline),
|
||||||
// if (!req.body.password) throw new MissingBodyData(`Missing uassword.`);
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// const users = this.instance.mongo.db.collection('zipline_users');
|
if (!user) throw new UserNotFoundError(`User was not found.`);
|
||||||
|
|
||||||
// const existingUser = await users.findOne({ username: req.body.username });
|
user.token = createToken();
|
||||||
// if (existingUser) throw new UserExistsError('User exists already');
|
this.users.save(user);
|
||||||
|
|
||||||
// const newUser: User = {
|
return reply.send({ updated: true });
|
||||||
// username: req.body.username,
|
}
|
||||||
// password: encryptPassword(req.body.password),
|
|
||||||
// token: createToken(),
|
|
||||||
// administrator: req.body.administrator
|
|
||||||
// };
|
|
||||||
|
|
||||||
// try {
|
@POST("/create")
|
||||||
// users.insertOne(newUser);
|
async create(
|
||||||
// } catch (e) {
|
req: FastifyRequest<{
|
||||||
// throw new Error(`Could not create user: ${e.message}`);
|
Body: { username: string; password: string; administrator: boolean };
|
||||||
// }
|
}>,
|
||||||
|
reply: FastifyReply
|
||||||
|
) {
|
||||||
|
if (!req.body.username) throw new MissingBodyData(`Missing username.`);
|
||||||
|
if (!req.body.password) throw new MissingBodyData(`Missing uassword.`);
|
||||||
|
|
||||||
// return reply.send(newUser);
|
const existing = await this.users.findOne({
|
||||||
// }
|
where: { username: req.body.username },
|
||||||
|
});
|
||||||
|
if (existing) throw new UserExistsError("User exists already");
|
||||||
|
|
||||||
// @Hook('preValidation')
|
try {
|
||||||
// public async preValidation(req: FastifyRequest, reply: FastifyReply) {
|
const user = await this.users.save(
|
||||||
// const adminRoutes = ['/api/user/create'];
|
new User(
|
||||||
|
req.body.username,
|
||||||
|
req.body.password,
|
||||||
|
createToken(),
|
||||||
|
req.body.administrator || false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
delete user.password;
|
||||||
|
return reply.send(user);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Could not create user: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (adminRoutes.includes(req.routerPath)) {
|
@Hook("preValidation")
|
||||||
// if (!req.cookies.zipline) return reply.send({ error: "You are not logged in" });
|
public async preValidation(req: FastifyRequest, reply: FastifyReply) {
|
||||||
|
// const adminRoutes = ['/api/user/create'];
|
||||||
// const admin = await this.instance.mongo.db.collection('zipline_users').findOne({ _id: req.cookies.zipline });
|
// if (adminRoutes.includes(req.routerPath)) {
|
||||||
// if (!admin) return reply.send({ error: "You are not an administrator" });
|
// if (!req.cookies.zipline) return reply.send({ error: "You are not logged in" });
|
||||||
// return;
|
// const admin = await this.instance.mongo.db.collection('zipline_users').findOne({ _id: req.cookies.zipline });
|
||||||
// }
|
// if (!admin) return reply.send({ error: "You are not an administrator" });
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// }
|
// return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class User {
|
export class User {
|
||||||
|
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id: number;
|
public id: number;
|
||||||
|
|
||||||
|
@ -18,10 +17,15 @@ export class User {
|
||||||
@Column("text")
|
@Column("text")
|
||||||
public token: string;
|
public token: string;
|
||||||
|
|
||||||
public constructor(username: string, password: string, token: string, administrator: boolean = false) {
|
public constructor(
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
token: string,
|
||||||
|
administrator: boolean = false
|
||||||
|
) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.administrator = administrator;
|
this.administrator = administrator;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
60
src/index.ts
60
src/index.ts
|
@ -1,55 +1,55 @@
|
||||||
import next from 'next';
|
import next from "next";
|
||||||
import fastify from 'fastify';
|
import fastify from "fastify";
|
||||||
import fastifyTypeorm from 'fastify-typeorm-plugin';
|
import fastifyTypeorm from "fastify-typeorm-plugin";
|
||||||
import fastifyCookies from 'fastify-cookie';
|
import fastifyCookies from "fastify-cookie";
|
||||||
import fastifyMultipart from 'fastify-multipart';
|
import fastifyMultipart from "fastify-multipart";
|
||||||
import { bootstrap } from 'fastify-decorators';
|
import { bootstrap } from "fastify-decorators";
|
||||||
import { RootController } from './controllers/RootController';
|
import { RootController } from "./controllers/RootController";
|
||||||
import { Console } from './lib/logger';
|
import { Console } from "./lib/logger";
|
||||||
import { AddressInfo } from 'net';
|
import { AddressInfo } from "net";
|
||||||
import { ConsoleFormatter } from './lib/ConsoleFormatter';
|
import { ConsoleFormatter } from "./lib/ConsoleFormatter";
|
||||||
import { bold, green, reset } from '@dicedtomato/colors';
|
import { bold, green, reset } from "@dicedtomato/colors";
|
||||||
import { Configuration } from './lib/Config';
|
import { Configuration } from "./lib/Config";
|
||||||
// import { UserController } from './controllers/UserController';
|
// import { UserController } from './controllers/UserController';
|
||||||
|
|
||||||
|
|
||||||
Console.setFormatter(new ConsoleFormatter());
|
Console.setFormatter(new ConsoleFormatter());
|
||||||
|
|
||||||
const config = Configuration.readConfig();
|
const config = Configuration.readConfig();
|
||||||
if (!config) process.exit(0);
|
if (!config) process.exit(0);
|
||||||
const server = fastify({});
|
const server = fastify({});
|
||||||
const dev = process.env.NODE_ENV !== 'production';
|
const dev = process.env.NODE_ENV !== "production";
|
||||||
const app = next({ dev, quiet: dev });
|
const app = next({ dev, quiet: dev });
|
||||||
const handle = app.getRequestHandler();
|
const handle = app.getRequestHandler();
|
||||||
|
|
||||||
Console.logger("Next").info(`Preparing app`);
|
Console.logger("Next").info(`Preparing app`);
|
||||||
app.prepare();
|
app.prepare();
|
||||||
|
|
||||||
if (dev) server.get('/_next/*', (req, reply) => {
|
if (dev)
|
||||||
return handle(req.raw, reply.raw).then(() => reply.sent = true);
|
server.get("/_next/*", (req, reply) => {
|
||||||
});
|
return handle(req.raw, reply.raw).then(() => (reply.sent = true));
|
||||||
|
});
|
||||||
|
|
||||||
server.all('/*', (req, reply) => {
|
server.all("/*", (req, reply) => {
|
||||||
return handle(req.raw, reply.raw).then(() => reply.sent = true);
|
return handle(req.raw, reply.raw).then(() => (reply.sent = true));
|
||||||
});
|
});
|
||||||
|
|
||||||
server.setNotFoundHandler((req, reply) => {
|
server.setNotFoundHandler((req, reply) => {
|
||||||
return app.render404(req.raw, reply.raw).then(() => reply.sent = true);
|
return app.render404(req.raw, reply.raw).then(() => (reply.sent = true));
|
||||||
})
|
});
|
||||||
|
|
||||||
server.register(fastifyMultipart);
|
server.register(fastifyMultipart);
|
||||||
|
|
||||||
server.register(fastifyTypeorm, {
|
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, {
|
||||||
controllers: [
|
controllers: [
|
||||||
// UserController,
|
// UserController,
|
||||||
RootController
|
RootController,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,11 +60,15 @@ server.register(bootstrap, {
|
||||||
// });
|
// });
|
||||||
|
|
||||||
server.register(fastifyCookies, {
|
server.register(fastifyCookies, {
|
||||||
secret: config.core.secret
|
secret: config.core.secret,
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(config.core.port, err => {
|
server.listen(config.core.port, (err) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
const info = server.server.address() as AddressInfo;
|
const info = server.server.address() as AddressInfo;
|
||||||
Console.logger("Server").info(`server listening on ${bold(`${green(info.address)}${reset(":")}${bold(green(info.port.toString()))}`)}`)
|
Console.logger("Server").info(
|
||||||
})
|
`server listening on ${bold(
|
||||||
|
`${green(info.address)}${reset(":")}${bold(green(info.port.toString()))}`
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from "fs";
|
||||||
import { resolve } from 'path';
|
import { resolve } from "path";
|
||||||
import { parse } from 'toml';
|
import { parse } from "toml";
|
||||||
import { ConnectionOptions } from 'typeorm';
|
import { ConnectionOptions } from "typeorm";
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
database: ConnectionOptions;
|
database: ConnectionOptions;
|
||||||
|
@ -21,11 +21,11 @@ export interface ConfigCore {
|
||||||
export class Configuration {
|
export class Configuration {
|
||||||
static readConfig(): Config {
|
static readConfig(): Config {
|
||||||
try {
|
try {
|
||||||
const data = readFileSync(resolve(process.cwd(), "Zipline.toml"), 'utf8');
|
const data = readFileSync(resolve(process.cwd(), "Zipline.toml"), "utf8");
|
||||||
return parse(data) as Config;
|
return parse(data) as Config;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,12 @@ import { bold, magenta, reset } from "@dicedtomato/colors";
|
||||||
import { ConsoleLevel, Formatter } from "./logger";
|
import { ConsoleLevel, Formatter } from "./logger";
|
||||||
|
|
||||||
export class ConsoleFormatter implements Formatter {
|
export class ConsoleFormatter implements Formatter {
|
||||||
format(message: string, origin: string, level: ConsoleLevel, time: Date): string {
|
format(
|
||||||
return `${bold(magenta(origin))} ${bold(">")} ${reset(message)}`
|
message: string,
|
||||||
|
origin: string,
|
||||||
|
level: ConsoleLevel,
|
||||||
|
time: Date
|
||||||
|
): string {
|
||||||
|
return `${bold(magenta(origin))} ${bold(">")} ${reset(message)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,4 @@ export interface Image {
|
||||||
user: any;
|
user: any;
|
||||||
views: number;
|
views: number;
|
||||||
_id?: any;
|
_id?: any;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
import aes from 'crypto-js/aes'
|
import aes from "crypto-js/aes";
|
||||||
import { compareSync, hashSync } from 'bcrypt';
|
import { compareSync, hashSync } from "bcrypt";
|
||||||
import { Configuration } from './Config';
|
import { Configuration } from "./Config";
|
||||||
|
|
||||||
const config = Configuration.readConfig();
|
const config = Configuration.readConfig();
|
||||||
if (!config) process.exit(0);
|
if (!config) process.exit(0);
|
||||||
|
|
||||||
export function createRandomId(length: number, charset: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
|
export function createRandomId(
|
||||||
let result = '';
|
length: number,
|
||||||
for (let i = 0; i < length; i++) result += charset.charAt(Math.floor(Math.random() * charset.length));
|
charset: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
) {
|
||||||
|
let result = "";
|
||||||
|
for (let i = 0; i < length; i++)
|
||||||
|
result += charset.charAt(Math.floor(Math.random() * charset.length));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createToken() {
|
export function createToken() {
|
||||||
return aes.encrypt(`${createRandomId(10)}.${Date.now()}`, config.core.secret).toString();
|
return aes
|
||||||
|
.encrypt(`${createRandomId(10)}.${Date.now()}`, config.core.secret)
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encryptPassword(pass: string) {
|
export function encryptPassword(pass: string) {
|
||||||
|
@ -21,4 +27,12 @@ export function encryptPassword(pass: string) {
|
||||||
|
|
||||||
export function checkPassword(will: string, equal: string) {
|
export function checkPassword(will: string, equal: string) {
|
||||||
return compareSync(will, equal);
|
return compareSync(will, equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createBaseCookie(id: number) {
|
||||||
|
return Buffer.from(id.toString()).toString("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readBaseCookie(data: string) {
|
||||||
|
return Buffer.from(data, "base64").toString();
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export class MissingBodyData extends Error { };
|
export class MissingBodyData extends Error {}
|
||||||
export class LoginError extends Error { };
|
export class LoginError extends Error {}
|
||||||
export class NotAdministratorError extends Error { };
|
export class NotAdministratorError extends Error {}
|
||||||
export class UserExistsError extends Error { };
|
export class UserExistsError extends Error {}
|
||||||
export class UserNotFoundError extends Error { };
|
export class UserNotFoundError extends Error {}
|
||||||
export class UserCredentialsError extends Error { };
|
export class UserCredentialsError extends Error {}
|
||||||
|
|
|
@ -15,10 +15,9 @@ export enum ConsoleLevel {
|
||||||
ERROR,
|
ERROR,
|
||||||
INFO,
|
INFO,
|
||||||
TRACE,
|
TRACE,
|
||||||
WARN
|
WARN,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class Console {
|
export class Console {
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
|
@ -59,4 +58,4 @@ export class Console {
|
||||||
const name = o instanceof Function ? o.name : o;
|
const name = o instanceof Function ? o.name : o;
|
||||||
return new Console(name);
|
return new Console(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,24 @@
|
||||||
import { ConsoleLevel } from "./Console";
|
import { ConsoleLevel } from "./Console";
|
||||||
import { brightGreen, blue } from '@dicedtomato/colors';
|
import { brightGreen, blue } from "@dicedtomato/colors";
|
||||||
|
|
||||||
|
|
||||||
export interface Formatter {
|
export interface Formatter {
|
||||||
format(message: string, origin: string, level: ConsoleLevel, time: Date): string;
|
format(
|
||||||
|
message: string,
|
||||||
|
origin: string,
|
||||||
|
level: ConsoleLevel,
|
||||||
|
time: Date
|
||||||
|
): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultFormatter implements Formatter {
|
export class DefaultFormatter implements Formatter {
|
||||||
public format(message: string, origin: string, level: ConsoleLevel, time: Date) {
|
public format(
|
||||||
return `[${time.toLocaleString()}] ${brightGreen(origin)} - ${blue(ConsoleLevel[level])}: ${message}`
|
message: string,
|
||||||
|
origin: string,
|
||||||
|
level: ConsoleLevel,
|
||||||
|
time: Date
|
||||||
|
) {
|
||||||
|
return `[${time.toLocaleString()}] ${brightGreen(origin)} - ${blue(
|
||||||
|
ConsoleLevel[level]
|
||||||
|
)}: ${message}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export * from './Console';
|
export * from "./Console";
|
||||||
export * from './Formatter';
|
export * from "./Formatter";
|
||||||
|
|
|
@ -6,13 +6,12 @@ export const LOGOUT = "LOGOUT";
|
||||||
export const UPDATE_USER = "UPDATE_USER";
|
export const UPDATE_USER = "UPDATE_USER";
|
||||||
export interface State {
|
export interface State {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
user: User
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
user: null
|
user: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function reducer(state: State = initialState, action) {
|
export function reducer(state: State = initialState, action) {
|
||||||
|
@ -26,4 +25,4 @@ export function reducer(state: State = initialState, action) {
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
import { createStore } from 'redux';
|
import { createStore } from "redux";
|
||||||
import { persistStore, persistReducer } from 'redux-persist';
|
import { persistStore, persistReducer } from "redux-persist";
|
||||||
import storage from 'redux-persist/lib/storage';
|
import storage from "redux-persist/lib/storage";
|
||||||
|
|
||||||
import { reducer } from './reducer'
|
import { reducer } from "./reducer";
|
||||||
|
|
||||||
const persistedReducer = persistReducer({
|
const persistedReducer = persistReducer(
|
||||||
key: 'root',
|
{
|
||||||
storage,
|
key: "root",
|
||||||
}, reducer);
|
storage,
|
||||||
|
},
|
||||||
|
reducer
|
||||||
|
);
|
||||||
|
|
||||||
let store = createStore(persistedReducer)
|
let store = createStore(persistedReducer);
|
||||||
let persistor = persistStore(store)
|
let persistor = persistStore(store);
|
||||||
|
|
||||||
export { store, persistor };
|
export { store, persistor };
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import { createMuiTheme } from '@material-ui/core/styles';
|
import { createMuiTheme } from "@material-ui/core/styles";
|
||||||
import { red } from '@material-ui/core/colors';
|
import { red } from "@material-ui/core/colors";
|
||||||
|
|
||||||
// Create a theme instance.
|
// Create a theme instance.
|
||||||
const theme = createMuiTheme({
|
const theme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
type: 'dark',
|
type: "dark",
|
||||||
primary: {
|
primary: {
|
||||||
main: '#556cd6',
|
main: "#556cd6",
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: '#19857b',
|
main: "#19857b",
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
main: red.A100,
|
main: red.A100,
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
default: '#121212',
|
default: "#121212",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from "react";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from "prop-types";
|
||||||
import Head from 'next/head';
|
import Head from "next/head";
|
||||||
import { ThemeProvider } from '@material-ui/core/styles';
|
import { ThemeProvider } from "@material-ui/core/styles";
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from "react-redux";
|
||||||
import { PersistGate } from 'redux-persist/integration/react'
|
import { PersistGate } from "redux-persist/integration/react";
|
||||||
import theme from '../lib/theme';
|
import theme from "../lib/theme";
|
||||||
import { store, persistor } from '../lib/store';
|
import { store, persistor } from "../lib/store";
|
||||||
import UIPlaceholder from '../components/UIPlaceholder';
|
import UIPlaceholder from "../components/UIPlaceholder";
|
||||||
|
|
||||||
export default function MyApp(props) {
|
export default function MyApp(props) {
|
||||||
const { Component, pageProps } = props;
|
const { Component, pageProps } = props;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const jssStyles = document.querySelector('#jss-server-side');
|
const jssStyles = document.querySelector("#jss-server-side");
|
||||||
if (jssStyles) {
|
if (jssStyles) {
|
||||||
jssStyles.parentElement.removeChild(jssStyles);
|
jssStyles.parentElement.removeChild(jssStyles);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,10 @@ export default function MyApp(props) {
|
||||||
<PersistGate loading={<UIPlaceholder />} persistor={persistor}>
|
<PersistGate loading={<UIPlaceholder />} persistor={persistor}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Zipline</title>
|
<title>Zipline</title>
|
||||||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="minimum-scale=1, initial-scale=1, width=device-width"
|
||||||
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
import Document, { Html, Head, Main, NextScript } from "next/document";
|
||||||
import { ServerStyleSheets } from '@material-ui/core/styles';
|
import { ServerStyleSheets } from "@material-ui/core/styles";
|
||||||
import theme from '../lib/theme';
|
import theme from "../lib/theme";
|
||||||
|
|
||||||
export default class MyDocument extends Document {
|
export default class MyDocument extends Document {
|
||||||
render() {
|
render() {
|
||||||
|
@ -63,6 +63,9 @@ MyDocument.getInitialProps = async (ctx) => {
|
||||||
return {
|
return {
|
||||||
...initialProps,
|
...initialProps,
|
||||||
// Styles fragment is rendered after the app and page rendering finish.
|
// Styles fragment is rendered after the app and page rendering finish.
|
||||||
styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
|
styles: [
|
||||||
|
...React.Children.toArray(initialProps.styles),
|
||||||
|
sheets.getStyleElement(),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,34 +1,33 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from "next/router";
|
||||||
import UI from '../components/UI';
|
import UI from "../components/UI";
|
||||||
import UIPlaceholder from '../components/UIPlaceholder';
|
import UIPlaceholder from "../components/UIPlaceholder";
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from "@material-ui/core/Typography";
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from "@material-ui/core/Paper";
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from "@material-ui/core/Divider";
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from "@material-ui/core/Button";
|
||||||
import Grid from '@material-ui/core/Grid';
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from "@material-ui/core/Dialog";
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from "@material-ui/core/DialogActions";
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from "@material-ui/core/DialogContent";
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||||
import Alert from '@material-ui/lab/Alert';
|
import Alert from "@material-ui/lab/Alert";
|
||||||
import Snackbar from '@material-ui/core/Snackbar';
|
import Snackbar from "@material-ui/core/Snackbar";
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from "copy-to-clipboard";
|
||||||
import { LOGOUT, UPDATE_USER } from '../lib/reducer';
|
import { LOGOUT, UPDATE_USER } from "../lib/reducer";
|
||||||
import { makeStyles } from '@material-ui/core';
|
import { makeStyles } from "@material-ui/core";
|
||||||
import { store } from '../lib/store';
|
import { store } from "../lib/store";
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from "react-redux";
|
||||||
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
margin: {
|
margin: {
|
||||||
margin: '5px'
|
margin: "5px",
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
padding: '10px'
|
padding: "10px",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function IndexPage() {
|
export default function IndexPage() {
|
||||||
|
@ -36,49 +35,53 @@ export default function IndexPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const [alertMessage, setAlertMessage] = useState('Copied token!');
|
const [alertMessage, setAlertMessage] = useState("Copied token!");
|
||||||
const [tokenOpen, setTokenOpen] = useState(false);
|
const [tokenOpen, setTokenOpen] = useState(false);
|
||||||
const [resetToken, setResetToken] = useState(false);
|
const [resetToken, setResetToken] = useState(false);
|
||||||
const [alertOpen, setAlertOpen] = useState(false);
|
const [alertOpen, setAlertOpen] = useState(false);
|
||||||
|
|
||||||
const handleCopyTokenThenClose = async () => {
|
const handleCopyTokenThenClose = async () => {
|
||||||
const data = await (await fetch('/api/user/current')).json();
|
const data = await (await fetch("/api/user/current")).json();
|
||||||
if (!data.error) {
|
if (!data.error) {
|
||||||
copy(data.token);
|
copy(data.token);
|
||||||
setAlertMessage('Copied token!');
|
setAlertMessage("Copied token!");
|
||||||
setTokenOpen(false);
|
setTokenOpen(false);
|
||||||
setAlertOpen(true);
|
setAlertOpen(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResetTokenThenClose = async () => {
|
const handleResetTokenThenClose = async () => {
|
||||||
const data = await (await fetch('/api/user/reset-token', { method: 'POST' })).json();
|
const data = await (
|
||||||
|
await fetch("/api/user/reset-token", { method: "POST" })
|
||||||
|
).json();
|
||||||
if (!data.error && data.updated) {
|
if (!data.error && data.updated) {
|
||||||
setAlertMessage('Reset token!');
|
setAlertMessage("Reset token!");
|
||||||
setResetToken(false);
|
setResetToken(false);
|
||||||
setAlertOpen(true);
|
setAlertOpen(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
const data = await (await fetch('/api/user/logout', { method: 'POST' })).json();
|
const data = await (
|
||||||
|
await fetch("/api/user/logout", { method: "POST" })
|
||||||
|
).json();
|
||||||
if (!data.error && data.clearStore) {
|
if (!data.error && data.clearStore) {
|
||||||
dispatch({ type: LOGOUT });
|
dispatch({ type: LOGOUT });
|
||||||
dispatch({ type: UPDATE_USER, payload: null });
|
dispatch({ type: UPDATE_USER, payload: null });
|
||||||
setAlertMessage('Logged out!');
|
setAlertMessage("Logged out!");
|
||||||
setAlertOpen(true);
|
setAlertOpen(true);
|
||||||
router.push('/login');
|
router.push("/login");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && !state.loggedIn) router.push('/login');
|
if (typeof window !== "undefined" && !state.loggedIn) router.push("/login");
|
||||||
else {
|
else {
|
||||||
return (
|
return (
|
||||||
<UI>
|
<UI>
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: "top",
|
||||||
horizontal: 'center',
|
horizontal: "center",
|
||||||
}}
|
}}
|
||||||
open={alertOpen}
|
open={alertOpen}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
|
@ -89,11 +92,20 @@ export default function IndexPage() {
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
<Paper elevation={3} className={classes.padding}>
|
<Paper elevation={3} className={classes.padding}>
|
||||||
<Typography variant="h5">Welcome back, {state.user.username}</Typography>
|
<Typography variant="h5">
|
||||||
<Typography color="textSecondary">You have <b>2</b> images</Typography>
|
Welcome back, {state.user.username}
|
||||||
|
</Typography>
|
||||||
|
<Typography color="textSecondary">
|
||||||
|
You have <b>2</b> images
|
||||||
|
</Typography>
|
||||||
<div className={classes.margin}>
|
<div className={classes.margin}>
|
||||||
<Typography variant="h5">Token</Typography>
|
<Typography variant="h5">Token</Typography>
|
||||||
<Button variant="contained" color="primary" className={classes.margin} onClick={() => setTokenOpen(true)}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
className={classes.margin}
|
||||||
|
onClick={() => setTokenOpen(true)}
|
||||||
|
>
|
||||||
Copy
|
Copy
|
||||||
</Button>
|
</Button>
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -105,19 +117,29 @@ export default function IndexPage() {
|
||||||
<DialogTitle id="alert-dialog-title">Are you sure?</DialogTitle>
|
<DialogTitle id="alert-dialog-title">Are you sure?</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
This token is used to upload images to Zipline, and should not be shared!
|
This token is used to upload images to Zipline, and should not
|
||||||
|
be shared!
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => setTokenOpen(true)} color="primary">
|
<Button onClick={() => setTokenOpen(true)} color="primary">
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleCopyTokenThenClose} color="primary" autoFocus>
|
<Button
|
||||||
|
onClick={handleCopyTokenThenClose}
|
||||||
|
color="primary"
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
Yes, copy!
|
Yes, copy!
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Button variant="contained" className={classes.margin} onClick={() => setResetToken(true)} style={{ backgroundColor: "#d6381c", color: "white" }}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
className={classes.margin}
|
||||||
|
onClick={() => setResetToken(true)}
|
||||||
|
style={{ backgroundColor: "#d6381c", color: "white" }}
|
||||||
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -129,14 +151,20 @@ export default function IndexPage() {
|
||||||
<DialogTitle id="alert-dialog-title">Are you sure?</DialogTitle>
|
<DialogTitle id="alert-dialog-title">Are you sure?</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
This token is used to upload images to Zipline, resetting your token will cause any uploading actions to not work until you update them your self.
|
This token is used to upload images to Zipline, resetting your
|
||||||
|
token will cause any uploading actions to not work until you
|
||||||
|
update them your self.
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => setResetToken(true)} color="primary">
|
<Button onClick={() => setResetToken(true)} color="primary">
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleResetTokenThenClose} color="primary" autoFocus>
|
<Button
|
||||||
|
onClick={handleResetTokenThenClose}
|
||||||
|
color="primary"
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
Yes, reset!
|
Yes, reset!
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
@ -146,23 +174,36 @@ export default function IndexPage() {
|
||||||
<div className={classes.margin}>
|
<div className={classes.margin}>
|
||||||
<Typography variant="h5">User</Typography>
|
<Typography variant="h5">User</Typography>
|
||||||
<TextField label="Username" className={classes.margin} fullWidth />
|
<TextField label="Username" className={classes.margin} fullWidth />
|
||||||
<TextField label="Password" type="password" className={classes.margin} fullWidth />
|
<TextField
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
className={classes.margin}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
<div className={classes.margin}>
|
<div className={classes.margin}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Button variant="contained" color="primary" fullWidth>Update</Button>
|
<Button variant="contained" color="primary" fullWidth>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Button variant="contained" style={{ backgroundColor: "#d6381c", color: "white" }} fullWidth onClick={handleLogout}>Logout</Button>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
style={{ backgroundColor: "#d6381c", color: "white" }}
|
||||||
|
fullWidth
|
||||||
|
onClick={handleLogout}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</UI>
|
</UI>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <UIPlaceholder />;
|
return <UIPlaceholder />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from "react";
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from "@material-ui/core/Typography";
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from '@material-ui/core/Card';
|
import Card from "@material-ui/core/Card";
|
||||||
import CardContent from '@material-ui/core/CardContent';
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
import CardActions from '@material-ui/core/CardActions';
|
import CardActions from "@material-ui/core/CardActions";
|
||||||
import Snackbar from '@material-ui/core/Snackbar';
|
import Snackbar from "@material-ui/core/Snackbar";
|
||||||
import Grid from '@material-ui/core/Grid';
|
import Grid from "@material-ui/core/Grid";
|
||||||
import { makeStyles } from '@material-ui/core';
|
import { makeStyles } from "@material-ui/core";
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from "react-redux";
|
||||||
import Router from 'next/router';
|
import Router from "next/router";
|
||||||
import { store, persistor } from '../lib/store';
|
import { store, persistor } from "../lib/store";
|
||||||
import { UPDATE_USER, LOGIN } from '../lib/reducer';
|
import { UPDATE_USER, LOGIN } from "../lib/reducer";
|
||||||
import UIPlaceholder from '../components/UIPlaceholder';
|
import UIPlaceholder from "../components/UIPlaceholder";
|
||||||
import Alert from '@material-ui/lab/Alert';
|
import Alert from "@material-ui/lab/Alert";
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
field: {
|
field: {
|
||||||
width: '100%'
|
width: "100%",
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
padding: '10px'
|
padding: "10px",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,54 +36,84 @@ export default function Index() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
const handleClose = (event, reason) => {
|
||||||
if (reason === 'clickaway') return;
|
if (reason === "clickaway") return;
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
const d = await (await fetch('/api/user/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) })).json()
|
const d = await (
|
||||||
|
await fetch("/api/user/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username, password }),
|
||||||
|
})
|
||||||
|
).json();
|
||||||
if (!d.error) {
|
if (!d.error) {
|
||||||
dispatch({ type: UPDATE_USER, payload: d });
|
dispatch({ type: UPDATE_USER, payload: d });
|
||||||
dispatch({ type: LOGIN })
|
dispatch({ type: LOGIN });
|
||||||
Router.push('/');
|
Router.push("/");
|
||||||
} else {
|
} else {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (state.loggedIn) Router.push('/');
|
if (state.loggedIn) Router.push("/");
|
||||||
else return (
|
else
|
||||||
<div>
|
return (
|
||||||
<Snackbar
|
<div>
|
||||||
anchorOrigin={{
|
<Snackbar
|
||||||
vertical: 'top',
|
anchorOrigin={{
|
||||||
horizontal: 'center',
|
vertical: "top",
|
||||||
}}
|
horizontal: "center",
|
||||||
open={open}
|
}}
|
||||||
autoHideDuration={6000}
|
open={open}
|
||||||
onClose={handleClose}
|
autoHideDuration={6000}
|
||||||
>
|
onClose={handleClose}
|
||||||
<Alert severity="error" variant="filled">
|
>
|
||||||
Could not login!
|
<Alert severity="error" variant="filled">
|
||||||
</Alert>
|
Could not login!
|
||||||
</Snackbar>
|
</Alert>
|
||||||
<Grid container spacing={0} direction="column" alignItems="center" justify="center" style={{ minHeight: '100vh' }}>
|
</Snackbar>
|
||||||
<Grid item xs={6}>
|
<Grid
|
||||||
<Card>
|
container
|
||||||
<CardContent>
|
spacing={0}
|
||||||
<Typography color="textSecondary" variant="h3" gutterBottom>
|
direction="column"
|
||||||
Login
|
alignItems="center"
|
||||||
</Typography>
|
justify="center"
|
||||||
<TextField label="Username" className={classes.field} onChange={(e) => setUsername(e.target.value)} />
|
style={{ minHeight: "100vh" }}
|
||||||
<TextField label="Password" type="password" className={classes.field} onChange={(e) => setPassword(e.target.value)} />
|
>
|
||||||
</CardContent>
|
<Grid item xs={6}>
|
||||||
<CardActions>
|
<Card>
|
||||||
<Button color="primary" variant="contained" className={classes.field} onClick={handleLogin}>Login</Button>
|
<CardContent>
|
||||||
</CardActions>
|
<Typography color="textSecondary" variant="h3" gutterBottom>
|
||||||
</Card>
|
Login
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
label="Username"
|
||||||
|
className={classes.field}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
className={classes.field}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
<CardActions>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
className={classes.field}
|
||||||
|
onClick={handleLogin}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</div>
|
||||||
</div >
|
);
|
||||||
)
|
return <UIPlaceholder />;
|
||||||
return <UIPlaceholder />
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import UI from '../components/UI';
|
import UI from "../components/UI";
|
||||||
import { Typography } from '@material-ui/core';
|
import { Typography } from "@material-ui/core";
|
||||||
|
|
||||||
export default function MyApp(props) {
|
export default function MyApp(props) {
|
||||||
return (
|
return (
|
||||||
|
@ -8,5 +8,5 @@ export default function MyApp(props) {
|
||||||
<Typography>stater</Typography>
|
<Typography>stater</Typography>
|
||||||
</UI>
|
</UI>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"lib": [
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
@ -20,12 +16,6 @@
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"noEmit": false
|
"noEmit": false
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["next-env.d.ts", "src"],
|
||||||
"next-env.d.ts",
|
"exclude": ["node_modules", ".next"]
|
||||||
"src"
|
}
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
".next"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue