🔱: [server] sync upgrade with 21 commits [trident-sync]

Update README.md
pull/7/head
xiaojunnuo 2023-01-29 15:26:58 +08:00
parent 62e3945d30
commit fbde7cbd93
59 changed files with 2126 additions and 2 deletions

29
.gitignore vendored
View File

@ -1,7 +1,32 @@
# IntelliJ project files
.vscode/
node_modules/
npm-debug.log
yarn-error.log
yarn.lock
package-lock.json
/.idea/
*/**/dist
*/**/pnpm-lock.yaml
*/**/stats.html
.idea
*.iml
out
gen
node_modules/
packages/*/test/*-private.js
/test/*.private.*
/*.log
/packages/ui/*/.idea
/packages/ui/*/node_modules
/packages/*/node_modules
/packages/ui/certd-server/tmp/
/packages/ui/certd-ui/dist/
/other
/dev-sidecar-test
/packages/core/certd/yarn.lock
/packages/test
/test/own
/pnpm-lock.yaml

View File

@ -0,0 +1,16 @@
logs/
npm-debug.log
yarn-error.log
node_modules/
package-lock.json
yarn.lock
coverage/
!dist/
.idea/
run/
.DS_Store
*.sw*
*.un~
.tsbuildinfo
.tsbuildinfo.*
/data/db.sqlite

View File

@ -0,0 +1,11 @@
# 🎨 editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -0,0 +1,7 @@
{
"extends": "./node_modules/mwts/",
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
"env": {
"jest": true
}
}

17
packages/ui/certd-server/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
logs/
npm-debug.log
yarn-error.log
node_modules/
package-lock.json
yarn.lock
coverage/
dist/
.idea/
run/
.DS_Store
*.sw*
*.un~
.tsbuildinfo
.tsbuildinfo.*
/data/db.sqlite
/pnpm-lock.yaml

View File

@ -0,0 +1,3 @@
module.exports = {
...require('mwts/.prettierrc.json')
}

View File

@ -0,0 +1,15 @@
FROM registry.cn-shenzhen.aliyuncs.com/greper/node:15.8.0-alpine
WORKDIR /home
COPY . .
# 如果各公司有自己的私有源可以替换registry地址
#RUN npm install --registry=https://registry.npmmirror.com
RUN npm install -g cnpm
RUN cnpm install
RUN npm run build:preview
# 如果端口更换,这边可以更新一下
EXPOSE 7001
CMD ["npm", "run", "online:preview"]

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 fast-crud
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,41 @@
# fast-server-js
base on midway
## QuickStart
> nodejs需要16以上
<!-- add docs here for user -->
### Development
```bash
$ npm i
# 如果遇到sqlite安装失败时
# 建议使用cnpm
# npm install -g cnpm
# cnpm install
$ npm run dev
$ open http://localhost:7001/
```
### Deploy
```bash
$ npm start
```
### npm scripts
- Use `npm run lint` to check code style.
- Use `npm test` to run unit test.
[midway]: https://midwayjs.org
see [midway docs][ https://midwayjs.org] for more detail.

View File

@ -0,0 +1,30 @@
# fast-server-js
## 快速入门
<!-- 在此次添加使用文档 -->
如需进一步了解,参见 [midway 文档][midway]。
### 本地开发
```bash
$ npm i
$ npm run dev
$ open http://localhost:7001/
```
### 部署
```bash
$ npm start
```
### 内置指令
- 使用 `npm run lint` 来做代码风格检查。
- 使用 `npm test` 来执行单元测试。
[midway]: https://midwayjs.org

View File

@ -0,0 +1,7 @@
const WebFramework = require('@midwayjs/koa').Framework;
const web = new WebFramework().configure({
port: 7001,
});
const { Bootstrap } = require('@midwayjs/bootstrap');
Bootstrap.load(web).run();

View File

@ -0,0 +1,77 @@
-- 表sys_permission
CREATE TABLE "sys_permission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(100) NOT NULL, "permission" varchar(100), "parent_id" integer NOT NULL DEFAULT (-1), "sort" integer NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (1, '系统管理', 'sys', -1, 1, 1, 1624085863636);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (2, '权限管理', 'sys:auth', 1, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (3, '用户管理', 'sys:auth:user', 2, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (4, '查看', 'sys:auth:user:view', 3, 100, 1, 1624189112333);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (5, '权限管理', 'sys:auth:per', 2, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (6, '查看', 'sys:auth:per:view', 5, 100, 1, 1624189161317);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (7, '角色管理', 'sys:auth:role', 2, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (8, '查看', 'sys:auth:role:view', 7, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (9, '修改', 'sys:auth:user:edit', 3, 300, 1, 1624189127688);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (10, '删除', 'sys:auth:user:remove', 3, 400, 1, 1624189133184);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (11, '添加', 'sys:auth:user:add', 3, 200, 1, 1624189142576);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (12, '修改', 'sys:auth:role:edit', 7, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (13, '删除', 'sys:auth:role:remove', 7, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (14, '添加', 'sys:auth:role:add', 7, 1, 1, 1);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (15, '修改', 'sys:auth:per:edit', 5, 300, 1, 1624189308837);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (16, '删除', 'sys:auth:per:remove', 5, 400, 1, 1624189256926);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (17, '添加', 'sys:auth:per:add', 5, 200, 1, 1624189283766);
INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (18,'授权','sys:auth:role:authz',7,100,1,1624335712144);
-- 表sys_role
CREATE TABLE "sys_role" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(100) NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
INSERT INTO sys_role (id, name, create_time, update_time) VALUES (1, '管理员', 1, 1623749138537);
INSERT INTO sys_role (id, name, create_time, update_time) VALUES (2, '只读角色', 1, 1623749138537);
-- 表sys_role_permission
CREATE TABLE "sys_role_permission" ("role_id" integer NOT NULL, "permission_id" integer NOT NULL, PRIMARY KEY ("role_id", "permission_id"));
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 1);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 2);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 3);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 4);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 5);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 6);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 7);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 8);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 9);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 10);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 11);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 12);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 13);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 14);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 15);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 16);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 17);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 18);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, -1);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 4);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 6);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 8);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 1);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 2);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 3);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 5);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 7);
INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, -1);
-- 表sys_user
CREATE TABLE "sys_user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "username" varchar(100) NOT NULL, "password" varchar(50) NOT NULL, "nick_name" varchar(50), "avatar" varchar(255), "phone_code" varchar(20), "mobile" varchar(20), "email" varchar(100),"remark" varchar(100), "status" integer NOT NULL DEFAULT (1), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));
INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 'admin', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,NULL);
INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (2, 'readonly', 'e10adc3949ba59abbe56e057f20f883e', '只读用户', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,'密码123456');
-- 表sys_user_role
CREATE TABLE "sys_user_role" ("role_id" integer NOT NULL, "user_id" integer NOT NULL, PRIMARY KEY ("role_id", "user_id"));
INSERT INTO sys_user_role (role_id, user_id) VALUES (1, 1);
INSERT INTO sys_user_role (role_id, user_id) VALUES (2, 2);
-- 索引IDX_223de54d6badbe43a5490450c3
CREATE UNIQUE INDEX "IDX_223de54d6badbe43a5490450c3" ON "sys_role" ("name");
-- 索引IDX_9e7164b2f1ea1348bc0eb0a7da
CREATE UNIQUE INDEX "IDX_9e7164b2f1ea1348bc0eb0a7da" ON "sys_user" ("username");

