Browse Source

add some thing

theme
npmrun 2 years ago
parent
commit
0ddfa51681
  1. 4
      .editorconfig
  2. 144
      packages/hapi-router/dist/hapi-router.cjs.js
  3. 2
      packages/hapi-router/dist/hapi-router.cjs.js.map
  4. 11
      packages/hapi-router/pnpm-lock.yaml
  5. 46
      packages/hapi-router/src/index.ts
  6. 7
      readme.md
  7. 5
      route.txt
  8. BIN
      source/db/data.db
  9. 8
      source/models/User.ts
  10. 26
      source/models/ha/user.ts
  11. 23
      source/plugins/index.ts
  12. 13
      source/route/htmx/index.ts
  13. 23
      source/route/views/index.ts
  14. 14
      source/route/views/index/index.ts
  15. 24
      source/route/views/user.ts
  16. 20
      source/schema/index.ts
  17. 1
      template/404.pug
  18. 2
      template/helper/helper.pug
  19. 1
      template/htmx/a.pug
  20. 1
      template/layout/layout.pug
  21. 5
      template/ui/header.pug
  22. 2
      template/views/index.pug
  23. 13
      template/views/user.pug

4
.editorconfig

@ -4,11 +4,11 @@ root = true # 应在文件顶部指定的特殊属性,true表示停止搜索
[*]
charset = utf-8 # 统一字符格式
indent_style = space # 缩进方式为空格
indent_size = 2 # 缩进为两个空格
indent_size = 4 # 缩进为两个空格
end_of_line = lf # 换行符统一用lf
insert_final_newline = true # 保证文件结尾留一个空行
trim_trailing_whitespace = true # 移除新一行前的空白字符
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
trim_trailing_whitespace = false

144
packages/hapi-router/dist/hapi-router.cjs.js

