mirror of
https://github.com/certd/certd.git
synced 2026-04-14 20:40:53 +08:00
chore:
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||
import {
|
||||
AccessGetter,
|
||||
AddonRequestHandleReq,
|
||||
Constants,
|
||||
CrudController,
|
||||
newAddon,
|
||||
ValidateException
|
||||
} from "@certd/lib-server";
|
||||
import { AuthService } from '../../../modules/sys/authority/service/auth-service.js';
|
||||
import { checkPlus } from '@certd/plus-core';
|
||||
import { AddonService } from "@certd/lib-server";
|
||||
import { AddonDefine } from "@certd/lib-server";
|
||||
import { AccessRequestHandleReq, newAccess } from "@certd/pipeline";
|
||||
import { http, logger, utils } from "@certd/basic";
|
||||
/**
|
||||
* Addon
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/addon')
|
||||
export class AddonController extends CrudController<AddonService> {
|
||||
@Inject()
|
||||
service: AddonService;
|
||||
@Inject()
|
||||
authService: AuthService;
|
||||
|
||||
getService(): AddonService {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/page', { summary: Constants.per.authOnly })
|
||||
async page(@Body(ALL) body) {
|
||||
body.query = body.query ?? {};
|
||||
delete body.query.userId;
|
||||
const buildQuery = qb => {
|
||||
qb.andWhere('user_id = :userId', { userId: this.getUserId() });
|
||||
};
|
||||
const res = await this.service.page({
|
||||
query: body.query,
|
||||
page: body.page,
|
||||
sort: body.sort,
|
||||
buildQuery,
|
||||
});
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@Post('/list', { summary: Constants.per.authOnly })
|
||||
async list(@Body(ALL) body) {
|
||||
body.query = body.query ?? {};
|
||||
body.query.userId = this.getUserId();
|
||||
return super.list(body);
|
||||
}
|
||||
|
||||
@Post('/add', { summary: Constants.per.authOnly })
|
||||
async add(@Body(ALL) bean) {
|
||||
bean.userId = this.getUserId();
|
||||
const type = bean.type;
|
||||
const addonType = bean.addonType;
|
||||
if (! type || !addonType){
|
||||
throw new ValidateException('请选择Addon类型');
|
||||
}
|
||||
const define: AddonDefine = this.service.getDefineByType(type,addonType);
|
||||
if (!define) {
|
||||
throw new ValidateException('Addon类型不存在');
|
||||
}
|
||||
if (define.needPlus) {
|
||||
checkPlus();
|
||||
}
|
||||
return super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update', { summary: Constants.per.authOnly })
|
||||
async update(@Body(ALL) bean) {
|
||||
await this.service.checkUserId(bean.id, this.getUserId());
|
||||
const old = await this.service.info(bean.id);
|
||||
if (!old) {
|
||||
throw new ValidateException('Addon配置不存在');
|
||||
}
|
||||
if (old.type !== bean.type ) {
|
||||
const addonType = old.type;
|
||||
const type = bean.type;
|
||||
const define: AddonDefine = this.service.getDefineByType(type,addonType);
|
||||
if (!define) {
|
||||
throw new ValidateException('Addon类型不存在');
|
||||
}
|
||||
if (define.needPlus) {
|
||||
checkPlus();
|
||||
}
|
||||
}
|
||||
delete bean.userId;
|
||||
return super.update(bean);
|
||||
}
|
||||
@Post('/info', { summary: Constants.per.authOnly })
|
||||
async info(@Query('id') id: number) {
|
||||
await this.service.checkUserId(id, this.getUserId());
|
||||
return super.info(id);
|
||||
}
|
||||
|
||||
@Post('/delete', { summary: Constants.per.authOnly })
|
||||
async delete(@Query('id') id: number) {
|
||||
await this.service.checkUserId(id, this.getUserId());
|
||||
return super.delete(id);
|
||||
}
|
||||
|
||||
@Post('/define', { summary: Constants.per.authOnly })
|
||||
async define(@Query('type') type: string,@Query('addonType') addonType: string) {
|
||||
const notification = this.service.getDefineByType(type,addonType);
|
||||
return this.ok(notification);
|
||||
}
|
||||
|
||||
@Post('/getTypeDict', { summary: Constants.per.authOnly })
|
||||
async getTypeDict(@Query('addonType') addonType: string) {
|
||||
const list: any = this.service.getDefineList(addonType);
|
||||
let dict = [];
|
||||
for (const item of list) {
|
||||
dict.push({
|
||||
value: item.name,
|
||||
label: item.title,
|
||||
needPlus: item.needPlus ?? false,
|
||||
icon: item.icon,
|
||||
});
|
||||
}
|
||||
dict = dict.sort(a => {
|
||||
return a.needPlus ? 0 : -1;
|
||||
});
|
||||
return this.ok(dict);
|
||||
}
|
||||
|
||||
@Post('/simpleInfo', { summary: Constants.per.authOnly })
|
||||
async simpleInfo(@Query('addonType') addonType: string,@Query('id') id: number) {
|
||||
if (id === 0) {
|
||||
//获取默认
|
||||
const res = await this.service.getDefault(this.getUserId(),addonType);
|
||||
if (!res) {
|
||||
throw new ValidateException('默认Addon配置不存在');
|
||||
}
|
||||
const simple = await this.service.getSimpleInfo(res.id);
|
||||
return this.ok(simple);
|
||||
}
|
||||
await this.authService.checkEntityUserId(this.ctx, this.service, id);
|
||||
const res = await this.service.getSimpleInfo(id);
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@Post('/getDefaultId', { summary: Constants.per.authOnly })
|
||||
async getDefaultId(@Query('addonType') addonType: string) {
|
||||
const res = await this.service.getDefault(this.getUserId(),addonType);
|
||||
return this.ok(res?.id);
|
||||
}
|
||||
|
||||
@Post('/setDefault', { summary: Constants.per.authOnly })
|
||||
async setDefault(@Query('addonType') addonType: string,@Query('id') id: number) {
|
||||
await this.service.checkUserId(id, this.getUserId());
|
||||
const res = await this.service.setDefault(id, this.getUserId(),addonType);
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
|
||||
@Post('/options', { summary: Constants.per.authOnly })
|
||||
async options(@Query('addonType') addonType: string) {
|
||||
const res = await this.service.list({
|
||||
query: {
|
||||
userId: this.getUserId(),
|
||||
addonType
|
||||
},
|
||||
});
|
||||
for (const item of res) {
|
||||
delete item.setting;
|
||||
}
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
|
||||
@Post('/handle', { summary: Constants.per.authOnly })
|
||||
async handle(@Body(ALL) body: AddonRequestHandleReq) {
|
||||
const userId = this.getUserId();
|
||||
let inputAddon = body.input.addon;
|
||||
if (body.input.id > 0) {
|
||||
const oldEntity = await this.service.info(body.input.id);
|
||||
if (oldEntity) {
|
||||
if (oldEntity.userId !== userId) {
|
||||
throw new Error('addon not found');
|
||||
}
|
||||
// const param: any = {
|
||||
// type: body.typeName,
|
||||
// setting: JSON.stringify(body.input.access),
|
||||
// };
|
||||
inputAddon = JSON.parse( oldEntity.setting)
|
||||
}
|
||||
}
|
||||
const ctx = {
|
||||
http: http,
|
||||
logger:logger,
|
||||
utils:utils,
|
||||
}
|
||||
const addon = await newAddon(body.addonType,body.typeName, inputAddon,ctx);
|
||||
const res = await addon.onRequest(body);
|
||||
return this.ok(res);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ export class LoginController extends BaseController {
|
||||
@Body(ALL)
|
||||
user: any
|
||||
) {
|
||||
await this.loginService.doCaptchaValidate({form:user})
|
||||
const token = await this.loginService.loginByPassword(user);
|
||||
this.writeTokenCookie(token);
|
||||
return this.ok(token);
|
||||
|
||||
@@ -6,12 +6,13 @@ import {RoleService} from '../../sys/authority/service/role-service.js';
|
||||
import {UserEntity} from '../../sys/authority/entity/user.js';
|
||||
import {SysSettingsService} from '@certd/lib-server';
|
||||
import {SysPrivateSettings} from '@certd/lib-server';
|
||||
import {cache, utils} from '@certd/basic';
|
||||
import { cache, logger, utils } from "@certd/basic";
|
||||
import {LoginErrorException} from '@certd/lib-server/dist/basic/exception/login-error-exception.js';
|
||||
import {CodeService} from '../../basic/service/code-service.js';
|
||||
import {TwoFactorService} from "../../mine/service/two-factor-service.js";
|
||||
import {UserSettingsService} from '../../mine/service/user-settings-service.js';
|
||||
import {isPlus} from "@certd/plus-core";
|
||||
import { AddonService } from "@certd/lib-server/dist/user/addon/service/addon-service.js";
|
||||
|
||||
/**
|
||||
* 系统用户
|
||||
@@ -35,6 +36,8 @@ export class LoginService {
|
||||
userSettingsService: UserSettingsService;
|
||||
@Inject()
|
||||
twoFactorService: TwoFactorService;
|
||||
@Inject()
|
||||
addonService: AddonService;
|
||||
|
||||
checkIsBlocked(username: string) {
|
||||
const blockDurationKey = `login_block_duration:${username}`;
|
||||
@@ -97,6 +100,31 @@ export class LoginService {
|
||||
throw new LoginErrorException(errorMessage, leftTimes);
|
||||
}
|
||||
|
||||
async doCaptchaValidate(opts:{form:any}){
|
||||
|
||||
const pubSetting = await this.sysSettingsService.getPublicSettings()
|
||||
|
||||
if (pubSetting.captchaEnabled) {
|
||||
const prvSetting = await this.sysSettingsService.getPrivateSettings()
|
||||
|
||||
const addon = await this.addonService.getById(prvSetting.captchaAddonId,0)
|
||||
if (!addon) {
|
||||
logger.warn('验证码插件还未配置,忽略验证码校验')
|
||||
return true
|
||||
}
|
||||
if (addon.addonType !== pubSetting.captchaType) {
|
||||
logger.warn('验证码插件类型错误,忽略验证码校验')
|
||||
return true
|
||||
}
|
||||
|
||||
return await addon.onValidate(opts.form)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async loginBySmsCode(req: { mobile: string; phoneCode: string; smsCode: string; randomStr: string }) {
|
||||
|
||||
|
||||
@@ -35,3 +35,4 @@ export * from './plugin-ksyun/index.js'
|
||||
export * from './plugin-apisix/index.js'
|
||||
export * from './plugin-dokploy/index.js'
|
||||
export * from './plugin-godaddy/index.js'
|
||||
export * from './plugin-captcha/index.js'
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server/dist/user/addon/api/index.js";
|
||||
import crypto from 'crypto';
|
||||
@IsAddon({
|
||||
addonType:"captcha",
|
||||
name: 'geetest',
|
||||
title: '极验验证码',
|
||||
desc: '',
|
||||
})
|
||||
export class GeeTestCaptcha extends BaseAddon {
|
||||
@AddonInput({
|
||||
title: 'captchaId',
|
||||
component: {
|
||||
placeholder: 'captchaId',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
captchaId = '';
|
||||
|
||||
@AddonInput({
|
||||
title: 'captchaKey',
|
||||
component: {
|
||||
placeholder: 'captchaKey',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
captchaKey = '';
|
||||
|
||||
|
||||
async onValidate(data?:any) {
|
||||
|
||||
// geetest 服务地址
|
||||
// geetest server url
|
||||
const API_SERVER = "http://gcaptcha4.geetest.com";
|
||||
|
||||
// geetest 验证接口
|
||||
// geetest server interface
|
||||
const API_URL = API_SERVER + "/validate" + "?captcha_id=" + this.captchaId;
|
||||
|
||||
|
||||
// 前端参数
|
||||
// web parameter
|
||||
var lot_number = data['lot_number'];
|
||||
var captcha_output = data['captcha_output'];
|
||||
var pass_token = data['pass_token'];
|
||||
var gen_time = data['gen_time'];
|
||||
|
||||
// 生成签名, 使用标准的hmac算法,使用用户当前完成验证的流水号lot_number作为原始消息message,使用客户验证私钥作为key
|
||||
// 采用sha256散列算法将message和key进行单向散列生成最终的 “sign_token” 签名
|
||||
// use lot_number + CAPTCHA_KEY, generate the signature
|
||||
var sign_token = this.hmac_sha256_encode(lot_number, this.captchaKey);
|
||||
|
||||
// 向极验转发前端数据 + “sign_token” 签名
|
||||
// send web parameter and “sign_token” to geetest server
|
||||
var datas = {
|
||||
'lot_number': lot_number,
|
||||
'captcha_output': captcha_output,
|
||||
'pass_token': pass_token,
|
||||
'gen_time': gen_time,
|
||||
'sign_token': sign_token
|
||||
};
|
||||
|
||||
// post request
|
||||
// 根据极验返回的用户验证状态, 网站主进行自己的业务逻辑
|
||||
// According to the user authentication status returned by the geetest, the website owner carries out his own business logic
|
||||
try{
|
||||
const res = await this.doRequest(datas, API_URL)
|
||||
if (res.result == "success") {
|
||||
// 验证成功
|
||||
// verification successful
|
||||
return true;
|
||||
} else {
|
||||
// 验证失败
|
||||
// verification failed
|
||||
this.logger.error("极验验证不通过 ",res.reason)
|
||||
return false;
|
||||
}
|
||||
}catch (e) {
|
||||
this.ctx.logger.error("极验验证服务异常",e)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
// Generate signature
|
||||
hmac_sha256_encode(value, key){
|
||||
var hash = crypto.createHmac("sha256", key)
|
||||
.update(value, 'utf8')
|
||||
.digest('hex');
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
// 发送post请求, 响应json数据如:{"result": "success", "reason": "", "captcha_args": {}}
|
||||
// Send a post request and respond to JSON data, such as: {result ":" success "," reason ":" "," captcha_args ": {}}
|
||||
async doRequest(datas, url){
|
||||
var options = {
|
||||
url: url,
|
||||
method: "POST",
|
||||
params: datas,
|
||||
timeout: 5000
|
||||
};
|
||||
const result = await this.ctx.http.request(options);
|
||||
return result.data;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './geetest/index.js';
|
||||
Reference in New Issue
Block a user