View File

@ -0,0 +1,4 @@
-- for preview 限制演示环境的数据修改
update sqlite_sequence set seq = 1000 where name = 'sys_user' ;
update sqlite_sequence set seq = 1000 where name = 'sys_permission' ;
update sqlite_sequence set seq = 1000 where name = 'sys_role' ;

View File

@ -0,0 +1,6 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
coveragePathIgnorePatterns: ['<rootDir>/test/'],
};

View File

@ -0,0 +1,82 @@
{
"name": "@fast-crud/fs-server-js",
"version": "0.2.0",
"description": "fast-server base midway",
"private": true,
"scripts": {
"start": "NODE_ENV=production node ./bootstrap.js",
"online": "NODE_ENV=production node ./bootstrap.js",
"online:preview": "NODE_ENV=preview node ./bootstrap.js",
"dev": "cross-env NODE_ENV=local midway-bin dev --ts",
"dev:preview": "cross-env NODE_ENV=preview midway-bin dev --ts",
"test": "midway-bin test --ts",
"cov": "midway-bin cov --ts",
"lint": "mwts check",
"lint:fix": "mwts fix",
"ci": "npm run cov",
"build": "midway-bin build -c",
"build:preview": "cross-env NODE_ENV=preview midway-bin build -c",
"check": "luckyeye",
"mig": "typeorm migration:create -n name"
},
"dependencies": {
"midway-flyway-js": "^3.0.0",
"@koa/cors": "^3.1.0",
"@midwayjs/bootstrap": "^3.0.0",
"@midwayjs/cache": "^3.0.0",
"@midwayjs/cli": "^1.2.38",
"@midwayjs/core": "^3.0.0",
"@midwayjs/decorator": "^3.0.0",
"@midwayjs/koa": "^3.0.0",
"@midwayjs/logger": "^2.17.0",
"@midwayjs/typeorm": "^3.5.3",
"@midwayjs/validate": "^3.0.0",
"@types/cache-manager": "^3.4.0",
"typeorm": "^0.3.10",
"cache-manager": "^3.4.3",
"dayjs": "^1.10.5",
"glob": "^7.1.7",
"jsonwebtoken": "^8.5.1",
"koa-bodyparser": "^4.3.0",
"lodash": "^4.17.21",
"log4js": "^6.3.0",
"md5": "^2.3.0",
"sqlite3": "^5.1.2",
"svg-captcha": "^1.4.0",
"@alicloud/pop-core": "^1.7.10"
},
"devDependencies": {
"@midwayjs/cli": "^1.0.0",
"@midwayjs/luckyeye": "^1.0.0",
"@midwayjs/mock": "^3.0.0",
"@midwayjs/orm": "^3.0.0",
"@types/jest": "^26.0.10",
"@types/koa-bodyparser": "^4.3.0",
"@types/node": "14",
"cross-env": "^6.0.0",
"jest": "^26.4.0",
"mwts": "^1.0.5",
"ts-jest": "^26.2.0",
"typescript": "^4.0.0",
"ts-node": "^10.0.0"
},
"engines": {
"node": ">=12.0.0"
},
"midway-bin-clean": [
".vscode/.tsbuildinfo",
"dist"
],
"midway-luckyeye": {
"packages": [
"midway_v2"
]
},
"repository": {
"type": "git",
"url": "https://github.com/fast-crud/fast-server-js"
},
"author": "Greper",
"license": "MIT"
}

View File

@ -0,0 +1,33 @@
import { Inject } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
import { Constants } from './constants';
export abstract class BaseController {
@Inject()
ctx: Context;
/**
*
* @param data
*/
ok(data) {
const res = {
...Constants.res.success,
data: undefined,
};
if (data) {
res.data = data;
}
return res;
}
/**
*
* @param message
*/
fail(msg, code) {
return {
code: code ? code : Constants.res.error.code,
msg: msg ? msg : Constants.res.error.code,
};
}
}

View File