@ -42,6 +42,44 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
function __values(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
@ -225,15 +263,16 @@ var routePlugin = (function () {
var keys = Reflect.ownKeys(prototype);
var _loop_1 = function (key) {
if (key !== "constructor") {
var ff = func[key];
var method = ff.$method || "GET";
var ff_1 = func[key];
var handler = undefined;
var method = ff_1.$method || "GET";
var route_1 = "";
if (ff.$route) {
if (ff_1.$route) {
if (isIndexEnd(fileNoExt)) {
route_1 = ff.$route;
route_1 = ff_1.$route;
}
else {
route_1 = fileNoExt + ff.$route;
route_1 = fileNoExt + ff_1.$route;
}
}
else {
@ -246,9 +285,9 @@ var routePlugin = (function () {
}
route_1 = removeIndex(route_1);
route_1 = prefix ? route_1[0] + prefix + "/" + route_1.slice(1) : route_1;
var options = ff.$options ? ff.$options : {};
var options = ff_1.$options ? ff_1.$options : {};
if (!options.auth) {
if (ff.$auth == undefined) {
if (ff_1.$auth == undefined) {
if (auth &&
auth.length &&
auth.filter(function (v) { return route_1.startsWith(v); }).length) {
@ -258,13 +297,13 @@ var routePlugin = (function () {
options.auth = false;
}
}
else if (ff.$auth) {
else if (ff_1.$auth) {
options.auth =
typeof ff.$auth === "boolean"
typeof ff_1.$auth === "boolean"
? type
: {
strategy: type,
mode: ff.$auth,
mode: ff_1.$auth,
};
}
else {
@ -272,7 +311,7 @@ var routePlugin = (function () {
}
}
if (!options.validate) {
var validateObj = ff.$validate || {};
var validateObj = ff_1.$validate || {};
if (options.auth && type === "jwt") {
if (validateObj.headers) {
validateObj.headers = validateObj.headers.keys({
@ -287,14 +326,79 @@ var routePlugin = (function () {
});
}
}
if (validateObj) {
if (validateObj && !!Object.keys(validateObj).length) {
if (validateObj.failAction === "log") {
if (!options.log)
options.log = {};
options.log.collect = true;
var errto_1 = validateObj.$errto;
handler = function () {
var argus = [];
for (var _i = 0; _i < arguments.length; _i++) {
argus[_i] = arguments[_i];
}
return __awaiter(this, void 0, void 0, function () {
var request, h;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
request = argus[0];
h = argus[1];
if (request.logs && !!request.logs.length && errto_1) {
request.yar.flash('error', request.logs.map(function (v) { return v.error.message; }));
return [2, h.redirect(errto_1)];
}
return [4, ff_1.call.apply(ff_1, __spread([this], argus))];
case 1: return [2, _a.sent()];
}
});
});
};
}
if (validateObj.failAction === "function") {
var errto_2 = validateObj.$errto;
validateObj.failAction = function (request, h, err) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (err.details) {
request.$joi_error = err.details.map(function (v) { return v.message; });
}
return [2, h.continue];
});
});
};
handler = function () {
var argus = [];
for (var _i = 0; _i < arguments.length; _i++) {
argus[_i] = arguments[_i];
}
return __awaiter(this, void 0, void 0, function () {
var request, h;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
request = argus[0];
h = argus[1];
if (request.$joi_error) {
loggerSite.debug('传输参数错误: ', request.$joi_error);
request.yar.flash('error', request.$joi_error);
delete request.$joi_error;
return [2, h.redirect(errto_2)];
}
return [4, ff_1.call.apply(ff_1, __spread([this], argus))];
case 1: return [2, _a.sent()];
}
});
});
};
}
options.validate = validateObj;
}
}
if (ff.$swagger) {
options.description = ff.$swagger[0];
options.notes = ff.$swagger[1];
options.tags = ff.$swagger[2];
if (ff_1.$swagger) {
options.description = ff_1.$swagger[0];
options.notes = ff_1.$swagger[1];
options.tags = ff_1.$swagger[2];
}
var str = route_1;
if ((typeof options.auth === "string" && options.auth) ||
@ -318,10 +422,16 @@ var routePlugin = (function () {
" 不需权限 : " + " " + full(method) + " " + str;
}
routes.push(str);
if (options.validate && options.validate.$errto) {
delete options.validate.$errto;
}
if (!handler) {
handler = ff_1;
}
server.route({
method: method,
path: route_1,
handler: ff,
handler: handler,
options: options,
});
}

2
packages/hapi-router/dist/hapi-router.cjs.js.map

File diff suppressed because one or more lines are too long

11
packages/hapi-router/pnpm-lock.yaml

@ -1,4 +1,4 @@
lockfileVersion: 5.3
lockfileVersion: 5.4
specifiers:
'@rollup/plugin-alias': ^3.1.1
@ -35,8 +35,8 @@ devDependencies:
ftp-deploy: 2.4.1
lodash: 4.17.21
rollup: 2.51.2
rollup-plugin-sourcemaps: 0.6.3_0092beb8efba53e1329cf7064a1da378
rollup-plugin-typescript2: 0.27.3_rollup@2.51.2+typescript@3.9.9
rollup-plugin-sourcemaps: 0.6.3_acjl5ohpxjj6cmu464deuhndpa
rollup-plugin-typescript2: 0.27.3_uiok2ebg7dovbc73ivn6j7a2ry
tslib: 2.3.0
typescript: 3.9.9
@ -453,6 +453,7 @@ packages:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
@ -922,7 +923,7 @@ packages:
engines: {node: '>= 4'}
dev: true
/rollup-plugin-sourcemaps/0.6.3_0092beb8efba53e1329cf7064a1da378:
/rollup-plugin-sourcemaps/0.6.3_acjl5ohpxjj6cmu464deuhndpa:
resolution: {integrity: sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==}
engines: {node: '>=10.0.0'}
peerDependencies:
@ -938,7 +939,7 @@ packages:
source-map-resolve: 0.6.0
dev: true
/rollup-plugin-typescript2/0.27.3_rollup@2.51.2+typescript@3.9.9:
/rollup-plugin-typescript2/0.27.3_uiok2ebg7dovbc73ivn6j7a2ry:
resolution: {integrity: sha512-gmYPIFmALj9D3Ga1ZbTZAKTXq1JKlTQBtj299DXhqYz9cL3g/AQfUvbb2UhH+Nf++cCq941W2Mv7UcrcgLzJJg==}
peerDependencies:
rollup: '>=1.26.3'

46
packages/hapi-router/src/index.ts

@ -45,6 +45,7 @@ class routePlugin {
for (const key of keys) {
if (key !== "constructor") {
let ff = func[key];
let handler:()=>void = undefined
// 默认方法
const method = ff.$method || "GET";
// 路由收集规则
@ -104,7 +105,41 @@ class routePlugin {
});
}
}
if (validateObj) {
if (validateObj&&!!Object.keys(validateObj).length) {
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));
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', request.$joi_error);
delete request.$joi_error
return h.redirect(errto);
}
return await ff.call(this, ...argus)
}
}
options.validate = validateObj;
}
}
@ -139,10 +174,17 @@ class routePlugin {
" 不需权限 : " + " " + 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: ff,
handler: handler,
options: options,
});
}

7
readme.md

@ -10,4 +10,9 @@
增加测试库
- @hapi/code
- @hapi/lab
暂时不太清楚怎么结合使用
暂时不太清楚怎么结合使用
## 对于验证库的处理方式参考
https://docs4dev.com/questions/345443
https://github.com/nelsonic/hapi-validation-question

5
route.txt

@ -7,6 +7,8 @@ D:\@code\project\hapi-demo\source\route\api对应路径:
不需权限 : POST /api/v1/user/login
需要权限 : DELETE /api/v1/user/del
需要权限 : GET /api/v1/user/userinfo
D:\@code\project\hapi-demo\source\route\htmx对应路径:
不需权限 : GET /htmx/clicked
D:\@code\project\hapi-demo\source\route\views对应路径:
不需权限(提供无需验证): GET /404
不需权限 : GET /css
@ -19,4 +21,5 @@ D:\@code\project\hapi-demo\source\route\views对应路径:
需要权限 : GET /logout
不需权限(提供无需验证): GET /register
不需权限 : POST /register
不需权限 : GET /nav
不需权限 : GET /nav
需要权限 : GET /user

BIN
source/db/data.db

Binary file not shown.

8
source/models/User.ts

@ -8,11 +8,15 @@ module.exports = function (sequelize, DataTypes) {
type: DataTypes.STRING,
allowNull: false
},
nickname: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
}
}, {
});
return User
};
};

