import ResponseTime from "./ResponseTime" import Send from "./Send" import { resolve } from "path" import { fileURLToPath } from "url" import path from "path" import ErrorHandler from "./ErrorHandler" import { AuthMiddleware } from "./Auth" import bodyParser from "koa-bodyparser" import Views from "./Views" import Session from "./Session" import etag from "@koa/etag" import conditional from "koa-conditional-get" import Controller from "./Controller/index.js" import app from "@/global" import fs from "fs" import helmet from "koa-helmet" import ratelimit from "koa-ratelimit" import { render } from "./PugHelper/sass.js" import AuthError from "@/utils/error/AuthError.js" import CommonError from "@/utils/error/CommonError.js" import { SiteConfigService } from "@/modules/SiteConfig/services/index.js" import config from "config/index.js" const __dirname = path.dirname(fileURLToPath(import.meta.url)) const publicPath = resolve(__dirname, "../../public") /** * 注册中间件 * @param {app} app */ export default async app => { // 响应时间 app.use(ResponseTime) // 拦截 Chrome DevTools 探测请求,直接返回 204 app.use((ctx, next) => { if (ctx.path === "/.well-known/appspecific/com.chrome.devtools.json") { ctx.status = 204 ctx.body = "" return } return next() }) // 跨域设置 app.use(async (ctx, next) => { ctx.set("Access-Control-Allow-Origin", "*") ctx.set("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS") ctx.set("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With") ctx.set("Access-Control-Allow-Credentials", true) if (ctx.method == "OPTIONS") { ctx.status = 200 } return await next() }) // 安全设置 app.use( helmet({ contentSecurityPolicy: { directives: { "script-src": ["'self'", "'unsafe-inline'"], }, }, }) ) // 应用限流 const db = new Map() app.use( ratelimit({ driver: "memory", db: db, duration: 60000, errorMessage: "Sometimes You Just Have to Slow Down.", id: ctx => ctx.ip, headers: { remaining: "Rate-Limit-Remaining", reset: "Rate-Limit-Reset", total: "Rate-Limit-Total", }, max: 100, disableHeader: false, whitelist: ctx => { // some logic that returns a boolean }, blacklist: ctx => { // some logic that returns a boolean }, }) ) // 提供全局数据 app.use(async (ctx, next) => { ctx.state.siteConfig = await SiteConfigService.getAll() ctx.state.$config = config return await next() }) // 错误处理,主要处理运行中抛出的错误 app.use(ErrorHandler()) // session设置 app.use(Session(app)) // 视图设置 app.use( Views(resolve(__dirname, "../views"), { extension: "pug", options: { basedir: resolve(__dirname, "../views"), filters: { // 处理scss scss: function (text, options) { //- process.env.SASS_PATH = "D:/@code/demo/koa3-demo/src/views/page/index" const root = path.resolve(__dirname, "../views") const publicPath = path.resolve(__dirname, "../../public") const publicScssPath = path.resolve(publicPath, "scss-compiler") const globalScss = `@import "_scss/global";` const result = render(globalScss + "\n" + text, { ...options, includePaths: [...(options.includePaths || []), path.resolve(__dirname, "../views")], }) return `` }, }, }, }) ) // 权限设置 app.use( AuthMiddleware({ whiteList: [ // 所有请求放行 { pattern: "/" }, { pattern: "/**/*" }, ], blackList: [ // 禁用api请求 // "/api", // "/api/", // "/api/**/*", ], }) ) // 验证用户 // 注入全局变量:ctx.state.user // app.use(VerifyUserMiddleware()) // 请求体解析 app.use(bodyParser()) app.use( await Controller({ root: path.resolve(__dirname, "../modules"), handleBeforeEachRequest: options => { const { auth = true } = options || {} return async (ctx, next) => { if (ctx.session && ctx.session.user) { ctx.state.user = ctx.session.user } else { const authorizationString = ctx.headers && ctx.headers["authorization"] if (authorizationString) { const token = authorizationString.replace(/^Bearer\s/, "") try { ctx.state.user = jwt.verify(token, process.env.JWT_SECRET) } catch (_) { // 无效token忽略 } } } if (auth === false && ctx.state.user) { throw new CommonError("不能登录查看") } if (auth === "try") { return next() } if (auth === true && !ctx.state.user) { throw new AuthError("需要登录才能访问") } return await next() } }, }) ) // 注册完成之后静态资源设置 app.use(async (ctx, next) => { if (ctx.body) return await next() if (ctx.status === 200) return await next() if (ctx.method.toLowerCase() === "get") { try { await Send(ctx, ctx.path, { root: publicPath, maxAge: 0, immutable: false }) } catch (err) { if (err.status !== 404) throw err } } await next() }) app.use(conditional()) app.use(etag()) }