@ -0,0 +1,150 @@
import { Inject } from '@midwayjs/decorator';
import { ValidateException } from './exception/validation-exception';
import * as _ from 'lodash';
import { Context } from '@midwayjs/koa';
/**
*
*/
export abstract class BaseService {
@Inject()
ctx: Context;
abstract getRepository();
/**
* ID
* @param id ID
* @param infoIgnoreProperty
*/
async info(id, infoIgnoreProperty?) {
if (!id) {
throw new ValidateException('id不能为空');
}
const info = await this.getRepository().findOne({
where:{ id }
});
if (info && infoIgnoreProperty) {
for (const property of infoIgnoreProperty) {
delete info[property];
}
}
return info;
}
/**
*
* @param option
*/
async find(options) {
return await this.getRepository().find(options);
}
/**
*
* @param ids ID [1,2,3] 1,2,3
*/
async delete(ids) {
if (ids instanceof Array) {
await this.getRepository().delete(ids);
} else if (typeof ids === 'string') {
await this.getRepository().delete(ids.split(','));
} else {
//ids是一个condition
await this.getRepository().delete(ids);
}
await this.modifyAfter(ids);
}
/**
* |
* @param param
*/
async addOrUpdate(param) {
await this.getRepository().save(param);
}
/**
*
* @param param
*/
async add(param) {
const now = new Date().getTime();
param.createTime = now;
param.updateTime = now;
await this.addOrUpdate(param);
await this.modifyAfter(param);
return {
id: param.id,
};
}
/**
*
* @param param
*/
async update(param) {
if (!param.id) throw new ValidateException('no id');
param.updateTime = new Date().getTime();
await this.addOrUpdate(param);
await this.modifyAfter(param);
}
/**
* ||
* @param data
*/
async modifyAfter(data) {}
/**
*
* @param query bean
* @param page
* @param order
* @param buildQuery
*/
async page(query, page = { offset: 0, limit: 20 }, order, buildQuery) {
if (page.offset == null) {
page.offset = 0;
}
if (page.limit == null) {
page.limit = 20;
}
const qb = this.getRepository().createQueryBuilder('main');
if (order && order.prop) {
qb.orderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC');
} else {
qb.orderBy('id', 'DESC');
}
qb.offset(page.offset).limit(page.limit);
//根据bean query
if (query) {
let whereSql = '';
let index = 0;
_.forEach(query, (value, key) => {
if (!value) {
return;
}
if (index !== 0) {
whereSql += ' and ';
}
whereSql += ` main.${key} = :${key} `;
index++;
});
if (index > 0) {
qb.where(whereSql, query);
}
}
//自定义query
if (buildQuery) {
buildQuery(qb);
}
const list = await qb.getMany();
const total = await qb.getCount();
return {
records: list,
total,
offset: page.offset,
limit: page.limit,
};
}
}

View File

@ -0,0 +1,28 @@
export const Constants = {
res: {
error: {
code: 1,
message: 'error',
},
success: {
code: 0,
message: 'success',
},
validation: {
code: 10,
message: '参数错误',
},
auth: {
code: 401,
message: '您还未登录或token已过期',
},
permission: {
code: 402,
message: '您没有权限',
},
preview: {
code: 10001,
message: '对不起,预览环境不允许修改此数据',
},
},
};

View File

@ -0,0 +1,50 @@
import { ALL, Body, Post, Query } from '@midwayjs/decorator';
import { BaseService } from './base-service';
import { BaseController } from './base-controller';
export abstract class CrudController<
T extends BaseService
> extends BaseController {
abstract getService();
@Post('/page')
async page(
@Body(ALL)
body
) {
const pageRet = await this.getService().page(
body?.query,
body?.page,
body?.sort,
null
);
return this.ok(pageRet);
}
@Post('/add')
async add(
@Body(ALL)
bean
) {
const id = await this.getService().add(bean);
return this.ok(id);
}
@Post('/update')
async update(
@Body(ALL)
bean
) {
await this.getService().update(bean);
return this.ok(null);
}
@Post('/delete')
async delete(
@Query('id')
id
) {
await this.getService().delete([id]);
return this.ok(null);
}
}

View File

@ -0,0 +1,11 @@
export class EnumItem {
value: string;
label: string;
color: string;
constructor(value, label, color) {
this.value = value;
this.label = label;
this.color = color;
}
}

View File

@ -0,0 +1,14 @@
import { Constants } from '../constants';
import { BaseException } from './base-exception';
/**
*
*/
export class AuthException extends BaseException {
constructor(message) {
super(
'AuthException',
Constants.res.auth.code,
message ? message : Constants.res.auth.message
);
}
}

View File

@ -0,0 +1,11 @@
/**
*
*/
export class BaseException extends Error {
status: number;
constructor(name, code, message) {
super(message);
this.name = name;
this.status = code;
}
}

View File

@ -0,0 +1,14 @@
import { Constants } from '../constants';
import { BaseException } from './base-exception';
/**
*
*/
export class CommonException extends BaseException {
constructor(message) {
super(
'CommonException',
Constants.res.error.code,
message ? message : Constants.res.error.message
);
}
}

View File

@ -0,0 +1,14 @@
import { Constants } from '../constants';
import { BaseException } from './base-exception';
/**
*
*/
export class PermissionException extends BaseException {
constructor(message) {
super(
'PermissionException',
Constants.res.permission.code,
message ? message : Constants.res.permission.message
);
}
}

View File

@ -0,0 +1,14 @@
import { Constants } from '../constants';
import { BaseException } from './base-exception';
/**
*
*/
export class PreviewException extends BaseException {
constructor(message) {
super(
'PreviewException',
Constants.res.preview.code,
message ? message : Constants.res.preview.message
);
}
}

View File

@ -0,0 +1,14 @@
import { Constants } from '../constants';
import { BaseException } from './base-exception';
/**
*
*/
export class ValidateException extends BaseException {
constructor(message) {
super(
'ValidateException',
Constants.res.validation.code,
message ? message : Constants.res.validation.message
);
}
}

View File

@ -0,0 +1,18 @@
export class Result<T> {
code: number;
msg: string;
data: T;
constructor(code, msg, data?) {
this.code = code;
this.msg = msg;
this.data = data;
}
static error(code = 1, msg) {
return new Result(code, msg, null);
}
static success(msg, data?) {
return new Result(0, msg, data);
}
}

View File