26
source/models/ha/user.ts

@ -0,0 +1,26 @@
module.exports = function (sequelize, DataTypes) {
const User = sequelize.define('ha-user', {
username: {
type: DataTypes.STRING,
allowNull: false
},
avatar: {
type: DataTypes.STRING,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
},
nickname: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
}
}, {
});
return User
};

23
source/plugins/index.ts

@ -21,6 +21,10 @@ export default [
prefix: "api"
},
{
dir: path.resolve(sourceDir, "route/htmx"),
prefix: "htmx"
},
{
dir: path.resolve(sourceDir, "route/views"),
prefix: ""
},
@ -37,20 +41,21 @@ export default [
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;
console.log("是否登录:",isLogin, request.path);
// @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;
}
// 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();
@ -60,7 +65,7 @@ export default [
{
flash: !!Object.keys(flash).length?flash:false,
isLogin: isLogin,
user: user
user: isLogin?request.auth.credentials:false,
},
// @ts-ignore
request.response.source.context

13
source/route/htmx/index.ts

@ -0,0 +1,13 @@
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("/clicked")
async clicked(request: Req, h: Res): ReturnValue {
// return 'aaaaaaaaaaaaaabbbbbbbbbbbbbbbb'
return h.view('htmx/a.pug')
}
}

23
source/route/views/index.ts

