import { Request, Response, NextFunction, Application } from 'express'; import bodyParser from 'body-parser'; import cors from 'cors'; import routes from '../api'; import config from '../config'; import jwt from 'express-jwt'; import fs from 'fs'; import { getFileContentByName, getPlatform, getToken } from '../config/util'; import Container from 'typedi'; import OpenService from '../services/open'; import rewrite from 'express-urlrewrite'; import UserService from '../services/user'; import handler from 'serve-handler'; import * as Sentry from '@sentry/node'; export default ({ app }: { app: Application }) => { app.enable('trust proxy'); app.use(cors()); app.use((req, res, next) => { if (req.path.startsWith('/api') || req.path.startsWith('/open')) { next(); } else { return handler(req, res, { public: 'dist', rewrites: [{ source: '**', destination: '/index.html' }], }); } }); app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); app.use( jwt({ secret: config.secret as string, algorithms: ['HS384'], }).unless({ path: [...config.apiWhiteList, /^\/open\//], }), ); app.use((req: Request, res, next) => { if (!req.headers) { req.platform = 'desktop'; } else { const platform = getPlatform(req.headers['user-agent'] || ''); req.platform = platform; } return next(); }); app.use(async (req, res, next) => { const headerToken = getToken(req); if (req.path.startsWith('/open/')) { const openService = Container.get(OpenService); const doc = await openService.findTokenByValue(headerToken); if (doc && doc.tokens && doc.tokens.length > 0) { const currentToken = doc.tokens.find((x) => x.value === headerToken); const keyMatch = req.path.match(/\/open\/([a-z]+)\/*/); const key = keyMatch && keyMatch[1]; if ( doc.scopes.includes(key as any) && currentToken && currentToken.expiration >= Math.round(Date.now() / 1000) ) { return next(); } } } if ( !headerToken && req.path && config.apiWhiteList.includes(req.path) && req.path !== '/api/crons/status' ) { return next(); } const remoteAddress = req.socket.remoteAddress; if ( remoteAddress === '::ffff:127.0.0.1' && req.path === '/api/crons/status' ) { return next(); } const data = fs.readFileSync(config.authConfigFile, 'utf8'); if (data) { const { token = '', tokens = {} } = JSON.parse(data); if (headerToken === token || tokens[req.platform] === headerToken) { return next(); } } const err: any = new Error('UnauthorizedError'); err.status = 401; next(err); }); app.use(async (req, res, next) => { if (!['/api/init/user', '/api/init/notification'].includes(req.path)) { return next(); } const userService = Container.get(UserService); const authInfo = await userService.getUserInfo(); const envDbContent = getFileContentByName(config.envDbFile); let isInitialized = true; if ( Object.keys(authInfo).length === 2 && authInfo.username === 'admin' && authInfo.password === 'admin' && envDbContent.length === 0 ) { isInitialized = false; } if (isInitialized) { return res.send({ code: 450, message: '未知错误' }); } else { return next(); } }); app.use(rewrite('/open/*', '/api/$1')); app.use(config.api.prefix, routes()); app.use((req, res, next) => { const err: any = new Error('Not Found'); err['status'] = 404; next(err); }); app.use(Sentry.Handlers.errorHandler()); app.use( ( err: Error & { status: number }, req: Request, res: Response, next: NextFunction, ) => { if (err.name === 'UnauthorizedError') { return res .status(err.status) .send({ code: 401, message: err.message }) .end(); } return next(err); }, ); app.use( ( err: Error & { status: number }, req: Request, res: Response, next: NextFunction, ) => { Sentry.captureException(err); res.status(err.status || 500); res.json({ code: err.status || 500, message: err.message, }); }, ); };