import { resolve } from "path"; import { scheduleJob } from "node-schedule"; import { createServer } from "net"; import jwt from "express-jwt"; import bodyParser from "body-parser"; import BotConfig from "@modules/config"; import useWebsocket from "express-ws"; import express, { Express } from "express"; import { Router } from 'express-serve-static-core'; import CheckRouter from "./routes/check"; import LoginRouter from "./routes/login"; import LogRouter from "./routes/log"; import UserRouter from "./routes/user"; import StatRouter from "./routes/stat"; export default class WebConsole { private readonly app: Express; private readonly secret: string; private isFirstListen: boolean = true; constructor( config: BotConfig ) { const cfg = config.webConsole; this.secret = cfg.jwtSecret; this.app = express(); this.createConsole( cfg.consolePort, cfg.tcpLoggerPort ); } private createConsole( port: number, tcp: number ) { this.app.use( express.static( resolve( __dirname, ".." ) ) ); this.app.use( bodyParser.json() ); this.app.use( bodyParser.urlencoded( { extended: false } ) ); /* 创建接口 */ this.useApi( "/check", CheckRouter, false ); this.useApi( "/api/login", LoginRouter, false ); this.useApi( "/api/log", LogRouter ); this.useApi( "/api/user", UserRouter ); this.useApi( "/api/stat", StatRouter ); /* 捕获错误 */ this.app.use( WebConsole.ApiErrorCatch ); useWebsocket( this.app ); createServer( socket => { let messageCache: string = ""; socket.setEncoding( "utf-8" ); socket.on( "data", res => { messageCache += res; } ); // @ts-ignore this.app.ws( "/ws/log", ( ws, req ) => { messageCache = ""; const cron: string = "*/2 * * * * ?"; const job = scheduleJob( cron, () => { if ( messageCache.length !== 0 ) { ws.send( messageCache ); messageCache = ""; } } ); ws.on( "close", () => job.cancel() && ws.close() ); } ); if ( this.isFirstListen ) { this.isFirstListen = false; this.app.listen( port ); } } ).listen( tcp ); } private static ApiErrorCatch( err, req, res, next ) { switch ( err.name ) { case "UnauthorizedError": res.status( 401 ).send( { code: 401, msg: "Please login.", data: 0 } ); break; } } /* 此处没有用 app.use+jwt.unless 进行全局隔离 */ /* WebSocket 疑似无法被过滤 所有 ws 连接被拦截 */ private static JWT( secret: string ) { return jwt( { secret, algorithms: [ "HS256" ], getToken( req ) { const auth = req.headers.authorization; if ( auth && auth.split( " " )[0] === "Bearer" ) { return auth.split( " " )[1] } else if ( req.query && req.query.token ) { return req.query.token; } return null; } } ); } private useApi( path: string, router: Router, token: boolean = true ): void { if ( token ) { this.app.use( path, WebConsole.JWT( this.secret ), router ); } else { this.app.use( path, router ); } } }