@ -0,0 +1,56 @@
import { join } from 'path';
import {FlywayHistory} from "midway-flyway-js/dist/entity";
import { MidwayConfig } from '@midwayjs/core';
export default {
// use for cookie sign key, should change to your own and keep security
keys: 'greper-is-666',
koa: {
port: 7001,
},
/**
*
*/
preview :{
enabled: false,
},
/**
*
*/
typeorm : {
dataSource: {
default: {
/**
*
*/
type: 'sqlite',
database: join(__dirname, '../../data/db.sqlite'),
synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true
logging: true,
// 配置实体模型 或者 entities: '/entity',
entities: ['/modules/authority/entity/*',FlywayHistory],
}
}
},
/**
*
*/
flyway : {
scriptDir:join(__dirname, '../../db/migration'),
},
biz : {
jwt: {
secret: 'greper-is-666',
expire: 7 * 24 * 60, //单位秒
},
auth: {
ignoreUrls: ['/', '/api/login', '/api/register'],
},
}
} as MidwayConfig;

View File

@ -0,0 +1,10 @@
import { MidwayConfig } from '@midwayjs/core';
export default {
/**
*
*/
preview: {
enabled: true,
}
} as MidwayConfig;

View File

@ -0,0 +1,10 @@
import { MidwayConfig } from '@midwayjs/core';
export default {
/**
*
*/
preview: {
enabled: true,
}
} as MidwayConfig;

View File

@ -0,0 +1,59 @@
import * as validateComp from '@midwayjs/validate';
import * as productionConfig from './config/config.production';
import * as previewConfig from './config/config.preview';
import * as defaultConfig from './config/config.default';
import { Configuration, App } from '@midwayjs/decorator';
import * as koa from '@midwayjs/koa';
import * as bodyParser from 'koa-bodyparser';
import * as orm from '@midwayjs/typeorm';
import * as cache from '@midwayjs/cache';
import * as cors from '@koa/cors';
import { join } from 'path';
import * as flyway from 'midway-flyway-js';
import {ReportMiddleware} from "./middleware/report";
import {GlobalExceptionMiddleware} from "./middleware/global-exception";
import {PreviewMiddleware} from "./middleware/preview";
import {AuthorityMiddleware} from "./middleware/authority";
@Configuration({
imports: [koa, orm, cache, flyway,validateComp],
importConfigs: [
{
default: defaultConfig,
preview: previewConfig,
production: productionConfig,
},
],
})
export class ContainerConfiguration {}
@Configuration({
conflictCheck: true,
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
@App()
app: koa.Application;
async onReady() {
//跨域
this.app.use(
cors({
origin: '*',
})
);
// bodyparser options see https://github.com/koajs/bodyparser
this.app.use(bodyParser());
//请求日志打印
this.app.useMiddleware([
ReportMiddleware,
//统一异常处理
GlobalExceptionMiddleware,
//预览模式限制修改id<1000的数据
PreviewMiddleware,
//授权处理
AuthorityMiddleware
])
}
}

View File

@ -0,0 +1,10 @@
import { Controller, Get, Provide } from '@midwayjs/decorator';
@Provide()
@Controller('/')
export class HomeController {
@Get('/')
async home(): Promise<string> {
return 'Hello Midwayjs!';
}
}

View File

@ -0,0 +1,48 @@
import { Config, Provide } from '@midwayjs/decorator';
import {
IWebMiddleware,
IMidwayKoaContext,
NextFunction
} from '@midwayjs/koa';
import * as _ from 'lodash';
import * as jwt from 'jsonwebtoken';
import { Constants } from '../basic/constants';
/**
*
*/
@Provide()
export class AuthorityMiddleware implements IWebMiddleware {
@Config('biz.jwt.secret')
private secret: string;
@Config('biz.auth.ignoreUrls')
private ignoreUrls: string[];
resolve() {
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
const { url } = ctx;
const token = ctx.get('Authorization');
// 路由地址为 admin前缀的 需要权限校验
// console.log('ctx', ctx);
const queryIndex = url.indexOf('?');
let uri = url;
if (queryIndex >= 0) {
uri = url.substring(0, queryIndex);
}
const yes = this.ignoreUrls.includes(uri);
if (yes) {
await next();
return;
}
try {
ctx.user = jwt.verify(token, this.secret);
} catch (err) {
ctx.status = 401;
ctx.body = Constants.res.auth;
return;
}
await next();
};
}
}

View File

@ -0,0 +1,27 @@
import { Provide } from '@midwayjs/decorator';
import {
IWebMiddleware,
IMidwayKoaContext,
NextFunction,
} from '@midwayjs/koa';
import { logger } from '../utils/logger';
import { Result } from '../basic/result';
@Provide()
export class GlobalExceptionMiddleware implements IWebMiddleware {
resolve() {
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
const { url } = ctx;
const startTime = Date.now();
logger.info('请求开始:', url);
try {
await next();
logger.info('请求完成', url, Date.now() - startTime + 'ms');
} catch (err) {
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
ctx.status = 200;
ctx.body = Result.error(err.code != null ? err.code : 1, err.message);
}
};
}
}

View File

@ -0,0 +1,50 @@
import { Config, Provide } from '@midwayjs/decorator';
import {
IMidwayKoaContext,
NextFunction,
IWebMiddleware,
} from '@midwayjs/koa';
import { PreviewException } from '../basic/exception/preview-exception';
/**
*
*/
@Provide()
export class PreviewMiddleware implements IWebMiddleware {
@Config('preview.enabled')
private preview: boolean;
resolve() {
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
if (!this.preview) {
await next();
return;
}
let { url, request } = ctx;
const body: any = request.body;
let id = body.id || request.query.id;
const roleId = body.roleId;
if (id == null && roleId != null) {
id = roleId;
}
if (id != null && typeof id === 'string') {
id = parseInt(id);
}
if (url.indexOf('?') !== -1) {
url = url.substring(0, url.indexOf('?'));
}
const isModify =
url.endsWith('update') ||
url.endsWith('delete') ||
url.endsWith('authz');
const isPreviewId = id < 1000;
if (this.preview && isModify && isPreviewId) {
throw new PreviewException(
'对不起,预览环境不允许修改此数据,如需体验请添加新数据'
);
}
await next();
return;
};
}
}

View File

