add login
This commit is contained in:
parent
f70cfd041a
commit
a72225aff7
@ -11,11 +11,11 @@
|
||||
"start": "ts-node src/server.ts",
|
||||
"check-types": "tsc"
|
||||
},
|
||||
"browserslist":{
|
||||
"production":[
|
||||
"browserslist": {
|
||||
"production": [
|
||||
"> 10%"
|
||||
],
|
||||
"development":[
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version"
|
||||
]
|
||||
@ -25,6 +25,7 @@
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.2",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"knex": "^0.21.14",
|
||||
"koa": "^2.13.0",
|
||||
"koa-bodyparser": "^4.3.0",
|
||||
@ -44,6 +45,7 @@
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/preset-react": "^7.12.10",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/knex": "^0.16.1",
|
||||
"@types/koa": "^2.11.6",
|
||||
"@types/koa-bodyparser": "^4.3.0",
|
||||
|
@ -1,3 +1,8 @@
|
||||
{
|
||||
"path":["data"]
|
||||
"path": [
|
||||
"data"
|
||||
],
|
||||
"localmode": true,
|
||||
"guest": false,
|
||||
"jwt_secretkey": "itsRandom"
|
||||
}
|
@ -20,7 +20,7 @@ export class ClientContentAccessor implements ContentAccessor{
|
||||
* not implement
|
||||
*/
|
||||
async findListByBasePath(basepath: string): Promise<Content[]>{
|
||||
throw new Error("");
|
||||
throw new Error("not implement");
|
||||
return [];
|
||||
}
|
||||
async update(c: Partial<Content> & { id: number; }): Promise<boolean>{
|
||||
|
@ -1,28 +1,34 @@
|
||||
import React, { createContext, useRef, useState } from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import {BrowserRouter, Route, Switch as RouterSwitch} from 'react-router-dom';
|
||||
import { Gallery, ContentAbout} from './page/mod';
|
||||
import {BackLinkContext} from './state';
|
||||
import { Gallery, ContentAbout, LoginPage, NotFoundPage} from './page/mod';
|
||||
import {UserContext} from './state';
|
||||
|
||||
import './css/style.css';
|
||||
|
||||
const FooProfile = ()=><div>test profile</div>;
|
||||
const App = () => {
|
||||
const [path,setPath] = useState("/");
|
||||
const [user,setUser] = useState("");
|
||||
const [userPermission,setUserPermission] = useState<string[]>([]);
|
||||
return (
|
||||
<BackLinkContext.Provider value={{path:path,setPath:setPath}}>
|
||||
<UserContext.Provider value={{
|
||||
username:user,
|
||||
setUsername:setUser,
|
||||
permission:userPermission,
|
||||
setPermission:setUserPermission}}>
|
||||
<BrowserRouter>
|
||||
<RouterSwitch>
|
||||
<Route path="/" exact render={()=><Gallery />}></Route>
|
||||
<Route path="/search" render={()=><Gallery />}></Route>
|
||||
<Route path="/doc" render={(prop)=><ContentAbout {...prop}/>}></Route>
|
||||
<Route path="/login" render={()=><LoginPage></LoginPage>}/>
|
||||
<Route path="/profile" component={FooProfile}></Route>
|
||||
<Route>
|
||||
<div>404 Not Found</div>
|
||||
<NotFoundPage/>
|
||||
</Route>
|
||||
</RouterSwitch>
|
||||
</BrowserRouter>
|
||||
</BackLinkContext.Provider>);
|
||||
</UserContext.Provider>);
|
||||
};
|
||||
|
||||
ReactDom.render(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import ReactDom from 'react-dom';
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
import React, { ReactNode, useContext, useState } from 'react';
|
||||
import {
|
||||
Button, CssBaseline, Divider, IconButton, List, ListItem, Drawer,
|
||||
AppBar, Toolbar, Typography, InputBase, ListItemIcon, ListItemText, Menu, MenuItem,
|
||||
@ -8,6 +8,7 @@ import {
|
||||
import { makeStyles, Theme, useTheme, fade } from '@material-ui/core/styles';
|
||||
import { ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon, AccountCircle } from '@material-ui/icons';
|
||||
import { Link as RouterLink, useRouteMatch } from 'react-router-dom';
|
||||
import { UserContext } from '../state';
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
@ -106,7 +107,6 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||
|
||||
export const Headline = (prop: {
|
||||
children?: React.ReactNode,
|
||||
isLogin?: boolean,
|
||||
classes?:{
|
||||
content?:string,
|
||||
toolbar?:string,
|
||||
@ -122,7 +122,9 @@ export const Headline = (prop: {
|
||||
const handleProfileMenuClose = () => setAnchorEl(null);
|
||||
const isProfileMenuOpened = Boolean(anchorEl);
|
||||
const menuId = 'primary-search-account-menu';
|
||||
const isLogin = prop.isLogin || false;
|
||||
const user_ctx = useContext(UserContext);
|
||||
const isLogin = user_ctx.username !== "";
|
||||
|
||||
const renderProfileMenu = (<Menu
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: "top" }}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import {List, ListItem, ListItemIcon, Tooltip, ListItemText} from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import {Link as RouterLink} from 'react-router-dom';
|
||||
import {LocationDescriptorObject} from 'history';
|
||||
import {List, ListItem, ListItemIcon, Tooltip, ListItemText} from '@material-ui/core';
|
||||
import {ArrowBack as ArrowBackIcon} from '@material-ui/icons';
|
||||
import {Link as RouterLink, useHistory} from 'react-router-dom';
|
||||
|
||||
export const NavItem = (props:{name:string,to:string, icon:React.ReactElement<any,any>})=>{
|
||||
const history = useHistory();
|
||||
return (<ListItem button key={props.name} component={RouterLink} to={props.to}>
|
||||
<ListItemIcon>
|
||||
<Tooltip title={props.name.toLocaleLowerCase()} placement="bottom">
|
||||
@ -18,4 +19,8 @@ export const NavList = (props: {children?:React.ReactNode})=>{
|
||||
return (<List>
|
||||
{props.children}
|
||||
</List>);
|
||||
}
|
||||
|
||||
export const BackItem = (props:{to?:string})=>{
|
||||
return <NavItem name="Back" to={props.to ?? "/"} icon={<ArrowBackIcon></ArrowBackIcon>}/>;
|
||||
}
|
11
src/client/page/404.tsx
Normal file
11
src/client/page/404.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import {Typography} from '@material-ui/core';
|
||||
import {ArrowBack as ArrowBackIcon} from '@material-ui/icons';
|
||||
import { Headline, BackItem, NavList } from '../component/mod';
|
||||
|
||||
export const NotFoundPage = ()=>{
|
||||
const menu = (<NavList><BackItem to="/"/></NavList>);
|
||||
return <Headline menu={menu}>
|
||||
<Typography variant='h2'>404 Not Found</Typography>
|
||||
</Headline>
|
||||
};
|
@ -5,8 +5,7 @@ import { LoadingCircle } from '../component/loading';
|
||||
import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@material-ui/core';
|
||||
import {ArrowBack as ArrowBackIcon } from '@material-ui/icons';
|
||||
import { getPresenter } from './reader/reader';
|
||||
import { ContentInfo, Headline, NavItem, NavList } from '../component/mod';
|
||||
import {BackLinkContext} from '../state';
|
||||
import { BackItem, ContentInfo, Headline, NavItem, NavList } from '../component/mod';
|
||||
|
||||
export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
|
||||
export const makeMangaReaderUrl = (id: number) => `/doc/${id}/reader`;
|
||||
@ -35,29 +34,20 @@ export const ContentAbout = (prop: { match: MatchType }) => {
|
||||
}
|
||||
const id = Number.parseInt(match.params['id']);
|
||||
const [info, setInfo] = useState<ContentState>({ content: undefined, notfound:false });
|
||||
const location = useLocation();
|
||||
console.log("state : "+location.state);
|
||||
const history = useHistory();
|
||||
const menu_list = (link?:string)=>(
|
||||
<NavList>
|
||||
<BackLinkContext.Consumer>
|
||||
{
|
||||
(ctx) => link === undefined ?
|
||||
<NavItem name="Back" to={ctx.path} icon={<ArrowBackIcon/>}/>
|
||||
: <NavItem name="Back" to={link} icon={<ArrowBackIcon/>}/>
|
||||
}
|
||||
</BackLinkContext.Consumer>
|
||||
<BackItem to={link}/>
|
||||
</NavList>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
console.log("mount content about");
|
||||
if (!isNaN(id)) {
|
||||
const c = await ContentAccessor.findById(id);
|
||||
setInfo({ content: c, notfound: c === undefined });
|
||||
}
|
||||
})()
|
||||
return ()=>{console.log("unmount content about")}
|
||||
}, []);
|
||||
const classes = useStyles();
|
||||
|
||||
|
@ -1,19 +1,23 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { NavList, NavItem, Headline } from '../component/mod';
|
||||
import {ArrowBack as ArrowBackIcon} from '@material-ui/icons';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { NavList, NavItem, Headline, BackItem } from '../component/mod';
|
||||
import {ArrowBack as ArrowBackIcon, Settings as SettingIcon,
|
||||
Collections as CollectionIcon, VideoLibrary as VideoIcon, Home as HomeIcon} from '@material-ui/icons';
|
||||
import {GalleryInfo} from '../component/mod';
|
||||
import {BackLinkContext} from '../state';
|
||||
import {useLocation} from 'react-router-dom';
|
||||
import { QueryStringToMap } from '../accessor/util';
|
||||
import { Divider } from '@material-ui/core';
|
||||
|
||||
export const Gallery = ()=>{
|
||||
const location = useLocation();
|
||||
const backctx = useContext(BackLinkContext);
|
||||
backctx.setPath("/");
|
||||
|
||||
const query = QueryStringToMap(location.search);
|
||||
|
||||
const menu_list = (<NavList>
|
||||
{Object.keys(query).length !== 0 && <NavItem name="Back" to="/" icon={<ArrowBackIcon></ArrowBackIcon>}></NavItem>}
|
||||
{location.search !== "" && <><BackItem/> <Divider/></>}
|
||||
<NavItem name="All" to="/" icon={<HomeIcon/>}/>
|
||||
<NavItem name="Manga" to="/search?content_type=manga" icon={<CollectionIcon/>}></NavItem>
|
||||
<NavItem name="Video" to="/search?content_type=video" icon={<VideoIcon/>}/>
|
||||
<Divider/>
|
||||
<NavItem name="Settings" to="/setting" icon={<SettingIcon/>}/>
|
||||
</NavList>);
|
||||
|
||||
return (<Headline menu={menu_list}>
|
||||
|
70
src/client/page/login.tsx
Normal file
70
src/client/page/login.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import {Headline} from '../component/mod';
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogContentText,
|
||||
DialogTitle, MenuList, Paper, TextField, Typography, useTheme } from '@material-ui/core';
|
||||
import { UserContext } from '../state';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
export const LoginPage = ()=>{
|
||||
const theme = useTheme();
|
||||
const [userLoginInfo,setUserLoginInfo]= useState({username:"",password:""});
|
||||
const [openDialog,setOpenDialog] = useState({open:false,message:""});
|
||||
const {username,setUsername,permission,setPermission} = useContext(UserContext);
|
||||
const history = useHistory();
|
||||
const handleDialogClose = ()=>{
|
||||
setOpenDialog({...openDialog,open:false});
|
||||
}
|
||||
const doLogin = async ()=>{
|
||||
const res = await fetch('/user/login',{
|
||||
method:'POST',
|
||||
body:JSON.stringify(userLoginInfo),
|
||||
headers:{"content-type":"application/json"}
|
||||
});
|
||||
try{
|
||||
const b = await res.json();
|
||||
if(res.status !== 200){
|
||||
setOpenDialog({open:true,message: b.detail});
|
||||
return;
|
||||
}
|
||||
setUsername(b.username);
|
||||
setPermission(b.permission);
|
||||
}
|
||||
catch(e){
|
||||
if(e instanceof Error){
|
||||
console.error(e);
|
||||
setOpenDialog({open:true,message:e.message});
|
||||
}
|
||||
else console.error(e);
|
||||
return;
|
||||
}
|
||||
history.push("/");
|
||||
}
|
||||
const menu = (<MenuList>
|
||||
</MenuList>);
|
||||
return <Headline menu={menu}>
|
||||
<Paper style={{ width: theme.spacing(40), padding: theme.spacing(4), alignSelf:'center'}}>
|
||||
<Typography variant="h4">Login</Typography>
|
||||
<div style={{minHeight:theme.spacing(2)}}></div>
|
||||
<form style={{display:'flex', flexFlow:'column', alignItems:'stretch'}}>
|
||||
<TextField label="username" onChange={(e)=>setUserLoginInfo({...userLoginInfo,username:e.target.value ?? "",})}></TextField>
|
||||
<TextField label="password" type="password"
|
||||
onChange={(e)=>setUserLoginInfo({...userLoginInfo,password:e.target.value ?? "",})}/>
|
||||
<div style={{minHeight:theme.spacing(2)}}></div>
|
||||
<div style={{display:'flex'}}>
|
||||
<Button onClick={doLogin}>login</Button>
|
||||
<Button>signin</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Paper>
|
||||
<Dialog open={openDialog.open}
|
||||
onClose={handleDialogClose}>
|
||||
<DialogTitle>Login Failed</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>detail : {openDialog.message}</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleDialogClose} color="primary" autoFocus>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Headline>
|
||||
}
|
@ -1,2 +1,4 @@
|
||||
export * from './contentinfo';
|
||||
export * from './gallery';
|
||||
export * from './gallery';
|
||||
export * from './login';
|
||||
export * from './404';
|
||||
|
@ -1,3 +1,8 @@
|
||||
import React, { createContext, useRef, useState } from 'react';
|
||||
|
||||
export const BackLinkContext = createContext({path:"",setPath:(s:string)=>{}});
|
||||
export const UserContext = createContext({
|
||||
username:"",
|
||||
permission:["openContent"],
|
||||
setUsername:(s:string)=>{},
|
||||
setPermission:(permission:string[])=>{}
|
||||
});
|
@ -8,7 +8,7 @@ export async function connectDB(){
|
||||
if(env != "production" && env != "development"){
|
||||
throw new Error("process unknown value in NODE_ENV: must be either \"development\" or \"production\"");
|
||||
}
|
||||
const init_need = existsSync(config[env].connection.filename);
|
||||
const init_need = !existsSync(config[env].connection.filename);
|
||||
const knex = Knex(config[env]);
|
||||
let tries = 0;
|
||||
for(;;){
|
||||
@ -30,6 +30,7 @@ export async function connectDB(){
|
||||
break;
|
||||
}
|
||||
if(init_need){
|
||||
console.log("first execute: initialize database...");
|
||||
const migrate = await import("../migrations/initial");
|
||||
await migrate.up(knex);
|
||||
}
|
||||
|
76
src/login.ts
Normal file
76
src/login.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import {sign, decode, verify} from 'jsonwebtoken';
|
||||
import Koa from 'koa';
|
||||
import Router from 'koa-router';
|
||||
import { sendError } from './route/error_handler';
|
||||
import Knex from 'knex'
|
||||
import { createKnexUserController } from './db/mod';
|
||||
import { request } from 'http';
|
||||
import { get_setting } from './setting';
|
||||
import { IUser } from './model/mod';
|
||||
|
||||
const loginTokenName = 'access_token'
|
||||
|
||||
export const createLoginMiddleware = (knex: Knex)=>{
|
||||
const userController = createKnexUserController(knex);
|
||||
return async (ctx: Koa.Context,next: Koa.Next)=>{
|
||||
const setting = get_setting();
|
||||
const secretKey = setting.jwt_secretkey;
|
||||
const body = ctx.request.body;
|
||||
if(!('username' in body)||!('password' in body)){
|
||||
sendError(400,"invalid form : username or password is not found in query.");
|
||||
return;
|
||||
}
|
||||
const username = body['username'];
|
||||
const password = body['password'];
|
||||
if(typeof username !== "string" || typeof password !== "string"){
|
||||
sendError(400,"invalid form : username or password is not string")
|
||||
return;
|
||||
}
|
||||
const user = await userController.findUser(username);
|
||||
if(user === undefined){
|
||||
sendError(401,"not authorized");
|
||||
return;
|
||||
}
|
||||
if(!user.password.check_password(password)){
|
||||
sendError(401,"not authorized");
|
||||
return;
|
||||
}
|
||||
const userPermission = await user.get_permissions();
|
||||
const payload = sign({
|
||||
username: user.username,
|
||||
permission: userPermission
|
||||
},secretKey,{expiresIn:'3d'});
|
||||
ctx.cookies.set(loginTokenName,payload,{httpOnly:true, secure: !setting.localmode,sameSite:'strict'});
|
||||
ctx.body = {ok:true, username: user.username, permission: userPermission}
|
||||
console.log(`${username} logined`);
|
||||
return;
|
||||
};
|
||||
};
|
||||
export const LogoutMiddleware = (ctx:Koa.Context,next:Koa.Next)=>{
|
||||
ctx.cookies.set(loginTokenName,undefined);
|
||||
ctx.body = {ok:true};
|
||||
return;
|
||||
}
|
||||
export const UserMiddleWare = async (ctx:Koa.Context,next:Koa.Next)=>{
|
||||
const secretKey = get_setting().jwt_secretkey;
|
||||
const payload = ctx.cookies.get(loginTokenName);
|
||||
if(payload == undefined){
|
||||
ctx.state['user'] = undefined;
|
||||
return await next();
|
||||
}
|
||||
ctx.state['user'] = verify(payload,secretKey);
|
||||
await next();
|
||||
}
|
||||
|
||||
export const getAdmin = async(knex : Knex)=>{
|
||||
const cntr = createKnexUserController(knex);
|
||||
const admin = await cntr.findUser("admin");
|
||||
if(admin === undefined){
|
||||
throw new Error("initial process failed!");//???
|
||||
}
|
||||
return admin;
|
||||
}
|
||||
|
||||
export const isAdminFirst = (admin: IUser)=>{
|
||||
return admin.password.hash === "unchecked" && admin.password.salt === "unchecked";
|
||||
}
|
@ -34,6 +34,7 @@ const ContentQueryHandler = (controller : ContentAccessor) => async (ctx: Contex
|
||||
const limit = ParseQueryNumber(ctx.query['limit']);
|
||||
const cursor = ParseQueryNumber(ctx.query['cursor']);
|
||||
const word: string|undefined = ctx.query['word'];
|
||||
const content_type:string|undefined = ctx.query['content_type'];
|
||||
const offset = ParseQueryNumber(ctx.query['offset']);
|
||||
if(limit === NaN || cursor === NaN || offset === NaN){
|
||||
sendError(400,"parameter limit, cursor or offset is not a number");
|
||||
@ -51,7 +52,8 @@ const ContentQueryHandler = (controller : ContentAccessor) => async (ctx: Contex
|
||||
cursor: cursor,
|
||||
eager_loading: true,
|
||||
offset: offset,
|
||||
use_offset: use_offset
|
||||
use_offset: use_offset,
|
||||
content_type:content_type,
|
||||
};
|
||||
let content = await controller.findList(option);
|
||||
ctx.body = content;
|
||||
|
@ -11,56 +11,64 @@ import { createKnexContentsAccessor } from './db/contents';
|
||||
import bodyparser from 'koa-bodyparser';
|
||||
import {error_handler} from './route/error_handler';
|
||||
|
||||
import {UserMiddleWare,createLoginMiddleware, isAdminFirst, getAdmin, LogoutMiddleware} from './login';
|
||||
|
||||
import {createInterface as createReadlineInterface} from 'readline';
|
||||
|
||||
//let Koa = require("koa");
|
||||
async function main(){
|
||||
let settings = get_setting();
|
||||
let db = await connectDB();
|
||||
const userAdmin = await getAdmin(db);
|
||||
if(await isAdminFirst(userAdmin)){
|
||||
const rl = createReadlineInterface({input:process.stdin,output:process.stdout});
|
||||
rl.setPrompt("put admin password : ");
|
||||
rl.prompt();
|
||||
const pw = await new Promise((res:(data:string)=>void,err)=>{
|
||||
rl.on('line',(data)=>res(data));
|
||||
});
|
||||
userAdmin.reset_password(pw);
|
||||
}
|
||||
let app = new Koa();
|
||||
app.use(bodyparser());
|
||||
app.use(error_handler);
|
||||
app.use(UserMiddleWare);
|
||||
//app.use(ctx=>{ctx.state['setting'] = settings});
|
||||
|
||||
const index_html = readFileSync("index.html");
|
||||
let router = new Router();
|
||||
|
||||
let settings = get_setting();
|
||||
|
||||
let db = await connectDB();
|
||||
let watcher = new Watcher(settings.path[0]);
|
||||
await watcher.setup([]);
|
||||
console.log(settings);
|
||||
router.get('/', async (ctx,next)=>{
|
||||
ctx.type = "html";
|
||||
ctx.body = index_html;
|
||||
});
|
||||
router.get('/dist/css/style.css',async (ctx,next)=>{
|
||||
ctx.type = "css";
|
||||
ctx.body = createReadStream("dist/css/style.css");
|
||||
});
|
||||
router.get('/dist/js/bundle.js',async (ctx,next)=>{
|
||||
ctx.type = "js";
|
||||
ctx.body = createReadStream("dist/js/bundle.js");
|
||||
});
|
||||
router.get('/dist/js/bundle.js.map',async (ctx,next)=>{
|
||||
ctx.type = "text";
|
||||
ctx.body = createReadStream("dist/js/bundle.js.map");
|
||||
});
|
||||
router.get('/doc/:rest(.*)'
|
||||
,async (ctx,next)=>{
|
||||
ctx.type = "html";
|
||||
ctx.body = index_html;
|
||||
const serveindex = (url:string)=>{
|
||||
router.get(url, (ctx)=>{ctx.type = 'html'; ctx.body = index_html;})
|
||||
}
|
||||
);
|
||||
router.get('/search'
|
||||
,async (ctx,next)=>{
|
||||
ctx.type = "html";
|
||||
ctx.body = index_html;
|
||||
}
|
||||
);
|
||||
let content_router = getContentRouter(createKnexContentsAccessor(db));
|
||||
serveindex('/');
|
||||
serveindex('/doc/:rest(.*)');
|
||||
serveindex('/search');
|
||||
serveindex('/login');
|
||||
|
||||
const static_file_server = (path:string,type:string) => {
|
||||
router.get('/'+path,async (ctx,next)=>{
|
||||
ctx.type = type; ctx.body = createReadStream(path);
|
||||
})}
|
||||
static_file_server('dist/css/style.css','css');
|
||||
static_file_server('dist/js/bundle.js','js');
|
||||
static_file_server('dist/js/bundle.js.map','text');
|
||||
|
||||
const content_router = getContentRouter(createKnexContentsAccessor(db));
|
||||
router.use('/content',content_router.routes());
|
||||
router.use('/content',content_router.allowedMethods());
|
||||
|
||||
router.post('/user/login',createLoginMiddleware(db));
|
||||
router.post('/user/logout',LogoutMiddleware);
|
||||
|
||||
let mm_count=0;
|
||||
let mm_count = 0;
|
||||
app.use(async (ctx,next)=>{
|
||||
console.log(`==========================${mm_count++}`);
|
||||
console.log(`connect ${ctx.ip} : ${ctx.method} ${ctx.url}`);
|
||||
const fromClient = ctx.state['user'] === undefined ? ctx.ip : ctx.state['user'].username;
|
||||
console.log(`${fromClient} : ${ctx.method} ${ctx.url}`);
|
||||
await next();
|
||||
//console.log(`404`);
|
||||
});
|
||||
@ -68,7 +76,7 @@ async function main(){
|
||||
app.use(router.allowedMethods());
|
||||
|
||||
console.log("start server");
|
||||
app.listen(8080,"0.0.0.0");
|
||||
app.listen(8080,settings.localmode ? "127.0.0.1" : "0.0.0.0");
|
||||
return app;
|
||||
}
|
||||
main();
|
@ -1,26 +1,28 @@
|
||||
import { Settings } from '@material-ui/icons';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
export type Setting = {
|
||||
path: string[],
|
||||
initial_admin_password:string,
|
||||
localmode: boolean,
|
||||
guest: boolean,
|
||||
jwt_secretkey: string
|
||||
}
|
||||
const default_setting:Setting = {
|
||||
path:[],
|
||||
initial_admin_password:"admin",
|
||||
localmode: true,
|
||||
guest:false,
|
||||
jwt_secretkey:"itsRandom",
|
||||
}
|
||||
let setting: null|Setting = null;
|
||||
const setEmptyToDefault = (target:any,default_table:any)=>{
|
||||
|
||||
const setEmptyToDefault = (target:any,default_table:Setting)=>{
|
||||
let diff_occur = false;
|
||||
for(const key in default_table){
|
||||
if(key === undefined || key in target){
|
||||
continue;
|
||||
}
|
||||
target[key] = default_table[key];
|
||||
target[key] = default_table[key as keyof Setting];
|
||||
diff_occur = true;
|
||||
}
|
||||
return diff_occur;
|
||||
@ -28,8 +30,8 @@ const setEmptyToDefault = (target:any,default_table:any)=>{
|
||||
|
||||
export const read_setting_from_file = ()=>{
|
||||
let ret = JSON.parse(readFileSync("settings.json",{encoding:"utf8"})) as Setting;
|
||||
const diff_occur = setEmptyToDefault(ret,default_setting);
|
||||
if(diff_occur){
|
||||
const partial_occur = setEmptyToDefault(ret,default_setting);
|
||||
if(partial_occur){
|
||||
writeFileSync("settings.json",JSON.stringify(ret));
|
||||
}
|
||||
return ret;
|
||||
|
Loading…
Reference in New Issue
Block a user