mirror of
https://github.com/certd/certd.git
synced 2026-04-28 16:17:25 +08:00
feat: 用户套餐,用户支付功能
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 授权配置
|
||||
*/
|
||||
@Entity('cd_access')
|
||||
export class AccessEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
userId: number;
|
||||
@Column({ comment: '名称', length: 100 })
|
||||
name: string;
|
||||
|
||||
@Column({ comment: '类型', length: 100 })
|
||||
type: string;
|
||||
|
||||
@Column({ name: 'setting', comment: '设置', length: 10240, nullable: true })
|
||||
setting: string;
|
||||
|
||||
@Column({ name: 'encrypt_setting', comment: '已加密设置', length: 10240, nullable: true })
|
||||
encryptSetting: string;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './entity/access.js';
|
||||
export * from './service/access-service.js';
|
||||
export * from './service/access-sys-getter.js';
|
||||
export * from './service/access-getter.js';
|
||||
export * from './service/encrypt-service.js';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { IAccessService } from '@certd/pipeline';
|
||||
|
||||
export class AccessGetter implements IAccessService {
|
||||
userId: number;
|
||||
getter: <T>(id: any, userId?: number) => Promise<T>;
|
||||
constructor(userId: number, getter: (id: any, userId: number) => Promise<any>) {
|
||||
this.userId = userId;
|
||||
this.getter = getter;
|
||||
}
|
||||
|
||||
async getById<T = any>(id: any) {
|
||||
return await this.getter<T>(id, this.userId);
|
||||
}
|
||||
|
||||
async getCommonById<T = any>(id: any) {
|
||||
return await this.getter<T>(id, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService, PageReq, PermissionException, ValidateException } from '../../../index.js';
|
||||
import { AccessEntity } from '../entity/access.js';
|
||||
import { AccessDefine, accessRegistry, newAccess } from '@certd/pipeline';
|
||||
import { EncryptService } from './encrypt-service.js';
|
||||
|
||||
/**
|
||||
* 授权
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class AccessService extends BaseService<AccessEntity> {
|
||||
@InjectEntityModel(AccessEntity)
|
||||
repository: Repository<AccessEntity>;
|
||||
|
||||
@Inject()
|
||||
encryptService: EncryptService;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async page(pageReq: PageReq<AccessEntity>) {
|
||||
const res = await super.page(pageReq);
|
||||
res.records = res.records.map(item => {
|
||||
delete item.encryptSetting;
|
||||
return item;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
async add(param) {
|
||||
this.encryptSetting(param, null);
|
||||
return await super.add(param);
|
||||
}
|
||||
|
||||
encryptSetting(param: any, oldSettingEntity?: AccessEntity) {
|
||||
const accessType = param.type;
|
||||
const accessDefine: AccessDefine = accessRegistry.getDefine(accessType);
|
||||
if (!accessDefine) {
|
||||
throw new ValidateException(`授权类型${accessType}不存在`);
|
||||
}
|
||||
const setting = param.setting;
|
||||
if (!setting) {
|
||||
return;
|
||||
}
|
||||
const json = JSON.parse(setting);
|
||||
let oldSetting = {};
|
||||
let encryptSetting = {};
|
||||
const firstEncrypt = !oldSettingEntity || !oldSettingEntity.encryptSetting || oldSettingEntity.encryptSetting === '{}';
|
||||
if (oldSettingEntity) {
|
||||
oldSetting = JSON.parse(oldSettingEntity.setting || '{}');
|
||||
encryptSetting = JSON.parse(oldSettingEntity.encryptSetting || '{}');
|
||||
}
|
||||
for (const key in json) {
|
||||
//加密
|
||||
let value = json[key];
|
||||
if (value && typeof value === 'string') {
|
||||
//去除前后空格
|
||||
value = value.trim();
|
||||
json[key] = value;
|
||||
}
|
||||
const accessInputDefine = accessDefine.input[key];
|
||||
if (!accessInputDefine) {
|
||||
continue;
|
||||
}
|
||||
if (!accessInputDefine.encrypt || !value || typeof value !== 'string') {
|
||||
//定义无需加密、value为空、不是字符串 这些不需要加密
|
||||
encryptSetting[key] = {
|
||||
value: value,
|
||||
encrypt: false,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstEncrypt || oldSetting[key] !== value) {
|
||||
//星号保护
|
||||
const length = value.length;
|
||||
const subIndex = Math.min(2, length);
|
||||
let starLength = length - subIndex * 2;
|
||||
starLength = Math.max(2, starLength);
|
||||
const starString = '*'.repeat(starLength);
|
||||
json[key] = value.substring(0, subIndex) + starString + value.substring(value.length - subIndex);
|
||||
encryptSetting[key] = {
|
||||
value: this.encryptService.encrypt(value),
|
||||
encrypt: true,
|
||||
};
|
||||
}
|
||||
//未改变情况下,不做修改
|
||||
}
|
||||
param.encryptSetting = JSON.stringify(encryptSetting);
|
||||
param.setting = JSON.stringify(json);
|
||||
}
|
||||
/**
|
||||
* 修改
|
||||
* @param param 数据
|
||||
*/
|
||||
async update(param) {
|
||||
const oldEntity = await this.info(param.id);
|
||||
if (oldEntity == null) {
|
||||
throw new ValidateException('该授权配置不存在,请确认是否已被删除');
|
||||
}
|
||||
this.encryptSetting(param, oldEntity);
|
||||
return await super.update(param);
|
||||
}
|
||||
|
||||
async getSimpleInfo(id: number) {
|
||||
const entity = await this.info(id);
|
||||
if (entity == null) {
|
||||
throw new ValidateException('该授权配置不存在,请确认是否已被删除');
|
||||
}
|
||||
return {
|
||||
id: entity.id,
|
||||
name: entity.name,
|
||||
userId: entity.userId,
|
||||
};
|
||||
}
|
||||
|
||||
async getAccessById(id: any, checkUserId: boolean, userId?: number): Promise<any> {
|
||||
const entity = await this.info(id);
|
||||
if (entity == null) {
|
||||
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
|
||||
}
|
||||
if (checkUserId) {
|
||||
if (userId == null) {
|
||||
throw new ValidateException('userId不能为空');
|
||||
}
|
||||
if (userId !== entity.userId) {
|
||||
throw new PermissionException('您对该Access授权无访问权限');
|
||||
}
|
||||
}
|
||||
|
||||
// const access = accessRegistry.get(entity.type);
|
||||
const setting = this.decryptAccessEntity(entity);
|
||||
const input = {
|
||||
id: entity.id,
|
||||
...setting,
|
||||
};
|
||||
return newAccess(entity.type, input);
|
||||
}
|
||||
|
||||
async getById(id: any, userId: number): Promise<any> {
|
||||
return await this.getAccessById(id, true, userId);
|
||||
}
|
||||
|
||||
decryptAccessEntity(entity: AccessEntity): any {
|
||||
let setting = {};
|
||||
if (entity.encryptSetting && entity.encryptSetting !== '{}') {
|
||||
setting = JSON.parse(entity.encryptSetting);
|
||||
for (const key in setting) {
|
||||
//解密
|
||||
const encryptValue = setting[key];
|
||||
let value = encryptValue.value;
|
||||
if (encryptValue.encrypt) {
|
||||
value = this.encryptService.decrypt(value);
|
||||
}
|
||||
setting[key] = value;
|
||||
}
|
||||
} else if (entity.setting) {
|
||||
setting = JSON.parse(entity.setting);
|
||||
}
|
||||
return setting;
|
||||
}
|
||||
|
||||
getDefineList() {
|
||||
return accessRegistry.getDefineList();
|
||||
}
|
||||
|
||||
getDefineByType(type: string) {
|
||||
return accessRegistry.getDefine(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { IAccessService } from '@certd/pipeline';
|
||||
import { AccessService } from './access-service.js';
|
||||
|
||||
export class AccessSysGetter implements IAccessService {
|
||||
accessService: AccessService;
|
||||
constructor(accessService: AccessService) {
|
||||
this.accessService = accessService;
|
||||
}
|
||||
|
||||
async getById<T = any>(id: any) {
|
||||
return await this.accessService.getAccessById(id, false);
|
||||
}
|
||||
|
||||
async getCommonById<T = any>(id: any) {
|
||||
return await this.accessService.getAccessById(id, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import crypto from 'crypto';
|
||||
import { SysPrivateSettings, SysSettingsService } from '../../../system/index.js';
|
||||
|
||||
/**
|
||||
* 授权
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Singleton)
|
||||
export class EncryptService {
|
||||
secretKey: Buffer;
|
||||
|
||||
@Inject()
|
||||
sysSettingService: SysSettingsService;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
const privateInfo: SysPrivateSettings = await this.sysSettingService.getSetting(SysPrivateSettings);
|
||||
this.secretKey = Buffer.from(privateInfo.encryptSecret, 'base64');
|
||||
}
|
||||
|
||||
// 加密函数
|
||||
encrypt(text: string) {
|
||||
const iv = crypto.randomBytes(16); // 初始化向量
|
||||
// const secretKey = crypto.randomBytes(32);
|
||||
// const key = Buffer.from(secretKey);
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', this.secretKey, iv);
|
||||
let encrypted = cipher.update(text);
|
||||
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||
return iv.toString('hex') + ':' + encrypted.toString('hex');
|
||||
}
|
||||
|
||||
// 解密函数
|
||||
decrypt(encryptedText: string) {
|
||||
const textParts = encryptedText.split(':');
|
||||
const iv = Buffer.from(textParts.shift(), 'hex');
|
||||
const encrypted = Buffer.from(textParts.join(':'), 'hex');
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(this.secretKey), iv);
|
||||
let decrypted = decipher.update(encrypted);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './access/index.js';
|
||||
Reference in New Issue
Block a user