@ -0,0 +1,28 @@
import { Provide } from '@midwayjs/decorator';
import {
IWebMiddleware,
IMidwayKoaContext,
NextFunction,
} from '@midwayjs/koa';
import { logger } from '../utils/logger';
@Provide()
export class ReportMiddleware implements IWebMiddleware {
resolve() {
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
const { url } = ctx;
logger.info('请求开始:', url);
const startTime = Date.now();
await next();
if (ctx.status !== 200) {
logger.error(
'请求失败:',
url,
ctx.status,
Date.now() - startTime + 'ms'
);
}
logger.info('请求完成:', url, ctx.status, Date.now() - startTime + 'ms');
};
}
}

View File

@ -0,0 +1,63 @@
import {
ALL,
Body,
Controller,
Inject,
Post,
Provide,
Query,
} from '@midwayjs/decorator';
import { CrudController } from '../../../basic/crud-controller';
import { PermissionService } from '../service/permission-service';
/**
*
*/
@Provide()
@Controller('/api/sys/authority/permission')
export class PermissionController extends CrudController<PermissionService> {
@Inject()
service: PermissionService;
getService() {
return this.service;
}
@Post('/page')
async page(
@Body(ALL)
body
) {
return await super.page(body);
}
@Post('/add')
async add(
@Body(ALL)
bean
) {
return await super.add(bean);
}
@Post('/update')
async update(
@Body(ALL)
bean
) {
return await super.update(bean);
}
@Post('/delete')
async delete(
@Query('id')
id
) {
return await super.delete(id);
}
@Post('/tree')
async tree() {
const tree = await this.service.tree({});
return this.ok(tree);
}
}

View File

@ -0,0 +1,96 @@
import {
ALL,
Body,
Controller,
Inject,
Post,
Provide,
Query,
} from '@midwayjs/decorator';
import { CrudController } from '../../../basic/crud-controller';
import { RoleService } from '../service/role-service';
/**
*
*/
@Provide()
@Controller('/api/sys/authority/role')
export class RoleController extends CrudController<RoleService> {
@Inject()
service: RoleService;
getService() {
return this.service;
}
@Post('/page')
async page(
@Body(ALL)
body
) {
return await super.page(body);
}
@Post('/list')
async list() {
const ret = await this.service.find({});
return this.ok(ret);
}
@Post('/add')
async add(
@Body(ALL)
bean
) {
return await super.add(bean);
}
@Post('/update')
async update(
@Body(ALL)
bean
) {
return await super.update(bean);
}
@Post('/delete')
async delete(
@Query('id')
id
) {
return await super.delete(id);
}
@Post('/getPermissionTree')
async getPermissionTree(
@Query('id')
id
) {
const ret = await this.service.getPermissionTreeByRoleId(id);
return this.ok(ret);
}
@Post('/getPermissionIds')
async getPermissionIds(
@Query('id')
id
) {
const ret = await this.service.getPermissionIdsByRoleId(id);
return this.ok(ret);
}
/**
*
* @param id
*/
@Post('/authz')
async authz(
@Body('roleId')
roleId,
@Body('permissionIds')
permissionIds
) {
await this.service.authz(roleId, permissionIds);
return this.ok(null);
}
}

View File

@ -0,0 +1,119 @@
import {
Provide,
Controller,
Post,
Inject,
Body,
Query,
ALL,
} from '@midwayjs/decorator';
import { UserService } from '../service/user-service';
import { CrudController } from '../../../basic/crud-controller';
import { RoleService } from '../service/role-service';
import { PermissionService } from '../service/permission-service';
/**
*
*/
@Provide()
@Controller('/api/sys/authority/user')
export class UserController extends CrudController<UserService> {
@Inject()
service: UserService;
@Inject()
roleService: RoleService;
@Inject()
permissionService: PermissionService;
getService() {
return this.service;
}
@Post('/page')
async page(
@Body(ALL)
body
) {
const ret = await super.page(body);
const users = ret.data.records;
//获取roles
const userIds = users.map((item) => item.id);
const userRoles = await this.roleService.getByUserIds(userIds);
const userRolesMap = new Map();
for (const ur of userRoles) {
let roles = userRolesMap.get(ur.userId);
if (roles == null) {
roles = [];
userRolesMap.set(ur.userId, roles);
}
roles.push(ur.roleId);
}
for (const record of users) {
//withRoles
record.roles = userRolesMap.get(record.id);
//删除密码字段
delete record.password;
}
return ret;
}
@Post('/add')
async add(
@Body(ALL)
bean
) {
return await super.add(bean);
}
@Post('/update')
async update(
@Body(ALL)
bean
) {
return await super.update(bean);
}
@Post('/delete')
async delete(
@Query('id')
id
) {
return await super.delete(id);
}
/**
*
*/
@Post('/mine')
public async mine() {
const id = this.ctx.user.id;
const info = await this.service.info(id, ['password']);
return this.ok(info);
}
/**
*
*/
@Post('/permissions')
public async permissions() {
const id = this.ctx.user.id;
const permissions = await this.service.getUserPermissions(id);
return this.ok(permissions);
}
/**
*
*/
@Post('/permissionTree')
public async permissionTree() {
const id = this.ctx.user.id;
const permissions = await this.service.getUserPermissions(id);
const tree = this.permissionService.buildTree(permissions);
return this.ok(tree);
}
}

View File

@ -0,0 +1,40 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
/**
*
*/
@Entity('sys_permission')
export class PermissionEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ comment: '标题', length: 100 })
title: string;
/**
*
* sys:user:read
*/
@Column({ comment: '权限代码', length: 100, nullable: true })
permission: string;
@Column({ name: 'parent_id', comment: '父节点ID', default: -1 })
parentId: number;
@Column({ comment: '排序号' })
sort: number;
@Column({
name: 'create_time',
comment: '创建时间',
default: () => 'CURRENT_TIMESTAMP',
})
createTime: Date;
@Column({
name: 'update_time',
comment: '修改时间',
default: () => 'CURRENT_TIMESTAMP',
})
updateTime: Date;
// @ManyToMany(type => RoleEntity, res => res.permissions)
// roles: RoleEntity[];
}

