42 changed files with 1391 additions and 1416 deletions
@ -1,211 +1,190 @@ |
|||||
// @ts-nocheck
|
// @ts-nocheck
|
||||
|
|
||||
import { walkDir, removeIndex, isIndexEnd } from "./util"; |
import { walkDir, removeIndex, isIndexEnd } from "./util" |
||||
import * as Joi from "joi"; |
import * as Joi from "joi" |
||||
const path = require("path"); |
const path = require("path") |
||||
const fs = require("fs"); |
const fs = require("fs") |
||||
|
|
||||
class routePlugin { |
class routePlugin { |
||||
public name: string = "routePlugin"; |
public name: string = "routePlugin" |
||||
public version: string = "0.0.1"; |
public version: string = "0.0.1" |
||||
public register(server: any, opts: any) { |
public register(server: any, opts: any) { |
||||
const sourceDir = opts.sourceDir; |
const sourceDir = opts.sourceDir |
||||
const type = opts.type || "jwt"; |
const type = opts.type || "jwt" |
||||
const auth = opts.auth || []; |
const auth = opts.auth || [] |
||||
let array = []; |
let array = [] |
||||
for (let i = 0; i < sourceDir.length; i++) { |
for (let i = 0; i < sourceDir.length; i++) { |
||||
const dir = sourceDir[i]; |
const dir = sourceDir[i] |
||||
console.log(dir); |
console.log(dir) |
||||
array.push(dir.dir + "对应路径:"); |
array.push(dir.dir + "对应路径:") |
||||
array = array.concat( |
array = array.concat(this.registerRoute(server, dir.dir, dir.prefix || "", auth, type)) |
||||
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( |
registerRoute(server, sourceDir, prefix, auth, type) { |
||||
path.resolve(process.cwd(), "route.txt"), |
const files = walkDir(sourceDir) |
||||
array.join("\n"), |
const routes = [] |
||||
{ |
files.forEach(file => { |
||||
encoding: "utf-8", |
let filename = file.relativeFileNoExt |
||||
} |
let array = filename.split(path.sep).slice(1) |
||||
); |
let fileNoExt = removeIndex("/" + array.join("/")) |
||||
} |
const moduleName = path.resolve(sourceDir, filename) |
||||
registerRoute(server, sourceDir, prefix, auth, type) { |
const obj = require(moduleName) |
||||
const files = walkDir(sourceDir); |
if (obj.default) { |
||||
const routes = []; |
const func = new (obj.default || obj)() |
||||
files.forEach((file) => { |
const prototype = Object.getPrototypeOf(func) |
||||
let filename = file.relativeFileNoExt; |
const keys = Reflect.ownKeys(prototype) |
||||
let array = filename.split(path.sep).slice(1); |
for (const key of keys) { |
||||
let fileNoExt = removeIndex("/" + array.join("/")); |
if (key !== "constructor") { |
||||
const moduleName = path.resolve(sourceDir, filename); |
let ff = func[key] |
||||
const obj = require(moduleName); |
let handler: () => void = undefined |
||||
if (obj.default) { |
// 默认方法
|
||||
const func = new (obj.default || obj)(); |
const method = ff.$method || "GET" |
||||
const prototype = Object.getPrototypeOf(func); |
// 路由收集规则
|
||||
const keys = Reflect.ownKeys(prototype); |
let route = "" |
||||
for (const key of keys) { |
if (ff.$route) { |
||||
if (key !== "constructor") { |
if (isIndexEnd(fileNoExt)) { |
||||
let ff = func[key]; |
route = ff.$route |
||||
let handler:()=>void = undefined |
} else { |
||||
// 默认方法
|
route = fileNoExt + ff.$route |
||||
const method = ff.$method || "GET"; |
} |
||||
// 路由收集规则
|
} else { |
||||
let route = ""; |
if (isIndexEnd(fileNoExt)) { |
||||
if (ff.$route) { |
route = fileNoExt + key.toString() |
||||
if (isIndexEnd(fileNoExt)) { |
} else { |
||||
route = ff.$route; |
route = fileNoExt + "/" + key.toString() |
||||
} 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); |
|
||||
} |
} |
||||
return await ff.call(this, ...argus) |
route = removeIndex(route) |
||||
} |
route = prefix ? route[0] + prefix + "/" + route.slice(1) : route |
||||
} |
// 配置规则
|
||||
if(validateObj.failAction === "function"){ |
const options = ff.$options ? ff.$options : {} |
||||
let errto = validateObj.$errto |
if (!options.auth) { |
||||
validateObj.failAction = async function(request, h, err){ |
if (ff.$auth == undefined) { |
||||
if(err.details){ |
if (auth && auth.length && auth.filter(v => route.startsWith(v)).length) { |
||||
request.$joi_error = err.details.map(v=>v.message) |
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; |
if (!options.validate) { |
||||
} |
let validateObj = ff.$validate || {} |
||||
handler = async function (...argus){ |
if (options.auth && type === "jwt") { |
||||
const request = argus[0] |
if (validateObj.headers) { |
||||
const h = argus[1] |
validateObj.headers = validateObj.headers.keys({ |
||||
if(request.$joi_error){ |
Authorization: Joi.string(), |
||||
loggerSite.debug('传输参数错误: ', request.$joi_error) |
}) |
||||
request.yar.flash('error', failReason); |
} else { |
||||
delete request.$joi_error |
validateObj.headers = Joi.object({ |
||||
return h.redirect(errto); |
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); |
}) |
||||
|
return routes |
||||
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; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
function full(str: string, length = 10) { |
function full(str: string, length = 10) { |
||||
let len = str.length; |
let len = str.length |
||||
let need = length - len; |
let need = length - len |
||||
if (need <= 0) return str; |
if (need <= 0) return str |
||||
return str + [...Array(need)].map((v, i) => " ").join(""); |
return str + [...Array(need)].map((v, i) => " ").join("") |
||||
} |
} |
||||
|
|
||||
const plugin = new routePlugin(); |
const plugin = new routePlugin() |
||||
|
|
||||
export { plugin }; |
export { plugin } |
||||
export * from "./util/decorators"; |
export * from "./util/decorators" |
||||
|
@ -1,40 +1,40 @@ |
|||||
// @ts-nocheck
|
// @ts-nocheck
|
||||
/** |
/** |
||||
* 方法 |
* 方法 |
||||
* @param opts 参数 |
* @param opts 参数 |
||||
*/ |
*/ |
||||
type TMethod = "GET" | "POST" | "PUT" | "DELETE" |
type TMethod = "GET" | "POST" | "PUT" | "DELETE" |
||||
export function method(opts?:TMethod|Array<TMethod>) { |
export function method(opts?: TMethod | Array<TMethod>) { |
||||
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
||||
target[propertyKey].$method = opts |
target[propertyKey].$method = opts |
||||
} |
} |
||||
} |
} |
||||
export function route(route?:string) { |
export function route(route?: string) { |
||||
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
||||
target[propertyKey].$route = route |
target[propertyKey].$route = route |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export function config(options:Object) { |
export function config(options: Object) { |
||||
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
||||
target[propertyKey].$options = options |
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) { |
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
||||
target[propertyKey].$auth = isAuth |
target[propertyKey].$auth = isAuth |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export function validate(validate:Object) { |
export function validate(validate: Object) { |
||||
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
||||
target[propertyKey].$validate = validate |
target[propertyKey].$validate = validate |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export function swagger(desc,notes,tags) { |
export function swagger(desc, notes, tags) { |
||||
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
return function (target, propertyKey: string, descriptor: PropertyDescriptor) { |
||||
target[propertyKey].$swagger = [desc,notes,tags] |
target[propertyKey].$swagger = [desc, notes, tags] |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,21 +1,21 @@ |
|||||
html{ |
html { |
||||
overflow-y: auto |
overflow-y: auto; |
||||
} |
} |
||||
|
|
||||
.message-container{ |
.message-container { |
||||
position: fixed; |
position: fixed; |
||||
right: 0; |
right: 0; |
||||
top: 0; |
top: 0; |
||||
z-index: 999; |
z-index: 999; |
||||
overflow: auto; |
overflow: auto; |
||||
height: 100vh; |
height: 100vh; |
||||
} |
} |
||||
.message-container::-webkit-scrollbar{ |
.message-container::-webkit-scrollbar { |
||||
display: none; |
display: none; |
||||
} |
} |
||||
.message-container .message{ |
.message-container .message { |
||||
min-width: 250px; |
min-width: 250px; |
||||
max-width: 250px; |
max-width: 250px; |
||||
margin: 25px; |
margin: 25px; |
||||
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; |
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; |
||||
} |
} |
||||
|
@ -1,27 +1,27 @@ |
|||||
import { Req } from "#/global"; |
import { Req } from "#/global" |
||||
|
|
||||
export async function validateJwt(decoded, request: Req, h) { |
export async function validateJwt(decoded, request: Req, h) { |
||||
if (decoded.id) { |
if (decoded.id) { |
||||
const User = request.getModel("User") |
const User = request.getModel("User") |
||||
const result = await User.findOne({ where: { id: decoded.id } }); |
const result = await User.findOne({ where: { id: decoded.id } }) |
||||
if (result == null) { |
if (result == null) { |
||||
return { isValid: false }; |
return { isValid: false } |
||||
|
} |
||||
|
return { isValid: true } |
||||
|
} else { |
||||
|
return { isValid: false } |
||||
} |
} |
||||
return { isValid: true }; |
|
||||
} else { |
|
||||
return { isValid: false }; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
export async function validateSession(request: Req, session) { |
export async function validateSession(request: Req, session) { |
||||
const User = request.getModel("User") |
const User = request.getModel("User") |
||||
if (session.id) { |
if (session.id) { |
||||
const result = await User.findOne({ where: { id: session.id } }); |
const result = await User.findOne({ where: { id: session.id } }) |
||||
if (result == null) { |
if (result == null) { |
||||
return { valid: false }; |
return { valid: false } |
||||
|
} |
||||
|
return { valid: true, credentials: result } |
||||
|
} else { |
||||
|
return { valid: false } |
||||
} |
} |
||||
return { valid: true, credentials: result }; |
|
||||
} else { |
|
||||
return { valid: false }; |
|
||||
} |
|
||||
} |
} |
||||
|
@ -1,37 +1,37 @@ |
|||||
import path from "path"; |
import path from "path" |
||||
|
|
||||
export default function () { |
export default function () { |
||||
return { |
return { |
||||
appenders: { |
appenders: { |
||||
file: { |
file: { |
||||
type: "file", |
type: "file", |
||||
filename: path.resolve(__dirname, "../", "./log", "./Site.log"), |
filename: path.resolve(__dirname, "../", "./log", "./Site.log"), |
||||
}, |
}, |
||||
SQL: { |
SQL: { |
||||
type: "file", |
type: "file", |
||||
filename: path.resolve(__dirname, "../", "./log", "./SQL.log"), |
filename: path.resolve(__dirname, "../", "./log", "./SQL.log"), |
||||
}, |
}, |
||||
console: { |
console: { |
||||
type: "console", |
type: "console", |
||||
}, |
}, |
||||
}, |
}, |
||||
categories: { |
categories: { |
||||
default: { |
default: { |
||||
appenders: ["console"], |
appenders: ["console"], |
||||
level: "all", |
level: "all", |
||||
}, |
}, |
||||
HAPI: { |
HAPI: { |
||||
appenders: ["console"], |
appenders: ["console"], |
||||
level: "all", |
level: "all", |
||||
}, |
}, |
||||
Site: { |
Site: { |
||||
appenders: ["file", "console"], |
appenders: ["file", "console"], |
||||
level: "debug", |
level: "debug", |
||||
}, |
}, |
||||
SQL: { |
SQL: { |
||||
appenders: ["SQL"], |
appenders: ["SQL"], |
||||
level: "debug", |
level: "debug", |
||||
}, |
}, |
||||
}, |
}, |
||||
}; |
} |
||||
} |
} |
||||
|
@ -1,21 +1,21 @@ |
|||||
// require("dotenv").config();
|
// require("dotenv").config();
|
||||
import { configure, getLogger } from "log4js"; |
import { configure, getLogger } from "log4js" |
||||
import log4jsConfig from "./log4js_config"; |
import log4jsConfig from "./log4js_config" |
||||
|
|
||||
configure(log4jsConfig()); |
configure(log4jsConfig()) |
||||
const loggerSite = getLogger("Site"); |
const loggerSite = getLogger("Site") |
||||
const loggerSQL = getLogger("SQL"); |
const loggerSQL = getLogger("SQL") |
||||
const logger = getLogger("HAPI"); |
const logger = getLogger("HAPI") |
||||
|
|
||||
loggerSite.level = "debug"; |
loggerSite.level = "debug" |
||||
loggerSQL.level = "debug"; |
loggerSQL.level = "debug" |
||||
|
|
||||
global.logger = logger; |
global.logger = logger |
||||
global.loggerSite = loggerSite; |
global.loggerSite = loggerSite |
||||
global.loggerSQL = loggerSQL; |
global.loggerSQL = loggerSQL |
||||
|
|
||||
import { run } from "./run"; |
import { run } from "./run" |
||||
|
|
||||
run().then((server) => { |
run().then(server => { |
||||
global.server = server; |
global.server = server |
||||
}); |
}) |
||||
|
@ -1,64 +1,67 @@ |
|||||
import { Sequelize, DataTypes, Optional, Model } from "sequelize" |
import { Sequelize, DataTypes, Optional, Model } from "sequelize" |
||||
|
|
||||
interface UserAttributes { |
interface UserAttributes { |
||||
id: number; |
id: number |
||||
username: string; |
username: string |
||||
password: string; |
password: string |
||||
nickname: string; |
nickname: string |
||||
email: string; |
email: string |
||||
|
|
||||
createdAt?: Date; |
createdAt?: Date |
||||
updatedAt?: Date; |
updatedAt?: Date |
||||
deletedAt?: Date; |
deletedAt?: Date |
||||
} |
} |
||||
|
|
||||
export interface UserInput extends Optional<UserAttributes, 'id'> { } |
export interface UserInput extends Optional<UserAttributes, "id"> {} |
||||
export interface UserOuput extends Required<UserAttributes> { } |
export interface UserOuput extends Required<UserAttributes> {} |
||||
export type TUserModel = ReturnType<typeof UserModel> |
export type TUserModel = ReturnType<typeof UserModel> |
||||
|
|
||||
type DT = typeof DataTypes |
type DT = typeof DataTypes |
||||
export default function UserModel(sequelize: Sequelize, DataTypes: DT) { |
export default function UserModel(sequelize: Sequelize, DataTypes: DT) { |
||||
class User extends Model<UserAttributes, UserInput> implements UserAttributes { |
class User extends Model<UserAttributes, UserInput> implements UserAttributes { |
||||
public id: number; |
public id: number |
||||
public username: string; |
public username: string |
||||
public password: string; |
public password: string |
||||
public nickname: string; |
public nickname: string |
||||
public email: string; |
public email: string |
||||
|
|
||||
// timestamps!
|
// timestamps!
|
||||
public readonly createdAt!: Date; |
public readonly createdAt!: Date |
||||
public readonly updatedAt!: Date; |
public readonly updatedAt!: Date |
||||
public readonly deletedAt!: Date; |
public readonly deletedAt!: Date |
||||
} |
} |
||||
User.init({ |
User.init( |
||||
id: { |
{ |
||||
type: DataTypes.INTEGER.UNSIGNED, |
id: { |
||||
autoIncrement: true, |
type: DataTypes.INTEGER.UNSIGNED, |
||||
primaryKey: true, |
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, |
sequelize, |
||||
allowNull: false |
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方法
|
// 覆盖User的toJSON方法
|
||||
interface User{ |
interface User { |
||||
toJSON: ()=> UserOuput |
toJSON: () => UserOuput |
||||
} |
} |
||||
return User |
return User |
||||
}; |
} |
||||
|
@ -1,29 +1,29 @@ |
|||||
import { publicDir } from "@/util"; |
import { publicDir } from "@/util" |
||||
const Inert = require("@hapi/inert"); |
const Inert = require("@hapi/inert") |
||||
|
|
||||
const filePlugin = { |
const filePlugin = { |
||||
name: "filePlugin", |
name: "filePlugin", |
||||
version: "0.0.1", |
version: "0.0.1", |
||||
register: async function (server, options) { |
register: async function (server, options) { |
||||
server.settings.routes = { |
server.settings.routes = { |
||||
files: { |
files: { |
||||
relativeTo: publicDir, |
relativeTo: publicDir, |
||||
}, |
}, |
||||
}; |
} |
||||
await server.register(Inert); |
await server.register(Inert) |
||||
server.route({ |
server.route({ |
||||
method: "GET", |
method: "GET", |
||||
path: "/public/{param*}", |
path: "/public/{param*}", |
||||
config: { auth: false }, |
config: { auth: false }, |
||||
handler: { |
handler: { |
||||
directory: { |
directory: { |
||||
path: publicDir, |
path: publicDir, |
||||
index: true, |
index: true, |
||||
redirectToSlash: true, |
redirectToSlash: true, |
||||
}, |
}, |
||||
}, |
}, |
||||
}); |
}) |
||||
}, |
}, |
||||
}; |
} |
||||
|
|
||||
export default filePlugin; |
export default filePlugin |
||||
|
@ -1,155 +1,159 @@ |
|||||
import filePlugin from "./file-plugin"; |
import filePlugin from "./file-plugin" |
||||
import path from "path"; |
import path from "path" |
||||
import { baseDir, sourceDir } from "@/util"; |
import { baseDir, sourceDir } from "@/util" |
||||
import { plugin as routePlugin } from "@noderun/hapi-router"; |
import { plugin as routePlugin } from "@noderun/hapi-router" |
||||
import {ServerRegisterPluginObject, Plugin, Server, Request, ResponseObject} from "@hapi/hapi" |
import { ServerRegisterPluginObject, Plugin, Server, Request, ResponseObject } from "@hapi/hapi" |
||||
import Hoek from "@hapi/hoek" |
import Hoek from "@hapi/hoek" |
||||
import HapiYar from "@hapi/yar"; |
import HapiYar from "@hapi/yar" |
||||
import HapiCrumb from "@hapi/crumb" |
import HapiCrumb from "@hapi/crumb" |
||||
import { Stream } from "stream"; |
import { Stream } from "stream" |
||||
import HapiPino from "hapi-pino"; |
import HapiPino from "hapi-pino" |
||||
|
|
||||
const pino = require('pino') |
const pino = require("pino") |
||||
const transport = pino.transport({ |
const transport = pino.transport({ |
||||
targets: [ |
targets: [ |
||||
// process.env.NODE_ENV !== 'production' ? {
|
// process.env.NODE_ENV !== 'production' ? {
|
||||
// target: 'pino-pretty',
|
// target: 'pino-pretty',
|
||||
// options: {
|
// options: {
|
||||
// colorize: true,
|
// colorize: true,
|
||||
// translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"'
|
// translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"'
|
||||
// },
|
// },
|
||||
// level: 'info' } : { target: 'pino/file', level: 'info' },
|
// level: 'info' } : { target: 'pino/file', level: 'info' },
|
||||
{ target: 'pino-pretty', level: 'trace', options: { |
{ |
||||
destination: path.resolve(baseDir, "./log/pino.log"), |
target: "pino-pretty", |
||||
colorize: true, |
level: "trace", |
||||
translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"', |
options: { |
||||
mkdir: true, |
destination: path.resolve(baseDir, "./log/pino.log"), |
||||
// append: false
|
colorize: true, |
||||
}} |
translateTime: 'HH:MM:ss.l mmmm dS yyyy "UTC"', |
||||
// { target: 'pino/file', level: 'trace', options: {
|
mkdir: true, |
||||
// destination: path.resolve(baseDir, "./log/pino.log"),
|
// append: false
|
||||
// mkdir: true,
|
}, |
||||
// // append: false
|
}, |
||||
// }}
|
// { target: 'pino/file', level: 'trace', options: {
|
||||
] |
// destination: path.resolve(baseDir, "./log/pino.log"),
|
||||
|
// mkdir: true,
|
||||
|
// // append: false
|
||||
|
// }}
|
||||
|
], |
||||
}) |
}) |
||||
|
|
||||
export default [ |
export default [ |
||||
{ |
{ |
||||
plugin: HapiPino, |
plugin: HapiPino, |
||||
options: { |
options: { |
||||
logPayload: true, |
logPayload: true, |
||||
logQueryParams: true, |
logQueryParams: true, |
||||
logPathParams: true, |
logPathParams: true, |
||||
logRouteTags: true, |
logRouteTags: true, |
||||
// logRequestStart: true,
|
// logRequestStart: true,
|
||||
instance: pino(transport), |
instance: pino(transport), |
||||
ignoreFunc: (options, request) => { |
ignoreFunc: (options, request) => { |
||||
return request.path.startsWith('/public') || request.path.startsWith('/404') |
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<any>, |
||||
|
}, |
||||
|
{ |
||||
|
plugin: routePlugin as Plugin<any>, |
||||
|
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<any>, |
|
||||
}, |
|
||||
{ |
|
||||
plugin: routePlugin as Plugin<any>, |
|
||||
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: { |
||||
plugin: { |
name: "flash", |
||||
name: "flash", |
version: "0.0.1", |
||||
version: "0.0.1", |
// https://github.com/hks-epod/paydash/blob/master/lib/flash.js
|
||||
// https://github.com/hks-epod/paydash/blob/master/lib/flash.js
|
register: function (server: Server, options) { |
||||
register: function (server: Server, options) { |
server.ext("onPreResponse", async function (request: Request, h) { |
||||
server.ext('onPreResponse', async function(request: Request, h) { |
// @ts-ignore
|
||||
// @ts-ignore
|
if (request.response.variety === "file") return h.continue // 返回文件时不处理
|
||||
if(request.response.variety === "file") return h.continue; // 返回文件时不处理
|
if (request.path.startsWith("/api") || request.path.startsWith("/htmx")) return h.continue |
||||
if(request.path.startsWith("/api") || request.path.startsWith("/htmx")) return h.continue; |
// 需要设置auth是try或者true才行
|
||||
// 需要设置auth是try或者true才行
|
const isLogin = request.auth.isAuthenticated |
||||
const isLogin = request.auth.isAuthenticated; |
loggerSite.debug(`是否登录:${isLogin}, 请求路径:${request.path}, 请求方法:${request.method}`) |
||||
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 = <any>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
|
// @ts-ignore
|
||||
request.response.source.context = Hoek.applyToDefaults( |
// console.log(isLogin, request.path, request.response.variety);
|
||||
{ |
// let user;
|
||||
flash: !!Object.keys(flash).length?flash:false, |
// if(isLogin){
|
||||
isLogin: isLogin, |
// const { id } = request.auth.credentials;
|
||||
user: isLogin?request.auth.credentials:false, |
// const User = request.getModel("User")
|
||||
}, |
// user = <any>await User.findOne({ where: { id: id } });
|
||||
// @ts-ignore
|
// user = user.toJSON();
|
||||
request.response.source.context |
// delete user.password;
|
||||
); |
// }
|
||||
// @ts-ignore
|
// @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<any>, |
||||
|
}, |
||||
|
{ |
||||
|
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; |
return false |
||||
}); |
}, |
||||
} |
cookieOptions: { |
||||
} as Plugin<any> |
path: "/", |
||||
}, |
isSecure: false, |
||||
{ |
}, |
||||
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; |
|
||||
}, |
}, |
||||
cookieOptions: { |
}, |
||||
path: '/', |
] as unknown as ServerRegisterPluginObject<any> |
||||
isSecure: false |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
] as unknown as ServerRegisterPluginObject<any>; |
|
||||
|
@ -1,13 +1,13 @@ |
|||||
import { Req, Res, ReturnValue } from "#/global"; |
import { Req, Res, ReturnValue } from "#/global" |
||||
import { auth, route } from "@noderun/hapi-router"; |
import { auth, route } from "@noderun/hapi-router" |
||||
|
|
||||
export default class { |
export default class { |
||||
@route("/{path*}") |
@route("/{path*}") |
||||
@auth(false) |
@auth(false) |
||||
async any(req: Req, h: Res): ReturnValue { |
async any(req: Req, h: Res): ReturnValue { |
||||
return { |
return { |
||||
code: 404, |
code: 404, |
||||
data: null, |
data: null, |
||||
}; |
} |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,83 +1,82 @@ |
|||||
import path from "path"; |
import path from "path" |
||||
import { gSuccess, gFail, uploadDir, uploadPath } from "@/util"; |
import { gSuccess, gFail, uploadDir, uploadPath } from "@/util" |
||||
import { dateTimeFormat } from "@/util/util" |
import { dateTimeFormat } from "@/util/util" |
||||
const fs = require("fs"); |
const fs = require("fs") |
||||
const multiparty = require("multiparty"); |
const multiparty = require("multiparty") |
||||
const FileType = require("file-type"); |
const FileType = require("file-type") |
||||
|
|
||||
function saveFile(file) { |
function saveFile(file) { |
||||
return new Promise(async (resolve, reject) => { |
return new Promise(async (resolve, reject) => { |
||||
const filename = file.originalFilename; |
const filename = file.originalFilename |
||||
const uploadedPath = file.path; |
const uploadedPath = file.path |
||||
const filetype = await FileType.fromFile(uploadedPath); |
const filetype = await FileType.fromFile(uploadedPath) |
||||
const _file = path.parse(filename); |
const _file = path.parse(filename) |
||||
if (filetype && (filetype.ext == "jpg" || filetype.ext == "png")) { |
if (filetype && (filetype.ext == "jpg" || filetype.ext == "png")) { |
||||
let _name = _file.name + "_" + dateTimeFormat(new Date(), "yyyy_MM_dd") + "_" + new Date().getTime() + _file.ext |
let _name = |
||||
const dstPath = path.resolve(uploadDir, _name); |
_file.name + "_" + dateTimeFormat(new Date(), "yyyy_MM_dd") + "_" + new Date().getTime() + _file.ext |
||||
fs.rename(uploadedPath, dstPath, function (err) { |
const dstPath = path.resolve(uploadDir, _name) |
||||
if (err) { |
fs.rename(uploadedPath, dstPath, function (err) { |
||||
console.log("rename error: " + err); |
if (err) { |
||||
reject(); |
console.log("rename error: " + err) |
||||
|
reject() |
||||
|
} else { |
||||
|
resolve(dstPath) |
||||
|
} |
||||
|
}) |
||||
} else { |
} else { |
||||
resolve(dstPath); |
fs.unlinkSync(uploadedPath) |
||||
|
reject(new Error(filename + "文件不是图片")) |
||||
} |
} |
||||
}); |
}) |
||||
} else { |
|
||||
fs.unlinkSync(uploadedPath); |
|
||||
reject(new Error(filename + "文件不是图片")); |
|
||||
} |
|
||||
}); |
|
||||
} |
} |
||||
|
|
||||
export default function (req, h) { |
export default function (req, h) { |
||||
const form = new multiparty.Form({ |
const form = new multiparty.Form({ |
||||
uploadDir: uploadDir, //路径需要对应自己的项目更改
|
uploadDir: uploadDir, //路径需要对应自己的项目更改
|
||||
/*设置文件保存路径 */ |
/*设置文件保存路径 */ |
||||
encoding: "utf-8", |
encoding: "utf-8", |
||||
/*编码设置 */ |
/*编码设置 */ |
||||
maxFilesSize: 20000 * 1024 * 1024, |
maxFilesSize: 20000 * 1024 * 1024, |
||||
/*设置文件最大值 20MB */ |
/*设置文件最大值 20MB */ |
||||
keepExtensions: true, |
keepExtensions: true, |
||||
/*保留后缀*/ |
/*保留后缀*/ |
||||
}); |
}) |
||||
return new Promise(async (resolve, reject) => { |
return new Promise(async (resolve, reject) => { |
||||
form.on("part", function (part) { |
form.on("part", function (part) { |
||||
console.log(part.filename); |
console.log(part.filename) |
||||
}); |
}) |
||||
form.on("progress", function (bytesReceived, bytesExpected) { |
form.on("progress", function (bytesReceived, bytesExpected) { |
||||
if (bytesExpected === null) { |
if (bytesExpected === null) { |
||||
return; |
return |
||||
} |
} |
||||
|
|
||||
var percentComplete = (bytesReceived / bytesExpected) * 100; |
var percentComplete = (bytesReceived / bytesExpected) * 100 |
||||
console.log( |
console.log("the form is " + Math.floor(percentComplete) + "%" + " complete") |
||||
"the form is " + Math.floor(percentComplete) + "%" + " complete" |
}) |
||||
); |
form.parse(req.payload, async function (err, fields, files) { |
||||
}); |
// console.log(err, fields, files);
|
||||
form.parse(req.payload, async function (err, fields, files) { |
|
||||
// console.log(err, fields, files);
|
|
||||
|
|
||||
if (err) { |
if (err) { |
||||
resolve(err.message); |
resolve(err.message) |
||||
return; |
return |
||||
} |
} |
||||
const errList = []; |
const errList = [] |
||||
const fileList = []; |
const fileList = [] |
||||
for (let i = 0; i < files.file.length; i++) { |
for (let i = 0; i < files.file.length; i++) { |
||||
const file = files.file[i]; |
const file = files.file[i] |
||||
try { |
try { |
||||
const dstPath = await saveFile(file); |
const dstPath = await saveFile(file) |
||||
fileList.push(dstPath); |
fileList.push(dstPath) |
||||
} catch (error) { |
} catch (error) { |
||||
errList.push(error.message); |
errList.push(error.message) |
||||
} |
} |
||||
} |
} |
||||
if (errList.length) { |
if (errList.length) { |
||||
resolve(gFail(null, errList.join("\n"))); |
resolve(gFail(null, errList.join("\n"))) |
||||
return; |
return |
||||
} |
} |
||||
// resolve(h.view("views/upload.ejs"));
|
// resolve(h.view("views/upload.ejs"));
|
||||
resolve([...new Set(fileList)]); |
resolve([...new Set(fileList)]) |
||||
}); |
}) |
||||
}); |
}) |
||||
} |
} |
||||
|
@ -1,29 +1,27 @@ |
|||||
import { config, method, route, swagger, validate } from "@noderun/hapi-router"; |
import { config, method, route, swagger, validate } from "@noderun/hapi-router" |
||||
import UploadFunc from "./_upload"; |
import UploadFunc from "./_upload" |
||||
import Joi from "joi"; |
import Joi from "joi" |
||||
export default class { |
export default class { |
||||
index(request, h) { |
index(request, h) { |
||||
return h.view("views/demo.ejs"); |
return h.view("views/demo.ejs") |
||||
} |
} |
||||
|
|
||||
@config({ |
@config({ |
||||
payload: { |
payload: { |
||||
maxBytes: 20000 * 1024 * 1024, |
maxBytes: 20000 * 1024 * 1024, |
||||
output: "stream", |
output: "stream", |
||||
parse: false, |
parse: false, |
||||
// multipart: true,
|
// multipart: true,
|
||||
allow: "multipart/form-data", |
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; |
|
||||
} |
|
||||
} |
} |
||||
|
@ -1,102 +1,96 @@ |
|||||
import { |
import { auth, method, route, swagger, validate, config } from "@noderun/hapi-router" |
||||
auth, |
import { gSuccess, gFail } from "@/util" |
||||
method, |
import * as bcrypt from "bcrypt" |
||||
route, |
import * as jwt from "jsonwebtoken" |
||||
swagger, |
import * as Joi from "joi" |
||||
validate, |
import { UserSchema } from "@/schema" |
||||
config, |
import { ReturnValue, Req, Res } from "#/global" |
||||
} 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 { |
export default class { |
||||
@validate({ |
@validate({ |
||||
payload: UserSchema, |
payload: UserSchema, |
||||
}) |
}) |
||||
@method("POST") |
@method("POST") |
||||
@swagger("用户注册", "返回注册用户的信息", ["api"]) |
@swagger("用户注册", "返回注册用户的信息", ["api"]) |
||||
@auth(false) |
@auth(false) |
||||
async register(request: Req, h: Res): ReturnValue { |
async register(request: Req, h: Res): ReturnValue { |
||||
let { username, password, email } = request.payload as any; |
let { username, password, email, } = request.payload as any |
||||
if (!username) username = email; |
if (!username) username = email |
||||
const User = request.getModel("User") |
const User = request.getModel("User") |
||||
logger.trace(username, email); |
logger.trace(username, email) |
||||
try { |
try { |
||||
const result = await User.findOne({ where: { username: username } }); |
const result = await User.findOne({ where: { username: username } }) |
||||
if (result != null) { |
if (result != null) { |
||||
return gFail(null, "已存在该用户"); |
return gFail(null, "已存在该用户") |
||||
} |
} |
||||
let salt = bcrypt.genSaltSync(10); |
let salt = bcrypt.genSaltSync(10) |
||||
let pwdLock = bcrypt.hashSync(password, salt); |
let pwdLock = bcrypt.hashSync(password, salt) |
||||
await User.create({ username, password: pwdLock, email }); |
// @ts-ignore
|
||||
return gSuccess("success", "you have a good heart."); |
await User.create({ username, password: pwdLock, email }) |
||||
} catch (e) { |
return gSuccess("success", "you have a good heart.") |
||||
return gFail(null, "新建用户失败"); |
} catch (e) { |
||||
|
return gFail(null, "新建用户失败") |
||||
|
} |
||||
} |
} |
||||
} |
|
||||
|
|
||||
@method("POST") |
@method("POST") |
||||
async logout(request: Req, h: Res): ReturnValue { |
async logout(request: Req, h: Res): ReturnValue { |
||||
request.cookieAuth.clear(); |
request.cookieAuth.clear() |
||||
return gSuccess("success"); |
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 = <any>await User.findOne({ where: { username: username } }); |
|
||||
if (result == null) { |
|
||||
return gFail(null, "不存在该用户"); |
|
||||
} |
} |
||||
const validUser = bcrypt.compareSync(password, result.password); |
|
||||
if (!validUser) { |
@validate({ |
||||
return gFail(null, "密码不正确"); |
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 = <any>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") |
@method("DELETE") |
||||
@auth() |
@auth() |
||||
@swagger("删除用户", "删除用户账号", ["sum"]) |
@swagger("删除用户", "删除用户账号", ["sum"]) |
||||
async del(request: Req, h: Res): ReturnValue { |
async del(request: Req, h: Res): ReturnValue { |
||||
const { id } = request.auth.credentials; |
const { id } = request.auth.credentials |
||||
const User = request.getModel("User") |
const User = request.getModel("User") |
||||
let result = await User.findOne({ where: { id: id } }); |
let result = await User.findOne({ where: { id: id } }) |
||||
if (result == null) { |
if (result == null) { |
||||
return gFail(null, "不存在该用户"); |
return gFail(null, "不存在该用户") |
||||
|
} |
||||
|
await result.destroy() |
||||
|
return gSuccess(null, "删除成功") |
||||
} |
} |
||||
await result.destroy(); |
|
||||
return gSuccess(null, "删除成功"); |
|
||||
} |
|
||||
|
|
||||
@method("GET") |
@method("GET") |
||||
@swagger("获取用户信息", "返回注册用户的信息", ["用户操作", "api"]) |
@swagger("获取用户信息", "返回注册用户的信息", ["用户操作", "api"]) |
||||
async userinfo(request: Req, h: Res): ReturnValue { |
async userinfo(request: Req, h: Res): ReturnValue { |
||||
const { id } = request.auth.credentials; |
const { id } = request.auth.credentials |
||||
const User = request.getModel("User") |
const User = request.getModel("User") |
||||
let result = <any>await User.findOne({ where: { id: id } }); |
let result = <any>await User.findOne({ where: { id: id } }) |
||||
if (result == null) { |
if (result == null) { |
||||
return gFail(null, "不存在该用户"); |
return gFail(null, "不存在该用户") |
||||
|
} |
||||
|
result = result.toJSON() |
||||
|
delete result.password |
||||
|
return gSuccess(result) |
||||
} |
} |
||||
result = result.toJSON(); |
|
||||
delete result.password; |
|
||||
return gSuccess(result); |
|
||||
} |
|
||||
} |
} |
||||
|
@ -1,96 +1,96 @@ |
|||||
import { Req, Res, ReturnValue } from "#/global"; |
import { Req, Res, ReturnValue } from "#/global" |
||||
import { LoginUserSchema, RegisterUserSchema, UserSchema } from "@/schema"; |
import { LoginUserSchema, RegisterUserSchema, UserSchema } from "@/schema" |
||||
import { gFail, gSuccess } from "@/util"; |
import { gFail, gSuccess } from "@/util" |
||||
import { auth, config, method, route, validate } from "@noderun/hapi-router"; |
import { auth, config, method, route, validate } from "@noderun/hapi-router" |
||||
import * as bcrypt from "bcrypt"; |
import * as bcrypt from "bcrypt" |
||||
/** |
/** |
||||
* 登录界面 |
* 登录界面 |
||||
*/ |
*/ |
||||
export default class { |
export default class { |
||||
@route("/login") |
@route("/login") |
||||
@auth("try") |
@auth("try") |
||||
@method("GET") |
@method("GET") |
||||
async login_GET(request: Req, h: Res): ReturnValue { |
async login_GET(request: Req, h: Res): ReturnValue { |
||||
if (request.auth.isAuthenticated) { |
if (request.auth.isAuthenticated) { |
||||
request.yar.flash('warning', '您已经登陆'); |
request.yar.flash("warning", "您已经登陆") |
||||
return h.redirect("/") |
return h.redirect("/") |
||||
} else { |
} else { |
||||
logger.debug("未登录"); |
logger.debug("未登录") |
||||
|
} |
||||
|
return h.view("views/login.pug") |
||||
} |
} |
||||
return h.view("views/login.pug"); |
|
||||
} |
|
||||
|
|
||||
@validate({ |
@validate({ |
||||
payload: LoginUserSchema, |
payload: LoginUserSchema, |
||||
$errto: '/login', |
$errto: "/login", |
||||
// failAction: 'log'
|
// failAction: 'log'
|
||||
failAction: 'function', |
failAction: "function", |
||||
failReason: '用户名或密码错误,请重试', |
failReason: "用户名或密码错误,请重试", |
||||
}) |
}) |
||||
@method("POST") |
@method("POST") |
||||
@route("/login") |
@route("/login") |
||||
async login_POST(request: Req, h: Res): ReturnValue { |
async login_POST(request: Req, h: Res): ReturnValue { |
||||
const { username, password, referrer } = request.payload as any; |
const { username, password, referrer } = request.payload as any |
||||
const User = request.getModel("User"); |
const User = request.getModel("User") |
||||
const account = <any>await User.findOne({ where: { username: username } }); |
const account = <any>await User.findOne({ where: { username: username } }) |
||||
|
|
||||
if (!account || !(await bcrypt.compare(password, account.password))) { |
if (!account || !(await bcrypt.compare(password, account.password))) { |
||||
request.yar.flash('error', 'Invalid username or password'); |
request.yar.flash("error", "Invalid username or password") |
||||
return h.redirect("/login"); |
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") |
@method("GET") |
||||
@auth() |
@auth() |
||||
async logout(request: Req, h: Res): ReturnValue { |
async logout(request: Req, h: Res): ReturnValue { |
||||
request.yar.flash('success', '用户已退出'); |
request.yar.flash("success", "用户已退出") |
||||
request.cookieAuth.clear(); |
request.cookieAuth.clear() |
||||
return h.redirect('/'); |
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("未登录"); |
|
||||
} |
} |
||||
return h.view("views/login.pug"); |
|
||||
} |
|
||||
|
|
||||
@validate({ |
@route("/register") |
||||
payload: RegisterUserSchema, |
@auth("try") |
||||
}) |
@method("GET") |
||||
@method("POST") |
async registerView(request: Req, h: Res): ReturnValue { |
||||
async register(request: Req, h: Res): ReturnValue { |
if (request.auth.isAuthenticated) { |
||||
let { username, password, email, nickname } = request.payload as any; |
request.yar.flash("warning", "您已经登陆") |
||||
if(!email){ |
return h.redirect("/") |
||||
request.yar.flash('error', '必须填写邮箱'); |
} else { |
||||
return h.redirect("/login"); |
logger.debug("未登录") |
||||
|
} |
||||
|
return h.view("views/login.pug") |
||||
} |
} |
||||
if (!username) username = email; |
|
||||
if (!nickname) nickname = username; |
@validate({ |
||||
const User = request.getModel("User") |
payload: RegisterUserSchema, |
||||
logger.trace(username, email); |
}) |
||||
try { |
@method("POST") |
||||
const result = await User.findOne({ where: { username: username } }); |
async register(request: Req, h: Res): ReturnValue { |
||||
if (result != null) { |
let { username, password, email, nickname } = request.payload as any |
||||
request.yar.flash('error', '已存在该用户'); |
if (!email) { |
||||
return h.redirect("/login"); |
request.yar.flash("error", "必须填写邮箱") |
||||
} |
return h.redirect("/login") |
||||
let salt = bcrypt.genSaltSync(10); |
} |
||||
let pwdLock = bcrypt.hashSync(password, salt); |
if (!username) username = email |
||||
await User.create({ username, nickname, password: pwdLock, email }); |
if (!nickname) nickname = username |
||||
return h.redirect("/") |
const User = request.getModel("User") |
||||
} catch (e) { |
logger.trace(username, email) |
||||
request.yar.flash('error', '注册用户失败'); |
try { |
||||
return h.redirect("/login"); |
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") |
||||
|
} |
||||
} |
} |
||||
} |
|
||||
} |
} |
||||
|
@ -1,100 +1,93 @@ |
|||||
import { |
import { auth, config, method, route, swagger, validate } from "@noderun/hapi-router" |
||||
auth, |
import { Req, Res, ReturnValue } from "#/global" |
||||
config, |
import path from "path" |
||||
method, |
import fs from "fs-extra" |
||||
route, |
import { baseDir } from "@/util" |
||||
swagger, |
import MarkdownIt from "markdown-it" |
||||
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 { |
export default class Index { |
||||
async css(request: Req, h: Res): ReturnValue { |
async css(request: Req, h: Res): ReturnValue { |
||||
return h.view("views/css.pug"); |
return h.view("views/css.pug") |
||||
} |
} |
||||
|
|
||||
@auth("try") |
@auth("try") |
||||
async index(request: Req, h: Res): ReturnValue { |
async index(request: Req, h: Res): ReturnValue { |
||||
if (request.auth.isAuthenticated) { |
if (request.auth.isAuthenticated) { |
||||
// 登录了
|
// 登录了
|
||||
} else { |
} else { |
||||
// 未登录
|
// 未登录
|
||||
|
} |
||||
|
return h.view("views/index.pug", { isLogin: request.auth.isAuthenticated }) |
||||
} |
} |
||||
return h.view("views/index.pug", { isLogin: request.auth.isAuthenticated }); |
|
||||
} |
|
||||
|
|
||||
@route("/about") |
@route("/about") |
||||
@auth("try") |
@auth("try") |
||||
async about(request: Req, h) { |
async about(request: Req, h) { |
||||
// console.log(request.auth);
|
// console.log(request.auth);
|
||||
// console.log(1);
|
// console.log(1);
|
||||
|
|
||||
// try {
|
// try {
|
||||
// const User = request.getModel("User");
|
// const User = request.getModel("User");
|
||||
|
|
||||
// console.log(await User.findOne({ where: { username: "xieyaxin" } }));
|
// console.log(await User.findOne({ where: { username: "xieyaxin" } }));
|
||||
// } catch (error) {
|
// } catch (error) {
|
||||
// console.log(error);
|
// console.log(error);
|
||||
// }
|
// }
|
||||
// console.log(2);
|
// console.log(2);
|
||||
const md = new MarkdownIt(); |
const md = new MarkdownIt() |
||||
var result = md.render('# markdown-it rulezz!'); |
var result = md.render("# markdown-it rulezz!") |
||||
return h.view("views/about.pug", { |
return h.view("views/about.pug", { |
||||
md: result |
md: result, |
||||
}); |
}) |
||||
} |
} |
||||
|
|
||||
@route("/docs/{path*}") |
@route("/docs/{path*}") |
||||
@auth() |
@auth() |
||||
async docs(req: Req, h: Res): ReturnValue { |
async docs(req: Req, h: Res): ReturnValue { |
||||
// const {id} = req.auth.credentials
|
// const {id} = req.auth.credentials
|
||||
// try {
|
// try {
|
||||
// req.cookieAuth.ttl(720 * 24 * 60 * 60 * 1000)
|
// req.cookieAuth.ttl(720 * 24 * 60 * 60 * 1000)
|
||||
// req.cookieAuth.set({ id: id });
|
// req.cookieAuth.set({ id: id });
|
||||
// } catch (error) {
|
// } catch (error) {
|
||||
// console.log(error);
|
// console.log(error);
|
||||
|
|
||||
// }
|
// }
|
||||
if (req.params && req.params.path.endsWith(".md")) { |
if (req.params && req.params.path.endsWith(".md")) { |
||||
// console.log(path.resolve(baseDir, "docs/"+"*.md"));
|
// console.log(path.resolve(baseDir, "docs/"+"*.md"));
|
||||
// console.log(await glob("docs/"+"*.md"));
|
// console.log(await glob("docs/"+"*.md"));
|
||||
const mdPath = path.resolve(baseDir, "docs/"+req.params.path) |
const mdPath = path.resolve(baseDir, "docs/" + req.params.path) |
||||
if(fs.existsSync(mdPath)){ |
if (fs.existsSync(mdPath)) { |
||||
const str = fs.readFileSync(mdPath, "utf8") |
const str = fs.readFileSync(mdPath, "utf8") |
||||
console.log("---->", mdPath); |
console.log("---->", mdPath) |
||||
|
|
||||
return h.view("views/css.pug", { |
return h.view("views/css.pug", { |
||||
content: str.toString() |
content: str.toString(), |
||||
}); |
}) |
||||
} |
} |
||||
// 解析文档
|
// 解析文档
|
||||
return h.view("views/css.pug"); |
return h.view("views/css.pug") |
||||
|
} |
||||
|
// 404页面
|
||||
|
return h.redirect("/404") |
||||
} |
} |
||||
// 404页面
|
@route("/{path*}") |
||||
return h.redirect("/404"); |
async any(req: Req, h: Res): ReturnValue { |
||||
} |
console.log("404: ", req.raw.req.url) |
||||
@route("/{path*}") |
return h.redirect("/404?r=" + encodeURIComponent(req.raw.req.url)) |
||||
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 { |
|
||||
// 未登录
|
|
||||
} |
} |
||||
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 |
|
||||
}); |
|
||||
} |
|
||||
} |
} |
||||
|
@ -1,9 +1,9 @@ |
|||||
import { Req, Res, ReturnValue } from "#/global"; |
import { Req, Res, ReturnValue } from "#/global" |
||||
import { gSuccess } from "@/util"; |
import { gSuccess } from "@/util" |
||||
|
|
||||
export default class Nav { |
export default class Nav { |
||||
async index(req: Req, h: Res): ReturnValue{ |
async index(req: Req, h: Res): ReturnValue { |
||||
const Constant = req.getModel("Constant") |
// const Constant = req.getModel("Constant")
|
||||
return gSuccess("31231") |
return gSuccess("31231") |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,146 +1,146 @@ |
|||||
"use strict"; |
"use strict" |
||||
import plugins from "@/plugins"; |
import plugins from "@/plugins" |
||||
import path from "path"; |
import path from "path" |
||||
import { baseDir, templateDir } from "@/util"; |
import { baseDir, templateDir } from "@/util" |
||||
import { validateJwt, validateSession } from "./auth"; |
import { validateJwt, validateSession } from "./auth" |
||||
import Hapi, { Server } from "@hapi/hapi"; |
import Hapi, { Server } from "@hapi/hapi" |
||||
import { Sequelize } from "sequelize"; |
import { Sequelize } from "sequelize" |
||||
import { Req } from "#/global"; |
import { Req } from "#/global" |
||||
// const Hapi = require("@hapi/hapi");
|
// const Hapi = require("@hapi/hapi");
|
||||
// const HapiSwagger = require("hapi-swagger");
|
// const HapiSwagger = require("hapi-swagger");
|
||||
// const HapiSwagger = require("hapi-swaggered-ui"); // swagger v2版本
|
// const HapiSwagger = require("hapi-swaggered-ui"); // swagger v2版本
|
||||
|
|
||||
const pugPluginAlias = require('pug-alias'); |
const pugPluginAlias = require("pug-alias") |
||||
|
|
||||
const run = async (): Promise<Server> => { |
const run = async (): Promise<Server> => { |
||||
const server = Hapi.server({ |
const server = Hapi.server({ |
||||
port: 3388, |
port: 3388, |
||||
host: "localhost", |
host: "localhost", |
||||
}); |
}) |
||||
await server.register([ |
await server.register([ |
||||
{ |
|
||||
plugin: require("hapi-sequelizejs"), |
|
||||
options: [ |
|
||||
{ |
{ |
||||
name: "data", // identifier
|
plugin: require("hapi-sequelizejs"), |
||||
models: [__dirname + "/models/**/*.ts"], // paths/globs to model files
|
options: [ |
||||
// ignoredModels: [__dirname + "/server/models/**/*.js"], // OPTIONAL: paths/globs to ignore files
|
{ |
||||
sequelize: new Sequelize({ |
name: "data", // identifier
|
||||
dialect: "sqlite", |
models: [__dirname + "/models/**/*.ts"], // paths/globs to model files
|
||||
storage: path.resolve(__dirname, "./db/data.db"), |
// ignoredModels: [__dirname + "/server/models/**/*.js"], // OPTIONAL: paths/globs to ignore files
|
||||
logging: false, |
sequelize: new Sequelize({ |
||||
// logging: loggerSQL.debug.bind(loggerSQL) // Alternative way to use custom logger, displays all messages
|
dialect: "sqlite", |
||||
}), // sequelize instance
|
storage: path.resolve(__dirname, "./db/data.db"), |
||||
sync: true, // sync models - default false
|
logging: false, |
||||
forceSync: false, // force sync (drops tables) - default 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
|
//===== JWT ===== Start
|
||||
// await server.register(require("hapi-auth-jwt2"));
|
// await server.register(require("hapi-auth-jwt2"));
|
||||
// server.auth.strategy("jwt", "jwt", {
|
// server.auth.strategy("jwt", "jwt", {
|
||||
// key: process.env.KEY, // Never Share your secret key
|
// key: process.env.KEY, // Never Share your secret key
|
||||
// validate: validateJwt, // validate function defined above
|
// validate: validateJwt, // validate function defined above
|
||||
// verifyOptions: { algorithms: ["HS256"] },
|
// verifyOptions: { algorithms: ["HS256"] },
|
||||
// });
|
// });
|
||||
//===== JWT ===== End
|
//===== JWT ===== End
|
||||
//===== session ===== Start
|
//===== session ===== Start
|
||||
// https://hapi.dev/module/cookie/api?v=11.0.2
|
// https://hapi.dev/module/cookie/api?v=11.0.2
|
||||
await server.register(require("@hapi/cookie")); |
await server.register(require("@hapi/cookie")) |
||||
server.auth.strategy("session", "cookie", { |
server.auth.strategy("session", "cookie", { |
||||
cookie: { |
cookie: { |
||||
ttl: 1000 * 60 * 60 * 24, |
ttl: 1000 * 60 * 60 * 24, |
||||
path: '/', // 测试退出时set-cookie失效,加上这个好了
|
path: "/", // 测试退出时set-cookie失效,加上这个好了
|
||||
name: "sid", //cookie的名字
|
name: "sid", //cookie的名字
|
||||
password: process.env.KEY, |
password: process.env.KEY, |
||||
isSecure: false, // false: 允许 Cookie 通过不安全的连接传输,这会使其受到攻击
|
isSecure: false, // false: 允许 Cookie 通过不安全的连接传输,这会使其受到攻击
|
||||
}, |
}, |
||||
redirectTo(request: Req){ |
redirectTo(request: Req) { |
||||
if (request.path.startsWith('/api')) { |
if (request.path.startsWith("/api")) { |
||||
return false |
return false |
||||
} |
} |
||||
return "/login" |
return "/login" |
||||
}, |
}, |
||||
appendNext: true, |
appendNext: true, |
||||
validateFunc: validateSession, |
validateFunc: validateSession, |
||||
}); |
}) |
||||
server.auth.default("session"); |
server.auth.default("session") |
||||
//===== session ===== End
|
//===== session ===== End
|
||||
|
|
||||
await server.register(plugins as any); |
|
||||
|
|
||||
/** |
await server.register(plugins as any) |
||||
* 模板引擎 |
|
||||
*/ |
|
||||
// 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') |
|
||||
}) |
|
||||
] |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
// http://localhost:3000/documentation
|
/** |
||||
await server.register([ |
* 模板引擎 |
||||
{ |
*/ |
||||
plugin: require("hapi-swagger"), |
// https://hapi.dev/module/vision/api/?v=6.1.0
|
||||
options: { |
await server.register(require("@hapi/vision")) |
||||
documentationPath: "/doc", |
server.views({ |
||||
info: { |
engines: { |
||||
title: "Dream 文档", |
ejs: require("ejs"), |
||||
version: "1.0.0", |
pug: require("pug"), |
||||
}, |
}, |
||||
grouping: "tags", |
isCached: process.env.NODE_ENV === "development" ? false : true, |
||||
tags: [ |
compileMode: "sync", // ejs
|
||||
{ |
relativeTo: baseDir, |
||||
name: "sum", |
layout: false, // ejs
|
||||
description: "working with maths", |
layoutPath: path.resolve(templateDir, "layout"), // ejs
|
||||
externalDocs: { |
path: "template", |
||||
description: "Find out more", |
// pug
|
||||
url: "http://example.org", |
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, |
||||
name: "store", |
plugins: [ |
||||
description: "storing data", |
pugPluginAlias({ |
||||
externalDocs: { |
// as Function
|
||||
description: "Find out more", |
"@": fn => fn.replace(/^@/, "template"), |
||||
url: "http://example.org", |
}), |
||||
|
], |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
// 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) => { |
process.on("unhandledRejection", err => { |
||||
console.log("unhandledRejection:", err); |
console.log("unhandledRejection:", err) |
||||
process.exit(1); |
process.exit(1) |
||||
}); |
}) |
||||
|
|
||||
export { run }; |
export { run } |
||||
|
@ -1,30 +1,32 @@ |
|||||
import * as Joi from "joi"; |
import * as Joi from "joi" |
||||
|
|
||||
export const UserSchema = Joi.object({ |
export const UserSchema = Joi.object({ |
||||
username: Joi.string().alphanum().min(6).max(35), |
username: Joi.string().alphanum().min(6).max(35), |
||||
password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), |
password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), |
||||
email: Joi.string().email({ |
email: Joi.string().email({ |
||||
minDomainSegments: 2, |
minDomainSegments: 2, |
||||
tlds: { allow: ["com", "net"] }, |
tlds: { allow: ["com", "net"] }, |
||||
}) |
}), |
||||
}).or("username", "email"); |
}).or("username", "email") |
||||
|
|
||||
export const RegisterUserSchema = Joi.object({ |
export const RegisterUserSchema = Joi.object({ |
||||
username: Joi.string().alphanum().min(6).max(35), |
username: Joi.string().alphanum().min(6).max(35), |
||||
password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), |
password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), |
||||
email: Joi.string().email({ |
email: Joi.string() |
||||
minDomainSegments: 2, |
.email({ |
||||
tlds: { allow: ["com", "net"] }, |
minDomainSegments: 2, |
||||
}).required(), |
tlds: { allow: ["com", "net"] }, |
||||
|
}) |
||||
|
.required(), |
||||
nickname: Joi.string().alphanum().min(4).max(35), |
nickname: Joi.string().alphanum().min(4).max(35), |
||||
}) |
}) |
||||
|
|
||||
export const LoginUserSchema = Joi.object({ |
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)
|
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(), |
password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(), |
||||
email: Joi.string().email({ |
email: Joi.string().email({ |
||||
minDomainSegments: 2, |
minDomainSegments: 2, |
||||
tlds: { allow: ["com", "net"] }, |
tlds: { allow: ["com", "net"] }, |
||||
}), |
}), |
||||
}).or("username", "email"); |
}).or("username", "email") |
||||
|
@ -1,13 +1,13 @@ |
|||||
import path from "path"; |
import path from "path" |
||||
export * from "./res-helper"; |
export * from "./res-helper" |
||||
|
|
||||
export const isDev = process.env.NODE_ENV === "development" |
export const isDev = process.env.NODE_ENV === "development" |
||||
export const isProd = process.env.NODE_ENV === "production" |
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 sourceDir = isProd ? path.resolve(__dirname, "../../dist") : path.resolve(__dirname, "../../source") |
||||
export const publicDir = path.resolve(__dirname, "../../public"); |
export const publicDir = path.resolve(__dirname, "../../public") |
||||
export const uploadDir = path.resolve(publicDir, "upload"); |
export const uploadDir = path.resolve(publicDir, "upload") |
||||
export const uploadPath = "/public/upload"; // 图片上传地址
|
export const uploadPath = "/public/upload" // 图片上传地址
|
||||
export const templateDir = path.resolve(baseDir, "template"); |
export const templateDir = path.resolve(baseDir, "template") |
||||
|
@ -1,29 +1,29 @@ |
|||||
export function gSuccess(data = null, message = "success") { |
export function gSuccess(data = null, message = "success") { |
||||
if (typeof data === "string") { |
if (typeof data === "string") { |
||||
|
return { |
||||
|
code: 200, |
||||
|
message: data, |
||||
|
data: null, |
||||
|
} |
||||
|
} |
||||
return { |
return { |
||||
code: 200, |
code: 200, |
||||
message: data, |
message: message, |
||||
data: null, |
data: data, |
||||
}; |
} |
||||
} |
|
||||
return { |
|
||||
code: 200, |
|
||||
message: message, |
|
||||
data: data, |
|
||||
}; |
|
||||
} |
} |
||||
|
|
||||
export function gFail(data = null, message = "fail") { |
export function gFail(data = null, message = "fail") { |
||||
if (typeof data === "string") { |
if (typeof data === "string") { |
||||
|
return { |
||||
|
code: 400, |
||||
|
message: data, |
||||
|
data: null, |
||||
|
} |
||||
|
} |
||||
return { |
return { |
||||
code: 400, |
code: 400, |
||||
message: data, |
message: message, |
||||
data: null, |
data: data, |
||||
}; |
} |
||||
} |
|
||||
return { |
|
||||
code: 400, |
|
||||
message: message, |
|
||||
data: data, |
|
||||
}; |
|
||||
} |
} |
||||
|
@ -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) { |
if (!date) { |
||||
return '' |
return "" |
||||
} |
} |
||||
if (typeof date === 'string') { |
if (typeof date === "string") { |
||||
date = date.replace('T', ' ').replace('Z', ''); |
date = date.replace("T", " ").replace("Z", "") |
||||
date = new Date(date.replace(/-/g, '/')) |
date = new Date(date.replace(/-/g, "/")) |
||||
} |
} |
||||
if (typeof date === 'number') { |
if (typeof date === "number") { |
||||
date = new Date(date) |
date = new Date(date) |
||||
} |
} |
||||
var o = { |
var o = { |
||||
'M+': date.getMonth() + 1, |
"M+": date.getMonth() + 1, |
||||
'd+': date.getDate(), |
"d+": date.getDate(), |
||||
'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, |
"h+": date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, |
||||
'H+': date.getHours(), |
"H+": date.getHours(), |
||||
'm+': date.getMinutes(), |
"m+": date.getMinutes(), |
||||
's+': date.getSeconds(), |
"s+": date.getSeconds(), |
||||
'q+': Math.floor((date.getMonth() + 3) / 3), |
"q+": Math.floor((date.getMonth() + 3) / 3), |
||||
'S': date.getMilliseconds() |
S: date.getMilliseconds(), |
||||
} |
} |
||||
var week = { |
var week = { |
||||
'0': '\u65e5', |
"0": "\u65e5", |
||||
'1': '\u4e00', |
"1": "\u4e00", |
||||
'2': '\u4e8c', |
"2": "\u4e8c", |
||||
'3': '\u4e09', |
"3": "\u4e09", |
||||
'4': '\u56db', |
"4": "\u56db", |
||||
'5': '\u4e94', |
"5": "\u4e94", |
||||
'6': '\u516d' |
"6": "\u516d", |
||||
} |
} |
||||
if (/(y+)/.test(fmt)) { |
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)) { |
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) { |
for (var k in o) { |
||||
if (new RegExp('(' + k + ')').test(fmt)) { |
if (new RegExp("(" + k + ")").test(fmt)) { |
||||
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) |
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)) |
||||
} |
} |
||||
} |
} |
||||
return fmt |
return fmt |
||||
} |
} |
||||
|
@ -1,9 +1,9 @@ |
|||||
extends layout/layout |
extends layout/layout |
||||
|
|
||||
block head |
block head |
||||
link(rel="stylesheet", href="/public/css/views/404.css") |
link(rel="stylesheet", href="/public/css/views/404.css") |
||||
|
|
||||
block content |
block content |
||||
div(style="text-align: center") |
div(style="text-align: center") |
||||
span.text404 404 |
span.text404 404 |
||||
div 重定向回:#{rollback} |
div 重定向回:#{rollback} |
||||
|
@ -1,62 +1,62 @@ |
|||||
//- 服务器反馈UI |
//- 服务器反馈UI |
||||
if flash |
if flash |
||||
.message-container |
.message-container |
||||
- index = 0 |
- index = 0 |
||||
if flash.error |
if flash.error |
||||
each item in flash.error |
each item in flash.error |
||||
- index++ |
- index++ |
||||
.message.is-danger(id="message"+index) |
.message.is-danger(id="message"+index) |
||||
.message-header |
.message-header |
||||
p 错误 |
p 错误 |
||||
button.delete(aria-label='delete' data-target="message"+index) |
button.delete(aria-label='delete' data-target="message"+index) |
||||
.message-body |
.message-body |
||||
| #{item} |
| #{item} |
||||
if flash.success |
if flash.success |
||||
each item in flash.success |
each item in flash.success |
||||
- index++ |
- index++ |
||||
.message.is-success(id="message"+index) |
.message.is-success(id="message"+index) |
||||
.message-header |
.message-header |
||||
p 成功 |
p 成功 |
||||
button.delete(aria-label='delete' data-target="message"+index) |
button.delete(aria-label='delete' data-target="message"+index) |
||||
.message-body |
.message-body |
||||
| #{item} |
| #{item} |
||||
if flash.info |
if flash.info |
||||
each item in flash.info |
each item in flash.info |
||||
- index++ |
- index++ |
||||
.message.is-info(id="message"+index) |
.message.is-info(id="message"+index) |
||||
.message-header |
.message-header |
||||
p 信息 |
p 信息 |
||||
button.delete(aria-label='delete' data-target="message"+index) |
button.delete(aria-label='delete' data-target="message"+index) |
||||
.message-body |
.message-body |
||||
| #{item} |
| #{item} |
||||
if flash.warning |
if flash.warning |
||||
each item in flash.warning |
each item in flash.warning |
||||
- index++ |
- index++ |
||||
.message.is-warning(id="message"+index) |
.message.is-warning(id="message"+index) |
||||
.message-header |
.message-header |
||||
p 警告 |
p 警告 |
||||
button.delete(aria-label='delete' data-target="message"+index) |
button.delete(aria-label='delete' data-target="message"+index) |
||||
.message-body |
.message-body |
||||
| #{item} |
| #{item} |
||||
//- .toast-container.top-0.end-0.p-3 |
//- .toast-container.top-0.end-0.p-3 |
||||
//- each item in flash.error |
//- each item in flash.error |
||||
//- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') |
//- .toast.show(role='alert', aria-live='assertive', aria-atomic='true') |
||||
//- .toast-header |
//- .toast-header |
||||
//- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") |
//- img.rounded.me-2(src='/public/image/icons/error.svg', alt='错误' style="width:20px;height: 20px;") |
||||
//- strong.me-auto 提示 |
//- strong.me-auto 提示 |
||||
//- //- small.text-muted just now |
//- //- small.text-muted just now |
||||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
||||
//- .toast-body. |
//- .toast-body. |
||||
//- #{item} |
//- #{item} |
||||
//- .toast-container.position-fixed.bottom-0.end-0.p-3 |
//- .toast-container.position-fixed.bottom-0.end-0.p-3 |
||||
//- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') |
//- #liveToast.toast(role='alert', aria-live='assertive', aria-atomic='true') |
||||
//- .toast-header |
//- .toast-header |
||||
//- img.rounded.me-2(src='...', alt='...') |
//- img.rounded.me-2(src='...', alt='...') |
||||
//- strong.me-auto Bootstrap |
//- strong.me-auto Bootstrap |
||||
//- small 11 mins ago |
//- small 11 mins ago |
||||
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
//- button.btn-close(type='button', data-bs-dismiss='toast', aria-label='Close') |
||||
//- .toast-body. |
//- .toast-body. |
||||
//- Hello, world! This is a toast message. |
//- Hello, world! This is a toast message. |
||||
//- ul |
//- ul |
||||
//- each item in flash.error |
//- each item in flash.error |
||||
//- li #{item} |
//- li #{item} |
||||
|
@ -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;") |
nav.is-fixed-top.navbar(role='navigation', aria-label='main navigation', style="box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;") |
||||
.container |
.container |
||||
.navbar-brand |
.navbar-brand |
||||
a.navbar-item(href='/') |
a.navbar-item(href='/') |
||||
img(src='https://bulma.io/images/bulma-logo.png', width='112', height='28') |
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') |
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') |
span(aria-hidden='true') |
||||
span(aria-hidden='true') |
span(aria-hidden='true') |
||||
#navbarBasicExample.navbar-menu |
#navbarBasicExample.navbar-menu |
||||
.navbar-start |
.navbar-start |
||||
a.navbar-item |
a.navbar-item |
||||
| 文档 |
| 文档 |
||||
.navbar-item.has-dropdown.is-hoverable |
.navbar-item.has-dropdown.is-hoverable |
||||
a.navbar-link |
a.navbar-link |
||||
| 更多 |
| 更多 |
||||
.navbar-dropdown |
.navbar-dropdown |
||||
a.navbar-item(href="/about") |
a.navbar-item(href="/about") |
||||
| 关于本站 |
| 关于本站 |
||||
a.navbar-item |
a.navbar-item |
||||
| 关于作者 |
| 关于作者 |
||||
hr.navbar-divider |
hr.navbar-divider |
||||
a.navbar-item |
a.navbar-item |
||||
| 报告问题 |
| 报告问题 |
||||
.navbar-end |
.navbar-end |
||||
if !isLogin |
if !isLogin |
||||
.navbar-item |
.navbar-item |
||||
.buttons |
.buttons |
||||
a.button.is-primary(href="/register") |
a.button.is-primary(href="/register") |
||||
strong 注册 |
strong 注册 |
||||
a.button.is-light(href="/login") |
a.button.is-light(href="/login") |
||||
| 登录 |
| 登录 |
||||
else |
else |
||||
.navbar-item |
.navbar-item.has-dropdown.is-hoverable |
||||
.buttons |
a.navbar-link |
||||
button.button.is-white |
| #{user.nickname} |
||||
| #{user.nickname} |
.navbar-dropdown |
||||
a.button.is-danger.is-light(href="/logout") |
a.navbar-item |
||||
| 退出 |
| 用户资料 |
||||
|
hr.navbar-divider |
||||
|
a.navbar-item |
||||
|
| 退出 |
||||
|
//- a.button.is-danger.is-light(href="/logout") |
||||
|
//- | 退出 |
||||
|
@ -1,12 +1,12 @@ |
|||||
extends /layout/layout |
extends /layout/layout |
||||
|
|
||||
block var |
block var |
||||
-title="关于" |
-title="关于" |
||||
|
|
||||
block head |
block head |
||||
|
|
||||
block content |
block content |
||||
section.section |
section.section |
||||
.container.content!= md |
.container.content!= md |
||||
|
|
||||
block script |
block script |
||||
|
@ -1,22 +1,22 @@ |
|||||
extends /layout/layout |
extends /layout/layout |
||||
|
|
||||
block var |
block var |
||||
-title="登陆" // 网页标题 |
-title="登陆" // 网页标题 |
||||
-hideHeader=true |
-hideHeader=true |
||||
|
|
||||
block head |
block head |
||||
+css("style/views/login.css") |
+css("style/views/login.css") |
||||
|
|
||||
block content |
block content |
||||
.login |
.login |
||||
h1.title.is-1 登录 |
h1.title.is-1 登录 |
||||
form(action='/login' method='post') |
form(action='/login' method='post') |
||||
input(id="referrer" type="text" name="referrer" class="form-control" style="display:none;") |
input(id="referrer" type="text" name="referrer" class="form-control" style="display:none;") |
||||
input(type='text', name='username', placeholder='用户名', required) |
input(type='text', name='username', placeholder='用户名', required) |
||||
input(type='password', name='password', placeholder='密码', required) |
input(type='password', name='password', placeholder='密码', required) |
||||
+security |
+security |
||||
button.btn.btn-primary.btn-block.btn-large(type='submit') 现在登录! |
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") 前往注册 |
a(href="/register" style="margin-top: 8px;color: white;font-size: 14px;display: inline-block;float: right") 前往注册 |
||||
|
|
||||
block script |
block script |
||||
+script("js/page/login.js") |
+script("js/page/login.js") |
||||
|
@ -1,15 +1,15 @@ |
|||||
extends @/layout/layout |
extends @/layout/layout |
||||
|
|
||||
block var |
block var |
||||
-title="用户信息" // 网页标题 |
-title="用户信息" // 网页标题 |
||||
//- -hideHeader=true |
//- -hideHeader=true |
||||
|
|
||||
block head |
block head |
||||
|
|
||||
block content |
block content |
||||
section.section |
section.section |
||||
.container |
.container |
||||
div nickname: #{user.nickname} |
div nickname: #{user.nickname} |
||||
div email: #{user.email} |
div email: #{user.email} |
||||
div username: #{user.username} |
div username: #{user.username} |
||||
div id: #{user.id} |
div id: #{user.id} |
||||
|
Loading…
Reference in new issue