From 994c7d6013472b0861a2cf2c3774e5b3d3f98f95 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Sat, 17 Dec 2022 15:06:22 +0800 Subject: [PATCH] format --- .prettierrc | 4 +- packages/hapi-router/src/index.ts | 367 +++++++++++++--------------- packages/hapi-router/src/util/decorators.ts | 20 +- packages/hapi-router/src/util/index.ts | 135 +++++----- public/js/common/main.js | 30 +-- public/js/page/login.js | 2 +- public/style/common/normalize.css | 122 ++++----- public/style/common/style.css | 32 +-- route.txt | 4 +- source/auth/index.ts | 38 +-- source/log4js_config.ts | 68 +++--- source/main.ts | 30 +-- source/models/Color.ts | 12 +- source/models/Constant.ts | 12 +- source/models/User.ts | 93 +++---- source/models/ha/user.ts | 26 +- source/models/ha/user_info.ts | 42 ++-- source/plugins/file-plugin.ts | 52 ++-- source/plugins/index.ts | 286 +++++++++++----------- source/route/api/index.ts | 20 +- source/route/api/v1/upload/_upload.ts | 143 ++++++----- source/route/api/v1/upload/index.ts | 48 ++-- source/route/api/v1/user/index.ts | 176 +++++++------ source/route/htmx/index.ts | 2 +- source/route/views/index.ts | 164 ++++++------- source/route/views/index/index.ts | 165 ++++++------- source/route/views/nav/index.ts | 8 +- source/run.ts | 262 ++++++++++---------- source/schema/index.ts | 34 +-- source/util/index.ts | 16 +- source/util/res-helper.ts | 44 ++-- source/util/util.ts | 60 ++--- template/404.pug | 8 +- template/helper/flush.pug | 120 ++++----- template/helper/form_security.pug | 2 +- template/md/about.md | 2 +- template/ui/header.pug | 79 +++--- template/views/about.pug | 6 +- template/views/index.pug | 12 +- template/views/login.pug | 20 +- template/views/user.pug | 14 +- types/global.d.ts | 27 +- 42 files changed, 1391 insertions(+), 1416 deletions(-) diff --git a/.prettierrc b/.prettierrc index d55c43f..380e87f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -7,5 +7,5 @@ "bracketSpacing": true, "arrowParens": "avoid", "htmlWhitespaceSensitivity": "ignore", - "printWidth": 120 -} \ No newline at end of file + "printWidth": 120 +} diff --git a/packages/hapi-router/src/index.ts b/packages/hapi-router/src/index.ts index b5ad8a5..2b983a7 100755 --- a/packages/hapi-router/src/index.ts +++ b/packages/hapi-router/src/index.ts @@ -1,211 +1,190 @@ // @ts-nocheck -import { walkDir, removeIndex, isIndexEnd } from "./util"; -import * as Joi from "joi"; -const path = require("path"); -const fs = require("fs"); +import { walkDir, removeIndex, isIndexEnd } from "./util" +import * as Joi from "joi" +const path = require("path") +const fs = require("fs") class routePlugin { - public name: string = "routePlugin"; - public version: string = "0.0.1"; - public register(server: any, opts: any) { - const sourceDir = opts.sourceDir; - const type = opts.type || "jwt"; - const auth = opts.auth || []; - let array = []; - for (let i = 0; i < sourceDir.length; i++) { - const dir = sourceDir[i]; - console.log(dir); - array.push(dir.dir + "对应路径:"); - array = array.concat( - this.registerRoute(server, dir.dir, dir.prefix || "", auth, type) - ); + public name: string = "routePlugin" + public version: string = "0.0.1" + public register(server: any, opts: any) { + const sourceDir = opts.sourceDir + const type = opts.type || "jwt" + const auth = opts.auth || [] + let array = [] + for (let i = 0; i < sourceDir.length; i++) { + const dir = sourceDir[i] + console.log(dir) + array.push(dir.dir + "对应路径:") + array = array.concat(this.registerRoute(server, dir.dir, dir.prefix || "", auth, type)) + } + fs.writeFileSync(path.resolve(process.cwd(), "route.txt"), array.join("\n"), { + encoding: "utf-8", + }) } - fs.writeFileSync( - path.resolve(process.cwd(), "route.txt"), - array.join("\n"), - { - encoding: "utf-8", - } - ); - } - registerRoute(server, sourceDir, prefix, auth, type) { - const files = walkDir(sourceDir); - const routes = []; - files.forEach((file) => { - let filename = file.relativeFileNoExt; - let array = filename.split(path.sep).slice(1); - let fileNoExt = removeIndex("/" + array.join("/")); - const moduleName = path.resolve(sourceDir, filename); - const obj = require(moduleName); - if (obj.default) { - const func = new (obj.default || obj)(); - const prototype = Object.getPrototypeOf(func); - const keys = Reflect.ownKeys(prototype); - for (const key of keys) { - if (key !== "constructor") { - let ff = func[key]; - let handler:()=>void = undefined - // 默认方法 - const method = ff.$method || "GET"; - // 路由收集规则 - let route = ""; - if (ff.$route) { - if (isIndexEnd(fileNoExt)) { - route = ff.$route; - } else { - route = fileNoExt + ff.$route; - } - } else { - if (isIndexEnd(fileNoExt)) { - route = fileNoExt + key.toString(); - } else { - route = fileNoExt + "/" + key.toString(); - } - } - route = removeIndex(route); - route = prefix ? route[0] + prefix + "/" + route.slice(1) : route; - // 配置规则 - const options = ff.$options ? ff.$options : {}; - if (!options.auth) { - if (ff.$auth == undefined) { - if ( - auth && - auth.length && - auth.filter((v) => route.startsWith(v)).length - ) { - options.auth = type; - } else { - options.auth = false; - } - } else if (ff.$auth) { - options.auth = - typeof ff.$auth === "boolean" - ? type - : { - strategy: type, - mode: ff.$auth, - }; - } else { - options.auth = false; - } - } - if (!options.validate) { - let validateObj = ff.$validate || {}; - if (options.auth && type === "jwt") { - if (validateObj.headers) { - validateObj.headers = validateObj.headers.keys({ - Authorization: Joi.string(), - }); - } else { - validateObj.headers = Joi.object({ - headers: Joi.object({ - Authorization: Joi.string(), - }).unknown(), // 注意加上这个 - }); - } - } - if (validateObj&&!!Object.keys(validateObj).length) { - const failReason = validateObj.failReason - delete validateObj.failReason - if(validateObj.failAction === "log"){ - if(!options.log) options.log = {} - options.log.collect = true - let errto = validateObj.$errto - handler = async function (...argus){ - const request = argus[0] - const h = argus[1] - if(request.logs&&!!request.logs.length && errto){ - // request.yar.flash('error', request.logs.map((v: any)=>v.error.message)); - request.yar.flash('error', failReason); - return h.redirect(errto); + registerRoute(server, sourceDir, prefix, auth, type) { + const files = walkDir(sourceDir) + const routes = [] + files.forEach(file => { + let filename = file.relativeFileNoExt + let array = filename.split(path.sep).slice(1) + let fileNoExt = removeIndex("/" + array.join("/")) + const moduleName = path.resolve(sourceDir, filename) + const obj = require(moduleName) + if (obj.default) { + const func = new (obj.default || obj)() + const prototype = Object.getPrototypeOf(func) + const keys = Reflect.ownKeys(prototype) + for (const key of keys) { + if (key !== "constructor") { + let ff = func[key] + let handler: () => void = undefined + // 默认方法 + const method = ff.$method || "GET" + // 路由收集规则 + let route = "" + if (ff.$route) { + if (isIndexEnd(fileNoExt)) { + route = ff.$route + } else { + route = fileNoExt + ff.$route + } + } else { + if (isIndexEnd(fileNoExt)) { + route = fileNoExt + key.toString() + } else { + route = fileNoExt + "/" + key.toString() + } } - return await ff.call(this, ...argus) - } - } - if(validateObj.failAction === "function"){ - let errto = validateObj.$errto - validateObj.failAction = async function(request, h, err){ - if(err.details){ - request.$joi_error = err.details.map(v=>v.message) + route = removeIndex(route) + route = prefix ? route[0] + prefix + "/" + route.slice(1) : route + // 配置规则 + const options = ff.$options ? ff.$options : {} + if (!options.auth) { + if (ff.$auth == undefined) { + if (auth && auth.length && auth.filter(v => route.startsWith(v)).length) { + options.auth = type + } else { + options.auth = false + } + } else if (ff.$auth) { + options.auth = + typeof ff.$auth === "boolean" + ? type + : { + strategy: type, + mode: ff.$auth, + } + } else { + options.auth = false + } } - return h.continue; - } - handler = async function (...argus){ - const request = argus[0] - const h = argus[1] - if(request.$joi_error){ - loggerSite.debug('传输参数错误: ', request.$joi_error) - request.yar.flash('error', failReason); - delete request.$joi_error - return h.redirect(errto); + if (!options.validate) { + let validateObj = ff.$validate || {} + if (options.auth && type === "jwt") { + if (validateObj.headers) { + validateObj.headers = validateObj.headers.keys({ + Authorization: Joi.string(), + }) + } else { + validateObj.headers = Joi.object({ + headers: Joi.object({ + Authorization: Joi.string(), + }).unknown(), // 注意加上这个 + }) + } + } + if (validateObj && !!Object.keys(validateObj).length) { + const failReason = validateObj.failReason + delete validateObj.failReason + if (validateObj.failAction === "log") { + if (!options.log) options.log = {} + options.log.collect = true + let errto = validateObj.$errto + handler = async function (...argus) { + const request = argus[0] + const h = argus[1] + if (request.logs && !!request.logs.length && errto) { + // request.yar.flash('error', request.logs.map((v: any)=>v.error.message)); + request.yar.flash("error", failReason) + return h.redirect(errto) + } + return await ff.call(this, ...argus) + } + } + if (validateObj.failAction === "function") { + let errto = validateObj.$errto + validateObj.failAction = async function (request, h, err) { + if (err.details) { + request.$joi_error = err.details.map(v => v.message) + } + return h.continue + } + handler = async function (...argus) { + const request = argus[0] + const h = argus[1] + if (request.$joi_error) { + loggerSite.debug("传输参数错误: ", request.$joi_error) + request.yar.flash("error", failReason) + delete request.$joi_error + return h.redirect(errto) + } + return await ff.call(this, ...argus) + } + } + options.validate = validateObj + } + } + // && route.startsWith("/api") + if (ff.$swagger) { + options.description = ff.$swagger[0] + options.notes = ff.$swagger[1] + options.tags = ff.$swagger[2] + } + let str = route + if ( + (typeof options.auth === "string" && options.auth) || + (typeof options.auth === "object" && options.auth.mode === "required") + ) { + str = " 需要权限 : " + " " + full(method) + " " + str + } else if (typeof options.auth === "object" && options.auth.mode === "optional") { + str = " 不需权限(提供即需验证): " + " " + full(method) + " " + str + } else if (typeof options.auth === "object" && options.auth.mode === "try") { + str = " 不需权限(提供无需验证): " + " " + full(method) + " " + str + } else { + str = " 不需权限 : " + " " + full(method) + " " + str } - return await ff.call(this, ...argus) + routes.push(str) + + if (options.validate && options.validate.$errto) { + delete options.validate.$errto + } + if (!handler) { + handler = ff + } + server.route({ + method: method, + path: route, + handler: handler, + options: options, + }) } } - options.validate = validateObj; - } - } - // && route.startsWith("/api") - if (ff.$swagger) { - options.description = ff.$swagger[0]; - options.notes = ff.$swagger[1]; - options.tags = ff.$swagger[2]; - } - let str = route; - if ( - (typeof options.auth === "string" && options.auth) || - (typeof options.auth === "object" && - options.auth.mode === "required") - ) { - str = - " 需要权限 : " + " " + full(method) + " " + str; - } else if ( - typeof options.auth === "object" && - options.auth.mode === "optional" - ) { - str = - " 不需权限(提供即需验证): " + " " + full(method) + " " + str; - } else if ( - typeof options.auth === "object" && - options.auth.mode === "try" - ) { - str = - " 不需权限(提供无需验证): " + " " + full(method) + " " + str; - } else { - str = - " 不需权限 : " + " " + full(method) + " " + str; } - routes.push(str); - - if(options.validate && options.validate.$errto){ - delete options.validate.$errto - } - if(!handler){ - handler = ff - } - server.route({ - method: method, - path: route, - handler: handler, - options: options, - }); - } - } - } - }); - return routes; - } + }) + return routes + } } function full(str: string, length = 10) { - let len = str.length; - let need = length - len; - if (need <= 0) return str; - return str + [...Array(need)].map((v, i) => " ").join(""); + let len = str.length + let need = length - len + if (need <= 0) return str + return str + [...Array(need)].map((v, i) => " ").join("") } -const plugin = new routePlugin(); +const plugin = new routePlugin() -export { plugin }; -export * from "./util/decorators"; +export { plugin } +export * from "./util/decorators" diff --git a/packages/hapi-router/src/util/decorators.ts b/packages/hapi-router/src/util/decorators.ts index 79cda4c..2f5635f 100644 --- a/packages/hapi-router/src/util/decorators.ts +++ b/packages/hapi-router/src/util/decorators.ts @@ -1,40 +1,40 @@ // @ts-nocheck /** - * 方法 - * @param opts 参数 -*/ + * 方法 + * @param opts 参数 + */ type TMethod = "GET" | "POST" | "PUT" | "DELETE" -export function method(opts?:TMethod|Array) { +export function method(opts?: TMethod | Array) { return function (target, propertyKey: string, descriptor: PropertyDescriptor) { target[propertyKey].$method = opts } } -export function route(route?:string) { +export function route(route?: string) { return function (target, propertyKey: string, descriptor: PropertyDescriptor) { target[propertyKey].$route = route } } -export function config(options:Object) { +export function config(options: Object) { return function (target, propertyKey: string, descriptor: PropertyDescriptor) { target[propertyKey].$options = options } } -export function auth(isAuth:boolean | "try" | "required" | "optional" = true) { +export function auth(isAuth: boolean | "try" | "required" | "optional" = true) { return function (target, propertyKey: string, descriptor: PropertyDescriptor) { target[propertyKey].$auth = isAuth } } -export function validate(validate:Object) { +export function validate(validate: Object) { return function (target, propertyKey: string, descriptor: PropertyDescriptor) { target[propertyKey].$validate = validate } } -export function swagger(desc,notes,tags) { +export function swagger(desc, notes, tags) { return function (target, propertyKey: string, descriptor: PropertyDescriptor) { - target[propertyKey].$swagger = [desc,notes,tags] + target[propertyKey].$swagger = [desc, notes, tags] } } diff --git a/packages/hapi-router/src/util/index.ts b/packages/hapi-router/src/util/index.ts index f445892..a02d731 100644 --- a/packages/hapi-router/src/util/index.ts +++ b/packages/hapi-router/src/util/index.ts @@ -3,80 +3,79 @@ const path = require("path") const fs = require("fs") -export function removeIndex(ss:any) { - const remove = (str:any) => { - if (str.endsWith("/index")) { - return str.slice(0, -6); - } - if (str.endsWith("index")) { - return str.slice(0, -5); +export function removeIndex(ss: any) { + const remove = (str: any) => { + if (str.endsWith("/index")) { + return str.slice(0, -6) + } + if (str.endsWith("index")) { + return str.slice(0, -5) + } + return str ? str : "/" } - return str ? str : "/"; - }; - let r = true; - let rr = ss; - while (r) { - if (rr.endsWith("/index")) { - rr = remove(rr); - } else { - r = false; + let r = true + let rr = ss + while (r) { + if (rr.endsWith("/index")) { + rr = remove(rr) + } else { + r = false + } } - } - return rr ? rr : "/"; + return rr ? rr : "/" } -export function isIndexEnd(str:any) { - return str.length == 1 && str.endsWith("/"); +export function isIndexEnd(str: any) { + return str.length == 1 && str.endsWith("/") } - export function walkDir( - filePath:any, - exclude = ["node_modules", "^_", ".git", ".idea", ".gitignore", "client","\.txt$","\.test\.js$","\.test\.ts$"] + filePath: any, + exclude = ["node_modules", "^_", ".git", ".idea", ".gitignore", "client", ".txt$", ".test.js$", ".test.ts$"], ) { - let files:any[] = []; - function Data(opts:any) { - this.relativeDir = opts.relativeDir; - this.relativeFile = opts.relativeFile; - this.filename = opts.filename; - this.file = opts.file; - this.absoluteFile = opts.absoluteFile; - this.relativeFileNoExt = opts.relativeFileNoExt; - this.absoluteDir = opts.absoluteDir; - } - function readDir(filePath, dirname = ".") { - let res = fs.readdirSync(filePath); - res.forEach((filename) => { - const filepath = path.resolve(filePath, filename); - const stat = fs.statSync(filepath); - const name = filepath.split(path.sep).slice(-1)[0]; - if (typeof exclude === "string" && new RegExp(exclude).test(name)) { - return; - } - if (Array.isArray(exclude)) { - for (let i = 0; i < exclude.length; i++) { - const excludeItem = exclude[i]; - if (new RegExp(excludeItem).test(name)) { - return; - } - } - } - if (!stat.isFile()) { - readDir(filepath, dirname + path.sep + name); - } else { - const data = new Data({ - relativeDir: dirname, - relativeFile: dirname + path.sep + path.parse(filepath).base, - relativeFileNoExt: dirname + path.sep + path.parse(filepath).name, - file: path.parse(filepath).base, - filename: path.parse(filepath).name, - absoluteFile: filepath, - absoluteDir: path.parse(filepath).dir, - }); - files.push(data); - } - }); - } - readDir(filePath); - return files; + let files: any[] = [] + function Data(opts: any) { + this.relativeDir = opts.relativeDir + this.relativeFile = opts.relativeFile + this.filename = opts.filename + this.file = opts.file + this.absoluteFile = opts.absoluteFile + this.relativeFileNoExt = opts.relativeFileNoExt + this.absoluteDir = opts.absoluteDir + } + function readDir(filePath, dirname = ".") { + let res = fs.readdirSync(filePath) + res.forEach(filename => { + const filepath = path.resolve(filePath, filename) + const stat = fs.statSync(filepath) + const name = filepath.split(path.sep).slice(-1)[0] + if (typeof exclude === "string" && new RegExp(exclude).test(name)) { + return + } + if (Array.isArray(exclude)) { + for (let i = 0; i < exclude.length; i++) { + const excludeItem = exclude[i] + if (new RegExp(excludeItem).test(name)) { + return + } + } + } + if (!stat.isFile()) { + readDir(filepath, dirname + path.sep + name) + } else { + const data = new Data({ + relativeDir: dirname, + relativeFile: dirname + path.sep + path.parse(filepath).base, + relativeFileNoExt: dirname + path.sep + path.parse(filepath).name, + file: path.parse(filepath).base, + filename: path.parse(filepath).name, + absoluteFile: filepath, + absoluteDir: path.parse(filepath).dir, + }) + files.push(data) + } + }) + } + readDir(filePath) + return files } diff --git a/public/js/common/main.js b/public/js/common/main.js index be6f62b..2bc823e 100644 --- a/public/js/common/main.js +++ b/public/js/common/main.js @@ -15,24 +15,26 @@ document.addEventListener("DOMContentLoaded", () => { }) }) - const $messages = Array.prototype.slice.call(document.querySelectorAll(".message-container .message button.delete"), 0) + const $messages = Array.prototype.slice.call( + document.querySelectorAll(".message-container .message button.delete"), + 0, + ) $messages.forEach((el, index) => { - let timeID; + let timeID function click() { - // Get the target from the "data-target" attribute - const target = el.dataset.target - const $target = document.getElementById(target) - el.removeEventListener("click", click) - $target.remove() - clearTimeout(timeID) + // Get the target from the "data-target" attribute + const target = el.dataset.target + const $target = document.getElementById(target) + el.removeEventListener("click", click) + $target.remove() + clearTimeout(timeID) } timeID = setTimeout(() => { - const target = el.dataset.target - const $target = document.getElementById(target) - el.removeEventListener("click", click) - $target.remove() - }, (index + 1) * 6000); + const target = el.dataset.target + const $target = document.getElementById(target) + el.removeEventListener("click", click) + $target.remove() + }, (index + 1) * 6000) el.addEventListener("click", click) }) }) - diff --git a/public/js/page/login.js b/public/js/page/login.js index 7ef26ef..9f92d06 100644 --- a/public/js/page/login.js +++ b/public/js/page/login.js @@ -14,4 +14,4 @@ if (referer) { let url = new URLSearchParams(window.location.search).get("next") referer.value = url ? url : "" } -console.log(referer); +console.log(referer) diff --git a/public/style/common/normalize.css b/public/style/common/normalize.css index c469989..5d8c1e3 100644 --- a/public/style/common/normalize.css +++ b/public/style/common/normalize.css @@ -8,9 +8,9 @@ * 2. Prevent adjustments of font size after orientation changes in iOS. */ - html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ } /* Sections @@ -21,7 +21,7 @@ */ body { - margin: 0; + margin: 0; } /** @@ -29,7 +29,7 @@ body { */ main { - display: block; + display: block; } /** @@ -38,8 +38,8 @@ main { */ h1 { - font-size: 2em; - margin: 0.67em 0; + font-size: 2em; + margin: 0.67em 0; } /* Grouping content @@ -51,9 +51,9 @@ h1 { */ hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ } /** @@ -62,8 +62,8 @@ hr { */ pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ } /* Text-level semantics @@ -74,7 +74,7 @@ pre { */ a { - background-color: transparent; + background-color: transparent; } /** @@ -83,9 +83,9 @@ a { */ abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ } /** @@ -94,7 +94,7 @@ abbr[title] { b, strong { - font-weight: bolder; + font-weight: bolder; } /** @@ -105,8 +105,8 @@ strong { code, kbd, samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ } /** @@ -114,7 +114,7 @@ samp { */ small { - font-size: 80%; + font-size: 80%; } /** @@ -124,18 +124,18 @@ small { sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } sub { - bottom: -0.25em; + bottom: -0.25em; } sup { - top: -0.5em; + top: -0.5em; } /* Embedded content @@ -146,7 +146,7 @@ sup { */ img { - border-style: none; + border-style: none; } /* Forms @@ -162,10 +162,10 @@ input, optgroup, select, textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ } /** @@ -174,8 +174,9 @@ textarea { */ button, -input { /* 1 */ - overflow: visible; +input { + /* 1 */ + overflow: visible; } /** @@ -184,8 +185,9 @@ input { /* 1 */ */ button, -select { /* 1 */ - text-transform: none; +select { + /* 1 */ + text-transform: none; } /** @@ -196,7 +198,7 @@ button, [type="button"], [type="reset"], [type="submit"] { - -webkit-appearance: button; + -webkit-appearance: button; } /** @@ -207,8 +209,8 @@ button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; + border-style: none; + padding: 0; } /** @@ -219,7 +221,7 @@ button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; + outline: 1px dotted ButtonText; } /** @@ -227,7 +229,7 @@ button:-moz-focusring, */ fieldset { - padding: 0.35em 0.75em 0.625em; + padding: 0.35em 0.75em 0.625em; } /** @@ -238,12 +240,12 @@ fieldset { */ legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ } /** @@ -251,7 +253,7 @@ legend { */ progress { - vertical-align: baseline; + vertical-align: baseline; } /** @@ -259,7 +261,7 @@ progress { */ textarea { - overflow: auto; + overflow: auto; } /** @@ -269,8 +271,8 @@ textarea { [type="checkbox"], [type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ } /** @@ -279,7 +281,7 @@ textarea { [type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { - height: auto; + height: auto; } /** @@ -288,8 +290,8 @@ textarea { */ [type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ } /** @@ -297,7 +299,7 @@ textarea { */ [type="search"]::-webkit-search-decoration { - -webkit-appearance: none; + -webkit-appearance: none; } /** @@ -306,8 +308,8 @@ textarea { */ ::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ } /* Interactive @@ -318,7 +320,7 @@ textarea { */ details { - display: block; + display: block; } /* @@ -326,7 +328,7 @@ details { */ summary { - display: list-item; + display: list-item; } /* Misc @@ -337,7 +339,7 @@ summary { */ template { - display: none; + display: none; } /** @@ -345,5 +347,5 @@ template { */ [hidden] { - display: none; + display: none; } diff --git a/public/style/common/style.css b/public/style/common/style.css index 227e8ef..484566d 100644 --- a/public/style/common/style.css +++ b/public/style/common/style.css @@ -1,21 +1,21 @@ -html{ - overflow-y: auto +html { + overflow-y: auto; } -.message-container{ - position: fixed; - right: 0; - top: 0; - z-index: 999; - overflow: auto; - height: 100vh; +.message-container { + position: fixed; + right: 0; + top: 0; + z-index: 999; + overflow: auto; + height: 100vh; } -.message-container::-webkit-scrollbar{ - display: none; +.message-container::-webkit-scrollbar { + display: none; } -.message-container .message{ - min-width: 250px; - max-width: 250px; - margin: 25px; - box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; +.message-container .message { + min-width: 250px; + max-width: 250px; + margin: 25px; + box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; } diff --git a/route.txt b/route.txt index 744d4ac..2da14ef 100644 --- a/route.txt +++ b/route.txt @@ -1,6 +1,6 @@ -D:\1XYX\demo\hapi-demo\source\route\htmx对应路径: +/home/topuser/桌面/demo/hapi-demo/source/route/htmx对应路径: 不需权限 : GET /htmx/clicked -D:\1XYX\demo\hapi-demo\source\route\views对应路径: +/home/topuser/桌面/demo/hapi-demo/source/route/views对应路径: 不需权限(提供无需验证): GET /404 不需权限 : GET /css 不需权限(提供无需验证): GET / diff --git a/source/auth/index.ts b/source/auth/index.ts index 67e182d..454953b 100644 --- a/source/auth/index.ts +++ b/source/auth/index.ts @@ -1,27 +1,27 @@ -import { Req } from "#/global"; +import { Req } from "#/global" export async function validateJwt(decoded, request: Req, h) { - if (decoded.id) { - const User = request.getModel("User") - const result = await User.findOne({ where: { id: decoded.id } }); - if (result == null) { - return { isValid: false }; + if (decoded.id) { + const User = request.getModel("User") + const result = await User.findOne({ where: { id: decoded.id } }) + if (result == null) { + return { isValid: false } + } + return { isValid: true } + } else { + return { isValid: false } } - return { isValid: true }; - } else { - return { isValid: false }; - } } export async function validateSession(request: Req, session) { - const User = request.getModel("User") - if (session.id) { - const result = await User.findOne({ where: { id: session.id } }); - if (result == null) { - return { valid: false }; + const User = request.getModel("User") + if (session.id) { + const result = await User.findOne({ where: { id: session.id } }) + if (result == null) { + return { valid: false } + } + return { valid: true, credentials: result } + } else { + return { valid: false } } - return { valid: true, credentials: result }; - } else { - return { valid: false }; - } } diff --git a/source/log4js_config.ts b/source/log4js_config.ts index c839aeb..7da21e5 100644 --- a/source/log4js_config.ts +++ b/source/log4js_config.ts @@ -1,37 +1,37 @@ -import path from "path"; +import path from "path" export default function () { - return { - appenders: { - file: { - type: "file", - filename: path.resolve(__dirname, "../", "./log", "./Site.log"), - }, - SQL: { - type: "file", - filename: path.resolve(__dirname, "../", "./log", "./SQL.log"), - }, - console: { - type: "console", - }, - }, - categories: { - default: { - appenders: ["console"], - level: "all", - }, - HAPI: { - appenders: ["console"], - level: "all", - }, - Site: { - appenders: ["file", "console"], - level: "debug", - }, - SQL: { - appenders: ["SQL"], - level: "debug", - }, - }, - }; + return { + appenders: { + file: { + type: "file", + filename: path.resolve(__dirname, "../", "./log", "./Site.log"), + }, + SQL: { + type: "file", + filename: path.resolve(__dirname, "../", "./log", "./SQL.log"), + }, + console: { + type: "console", + }, + }, + categories: { + default: { + appenders: ["console"], + level: "all", + }, + HAPI: { + appenders: ["console"], + level: "all", + }, + Site: { + appenders: ["file", "console"], + level: "debug", + }, + SQL: { + appenders: ["SQL"], + level: "debug", + }, + }, + } } diff --git a/source/main.ts b/source/main.ts index 139667e..4633b3c 100644 --- a/source/main.ts +++ b/source/main.ts @@ -1,21 +1,21 @@ // require("dotenv").config(); -import { configure, getLogger } from "log4js"; -import log4jsConfig from "./log4js_config"; +import { configure, getLogger } from "log4js" +import log4jsConfig from "./log4js_config" -configure(log4jsConfig()); -const loggerSite = getLogger("Site"); -const loggerSQL = getLogger("SQL"); -const logger = getLogger("HAPI"); +configure(log4jsConfig()) +const loggerSite = getLogger("Site") +const loggerSQL = getLogger("SQL") +const logger = getLogger("HAPI") -loggerSite.level = "debug"; -loggerSQL.level = "debug"; +loggerSite.level = "debug" +loggerSQL.level = "debug" -global.logger = logger; -global.loggerSite = loggerSite; -global.loggerSQL = loggerSQL; +global.logger = logger +global.loggerSite = loggerSite +global.loggerSQL = loggerSQL -import { run } from "./run"; +import { run } from "./run" -run().then((server) => { - global.server = server; -}); +run().then(server => { + global.server = server +}) diff --git a/source/models/Color.ts b/source/models/Color.ts index b6daa45..a2b8dd7 100644 --- a/source/models/Color.ts +++ b/source/models/Color.ts @@ -4,20 +4,20 @@ module.exports = function (sequelize, DataTypes) { { // 图片地址 color: { - type: DataTypes.STRING + type: DataTypes.STRING, }, title: { type: DataTypes.STRING, - allowNull: true + allowNull: true, }, describe: { type: DataTypes.STRING, - allowNull: true + allowNull: true, }, }, { timestamps: false, - } - ); + }, + ) return Color -}; \ No newline at end of file +} diff --git a/source/models/Constant.ts b/source/models/Constant.ts index 06c467c..ed95c76 100644 --- a/source/models/Constant.ts +++ b/source/models/Constant.ts @@ -4,21 +4,21 @@ module.exports = function (sequelize, DataTypes) { { // 键 key: { - type: DataTypes.STRING + type: DataTypes.STRING, }, // 值 value: { type: DataTypes.STRING, - allowNull: true + allowNull: true, }, describe: { type: DataTypes.STRING, - allowNull: true + allowNull: true, }, }, { timestamps: false, - } - ); + }, + ) return Constant -}; \ No newline at end of file +} diff --git a/source/models/User.ts b/source/models/User.ts index 8fc769b..0c0b144 100644 --- a/source/models/User.ts +++ b/source/models/User.ts @@ -1,64 +1,67 @@ import { Sequelize, DataTypes, Optional, Model } from "sequelize" interface UserAttributes { - id: number; - username: string; - password: string; - nickname: string; - email: string; + id: number + username: string + password: string + nickname: string + email: string - createdAt?: Date; - updatedAt?: Date; - deletedAt?: Date; + createdAt?: Date + updatedAt?: Date + deletedAt?: Date } -export interface UserInput extends Optional { } -export interface UserOuput extends Required { } +export interface UserInput extends Optional {} +export interface UserOuput extends Required {} export type TUserModel = ReturnType type DT = typeof DataTypes export default function UserModel(sequelize: Sequelize, DataTypes: DT) { class User extends Model implements UserAttributes { - public id: number; - public username: string; - public password: string; - public nickname: string; - public email: string; + public id: number + public username: string + public password: string + public nickname: string + public email: string // timestamps! - public readonly createdAt!: Date; - public readonly updatedAt!: Date; - public readonly deletedAt!: Date; + public readonly createdAt!: Date + public readonly updatedAt!: Date + public readonly deletedAt!: Date } - User.init({ - id: { - type: DataTypes.INTEGER.UNSIGNED, - autoIncrement: true, - primaryKey: true, + User.init( + { + id: { + type: DataTypes.INTEGER.UNSIGNED, + autoIncrement: true, + primaryKey: true, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + }, + nickname: { + type: DataTypes.STRING, + allowNull: false, + }, + email: { + type: DataTypes.STRING, + }, }, - username: { - type: DataTypes.STRING, - allowNull: false + { + sequelize, + timestamps: true, + paranoid: true, // 对模型施加了一个软删除 }, - password: { - type: DataTypes.STRING, - allowNull: false - }, - nickname: { - type: DataTypes.STRING, - allowNull: false - }, - email: { - type: DataTypes.STRING, - } - }, { - sequelize, - timestamps: true, - paranoid: true // 对模型施加了一个软删除 - }) + ) // 覆盖User的toJSON方法 - interface User{ - toJSON: ()=> UserOuput + interface User { + toJSON: () => UserOuput } return User -}; +} diff --git a/source/models/ha/user.ts b/source/models/ha/user.ts index 4f47933..8abfdbc 100644 --- a/source/models/ha/user.ts +++ b/source/models/ha/user.ts @@ -2,17 +2,19 @@ import { Sequelize, DataTypes } from "sequelize" type DT = typeof DataTypes module.exports = function (sequelize: Sequelize, DataTypes: DT) { - const User = sequelize.define('ha-user', { - username: { - type: DataTypes.STRING, - allowNull: false + const User = sequelize.define( + "ha-user", + { + username: { + type: DataTypes.STRING, + allowNull: false, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + }, }, - password: { - type: DataTypes.STRING, - allowNull: false - } - }, { - - }); + {}, + ) return User -}; +} diff --git a/source/models/ha/user_info.ts b/source/models/ha/user_info.ts index 90ed365..1a78130 100644 --- a/source/models/ha/user_info.ts +++ b/source/models/ha/user_info.ts @@ -2,28 +2,30 @@ import { Sequelize, DataTypes } from "sequelize" type DT = typeof DataTypes module.exports = function (sequelize: Sequelize, DataTypes: DT) { - const UserInfo = sequelize.define('ha-user_info', { - nickname: { - type: DataTypes.STRING, - allowNull: false + const UserInfo = sequelize.define( + "ha-user_info", + { + nickname: { + type: DataTypes.STRING, + allowNull: false, + }, + email: { + type: DataTypes.STRING, + }, + avatar: { + type: DataTypes.STRING, + }, + tel: { + type: DataTypes.STRING, + }, }, - email: { - type: DataTypes.STRING, - }, - avatar: { - type: DataTypes.STRING, - }, - tel: { - type: DataTypes.STRING, - } - }, { - - }); + {}, + ) // @ts-ignore UserInfo.associate = function (models) { - models['ha-user'].hasOne(models['ha-user_info']); - models['ha-user_info'].belongsTo(models['ha-user'], { foreignKey: 'user_id' }); - }; + models["ha-user"].hasOne(models["ha-user_info"]) + models["ha-user_info"].belongsTo(models["ha-user"], { foreignKey: "user_id" }) + } return UserInfo -}; +} diff --git a/source/plugins/file-plugin.ts b/source/plugins/file-plugin.ts index 97f4a6b..93e0015 100644 --- a/source/plugins/file-plugin.ts +++ b/source/plugins/file-plugin.ts @@ -1,29 +1,29 @@ -import { publicDir } from "@/util"; -const Inert = require("@hapi/inert"); +import { publicDir } from "@/util" +const Inert = require("@hapi/inert") const filePlugin = { - name: "filePlugin", - version: "0.0.1", - register: async function (server, options) { - server.settings.routes = { - files: { - relativeTo: publicDir, - }, - }; - await server.register(Inert); - server.route({ - method: "GET", - path: "/public/{param*}", - config: { auth: false }, - handler: { - directory: { - path: publicDir, - index: true, - redirectToSlash: true, - }, - }, - }); - }, -}; + name: "filePlugin", + version: "0.0.1", + register: async function (server, options) { + server.settings.routes = { + files: { + relativeTo: publicDir, + }, + } + await server.register(Inert) + server.route({ + method: "GET", + path: "/public/{param*}", + config: { auth: false }, + handler: { + directory: { + path: publicDir, + index: true, + redirectToSlash: true, + }, + }, + }) + }, +} -export default filePlugin; +export default filePlugin diff --git a/source/plugins/index.ts b/source/plugins/index.ts index 4ccd53c..af12679 100644 --- a/source/plugins/index.ts +++ b/source/plugins/index.ts @@ -1,155 +1,159 @@ -import filePlugin from "./file-plugin"; -import path from "path"; -import { baseDir, sourceDir } from "@/util"; -import { plugin as routePlugin } from "@noderun/hapi-router"; -import {ServerRegisterPluginObject, Plugin, Server, Request, ResponseObject} from "@hapi/hapi" +import filePlugin from "./file-plugin" +import path from "path" +import { baseDir, sourceDir } from "@/util" +import { plugin as routePlugin } from "@noderun/hapi-router" +import { ServerRegisterPluginObject, Plugin, Server, Request, ResponseObject } from "@hapi/hapi" import Hoek from "@hapi/hoek" -import HapiYar from "@hapi/yar"; +import HapiYar from "@hapi/yar" import HapiCrumb from "@hapi/crumb" -import { Stream } from "stream"; -import HapiPino from "hapi-pino"; +import { Stream } from "stream" +import HapiPino from "hapi-pino" -const pino = require('pino') +const pino = require("pino") const transport = pino.transport({ - targets: [ - // process.env.NODE_ENV !== 'production' ? { - // target: 'pino-pretty', - // options: { - // colorize: true, - // translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"' - // }, - // level: 'info' } : { target: 'pino/file', level: 'info' }, - { target: 'pino-pretty', level: 'trace', options: { - destination: path.resolve(baseDir, "./log/pino.log"), - colorize: true, - translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"', - mkdir: true, - // append: false - }} - // { target: 'pino/file', level: 'trace', options: { - // destination: path.resolve(baseDir, "./log/pino.log"), - // mkdir: true, - // // append: false - // }} - ] + targets: [ + // process.env.NODE_ENV !== 'production' ? { + // target: 'pino-pretty', + // options: { + // colorize: true, + // translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"' + // }, + // level: 'info' } : { target: 'pino/file', level: 'info' }, + { + target: "pino-pretty", + level: "trace", + options: { + destination: path.resolve(baseDir, "./log/pino.log"), + colorize: true, + translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"', + mkdir: true, + // append: false + }, + }, + // { target: 'pino/file', level: 'trace', options: { + // destination: path.resolve(baseDir, "./log/pino.log"), + // mkdir: true, + // // append: false + // }} + ], }) export default [ - { - plugin: HapiPino, - options: { - logPayload: true, - logQueryParams: true, - logPathParams: true, - logRouteTags: true, - // logRequestStart: true, - instance: pino(transport), - ignoreFunc: (options, request) => { - return request.path.startsWith('/public') || request.path.startsWith('/404') + { + plugin: HapiPino, + options: { + logPayload: true, + logQueryParams: true, + logPathParams: true, + logRouteTags: true, + // logRequestStart: true, + instance: pino(transport), + ignoreFunc: (options, request) => { + return request.path.startsWith("/public") || request.path.startsWith("/404") + }, + // stream: fs.createWriteStream(path.resolve(baseDir, "./log/pino.log"), { encoding: "utf-8" }) + // prettyPrint: process.env.NODE_ENV !== 'production', + // Redact Authorization headers, see https://getpino.io/#/docs/redaction + // redact: ['req.headers.cookie'] + }, + }, + { + plugin: filePlugin as unknown as Plugin, + }, + { + plugin: routePlugin as Plugin, + options: { + auth: ["/api"], + sourceDir: [ + // { + // dir: path.resolve(sourceDir, "route/api"), + // prefix: "api" + // }, + { + dir: path.resolve(sourceDir, "route/htmx"), + prefix: "htmx", + }, + { + dir: path.resolve(sourceDir, "route/views"), + prefix: "", + }, + ], + type: "session", }, - // stream: fs.createWriteStream(path.resolve(baseDir, "./log/pino.log"), { encoding: "utf-8" }) - // prettyPrint: process.env.NODE_ENV !== 'production', - // Redact Authorization headers, see https://getpino.io/#/docs/redaction - // redact: ['req.headers.cookie'] - } - }, - { - plugin: filePlugin as unknown as Plugin, - }, - { - plugin: routePlugin as Plugin, - options: { - auth: ['/api'], - sourceDir: [ - // { - // dir: path.resolve(sourceDir, "route/api"), - // prefix: "api" - // }, - { - dir: path.resolve(sourceDir, "route/htmx"), - prefix: "htmx" - }, - { - dir: path.resolve(sourceDir, "route/views"), - prefix: "" - }, - ], - type: "session" }, - }, - { - plugin: { - name: "flash", - version: "0.0.1", - // https://github.com/hks-epod/paydash/blob/master/lib/flash.js - register: function (server: Server, options) { - server.ext('onPreResponse', async function(request: Request, h) { - // @ts-ignore - if(request.response.variety === "file") return h.continue; // 返回文件时不处理 - if(request.path.startsWith("/api") || request.path.startsWith("/htmx")) return h.continue; - // 需要设置auth是try或者true才行 - const isLogin = request.auth.isAuthenticated; - loggerSite.debug(`是否登录:${isLogin}, 请求路径:${request.path}, 请求方法:${request.method}`) + { + plugin: { + name: "flash", + version: "0.0.1", + // https://github.com/hks-epod/paydash/blob/master/lib/flash.js + register: function (server: Server, options) { + server.ext("onPreResponse", async function (request: Request, h) { + // @ts-ignore + if (request.response.variety === "file") return h.continue // 返回文件时不处理 + if (request.path.startsWith("/api") || request.path.startsWith("/htmx")) return h.continue + // 需要设置auth是try或者true才行 + const isLogin = request.auth.isAuthenticated + loggerSite.debug(`是否登录:${isLogin}, 请求路径:${request.path}, 请求方法:${request.method}`) - // @ts-ignore - // console.log(isLogin, request.path, request.response.variety); - // let user; - // if(isLogin){ - // const { id } = request.auth.credentials; - // const User = request.getModel("User") - // user = await User.findOne({ where: { id: id } }); - // user = user.toJSON(); - // delete user.password; - // } - // @ts-ignore - if (request.yar && request.yar.flash && request.response.variety === 'view') { - var flash = request.yar.flash(); - request.yar.set('_flash', {}); // @ts-ignore - request.response.source.context = Hoek.applyToDefaults( - { - flash: !!Object.keys(flash).length?flash:false, - isLogin: isLogin, - user: isLogin?request.auth.credentials:false, - }, - // @ts-ignore - request.response.source.context - ); + // console.log(isLogin, request.path, request.response.variety); + // let user; + // if(isLogin){ + // const { id } = request.auth.credentials; + // const User = request.getModel("User") + // user = await User.findOne({ where: { id: id } }); + // user = user.toJSON(); + // delete user.password; + // } // @ts-ignore + if (request.yar && request.yar.flash && request.response.variety === "view") { + var flash = request.yar.flash() + request.yar.set("_flash", {}) + // @ts-ignore + request.response.source.context = Hoek.applyToDefaults( + { + flash: !!Object.keys(flash).length ? flash : false, + isLogin: isLogin, + user: isLogin ? request.auth.credentials : false, + }, + // @ts-ignore + request.response.source.context, + ) + // @ts-ignore + } + return h.continue + }) + }, + } as Plugin, + }, + { + plugin: HapiYar, + options: { + name: "yar", + storeBlank: false, + cookieOptions: { + password: "dsRhw1Y5UZqB8SjfClbkrX9PF7yuDMV3JItcW0G4vgpaxONo6mzenHLQET2AiKyPUjjdgjo10amjfghy", + path: "/", + isSecure: false, + }, + }, + }, + { + plugin: HapiCrumb, + options: { + autoGenerate: true, + logUnauthorized: true, + skip: function (request, reply) { + // 流的话直接通过,不需要验证,主要用于传递form-data数据时这里通不过 + if (request.payload instanceof Stream) { + return true } - return h.continue; - }); - } - } as Plugin - }, - { - plugin: HapiYar, - options: { - name: "yar", - storeBlank: false, - cookieOptions: { - password: "dsRhw1Y5UZqB8SjfClbkrX9PF7yuDMV3JItcW0G4vgpaxONo6mzenHLQET2AiKyPUjjdgjo10amjfghy", - path: '/', - isSecure: false - } - } - }, - { - plugin: HapiCrumb, - options: { - autoGenerate: true, - logUnauthorized: true, - skip: function(request, reply) { - // 流的话直接通过,不需要验证,主要用于传递form-data数据时这里通不过 - if(request.payload instanceof Stream){ - return true - } - return false; + return false + }, + cookieOptions: { + path: "/", + isSecure: false, + }, }, - cookieOptions: { - path: '/', - isSecure: false - } - } - }, -] as unknown as ServerRegisterPluginObject; + }, +] as unknown as ServerRegisterPluginObject diff --git a/source/route/api/index.ts b/source/route/api/index.ts index 8051e80..9a66c3d 100644 --- a/source/route/api/index.ts +++ b/source/route/api/index.ts @@ -1,13 +1,13 @@ -import { Req, Res, ReturnValue } from "#/global"; -import { auth, route } from "@noderun/hapi-router"; +import { Req, Res, ReturnValue } from "#/global" +import { auth, route } from "@noderun/hapi-router" export default class { - @route("/{path*}") - @auth(false) - async any(req: Req, h: Res): ReturnValue { - return { - code: 404, - data: null, - }; - } + @route("/{path*}") + @auth(false) + async any(req: Req, h: Res): ReturnValue { + return { + code: 404, + data: null, + } + } } diff --git a/source/route/api/v1/upload/_upload.ts b/source/route/api/v1/upload/_upload.ts index f07fbc3..d1b7129 100644 --- a/source/route/api/v1/upload/_upload.ts +++ b/source/route/api/v1/upload/_upload.ts @@ -1,83 +1,82 @@ -import path from "path"; -import { gSuccess, gFail, uploadDir, uploadPath } from "@/util"; +import path from "path" +import { gSuccess, gFail, uploadDir, uploadPath } from "@/util" import { dateTimeFormat } from "@/util/util" -const fs = require("fs"); -const multiparty = require("multiparty"); -const FileType = require("file-type"); +const fs = require("fs") +const multiparty = require("multiparty") +const FileType = require("file-type") function saveFile(file) { - return new Promise(async (resolve, reject) => { - const filename = file.originalFilename; - const uploadedPath = file.path; - const filetype = await FileType.fromFile(uploadedPath); - const _file = path.parse(filename); - if (filetype && (filetype.ext == "jpg" || filetype.ext == "png")) { - let _name = _file.name + "_" + dateTimeFormat(new Date(), "yyyy_MM_dd") + "_" + new Date().getTime() + _file.ext - const dstPath = path.resolve(uploadDir, _name); - fs.rename(uploadedPath, dstPath, function (err) { - if (err) { - console.log("rename error: " + err); - reject(); + return new Promise(async (resolve, reject) => { + const filename = file.originalFilename + const uploadedPath = file.path + const filetype = await FileType.fromFile(uploadedPath) + const _file = path.parse(filename) + if (filetype && (filetype.ext == "jpg" || filetype.ext == "png")) { + let _name = + _file.name + "_" + dateTimeFormat(new Date(), "yyyy_MM_dd") + "_" + new Date().getTime() + _file.ext + const dstPath = path.resolve(uploadDir, _name) + fs.rename(uploadedPath, dstPath, function (err) { + if (err) { + console.log("rename error: " + err) + reject() + } else { + resolve(dstPath) + } + }) } else { - resolve(dstPath); + fs.unlinkSync(uploadedPath) + reject(new Error(filename + "文件不是图片")) } - }); - } else { - fs.unlinkSync(uploadedPath); - reject(new Error(filename + "文件不是图片")); - } - }); + }) } export default function (req, h) { - const form = new multiparty.Form({ - uploadDir: uploadDir, //路径需要对应自己的项目更改 - /*设置文件保存路径 */ - encoding: "utf-8", - /*编码设置 */ - maxFilesSize: 20000 * 1024 * 1024, - /*设置文件最大值 20MB */ - keepExtensions: true, - /*保留后缀*/ - }); - return new Promise(async (resolve, reject) => { - form.on("part", function (part) { - console.log(part.filename); - }); - form.on("progress", function (bytesReceived, bytesExpected) { - if (bytesExpected === null) { - return; - } + const form = new multiparty.Form({ + uploadDir: uploadDir, //路径需要对应自己的项目更改 + /*设置文件保存路径 */ + encoding: "utf-8", + /*编码设置 */ + maxFilesSize: 20000 * 1024 * 1024, + /*设置文件最大值 20MB */ + keepExtensions: true, + /*保留后缀*/ + }) + return new Promise(async (resolve, reject) => { + form.on("part", function (part) { + console.log(part.filename) + }) + form.on("progress", function (bytesReceived, bytesExpected) { + if (bytesExpected === null) { + return + } - var percentComplete = (bytesReceived / bytesExpected) * 100; - console.log( - "the form is " + Math.floor(percentComplete) + "%" + " complete" - ); - }); - form.parse(req.payload, async function (err, fields, files) { - // console.log(err, fields, files); + var percentComplete = (bytesReceived / bytesExpected) * 100 + console.log("the form is " + Math.floor(percentComplete) + "%" + " complete") + }) + form.parse(req.payload, async function (err, fields, files) { + // console.log(err, fields, files); - if (err) { - resolve(err.message); - return; - } - const errList = []; - const fileList = []; - for (let i = 0; i < files.file.length; i++) { - const file = files.file[i]; - try { - const dstPath = await saveFile(file); - fileList.push(dstPath); - } catch (error) { - errList.push(error.message); - } - } - if (errList.length) { - resolve(gFail(null, errList.join("\n"))); - return; - } - // resolve(h.view("views/upload.ejs")); - resolve([...new Set(fileList)]); - }); - }); + if (err) { + resolve(err.message) + return + } + const errList = [] + const fileList = [] + for (let i = 0; i < files.file.length; i++) { + const file = files.file[i] + try { + const dstPath = await saveFile(file) + fileList.push(dstPath) + } catch (error) { + errList.push(error.message) + } + } + if (errList.length) { + resolve(gFail(null, errList.join("\n"))) + return + } + // resolve(h.view("views/upload.ejs")); + resolve([...new Set(fileList)]) + }) + }) } diff --git a/source/route/api/v1/upload/index.ts b/source/route/api/v1/upload/index.ts index 204e558..e8cbc9e 100644 --- a/source/route/api/v1/upload/index.ts +++ b/source/route/api/v1/upload/index.ts @@ -1,29 +1,27 @@ -import { config, method, route, swagger, validate } from "@noderun/hapi-router"; -import UploadFunc from "./_upload"; -import Joi from "joi"; +import { config, method, route, swagger, validate } from "@noderun/hapi-router" +import UploadFunc from "./_upload" +import Joi from "joi" export default class { - index(request, h) { - return h.view("views/demo.ejs"); - } + index(request, h) { + return h.view("views/demo.ejs") + } - @config({ - payload: { - maxBytes: 20000 * 1024 * 1024, - output: "stream", - parse: false, - // multipart: true, - allow: "multipart/form-data", + @config({ + payload: { + maxBytes: 20000 * 1024 * 1024, + output: "stream", + parse: false, + // multipart: true, + allow: "multipart/form-data", + }, + }) + @method("POST") + @swagger("文件上传", "文件上传a ", ["sum", "api"]) + async upload(req, h) { + const startTime = new Date().getTime() + const res = await UploadFunc(req, h) + const endTime = new Date().getTime() + console.log(`该请求处理时间为:${Number(endTime - startTime).toFixed(2)}ms`) + return res } - }) - @method("POST") - @swagger("文件上传", "文件上传a ", ["sum", "api"]) - async upload(req, h) { - const startTime = new Date().getTime(); - const res = await UploadFunc(req, h); - const endTime = new Date().getTime(); - console.log( - `该请求处理时间为:${Number(endTime - startTime).toFixed(2)}ms` - ); - return res; - } } diff --git a/source/route/api/v1/user/index.ts b/source/route/api/v1/user/index.ts index 0854680..7c03ea6 100644 --- a/source/route/api/v1/user/index.ts +++ b/source/route/api/v1/user/index.ts @@ -1,102 +1,96 @@ -import { - auth, - method, - route, - swagger, - validate, - config, -} from "@noderun/hapi-router"; -import { gSuccess, gFail } from "@/util"; -import * as bcrypt from "bcrypt"; -import * as jwt from "jsonwebtoken"; -import * as Joi from "joi"; -import { UserSchema } from "@/schema"; -import { ReturnValue, Req, Res } from "#/global"; +import { auth, method, route, swagger, validate, config } from "@noderun/hapi-router" +import { gSuccess, gFail } from "@/util" +import * as bcrypt from "bcrypt" +import * as jwt from "jsonwebtoken" +import * as Joi from "joi" +import { UserSchema } from "@/schema" +import { ReturnValue, Req, Res } from "#/global" export default class { - @validate({ - payload: UserSchema, - }) - @method("POST") - @swagger("用户注册", "返回注册用户的信息", ["api"]) - @auth(false) - async register(request: Req, h: Res): ReturnValue { - let { username, password, email } = request.payload as any; - if (!username) username = email; - const User = request.getModel("User") - logger.trace(username, email); - try { - const result = await User.findOne({ where: { username: username } }); - if (result != null) { - return gFail(null, "已存在该用户"); - } - let salt = bcrypt.genSaltSync(10); - let pwdLock = bcrypt.hashSync(password, salt); - await User.create({ username, password: pwdLock, email }); - return gSuccess("success", "you have a good heart."); - } catch (e) { - return gFail(null, "新建用户失败"); + @validate({ + payload: UserSchema, + }) + @method("POST") + @swagger("用户注册", "返回注册用户的信息", ["api"]) + @auth(false) + async register(request: Req, h: Res): ReturnValue { + let { username, password, email, } = request.payload as any + if (!username) username = email + const User = request.getModel("User") + logger.trace(username, email) + try { + const result = await User.findOne({ where: { username: username } }) + if (result != null) { + return gFail(null, "已存在该用户") + } + let salt = bcrypt.genSaltSync(10) + let pwdLock = bcrypt.hashSync(password, salt) + // @ts-ignore + await User.create({ username, password: pwdLock, email }) + return gSuccess("success", "you have a good heart.") + } catch (e) { + return gFail(null, "新建用户失败") + } } - } - @method("POST") - async logout(request: Req, h: Res): ReturnValue { - request.cookieAuth.clear(); - return gSuccess("success"); - } - - @validate({ - payload: UserSchema, - }) - @auth(false) - @method("POST") - @swagger("用户登录", "返回注册用户的信息", ["api"]) - async login(request: Req, h: Res): ReturnValue { - let { username, password } = request.payload as any; - const User = request.getModel("User") - const result = await User.findOne({ where: { username: username } }); - if (result == null) { - return gFail(null, "不存在该用户"); + @method("POST") + async logout(request: Req, h: Res): ReturnValue { + request.cookieAuth.clear() + return gSuccess("success") } - const validUser = bcrypt.compareSync(password, result.password); - if (!validUser) { - return gFail(null, "密码不正确"); + + @validate({ + payload: UserSchema, + }) + @auth(false) + @method("POST") + @swagger("用户登录", "返回注册用户的信息", ["api"]) + async login(request: Req, h: Res): ReturnValue { + let { username, password } = request.payload as any + const User = request.getModel("User") + const result = await User.findOne({ where: { username: username } }) + if (result == null) { + return gFail(null, "不存在该用户") + } + const validUser = bcrypt.compareSync(password, result.password) + if (!validUser) { + return gFail(null, "密码不正确") + } + //===== JWT ===== Start + // let token = jwt.sign({ id: result.id }, process.env.KEY); + // return gSuccess({ token: token }); + //===== JWT ===== End + //===== session ===== Start + request.cookieAuth.set({ id: result.id }) + //===== session ===== End + return gSuccess({}) } - //===== JWT ===== Start - // let token = jwt.sign({ id: result.id }, process.env.KEY); - // return gSuccess({ token: token }); - //===== JWT ===== End - //===== session ===== Start - request.cookieAuth.set({ id: result.id }); - //===== session ===== End - return gSuccess({}); - } - @method("DELETE") - @auth() - @swagger("删除用户", "删除用户账号", ["sum"]) - async del(request: Req, h: Res): ReturnValue { - const { id } = request.auth.credentials; - const User = request.getModel("User") - let result = await User.findOne({ where: { id: id } }); - if (result == null) { - return gFail(null, "不存在该用户"); + @method("DELETE") + @auth() + @swagger("删除用户", "删除用户账号", ["sum"]) + async del(request: Req, h: Res): ReturnValue { + const { id } = request.auth.credentials + const User = request.getModel("User") + let result = await User.findOne({ where: { id: id } }) + if (result == null) { + return gFail(null, "不存在该用户") + } + await result.destroy() + return gSuccess(null, "删除成功") } - await result.destroy(); - return gSuccess(null, "删除成功"); - } - @method("GET") - @swagger("获取用户信息", "返回注册用户的信息", ["用户操作", "api"]) - async userinfo(request: Req, h: Res): ReturnValue { - const { id } = request.auth.credentials; - const User = request.getModel("User") - let result = await User.findOne({ where: { id: id } }); - if (result == null) { - return gFail(null, "不存在该用户"); + @method("GET") + @swagger("获取用户信息", "返回注册用户的信息", ["用户操作", "api"]) + async userinfo(request: Req, h: Res): ReturnValue { + const { id } = request.auth.credentials + const User = request.getModel("User") + let result = await User.findOne({ where: { id: id } }) + if (result == null) { + return gFail(null, "不存在该用户") + } + result = result.toJSON() + delete result.password + return gSuccess(result) } - result = result.toJSON(); - delete result.password; - return gSuccess(result); - } } diff --git a/source/route/htmx/index.ts b/source/route/htmx/index.ts index 7ac90af..12d1879 100644 --- a/source/route/htmx/index.ts +++ b/source/route/htmx/index.ts @@ -8,6 +8,6 @@ export default class { @route("/clicked") async clicked(request: Req, h: Res): ReturnValue { // return 'aaaaaaaaaaaaaabbbbbbbbbbbbbbbb' - return h.view('htmx/a.pug') + return h.view("htmx/a.pug") } } diff --git a/source/route/views/index.ts b/source/route/views/index.ts index 4108291..eb19370 100644 --- a/source/route/views/index.ts +++ b/source/route/views/index.ts @@ -1,96 +1,96 @@ -import { Req, Res, ReturnValue } from "#/global"; -import { LoginUserSchema, RegisterUserSchema, UserSchema } from "@/schema"; -import { gFail, gSuccess } from "@/util"; -import { auth, config, method, route, validate } from "@noderun/hapi-router"; -import * as bcrypt from "bcrypt"; +import { Req, Res, ReturnValue } from "#/global" +import { LoginUserSchema, RegisterUserSchema, UserSchema } from "@/schema" +import { gFail, gSuccess } from "@/util" +import { auth, config, method, route, validate } from "@noderun/hapi-router" +import * as bcrypt from "bcrypt" /** * 登录界面 */ export default class { - @route("/login") - @auth("try") - @method("GET") - async login_GET(request: Req, h: Res): ReturnValue { - if (request.auth.isAuthenticated) { - request.yar.flash('warning', '您已经登陆'); - return h.redirect("/") - } else { - logger.debug("未登录"); + @route("/login") + @auth("try") + @method("GET") + async login_GET(request: Req, h: Res): ReturnValue { + if (request.auth.isAuthenticated) { + request.yar.flash("warning", "您已经登陆") + return h.redirect("/") + } else { + logger.debug("未登录") + } + return h.view("views/login.pug") } - return h.view("views/login.pug"); - } - @validate({ - payload: LoginUserSchema, - $errto: '/login', - // failAction: 'log' - failAction: 'function', - failReason: '用户名或密码错误,请重试', - }) - @method("POST") - @route("/login") - async login_POST(request: Req, h: Res): ReturnValue { - const { username, password, referrer } = request.payload as any; - const User = request.getModel("User"); - const account = await User.findOne({ where: { username: username } }); + @validate({ + payload: LoginUserSchema, + $errto: "/login", + // failAction: 'log' + failAction: "function", + failReason: "用户名或密码错误,请重试", + }) + @method("POST") + @route("/login") + async login_POST(request: Req, h: Res): ReturnValue { + const { username, password, referrer } = request.payload as any + const User = request.getModel("User") + const account = await User.findOne({ where: { username: username } }) - if (!account || !(await bcrypt.compare(password, account.password))) { - request.yar.flash('error', 'Invalid username or password'); - return h.redirect("/login"); + if (!account || !(await bcrypt.compare(password, account.password))) { + request.yar.flash("error", "Invalid username or password") + return h.redirect("/login") + } + request.cookieAuth.set({ id: account.id, nickname: account.nickname }) + request.yar.flash("success", "用户已登录") + return h.redirect(referrer ? referrer : "/") } - request.cookieAuth.set({ id: account.id, nickname: account.nickname }); - request.yar.flash('success', '用户已登录'); - return h.redirect(referrer ? referrer : "/"); - } - @method("GET") - @auth() - async logout(request: Req, h: Res): ReturnValue { - request.yar.flash('success', '用户已退出'); - request.cookieAuth.clear(); - return h.redirect('/'); - } - - @route("/register") - @auth("try") - @method("GET") - async registerView(request: Req, h: Res): ReturnValue { - if (request.auth.isAuthenticated) { - request.yar.flash('warning', '您已经登陆'); - return h.redirect("/") - } else { - logger.debug("未登录"); + @method("GET") + @auth() + async logout(request: Req, h: Res): ReturnValue { + request.yar.flash("success", "用户已退出") + request.cookieAuth.clear() + return h.redirect("/") } - return h.view("views/login.pug"); - } - @validate({ - payload: RegisterUserSchema, - }) - @method("POST") - async register(request: Req, h: Res): ReturnValue { - let { username, password, email, nickname } = request.payload as any; - if(!email){ - request.yar.flash('error', '必须填写邮箱'); - return h.redirect("/login"); + @route("/register") + @auth("try") + @method("GET") + async registerView(request: Req, h: Res): ReturnValue { + if (request.auth.isAuthenticated) { + request.yar.flash("warning", "您已经登陆") + return h.redirect("/") + } else { + logger.debug("未登录") + } + return h.view("views/login.pug") } - if (!username) username = email; - if (!nickname) nickname = username; - const User = request.getModel("User") - logger.trace(username, email); - try { - const result = await User.findOne({ where: { username: username } }); - if (result != null) { - request.yar.flash('error', '已存在该用户'); - return h.redirect("/login"); - } - let salt = bcrypt.genSaltSync(10); - let pwdLock = bcrypt.hashSync(password, salt); - await User.create({ username, nickname, password: pwdLock, email }); - return h.redirect("/") - } catch (e) { - request.yar.flash('error', '注册用户失败'); - return h.redirect("/login"); + + @validate({ + payload: RegisterUserSchema, + }) + @method("POST") + async register(request: Req, h: Res): ReturnValue { + let { username, password, email, nickname } = request.payload as any + if (!email) { + request.yar.flash("error", "必须填写邮箱") + return h.redirect("/login") + } + if (!username) username = email + if (!nickname) nickname = username + const User = request.getModel("User") + logger.trace(username, email) + try { + const result = await User.findOne({ where: { username: username } }) + if (result != null) { + request.yar.flash("error", "已存在该用户") + return h.redirect("/login") + } + let salt = bcrypt.genSaltSync(10) + let pwdLock = bcrypt.hashSync(password, salt) + await User.create({ username, nickname, password: pwdLock, email }) + return h.redirect("/") + } catch (e) { + request.yar.flash("error", "注册用户失败") + return h.redirect("/login") + } } - } } diff --git a/source/route/views/index/index.ts b/source/route/views/index/index.ts index fa14b96..eb86153 100644 --- a/source/route/views/index/index.ts +++ b/source/route/views/index/index.ts @@ -1,100 +1,93 @@ -import { - auth, - config, - method, - route, - swagger, - validate, -} from "@noderun/hapi-router"; -import { Req, Res, ReturnValue } from "#/global"; -import path from "path"; -import fs from "fs-extra"; -import { baseDir } from "@/util"; -import MarkdownIt from "markdown-it"; +import { auth, config, method, route, swagger, validate } from "@noderun/hapi-router" +import { Req, Res, ReturnValue } from "#/global" +import path from "path" +import fs from "fs-extra" +import { baseDir } from "@/util" +import MarkdownIt from "markdown-it" export default class Index { - async css(request: Req, h: Res): ReturnValue { - return h.view("views/css.pug"); - } + async css(request: Req, h: Res): ReturnValue { + return h.view("views/css.pug") + } - @auth("try") - async index(request: Req, h: Res): ReturnValue { - if (request.auth.isAuthenticated) { - // 登录了 - } else { - // 未登录 + @auth("try") + async index(request: Req, h: Res): ReturnValue { + if (request.auth.isAuthenticated) { + // 登录了 + } else { + // 未登录 + } + return h.view("views/index.pug", { isLogin: request.auth.isAuthenticated }) } - return h.view("views/index.pug", { isLogin: request.auth.isAuthenticated }); - } - @route("/about") - @auth("try") - async about(request: Req, h) { - // console.log(request.auth); - // console.log(1); + @route("/about") + @auth("try") + async about(request: Req, h) { + // console.log(request.auth); + // console.log(1); - // try { - // const User = request.getModel("User"); + // try { + // const User = request.getModel("User"); - // console.log(await User.findOne({ where: { username: "xieyaxin" } })); - // } catch (error) { - // console.log(error); - // } - // console.log(2); - const md = new MarkdownIt(); - var result = md.render('# markdown-it rulezz!'); - return h.view("views/about.pug", { - md: result - }); - } + // console.log(await User.findOne({ where: { username: "xieyaxin" } })); + // } catch (error) { + // console.log(error); + // } + // console.log(2); + const md = new MarkdownIt() + var result = md.render("# markdown-it rulezz!") + return h.view("views/about.pug", { + md: result, + }) + } - @route("/docs/{path*}") - @auth() - async docs(req: Req, h: Res): ReturnValue { - // const {id} = req.auth.credentials - // try { - // req.cookieAuth.ttl(720 * 24 * 60 * 60 * 1000) - // req.cookieAuth.set({ id: id }); - // } catch (error) { - // console.log(error); + @route("/docs/{path*}") + @auth() + async docs(req: Req, h: Res): ReturnValue { + // const {id} = req.auth.credentials + // try { + // req.cookieAuth.ttl(720 * 24 * 60 * 60 * 1000) + // req.cookieAuth.set({ id: id }); + // } catch (error) { + // console.log(error); - // } - if (req.params && req.params.path.endsWith(".md")) { - // console.log(path.resolve(baseDir, "docs/"+"*.md")); - // console.log(await glob("docs/"+"*.md")); - const mdPath = path.resolve(baseDir, "docs/"+req.params.path) - if(fs.existsSync(mdPath)){ - const str = fs.readFileSync(mdPath, "utf8") - console.log("---->", mdPath); + // } + if (req.params && req.params.path.endsWith(".md")) { + // console.log(path.resolve(baseDir, "docs/"+"*.md")); + // console.log(await glob("docs/"+"*.md")); + const mdPath = path.resolve(baseDir, "docs/" + req.params.path) + if (fs.existsSync(mdPath)) { + const str = fs.readFileSync(mdPath, "utf8") + console.log("---->", mdPath) - return h.view("views/css.pug", { - content: str.toString() - }); - } - // 解析文档 - return h.view("views/css.pug"); + return h.view("views/css.pug", { + content: str.toString(), + }) + } + // 解析文档 + return h.view("views/css.pug") + } + // 404页面 + return h.redirect("/404") } - // 404页面 - return h.redirect("/404"); - } - @route("/{path*}") - async any(req: Req, h: Res): ReturnValue { - console.log('404: ',req.raw.req.url); - return h.redirect("/404?r="+encodeURIComponent(req.raw.req.url)); - } - @route("/404") - @auth("try") - async 404(request: Req, h: Res): ReturnValue { - if (request.auth.isAuthenticated) { - // 登录了 - } else { - // 未登录 + @route("/{path*}") + async any(req: Req, h: Res): ReturnValue { + console.log("404: ", req.raw.req.url) + return h.redirect("/404?r=" + encodeURIComponent(req.raw.req.url)) } - if(request.query?.r){ - // 可重定向返回 + @route("/404") + @auth("try") + async 404(request: Req, h: Res): ReturnValue { + if (request.auth.isAuthenticated) { + // 登录了 + } else { + // 未登录 + } + if (request.query?.r) { + // 可重定向返回 + } + return h.view("404.pug", { + rollback: request.query?.r, + }) } - return h.view("404.pug", { - rollback: request.query?.r - }); - } } diff --git a/source/route/views/nav/index.ts b/source/route/views/nav/index.ts index 7c69e30..3c686c8 100644 --- a/source/route/views/nav/index.ts +++ b/source/route/views/nav/index.ts @@ -1,9 +1,9 @@ -import { Req, Res, ReturnValue } from "#/global"; -import { gSuccess } from "@/util"; +import { Req, Res, ReturnValue } from "#/global" +import { gSuccess } from "@/util" export default class Nav { - async index(req: Req, h: Res): ReturnValue{ - const Constant = req.getModel("Constant") + async index(req: Req, h: Res): ReturnValue { + // const Constant = req.getModel("Constant") return gSuccess("31231") } } diff --git a/source/run.ts b/source/run.ts index b4dc9c0..b697b77 100644 --- a/source/run.ts +++ b/source/run.ts @@ -1,146 +1,146 @@ -"use strict"; -import plugins from "@/plugins"; -import path from "path"; -import { baseDir, templateDir } from "@/util"; -import { validateJwt, validateSession } from "./auth"; -import Hapi, { Server } from "@hapi/hapi"; -import { Sequelize } from "sequelize"; -import { Req } from "#/global"; +"use strict" +import plugins from "@/plugins" +import path from "path" +import { baseDir, templateDir } from "@/util" +import { validateJwt, validateSession } from "./auth" +import Hapi, { Server } from "@hapi/hapi" +import { Sequelize } from "sequelize" +import { Req } from "#/global" // const Hapi = require("@hapi/hapi"); // const HapiSwagger = require("hapi-swagger"); // const HapiSwagger = require("hapi-swaggered-ui"); // swagger v2版本 -const pugPluginAlias = require('pug-alias'); +const pugPluginAlias = require("pug-alias") const run = async (): Promise => { - const server = Hapi.server({ - port: 3388, - host: "localhost", - }); - await server.register([ - { - plugin: require("hapi-sequelizejs"), - options: [ + const server = Hapi.server({ + port: 3388, + host: "localhost", + }) + await server.register([ { - name: "data", // identifier - models: [__dirname + "/models/**/*.ts"], // paths/globs to model files - // ignoredModels: [__dirname + "/server/models/**/*.js"], // OPTIONAL: paths/globs to ignore files - sequelize: new Sequelize({ - dialect: "sqlite", - storage: path.resolve(__dirname, "./db/data.db"), - logging: false, - // logging: loggerSQL.debug.bind(loggerSQL) // Alternative way to use custom logger, displays all messages - }), // sequelize instance - sync: true, // sync models - default false - forceSync: false, // force sync (drops tables) - default false + plugin: require("hapi-sequelizejs"), + options: [ + { + name: "data", // identifier + models: [__dirname + "/models/**/*.ts"], // paths/globs to model files + // ignoredModels: [__dirname + "/server/models/**/*.js"], // OPTIONAL: paths/globs to ignore files + sequelize: new Sequelize({ + dialect: "sqlite", + storage: path.resolve(__dirname, "./db/data.db"), + logging: false, + // logging: loggerSQL.debug.bind(loggerSQL) // Alternative way to use custom logger, displays all messages + }), // sequelize instance + sync: true, // sync models - default false + forceSync: false, // force sync (drops tables) - default false + }, + ], }, - ], - }, - ]); + ]) - //===== JWT ===== Start - // await server.register(require("hapi-auth-jwt2")); - // server.auth.strategy("jwt", "jwt", { - // key: process.env.KEY, // Never Share your secret key - // validate: validateJwt, // validate function defined above - // verifyOptions: { algorithms: ["HS256"] }, - // }); - //===== JWT ===== End - //===== session ===== Start - // https://hapi.dev/module/cookie/api?v=11.0.2 - await server.register(require("@hapi/cookie")); - server.auth.strategy("session", "cookie", { - cookie: { - ttl: 1000 * 60 * 60 * 24, - path: '/', // 测试退出时set-cookie失效,加上这个好了 - name: "sid", //cookie的名字 - password: process.env.KEY, - isSecure: false, // false: 允许 Cookie 通过不安全的连接传输,这会使其受到攻击 - }, - redirectTo(request: Req){ - if (request.path.startsWith('/api')) { - return false - } - return "/login" - }, - appendNext: true, - validateFunc: validateSession, - }); - server.auth.default("session"); - //===== session ===== End - - await server.register(plugins as any); + //===== JWT ===== Start + // await server.register(require("hapi-auth-jwt2")); + // server.auth.strategy("jwt", "jwt", { + // key: process.env.KEY, // Never Share your secret key + // validate: validateJwt, // validate function defined above + // verifyOptions: { algorithms: ["HS256"] }, + // }); + //===== JWT ===== End + //===== session ===== Start + // https://hapi.dev/module/cookie/api?v=11.0.2 + await server.register(require("@hapi/cookie")) + server.auth.strategy("session", "cookie", { + cookie: { + ttl: 1000 * 60 * 60 * 24, + path: "/", // 测试退出时set-cookie失效,加上这个好了 + name: "sid", //cookie的名字 + password: process.env.KEY, + isSecure: false, // false: 允许 Cookie 通过不安全的连接传输,这会使其受到攻击 + }, + redirectTo(request: Req) { + if (request.path.startsWith("/api")) { + return false + } + return "/login" + }, + appendNext: true, + validateFunc: validateSession, + }) + server.auth.default("session") + //===== session ===== End - /** - * 模板引擎 - */ - // https://hapi.dev/module/vision/api/?v=6.1.0 - await server.register(require("@hapi/vision")); - server.views({ - engines: { - ejs: require("ejs"), - pug: require("pug"), - }, - isCached: process.env.NODE_ENV === "development" ? false : true, - compileMode: "sync", // ejs - relativeTo: baseDir, - layout: false, // ejs - layoutPath: path.resolve(templateDir, "layout"), // ejs - path: "template", - // pug - compileOptions: { - // By default Pug uses relative paths (e.g. ../root.pug), when using absolute paths (e.g. include /root.pug), basedir is prepended. - // https://pugjs.org/language/includes.html - basedir: templateDir, - plugins: [ - pugPluginAlias({ - // as Function - '@': fn => fn.replace(/^@/, 'template') - }) - ] - }, - }); + await server.register(plugins as any) - // http://localhost:3000/documentation - await server.register([ - { - plugin: require("hapi-swagger"), - options: { - documentationPath: "/doc", - info: { - title: "Dream 文档", - version: "1.0.0", + /** + * 模板引擎 + */ + // https://hapi.dev/module/vision/api/?v=6.1.0 + await server.register(require("@hapi/vision")) + server.views({ + engines: { + ejs: require("ejs"), + pug: require("pug"), }, - grouping: "tags", - tags: [ - { - name: "sum", - description: "working with maths", - externalDocs: { - description: "Find out more", - url: "http://example.org", - }, - }, - { - name: "store", - description: "storing data", - externalDocs: { - description: "Find out more", - url: "http://example.org", + isCached: process.env.NODE_ENV === "development" ? false : true, + compileMode: "sync", // ejs + relativeTo: baseDir, + layout: false, // ejs + layoutPath: path.resolve(templateDir, "layout"), // ejs + path: "template", + // pug + compileOptions: { + // By default Pug uses relative paths (e.g. ../root.pug), when using absolute paths (e.g. include /root.pug), basedir is prepended. + // https://pugjs.org/language/includes.html + basedir: templateDir, + plugins: [ + pugPluginAlias({ + // as Function + "@": fn => fn.replace(/^@/, "template"), + }), + ], + }, + }) + + // http://localhost:3000/documentation + await server.register([ + { + plugin: require("hapi-swagger"), + options: { + documentationPath: "/doc", + info: { + title: "Dream 文档", + version: "1.0.0", + }, + grouping: "tags", + tags: [ + { + name: "sum", + description: "working with maths", + externalDocs: { + description: "Find out more", + url: "http://example.org", + }, + }, + { + name: "store", + description: "storing data", + externalDocs: { + description: "Find out more", + url: "http://example.org", + }, + }, + ], }, - }, - ], - }, - }, - ]); - await server.start(); - logger.trace("Server running on %s", server.info.uri); - return server; -}; + }, + ]) + await server.start() + logger.trace("Server running on %s", server.info.uri) + return server +} -process.on("unhandledRejection", (err) => { - console.log("unhandledRejection:", err); - process.exit(1); -}); +process.on("unhandledRejection", err => { + console.log("unhandledRejection:", err) + process.exit(1) +}) -export { run }; +export { run } diff --git a/source/schema/index.ts b/source/schema/index.ts index 53c900e..8742c5c 100644 --- a/source/schema/index.ts +++ b/source/schema/index.ts @@ -1,30 +1,32 @@ -import * as Joi from "joi"; +import * as Joi from "joi" export const UserSchema = Joi.object({ - username: Joi.string().alphanum().min(6).max(35), - password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), - email: Joi.string().email({ - minDomainSegments: 2, - tlds: { allow: ["com", "net"] }, - }) -}).or("username", "email"); + username: Joi.string().alphanum().min(6).max(35), + password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), + email: Joi.string().email({ + minDomainSegments: 2, + tlds: { allow: ["com", "net"] }, + }), +}).or("username", "email") export const RegisterUserSchema = Joi.object({ username: Joi.string().alphanum().min(6).max(35), password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), - email: Joi.string().email({ - minDomainSegments: 2, - tlds: { allow: ["com", "net"] }, - }).required(), + email: Joi.string() + .email({ + minDomainSegments: 2, + tlds: { allow: ["com", "net"] }, + }) + .required(), nickname: Joi.string().alphanum().min(4).max(35), }) export const LoginUserSchema = Joi.object({ - referrer: Joi.string().allow('').optional(), + referrer: Joi.string().allow("").optional(), username: Joi.string().min(6).max(35), //Joi.string().alphanum().min(6).max(35) password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), email: Joi.string().email({ - minDomainSegments: 2, - tlds: { allow: ["com", "net"] }, + minDomainSegments: 2, + tlds: { allow: ["com", "net"] }, }), -}).or("username", "email"); +}).or("username", "email") diff --git a/source/util/index.ts b/source/util/index.ts index a2913fd..56b8e27 100644 --- a/source/util/index.ts +++ b/source/util/index.ts @@ -1,13 +1,13 @@ -import path from "path"; -export * from "./res-helper"; +import path from "path" +export * from "./res-helper" export const isDev = process.env.NODE_ENV === "development" export const isProd = process.env.NODE_ENV === "production" -export const baseDir = path.resolve(__dirname, "../../"); +export const baseDir = path.resolve(__dirname, "../../") -export const sourceDir = isProd? path.resolve(__dirname, "../../dist") : path.resolve(__dirname, "../../source"); -export const publicDir = path.resolve(__dirname, "../../public"); -export const uploadDir = path.resolve(publicDir, "upload"); -export const uploadPath = "/public/upload"; // 图片上传地址 -export const templateDir = path.resolve(baseDir, "template"); +export const sourceDir = isProd ? path.resolve(__dirname, "../../dist") : path.resolve(__dirname, "../../source") +export const publicDir = path.resolve(__dirname, "../../public") +export const uploadDir = path.resolve(publicDir, "upload") +export const uploadPath = "/public/upload" // 图片上传地址 +export const templateDir = path.resolve(baseDir, "template") diff --git a/source/util/res-helper.ts b/source/util/res-helper.ts index 0c7078a..f83246c 100644 --- a/source/util/res-helper.ts +++ b/source/util/res-helper.ts @@ -1,29 +1,29 @@ export function gSuccess(data = null, message = "success") { - if (typeof data === "string") { + if (typeof data === "string") { + return { + code: 200, + message: data, + data: null, + } + } return { - code: 200, - message: data, - data: null, - }; - } - return { - code: 200, - message: message, - data: data, - }; + code: 200, + message: message, + data: data, + } } export function gFail(data = null, message = "fail") { - if (typeof data === "string") { + if (typeof data === "string") { + return { + code: 400, + message: data, + data: null, + } + } return { - code: 400, - message: data, - data: null, - }; - } - return { - code: 400, - message: message, - data: data, - }; + code: 400, + message: message, + data: data, + } } diff --git a/source/util/util.ts b/source/util/util.ts index 461cb7c..332d892 100644 --- a/source/util/util.ts +++ b/source/util/util.ts @@ -1,43 +1,47 @@ -export function dateTimeFormat(date, fmt = 'yyyy-MM-dd HH:mm:ss') { //日期时间格式化 +export function dateTimeFormat(date, fmt = "yyyy-MM-dd HH:mm:ss") { + //日期时间格式化 if (!date) { - return '' + return "" } - if (typeof date === 'string') { - date = date.replace('T', ' ').replace('Z', ''); - date = new Date(date.replace(/-/g, '/')) + if (typeof date === "string") { + date = date.replace("T", " ").replace("Z", "") + date = new Date(date.replace(/-/g, "/")) } - if (typeof date === 'number') { - date = new Date(date) + if (typeof date === "number") { + date = new Date(date) } var o = { - 'M+': date.getMonth() + 1, - 'd+': date.getDate(), - 'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, - 'H+': date.getHours(), - 'm+': date.getMinutes(), - 's+': date.getSeconds(), - 'q+': Math.floor((date.getMonth() + 3) / 3), - 'S': date.getMilliseconds() + "M+": date.getMonth() + 1, + "d+": date.getDate(), + "h+": date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, + "H+": date.getHours(), + "m+": date.getMinutes(), + "s+": date.getSeconds(), + "q+": Math.floor((date.getMonth() + 3) / 3), + S: date.getMilliseconds(), } var week = { - '0': '\u65e5', - '1': '\u4e00', - '2': '\u4e8c', - '3': '\u4e09', - '4': '\u56db', - '5': '\u4e94', - '6': '\u516d' + "0": "\u65e5", + "1": "\u4e00", + "2": "\u4e8c", + "3": "\u4e09", + "4": "\u56db", + "5": "\u4e94", + "6": "\u516d", } if (/(y+)/.test(fmt)) { - fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) + fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)) } if (/(E+)/.test(fmt)) { - fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '\u661f\u671f' : '\u5468') : '') + week[date.getDay() + '']) + fmt = fmt.replace( + RegExp.$1, + (RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? "\u661f\u671f" : "\u5468") : "") + week[date.getDay() + ""], + ) } for (var k in o) { - if (new RegExp('(' + k + ')').test(fmt)) { - fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) - } + if (new RegExp("(" + k + ")").test(fmt)) { + fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)) + } } return fmt - } \ No newline at end of file +} diff --git a/template/404.pug b/template/404.pug index dee416c..01de240 100644 --- a/template/404.pug +++ b/template/404.pug @@ -1,9 +1,9 @@ extends layout/layout block head - link(rel="stylesheet", href="/public/css/views/404.css") + link(rel="stylesheet", href="/public/css/views/404.css") block content - div(style="text-align: center") - span.text404 404 - div 重定向回:#{rollback} + div(style="text-align: center") + span.text404 404 + div 重定向回:#{rollback} diff --git a/template/helper/flush.pug b/template/helper/flush.pug index a07d9e5..b2a51f3 100644 --- a/template/helper/flush.pug +++ b/template/helper/flush.pug @@ -1,62 +1,62 @@ //- 服务器反馈UI if flash - .message-container - - index = 0 - if flash.error - each item in flash.error - - index++ - .message.is-danger(id="message"+index) - .message-header - p 错误 - button.delete(aria-label='delete' data-target="message"+index) - .message-body - | #{item} - if flash.success - each item in flash.success - - index++ - .message.is-success(id="message"+index) - .message-header - p 成功 - button.delete(aria-label='delete' data-target="message"+index) - .message-body - | #{item} - if flash.info - each item in flash.info - - index++ - .message.is-info(id="message"+index) - .message-header - p 信息 - button.delete(aria-label='delete' data-target="message"+index) - .message-body - | #{item} - if flash.warning - each item in flash.warning - - index++ - .message.is-warning(id="message"+index) - .message-header - p 警告 - button.delete(aria-label='delete' data-target="message"+index) - .message-body - | #{item} - //- .toast-container.top-0.end-0.p-3 - //- each item in flash.error - //- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') - //- .toast-header - //- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") - //- strong.me-auto 提示 - //- //- small.text-muted just now - //- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') - //- .toast-body. - //- #{item} - //- .toast-container.position-fixed.bottom-0.end-0.p-3 - //- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') - //- .toast-header - //- img.rounded.me-2(src='...', alt='...') - //- strong.me-auto Bootstrap - //- small 11 mins ago - //- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') - //- .toast-body. - //- Hello, world! This is a toast message. - //- ul - //- each item in flash.error - //- li #{item} + .message-container + - index = 0 + if flash.error + each item in flash.error + - index++ + .message.is-danger(id="message"+index) + .message-header + p 错误 + button.delete(aria-label='delete' data-target="message"+index) + .message-body + | #{item} + if flash.success + each item in flash.success + - index++ + .message.is-success(id="message"+index) + .message-header + p 成功 + button.delete(aria-label='delete' data-target="message"+index) + .message-body + | #{item} + if flash.info + each item in flash.info + - index++ + .message.is-info(id="message"+index) + .message-header + p 信息 + button.delete(aria-label='delete' data-target="message"+index) + .message-body + | #{item} + if flash.warning + each item in flash.warning + - index++ + .message.is-warning(id="message"+index) + .message-header + p 警告 + button.delete(aria-label='delete' data-target="message"+index) + .message-body + | #{item} + //- .toast-container.top-0.end-0.p-3 + //- each item in flash.error + //- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') + //- .toast-header + //- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") + //- strong.me-auto 提示 + //- //- small.text-muted just now + //- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') + //- .toast-body. + //- #{item} + //- .toast-container.position-fixed.bottom-0.end-0.p-3 + //- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') + //- .toast-header + //- img.rounded.me-2(src='...', alt='...') + //- strong.me-auto Bootstrap + //- small 11 mins ago + //- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') + //- .toast-body. + //- Hello, world! This is a toast message. + //- ul + //- each item in flash.error + //- li #{item} diff --git a/template/helper/form_security.pug b/template/helper/form_security.pug index 9ea5c53..79ee085 100644 --- a/template/helper/form_security.pug +++ b/template/helper/form_security.pug @@ -1,2 +1,2 @@ if crumb - \ No newline at end of file + diff --git a/template/md/about.md b/template/md/about.md index 17eb14d..be12c42 100644 --- a/template/md/about.md +++ b/template/md/about.md @@ -1 +1 @@ -## 关于我 \ No newline at end of file +## 关于我 diff --git a/template/ui/header.pug b/template/ui/header.pug index a739e00..13fd7bd 100644 --- a/template/ui/header.pug +++ b/template/ui/header.pug @@ -1,39 +1,44 @@ nav.is-fixed-top.navbar(role='navigation', aria-label='main navigation', style="box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;") .container - .navbar-brand - a.navbar-item(href='/') - img(src='https://bulma.io/images/bulma-logo.png', width='112', height='28') - a.navbar-burger(role='button', aria-label='menu', aria-expanded='false', data-target='navbarBasicExample') - span(aria-hidden='true') - span(aria-hidden='true') - span(aria-hidden='true') - #navbarBasicExample.navbar-menu - .navbar-start - a.navbar-item - | 文档 - .navbar-item.has-dropdown.is-hoverable - a.navbar-link - | 更多 - .navbar-dropdown - a.navbar-item(href="/about") - | 关于本站 - a.navbar-item - | 关于作者 - hr.navbar-divider - a.navbar-item - | 报告问题 - .navbar-end - if !isLogin - .navbar-item - .buttons - a.button.is-primary(href="/register") - strong 注册 - a.button.is-light(href="/login") - | 登录 - else - .navbar-item - .buttons - button.button.is-white - | #{user.nickname} - a.button.is-danger.is-light(href="/logout") - | 退出 + .navbar-brand + a.navbar-item(href='/') + img(src='https://bulma.io/images/bulma-logo.png', width='112', height='28') + a.navbar-burger(role='button', aria-label='menu', aria-expanded='false', data-target='navbarBasicExample') + span(aria-hidden='true') + span(aria-hidden='true') + span(aria-hidden='true') + #navbarBasicExample.navbar-menu + .navbar-start + a.navbar-item + | 文档 + .navbar-item.has-dropdown.is-hoverable + a.navbar-link + | 更多 + .navbar-dropdown + a.navbar-item(href="/about") + | 关于本站 + a.navbar-item + | 关于作者 + hr.navbar-divider + a.navbar-item + | 报告问题 + .navbar-end + if !isLogin + .navbar-item + .buttons + a.button.is-primary(href="/register") + strong 注册 + a.button.is-light(href="/login") + | 登录 + else + .navbar-item.has-dropdown.is-hoverable + a.navbar-link + | #{user.nickname} + .navbar-dropdown + a.navbar-item + | 用户资料 + hr.navbar-divider + a.navbar-item + | 退出 + //- a.button.is-danger.is-light(href="/logout") + //- | 退出 diff --git a/template/views/about.pug b/template/views/about.pug index 56cf5cb..2d75eb3 100644 --- a/template/views/about.pug +++ b/template/views/about.pug @@ -1,12 +1,12 @@ extends /layout/layout block var - -title="关于" + -title="关于" block head block content - section.section - .container.content!= md + section.section + .container.content!= md block script diff --git a/template/views/index.pug b/template/views/index.pug index b36661f..e295dd1 100644 --- a/template/views/index.pug +++ b/template/views/index.pug @@ -9,16 +9,6 @@ block head block content section.section .container - h1.title - | Hello World - p.subtitle - | My first website with - strong Bulma - | ! + div sda if isLogin button(hx-get="/htmx/clicked" hx-push-url="/about" hx-trigger="click" hx-target="this" hx-swap="outerHTML") Click Me! -
- - +security - -
diff --git a/template/views/login.pug b/template/views/login.pug index 975d61f..0bf8977 100644 --- a/template/views/login.pug +++ b/template/views/login.pug @@ -1,22 +1,22 @@ extends /layout/layout block var - -title="登陆" // 网页标题 - -hideHeader=true + -title="登陆" // 网页标题 + -hideHeader=true block head +css("style/views/login.css") block content .login - h1.title.is-1 登录 - form(action='/login' method='post') - input(id="referrer" type="text" name="referrer" class="form-control" style="display:none;") - input(type='text', name='username', placeholder='用户名', required) - input(type='password', name='password', placeholder='密码', required) - +security - button.btn.btn-primary.btn-block.btn-large(type='submit') 现在登录! - a(href="/register" style="margin-top: 8px;color: white;font-size: 14px;display: inline-block;float: right") 前往注册 + h1.title.is-1 登录 + form(action='/login' method='post') + input(id="referrer" type="text" name="referrer" class="form-control" style="display:none;") + input(type='text', name='username', placeholder='用户名', required) + input(type='password', name='password', placeholder='密码', required) + +security + button.btn.btn-primary.btn-block.btn-large(type='submit') 现在登录! + a(href="/register" style="margin-top: 8px;color: white;font-size: 14px;display: inline-block;float: right") 前往注册 block script +script("js/page/login.js") diff --git a/template/views/user.pug b/template/views/user.pug index c10da32..daa493c 100644 --- a/template/views/user.pug +++ b/template/views/user.pug @@ -1,15 +1,15 @@ extends @/layout/layout block var - -title="用户信息" // 网页标题 + -title="用户信息" // 网页标题 //- -hideHeader=true block head block content - section.section - .container - div nickname: #{user.nickname} - div email: #{user.email} - div username: #{user.username} - div id: #{user.id} + section.section + .container + div nickname: #{user.nickname} + div email: #{user.email} + div username: #{user.username} + div id: #{user.id} diff --git a/types/global.d.ts b/types/global.d.ts index f3fbdb2..e262d83 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -1,29 +1,26 @@ -import { Logger } from "log4js"; -import { Server } from "@hapi/hapi"; -import { Request, ResponseToolkit, Lifecycle } from "@hapi/hapi"; -import { Model, ModelCtor } from "Sequelize"; -import { TUserModel } from "@/models/User"; +import { Logger } from "log4js" +import { Server } from "@hapi/hapi" +import { Request, ResponseToolkit, Lifecycle } from "@hapi/hapi" +import { TUserModel } from "@/models/User" declare global { - var server: Server; - var logger: Logger; - var loggerSite: Logger; - var loggerSQL: Logger; + var server: Server + var logger: Logger + var loggerSite: Logger + var loggerSQL: Logger } interface Models { - "User": TUserModel + User: TUserModel } -declare module '@hapi/hapi' { +declare module "@hapi/hapi" { interface Request { getModel(name: T): M } - interface ResponseToolkit { - - } + interface ResponseToolkit {} } export declare type Req = Request export declare type Res = ResponseToolkit -export declare type ReturnValue = Promise \ No newline at end of file +export declare type ReturnValue = Promise