View File

@ -0,0 +1,12 @@
import { Entity, PrimaryColumn } from 'typeorm';
/**
*
*/
@Entity('sys_role_permission')
export class RolePermissionEntity {
@PrimaryColumn({ name: 'role_id' })
roleId: number;
@PrimaryColumn({ name: 'permission_id' })
permissionId: number;
}

View File

@ -0,0 +1,43 @@
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
/**
*
*/
@Entity('sys_role')
export class RoleEntity {
@PrimaryGeneratedColumn()
id: number;
@Index({ unique: true })
@Column({ comment: '角色名称', length: 100 })
name: string;
@Column({
name: 'create_time',
comment: '创建时间',
default: () => 'CURRENT_TIMESTAMP',
})
createTime: Date;
@Column({
name: 'update_time',
comment: '修改时间',
default: () => 'CURRENT_TIMESTAMP',
})
updateTime: Date;
// @ManyToMany(type => PermissionEntity, res => res.roles)
// @JoinTable({
// name: 'sys_role_resources',
// joinColumn: {
// name: 'roleId',
// referencedColumnName: 'id',
// },
// inverseJoinColumn: {
// name: 'resourceId',
// referencedColumnName: 'id',
// },
// })
// resources: PermissionEntity[];
// @ManyToMany(type => UserEntity, res => res.roles)
// users: UserEntity[];
}

View File

@ -0,0 +1,12 @@
import { Entity, PrimaryColumn } from 'typeorm';
/**
*
*/
@Entity('sys_user_role')
export class UserRoleEntity {
@PrimaryColumn({ name: 'role_id' })
roleId: number;
@PrimaryColumn({ name: 'user_id' })
userId: number;
}

View File

@ -0,0 +1,63 @@
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
/**
*
*/
@Entity('sys_user')
export class UserEntity {
@PrimaryGeneratedColumn()
id: number;
@Index({ unique: true })
@Column({ comment: '用户名', length: 100 })
username: string;
@Column({ comment: '密码', length: 100 })
password: string;
@Column({ name: 'nick_name', comment: '昵称', length: 100, nullable: true })
nickName: string;
@Column({ comment: '头像', length: 255, nullable: true })
avatar: string;
@Column({ name: 'phone_code', comment: '区号', length: 20, nullable: true })
phoneCode: string;
@Column({ comment: '手机', length: 20, nullable: true })
mobile: string;
@Column({ comment: '邮箱', length: 50, nullable: true })
email: string;
@Column({ comment: '备注', length: 100, nullable: true })
remark: string;
@Column({ comment: '状态 0:禁用 1启用', default: 1, type: 'int' })
status: number;
@Column({
name: 'create_time',
comment: '创建时间',
default: () => 'CURRENT_TIMESTAMP',
})
createTime: Date;
@Column({
name: 'update_time',
comment: '修改时间',
default: () => 'CURRENT_TIMESTAMP',
})
updateTime: Date;
// @ManyToMany(type => RoleEntity, res => res.users)
// @JoinTable({
// name: 'sys_user_roles',
// joinColumn: {
// name: 'userId',
// referencedColumnName: 'id',
// },
// inverseJoinColumn: {
// name: 'roleId',
// referencedColumnName: 'id',
// },
// })
// roles: RoleEntity[];
}

View File

@ -0,0 +1,17 @@
import { EnumItem } from '../../../basic/enum-item';
import * as _ from 'lodash';
class ResourceTypes {
MENU = new EnumItem('menu', '菜单', 'blue');
BTN = new EnumItem('btn', '按钮', 'green');
ROUTE = new EnumItem('route', '路由', 'red');
names() {
const list = [];
_.forEach(this, (item, key) => {
list.push(item);
});
return list;
}
}
export const ResourceTypeEnum = new ResourceTypes();

View File

@ -0,0 +1,52 @@
import { Provide } from '@midwayjs/decorator';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service';
import { PermissionEntity } from '../entity/permission';
/**
*
*/
@Provide()
export class PermissionService extends BaseService {
@InjectEntityModel(PermissionEntity)
repository: Repository<PermissionEntity>;
getRepository() {
return this.repository;
}
async tree(options: any = {}) {
if (options.order == null) {
options.order = {
sort: 'ASC',
};
}
const list = await this.find(options);
return this.buildTree(list);
}
buildTree(list: any) {
const idMap = {};
const root = [];
for (const item of list) {
idMap[item.id] = item;
if (item.parentId == null || item.parentId <= 0) {
root.push(item);
}
}
for (const item of list) {
if (item.parentId > 0) {
const parent = idMap[item.parentId];
if (parent) {
if (parent.children == null) {
parent.children = [];
}
parent.children.push(item);
}
}
}
return root;
}
}

View File

@ -0,0 +1,18 @@
import { Provide } from '@midwayjs/decorator';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service';
import { RolePermissionEntity } from '../entity/role-permission';
/**
* ->
*/
@Provide()
export class RolePermissionService extends BaseService {
@InjectEntityModel(RolePermissionEntity)
repository: Repository<RolePermissionEntity>;
getRepository() {
return this.repository;
}
}

View File