@ -1,5 +1,5 @@
import { Req, Res, ReturnValue } from "#/global";
import { UserSchema } from "@/schema";
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";
@ -20,6 +20,12 @@ export default class {
return h.view("views/login.pug");
}
@validate({
payload: LoginUserSchema,
$errto: '/login',
// failAction: 'log'
failAction: 'function'
})
@method("POST")
@route("/login")
async login_POST(request: Req, h: Res): ReturnValue {
@ -35,7 +41,8 @@ export default class {
request.yar.flash('error', 'Invalid username or password');
return h.redirect("/login");
}
request.cookieAuth.set({ id: account.id });
request.cookieAuth.set({ id: account.id, nickname: account.nickname });
console.log(account.nickname);
request.yar.flash('success', '用户已登录');
return h.redirect(referrer ? referrer : "/");
}
@ -61,10 +68,18 @@ export default class {
return h.view("views/login.pug");
}
@validate({
payload: RegisterUserSchema,
})
@method("POST")
async register(request: Req, h: Res): ReturnValue {
let { username, password, email } = request.payload as any;
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 {
@ -75,7 +90,7 @@ export default class {
}
let salt = bcrypt.genSaltSync(10);
let pwdLock = bcrypt.hashSync(password, salt);
await User.create({ username, password: pwdLock, email });
await User.create({ username, nickname, password: pwdLock, email });
return h.redirect("/")
} catch (e) {
request.yar.flash('error', '注册用户失败');

14
source/route/views/index/index.ts

@ -7,9 +7,6 @@ import {
validate,
} from "@noderun/hapi-router";
import { Req, Res, ReturnValue } from "#/global";
import Joi from "joi";
import * as bcrypt from "bcrypt";
import glob from "fast-glob";
import path from "path";
import fs from "fs-extra";
import { baseDir } from "@/util";
@ -67,7 +64,9 @@ export default class Index {
// 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")
const str = fs.readFileSync(mdPath, "utf8")
console.log("---->", mdPath);
return h.view("views/css.pug", {
content: str.toString()
});
@ -80,8 +79,7 @@ export default class Index {
}
@route("/{path*}")
async any(req: Req, h: Res): ReturnValue {
console.log(req.raw.req.url);
console.log('404: ',req.raw.req.url);
return h.redirect("/404?r="+encodeURIComponent(req.raw.req.url));
}
@route("/404")
@ -95,6 +93,8 @@ export default class Index {
if(request.query?.r){
// 可重定向返回
}
return h.view("404.pug");
return h.view("404.pug", {
rollback: request.query?.r
});
}
}

24
source/route/views/user.ts

@ -0,0 +1,24 @@
import { Req, Res, ReturnValue } from "#/global"
import { 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 {
@method("GET")
@auth()
async index(request: Req, h: Res): ReturnValue {
const { id } = request.auth.credentials
const User = request.getModel("User")
let result = <any>await User.findOne({ where: { id: id } })
if (result == null) {
return gFail(null, "不存在该用户")
}
result = result.toJSON()
delete result.password
console.log(result);
return h.view("views/user.pug", { userinfo: result })
}
}

20
source/schema/index.ts

@ -8,3 +8,23 @@ export const UserSchema = Joi.object({
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(),
nickname: Joi.string().alphanum().min(4).max(35),
})
export const LoginUserSchema = Joi.object({
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"] },
}),
}).or("username", "email");

1
template/404.pug

@ -6,3 +6,4 @@ block head
block content
div(style="text-align: center")
span.text404 404
div 重定向回:#{rollback}

2
template/helper/helper.pug

@ -1,5 +1,5 @@
mixin css(href)
link(rel="stylesheet" href="/public/"+href)
link(rel="stylesheet", href="/public/"+href)
mixin script(src)
script(src="/public/"+src)

1
template/htmx/a.pug

@ -0,0 +1 @@
div aaaaaaasdaasdasdada

1
template/layout/layout.pug

@ -17,5 +17,6 @@ html(lang="zh-cn" class=hideHeader?"":"has-navbar-fixed-top")
if !hideHeader
include @/ui/header.pug
block content
script(src="https://unpkg.com/htmx.org@1.8.4" integrity="sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV" crossorigin="anonymous")
+script("js/common/main.js")
block script

5
template/ui/header.pug

@ -1,4 +1,5 @@
nav.is-fixed-top.navbar(role='navigation', aria-label='main navigation')
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')
@ -33,6 +34,6 @@ nav.is-fixed-top.navbar(role='navigation', aria-label='main navigation')
.navbar-item
.buttons
button.button.is-white
| #{user.username}
| #{user.nickname}
a.button.is-danger.is-light(href="/logout")
| 退出

2
template/views/index.pug

@ -15,3 +15,5 @@ block content
| My first website with
strong Bulma
| !
if isLogin
button(hx-get="/htmx/clicked" hx-push-url="/about" hx-trigger="click" hx-target="this" hx-swap="outerHTML") Click Me!

13
template/views/user.pug

@ -0,0 +1,13 @@
extends @/layout/layout
block var
-title="用户信息" // 网页标题
//- -hideHeader=true
block head
block content
section.section
.container
div username: #{user.username}
div id: #{user.id}
Loading…
Cancel
Save