@ -0,0 +1,101 @@
import { Inject, Provide } from '@midwayjs/decorator';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service';
import { RoleEntity } from '../entity/role';
import { UserRoleService } from './user-role-service';
import { RolePermissionEntity } from '../entity/role-permission';
import { PermissionService } from './permission-service';
import * as _ from 'lodash';
import { RolePermissionService } from './role-permission-service';
/**
*
*/
@Provide()
export class RoleService extends BaseService {
@InjectEntityModel(RoleEntity)
repository: Repository<RoleEntity>;
@Inject()
userRoleService: UserRoleService;
@Inject()
permissionService: PermissionService;
@Inject()
rolePermissionService: RolePermissionService;
getRepository() {
return this.repository;
}
async getRoleIdsByUserId(id: any) {
const userRoles = await this.userRoleService.find({
where: { userId: id },
});
return userRoles.map(item => item.roleId);
}
async getByUserIds(ids: any) {
return await this.userRoleService.find({
where: {
userId: In(ids),
},
});
}
async getPermissionByRoleIds(roleIds: any) {
return await this.permissionService.repository
.createQueryBuilder('permission')
.innerJoinAndSelect(
RolePermissionEntity,
'rp',
'rp.permissionId = permission.id and rp.roleId in (:...roleIds)',
{ roleIds }
)
.getMany();
}
async addRoles(userId: number, roles) {
if (roles == null || roles.length === 0) {
return;
}
for (const roleId of roles) {
await this.userRoleService.add({
userId,
roleId,
});
}
}
async updateRoles(userId, roles) {
if (roles == null) {
return;
}
const oldRoleIds = await this.getRoleIdsByUserId(userId);
if (_.xor(roles, oldRoleIds).length === 0) {
//如果两个数组相等,则不修改
return;
}
//先删除所有
await this.userRoleService.delete({ userId });
//再添加
await this.addRoles(userId, roles);
}
async getPermissionTreeByRoleId(id: any) {
const list = await this.getPermissionByRoleIds([id]);
return this.permissionService.buildTree(list);
}
async getPermissionIdsByRoleId(id: any) {
const list = await this.getPermissionByRoleIds([id]);
return list.map(item => item.id);
}
async authz(roleId: any, permissionIds: any) {
await this.rolePermissionService.delete({ roleId });
for (const permissionId of permissionIds) {
await this.rolePermissionService.add({
roleId,
permissionId,
});
}
}
}

View File

@ -0,0 +1,18 @@
import { Provide } from '@midwayjs/decorator';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service';
import { UserRoleEntity } from '../entity/user-role';
/**
* ->
*/
@Provide()
export class UserRoleService extends BaseService {
@InjectEntityModel(UserRoleEntity)
repository: Repository<UserRoleEntity>;
getRepository() {
return this.repository;
}
}

View File

@ -0,0 +1,113 @@
import { Inject, Provide } from '@midwayjs/decorator';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from '../entity/user';
import * as _ from 'lodash';
import * as md5 from 'md5';
import { CommonException } from '../../../basic/exception/common-exception';
import { BaseService } from '../../../basic/base-service';
import { logger } from '../../../utils/logger';
import { RoleService } from './role-service';
import { PermissionService } from './permission-service';
import { UserRoleService } from './user-role-service';
/**
*
*/
@Provide()
export class UserService extends BaseService {
@InjectEntityModel(UserEntity)
repository: Repository<UserEntity>;
@Inject()
roleService: RoleService;
@Inject()
permissionService: PermissionService;
@Inject()
userRoleService: UserRoleService;
getRepository() {
return this.repository;
}
/**
*
*/
async mine() {
const info = await this.repository.findOne({
where: {
id: this.ctx.user.id,
}
});
delete info.password;
return info;
}
/**
*
* @param param
*/
async add(param) {
const exists = await this.repository.findOne({
where:{
username: param.username,
}
});
if (!_.isEmpty(exists)) {
throw new CommonException('用户名已经存在');
}
const password = param.password ?? '123456';
param.password = md5(password); // 默认密码 建议未改密码不能登陆
await super.add(param);
//添加角色
if (param.roles && param.roles.length > 0) {
await this.roleService.addRoles(param.id, param.roles);
}
return param.id;
}
/**
*
* @param param
*/
async update(param) {
if (param.id == null) {
throw new CommonException('id不能为空');
}
const userInfo = await this.repository.findOne({
where:{ id: param.id }
});
if (!userInfo) {
throw new CommonException('用户不存在');
}
delete param.username;
if (!_.isEmpty(param.password)) {
param.password = md5(param.password);
} else {
delete param.password;
}
await super.update(param);
await this.roleService.updateRoles(param.id, param.roles);
}
async findOne(param) {
return this.repository.findOne({
where:param
});
}
checkPassword(rawPassword: any, md5Password: any) {
logger.info('md5', md5('123456'));
return md5(rawPassword) === md5Password;
}
/**
*
* @param id
*/
async getUserPermissions(id: any) {
const roleIds = await this.roleService.getRoleIdsByUserId(id);
return await this.roleService.getPermissionByRoleIds(roleIds);
}
}

View File

@ -0,0 +1,56 @@
import { Rule,RuleType } from '@midwayjs/validate';
import { ALL, Inject } from '@midwayjs/decorator';
import { Body } from '@midwayjs/decorator';
import { Controller, Post, Provide } from '@midwayjs/decorator';
import { BaseController } from '../../../basic/base-controller';
import { CodeService } from '../service/code-service';
export class SmsCodeReq {
@Rule(RuleType.number().required())
phoneCode: number;
@Rule(RuleType.string().required())
mobile: string;
@Rule(RuleType.string().required().max(10))
randomStr: string;
@Rule(RuleType.number().required().max(4))
imgCode: string;
}
// const enumsMap = {};
// glob('src/modules/**/enums/*.ts', {}, (err, matches) => {
// console.log('matched', matches);
// for (const filePath of matches) {
// const module = require('/' + filePath);
// console.log('modules', module);
// }
// });
/**
*/
@Provide()
@Controller('/api/basic')
export class BasicController extends BaseController {
@Inject()
codeService: CodeService;
@Post('/sendSmsCode')
public sendSmsCode(
@Body(ALL)
body: SmsCodeReq
) {
// 设置缓存内容
return this.ok(null);
}
@Post('/captcha')
public async getCaptcha(
@Body()
randomStr
) {
console.assert(randomStr < 10, 'randomStr 过长');
const captcha = await this.codeService.generateCaptcha(randomStr);
return this.ok(captcha.data);
}
}

View File

@ -0,0 +1,57 @@
import { Inject, Provide } from '@midwayjs/decorator';
import { CacheManager } from '@midwayjs/cache';
const svgCaptcha = require('svg-captcha');
// {data: '<svg.../svg>', text: 'abcd'}
/**
*/
@Provide()
export class CodeService {
@Inject()
cache: CacheManager; // 依赖注入CacheManager
/**
*/
async generateCaptcha(randomStr) {
console.assert(randomStr < 10, 'randomStr 过长');
const c = svgCaptcha.create();
//{data: '<svg.../svg>', text: 'abcd'}
const imgCode = c.text; // = RandomUtil.randomStr(4, true);
await this.cache.set('imgCode:' + randomStr, imgCode, {
ttl: 2 * 60 * 1000, //过期时间 2分钟
});
return c;
}
async getCaptchaText(randomStr) {
return await this.cache.get('imgCode:' + randomStr);
}
async removeCaptcha(randomStr) {
await this.cache.del('imgCode:' + randomStr);
}
async checkCaptcha(randomStr, userCaptcha) {
const code = await this.getCaptchaText(randomStr);
if (code == null) {
throw new Error('验证码已过期');
}
if (code !== userCaptcha) {
throw new Error('验证码不正确');
}
return true;
}
/**
*/
async sendSms(phoneCode, mobile, smsCode) {
console.assert(phoneCode != null && mobile != null, '手机号不能为空');
console.assert(smsCode != null, '验证码不能为空');
}
/**
* loginBySmsCode
*/
async loginBySmsCode(user, smsCode) {
console.assert(user.mobile != null, '手机号不能为空');
}
}

View File

@ -0,0 +1,31 @@
import {
Body,
Controller,
Inject,
Post,
Provide,
ALL,
} from '@midwayjs/decorator';
import { LoginService } from '../service/login-service';
import { BaseController } from '../../../basic/base-controller';
/**
*/
@Provide()
@Controller('/api/')
export class LoginController extends BaseController {
@Inject()
loginService: LoginService;
@Post('/login')
public async login(
@Body(ALL)
user
) {
const token = await this.loginService.login(user);
return this.ok(token);
}
@Post('/logout')
public logout() {}
}

View File

@ -0,0 +1,52 @@
import { Config, Inject, Provide } from '@midwayjs/decorator';
import { UserService } from '../../authority/service/user-service';
import * as jwt from 'jsonwebtoken';
import { CommonException } from '../../../basic/exception/common-exception';
/**
*
*/
@Provide()
export class LoginService {
@Inject()
userService: UserService;
@Config('biz.jwt')
private jwt: any;
/**
* login
*/
async login(user) {
console.assert(user.username != null, '用户名不能为空');
const info = await this.userService.findOne({ username: user.username });
if (info == null) {
throw new CommonException('用户名或密码错误');
}
const right = this.userService.checkPassword(user.password, info.password);
if (!right) {
throw new CommonException('用户名或密码错误');
}
return this.generateToken(info);
}
/**
* token
* @param user
*/
async generateToken(user) {
const tokenInfo = {
username: user.username,
id: user.id,
};
const expire = this.jwt.expire;
const token = jwt.sign(tokenInfo, this.jwt.secret, {
expiresIn: expire,
});
return {
token,
expire,
};
}
}

View File

@ -0,0 +1,12 @@
const log4js = require('log4js');
const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info';
const path = require('path');
const filename = path.join('/logs/server.log');
log4js.configure({
appenders: {
std: { type: 'stdout', level: 'debug' },
file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename },
},
categories: { default: { appenders: ['std'], level } },
});
export const logger = log4js.getLogger('fast');

View File

@ -0,0 +1,43 @@
const numbers = '0123456789';
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const specials = '~!@#$%^*()_+-=[]{}|;:,./<>?';
/**
* Generate random string
* @param {Number} length
* @param {Object} options
*/
function randomStr(length, options) {
length || (length = 8);
options || (options = {});
let chars = '';
let result = '';
if (options === true) {
chars = numbers + letters;
} else if (typeof options === 'string') {
chars = options;
} else {
if (options.numbers !== false) {
chars += typeof options.numbers === 'string' ? options.numbers : numbers;
}
if (options.letters !== false) {
chars += typeof options.letters === 'string' ? options.letters : letters;
}
if (options.specials) {
chars +=
typeof options.specials === 'string' ? options.specials : specials;
}
}
while (length > 0) {
length--;
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
export const RandomUtil = { randomStr };

View File

@ -0,0 +1,25 @@
import { createApp, close, createHttpRequest } from '@midwayjs/mock';
import { Framework } from '@midwayjs/koa';
import * as assert from 'assert';
describe('test/controller/home.test.ts', () => {
it('should POST /api/get_user', async () => {
// create app
const app = await createApp<Framework>();
// make request
const result = await createHttpRequest(app).post('/api/get_user').query({ uid: 123 });
// use expect by jest
expect(result.status).toBe(200);
expect(result.body.message).toBe('OK');
// or use assert
assert.deepStrictEqual(result.status, 200);
assert.deepStrictEqual(result.body.data.uid, '123');
// close app
await close(app);
});
});

View File

@ -0,0 +1,26 @@
import { createApp, close, createHttpRequest } from '@midwayjs/mock';
import { Framework } from '@midwayjs/koa';
import * as assert from 'assert';
describe('test/controller/home.test.ts', () => {
it('should GET /', async () => {
// create app
const app = await createApp<Framework>();
// make request
const result = await createHttpRequest(app).get('/');
// use expect by jest
expect(result.status).toBe(200);
expect(result.text).toBe('Hello Midwayjs!');
// or use assert
assert.deepStrictEqual(result.status, 200);
assert.deepStrictEqual(result.text, 'Hello Midwayjs!');
// close app
await close(app);
});
});

View File

@ -0,0 +1,24 @@
{
"compileOnSave": true,
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"inlineSourceMap":true,
"noImplicitThis": true,
"noUnusedLocals": true,
"stripInternal": true,
"skipLibCheck": false,
"pretty": true,
"declaration": true,
"typeRoots": [ "./typings", "./node_modules/@types"],
"outDir": "dist"
},
"exclude": [
"dist",
"node_modules",
"test"
]
}