feat: 用户套餐,用户支付功能

This commit is contained in:
xiaojunnuo
2024-12-22 14:00:46 +08:00
parent d70e2b66a3
commit a019956698
69 changed files with 2071 additions and 738 deletions
@@ -10,27 +10,12 @@ CREATE TABLE "cd_product"
"price" integer,
"intro" varchar(10240),
"order" integer,
"is_bootstrap" boolean,
"support_buy" boolean,
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE TABLE "cd_payment"
(
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"type" varchar(100),
"title" varchar(100),
"setting" text,
"order" integer,
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE TABLE "cd_trade"
(
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
@@ -55,7 +40,7 @@ CREATE TABLE "cd_trade"
CREATE INDEX "index_trade_user_id" ON "cd_trade" ("user_id");
CREATE UNIQUE INDEX "index_trade_trade_no" ON "cd_trade" ("trade_no");
CREATE INDEX "index_trade_pay_no" ON "cd_trade" ("pay_type","pay_no");
CREATE INDEX "index_trade_pay_no" ON "cd_trade" ("pay_type", "pay_no");
CREATE TABLE "cd_user_suite"
@@ -68,7 +53,10 @@ CREATE TABLE "cd_user_suite"
"title" varchar(100),
"content" text,
"duration" integer,
"used_deploy_count" integer,
"deploy_count_used" integer,
"is_present" boolean,
"is_bootstrap" boolean,
"disabled" boolean NOT NULL DEFAULT (false),
"active_time" integer,
"expires_time" integer,
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
@@ -84,3 +72,63 @@ DROP TABLE IF EXISTS "cd_cert_issuer";
DROP TABLE IF EXISTS "cd_task";
DROP TABLE IF EXISTS "cd_task_history";
CREATE TABLE "cd_cert_info"
(
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"user_id" integer,
"domain" varchar(100),
"domains" varchar(10240),
"domain_count" integer,
"pipeline_id" integer,
"apply_time" integer,
"from_type" varchar(100),
"cert_provider" varchar(100),
"expires_time" integer,
"cert_info" text,
"cert_file" varchar(100),
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE INDEX "index_cert_info_user_id" ON "cd_cert_info" ("user_id");
CREATE INDEX "index_cert_info_domain" ON "cd_cert_info" ("domain");
CREATE INDEX "index_cert_info_domains" ON "cd_cert_info" ("domains");
CREATE INDEX "index_cert_info_pipeline" ON "cd_cert_info" ("pipeline_id");
CREATE TABLE "cd_site_info"
(
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"user_id" integer,
"name" varchar(100),
"domain" varchar(100),
"domains" varchar(10240),
"cert_info" varchar(10240),
"cert_provider" varchar(100),
"cert_status" varchar(100),
"cert_expires_time" integer,
"last_check_time" integer,
"check_status" varchar(100),
"pipeline_id" integer,
"cert_info_id" integer,
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE INDEX "index_site_info_user_id" ON "cd_site_info" ("user_id");
CREATE INDEX "index_site_info_domain" ON "cd_site_info" ("domain");
CREATE INDEX "index_site_info_domains" ON "cd_site_info" ("domains");
CREATE INDEX "index_site_info_pipeline" ON "cd_site_info" ("pipeline_id");
ALTER TABLE pi_pipeline
ADD COLUMN "type" varchar(50);
ALTER TABLE pi_pipeline
ADD COLUMN "from" varchar(50);
+1
View File
@@ -82,6 +82,7 @@
"lodash-es": "^4.17.21",
"log4js": "^6.7.1",
"lru-cache": "^11.0.1",
"mitt": "^3.0.1",
"mwts": "^1.3.0",
"mwtsc": "^1.4.0",
"mysql": "^2.18.1",
@@ -0,0 +1,73 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { Constants, CrudController } from '@certd/lib-server';
import { AuthService } from '../../modules/sys/authority/service/auth-service.js';
import { SiteInfoService } from '../../modules/monitor/service/site-info-service.js';
/**
* 通知
*/
@Provide()
@Controller('/api/monitor/site')
export class SiteInfoController extends CrudController<SiteInfoService> {
@Inject()
service: SiteInfoService;
@Inject()
authService: AuthService;
getService(): SiteInfoService {
return this.service;
}
@Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
body.query.userId = this.getUserId();
const res = await this.service.page({
query: body.query,
page: body.page,
sort: body.sort,
});
return this.ok(res);
}
@Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body: any) {
body.query = body.query ?? {};
body.query.userId = this.getUserId();
return await super.list(body);
}
@Post('/add', { summary: Constants.per.authOnly })
async add(@Body(ALL) bean: any) {
bean.userId = this.getUserId();
return await super.add(bean);
}
@Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.getUserId());
delete bean.userId;
return await super.update(bean);
}
@Post('/info', { summary: Constants.per.authOnly })
async info(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
return await super.info(id);
}
@Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
return await super.delete(id);
}
@Post('/all', { summary: Constants.per.authOnly })
async all() {
const list: any = await this.service.find({
where: {
userId: this.getUserId(),
},
});
return this.ok(list);
}
}
@@ -0,0 +1,74 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { Constants, CrudController } from '@certd/lib-server';
import { AuthService } from '../../modules/sys/authority/service/auth-service.js';
import { CertInfoService } from '../../modules/monitor/service/cert-info-service.js';
/**
* 通知
*/
@Provide()
@Controller('/api/monitor/cert')
export class CertInfoController extends CrudController<CertInfoService> {
@Inject()
service: CertInfoService;
@Inject()
authService: AuthService;
getService(): CertInfoService {
return this.service;
}
@Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
body.query.userId = this.getUserId();
const res = await this.service.page({
query: body.query,
page: body.page,
sort: body.sort,
});
return this.ok(res);
}
@Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body: any) {
body.query = body.query ?? {};
body.query.userId = this.getUserId();
return await super.list(body);
}
@Post('/add', { summary: Constants.per.authOnly })
async add(@Body(ALL) bean: any) {
bean.userId = this.getUserId();
return await super.add(bean);
}
@Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.getUserId());
delete bean.userId;
return await super.update(bean);
}
@Post('/info', { summary: Constants.per.authOnly })
async info(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
return await super.info(id);
}
@Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
return await super.delete(id);
}
@Post('/all', { summary: Constants.per.authOnly })
async all() {
const list: any = await this.service.find({
where: {
userId: this.getUserId(),
},
});
return this.ok(list);
}
}
@@ -1,6 +1,6 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { Constants, CrudController } from '@certd/lib-server';
import { AccessService } from '../../modules/pipeline/service/access-service.js';
import { AccessService } from '@certd/lib-server';
import { AuthService } from '../../modules/sys/authority/service/auth-service.js';
import { AccessDefine } from '@certd/pipeline';
@@ -10,9 +10,8 @@ import {
PluginRequestHandleReq,
TaskInstanceContext,
} from '@certd/pipeline';
import { AccessService } from '../../modules/pipeline/service/access-service.js';
import { AccessService, AccessGetter } from '@certd/lib-server';
import { EmailService } from '../../modules/basic/service/email-service.js';
import { AccessGetter } from '../../modules/pipeline/service/access-getter.js';
import { http, HttpRequestConfig, logger, mergeUtils, utils } from '@certd/basic';
import { NotificationService } from '../../modules/pipeline/service/notification-service.js';
@@ -1,5 +1,5 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { AccessService } from '../../../modules/pipeline/service/access-service.js';
import { AccessService } from '@certd/lib-server';
import { AccessController } from '../../pipeline/access-controller.js';
import { checkComm } from '@certd/plus-core';
@@ -5,8 +5,8 @@ import { SmsServiceFactory } from '../sms/factory.js';
import { ISmsService } from '../sms/api.js';
import { CodeErrorException } from '@certd/lib-server/dist/basic/exception/code-error-exception.js';
import { EmailService } from './email-service.js';
import { AccessService } from '../../pipeline/service/access-service.js';
import { AccessSysGetter } from '../../pipeline/service/access-sys-getter.js';
import { AccessService } from '@certd/lib-server';
import { AccessSysGetter } from '@certd/lib-server';
import { isComm } from '@certd/plus-core';
// {data: '<svg.../svg>', text: 'abcd'}
@@ -7,7 +7,7 @@ import { createDnsProvider, IDnsProvider, parseDomain } from '@certd/plugin-cert
import { CnameProvider, CnameRecord } from '@certd/pipeline';
import { cache, http, logger, utils } from '@certd/basic';
import { AccessService } from '../../pipeline/service/access-service.js';
import { AccessService } from '@certd/lib-server';
import { isDev } from '@certd/basic';
import { walkTxtRecord } from '@certd/acme-client';
import { CnameProviderService } from './cname-provider-service.js';
@@ -1,36 +0,0 @@
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;
}
@@ -29,6 +29,14 @@ export class PipelineEntity {
@Column({ comment: '启用/禁用', nullable: true, default: false })
disabled: boolean;
// cert: 证书; backup: 备份; custom:自定义;
@Column({ comment: '类型', nullable: true, default: 'cert' })
type: string;
// custom: 自定义; monitor: 监控;
@Column({ comment: '来源', nullable: true, default: 'custom' })
from: string;
@Column({
name: 'last_history_time',
comment: '最后一次执行时间',
@@ -1,18 +0,0 @@
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);
}
}
@@ -1,175 +0,0 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService, PageReq, PermissionException, ValidateException } from '@certd/lib-server';
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;
//@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);
}
}
@@ -1,17 +0,0 @@
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);
}
}
@@ -1,44 +0,0 @@
import { Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import crypto from 'crypto';
import { SysSettingsService } from '@certd/lib-server';
import { SysPrivateSettings } from '@certd/lib-server';
/**
* 授权
*/
@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();
}
}
@@ -1,11 +1,20 @@
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, MoreThan, Repository } from 'typeorm';
import { BaseService, NeedVIPException, PageReq, SysPublicSettings, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import {
AccessGetter,
AccessService,
BaseService,
NeedSuiteException,
NeedVIPException,
PageReq,
SysPublicSettings,
SysSettingsService,
SysSiteInfo,
} from '@certd/lib-server';
import { PipelineEntity } from '../entity/pipeline.js';
import { PipelineDetail } from '../entity/vo/pipeline-detail.js';
import { Executor, Pipeline, ResultType, RunHistory, SysInfo, UserInfo } from '@certd/pipeline';
import { AccessService } from './access-service.js';
import { Executor, Pipeline, ResultType, RunHistory, RunnableCollection, SysInfo, UserInfo } from '@certd/pipeline';
import { DbStorage } from './db-storage.js';
import { StorageService } from './storage-service.js';
import { Cron } from '../../cron/cron.js';
@@ -15,26 +24,26 @@ import { HistoryLogEntity } from '../entity/history-log.js';
import { HistoryLogService } from './history-log-service.js';
import { EmailService } from '../../basic/service/email-service.js';
import { UserService } from '../../sys/authority/service/user-service.js';
import { AccessGetter } from './access-getter.js';
import { CnameRecordService } from '../../cname/service/cname-record-service.js';
import { CnameProxyService } from './cname-proxy-service.js';
import { PluginConfigGetter } from '../../plugin/service/plugin-config-getter.js';
import dayjs from 'dayjs';
import { DbAdapter } from '../../db/index.js';
import { isComm, isPlus } from '@certd/plus-core';
import { isComm } from '@certd/plus-core';
import { logger } from '@certd/basic';
import { UrlService } from './url-service.js';
import { NotificationService } from './notification-service.js';
import { NotificationGetter } from './notification-getter.js';
import { UserSuiteService } from '@certd/commercial-core';
import { CertInfoService } from '../../monitor/service/cert-info-service.js';
const runningTasks: Map<string | number, Executor> = new Map();
const freeCount = 10;
/**
* 证书申请
*/
@Provide()
@Scope(ScopeEnum.Singleton)
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class PipelineService extends BaseService<PipelineEntity> {
@InjectEntityModel(PipelineEntity)
repository: Repository<PipelineEntity>;
@@ -60,6 +69,9 @@ export class PipelineService extends BaseService<PipelineEntity> {
@Inject()
userService: UserService;
@Inject()
userSuiteService: UserSuiteService;
@Inject()
cron: Cron;
@@ -75,6 +87,9 @@ export class PipelineService extends BaseService<PipelineEntity> {
@Inject()
dbAdapter: DbAdapter;
@Inject()
certInfoService: CertInfoService;
//@ts-ignore
getRepository() {
return this.repository;
@@ -144,26 +159,19 @@ export class PipelineService extends BaseService<PipelineEntity> {
//修改
old = await this.info(bean.id);
}
const pipeline = JSON.parse(bean.content || '{}');
const isUpdate = bean.id > 0 && old != null;
let domains = [];
RunnableCollection.each(pipeline.stages, (runnable: any) => {
if (runnable.runnableType === 'step' && runnable.type.startsWith('CertApply')) {
domains = runnable.input.domains || [];
}
});
if (!isUpdate) {
//如果是添加,校验数量
if (!isPlus()) {
const count = await this.repository.count();
if (count >= freeCount) {
throw new NeedVIPException(`基础版最多只能创建${freeCount}条流水线`);
}
}
const userId = bean.userId;
const userIsAdmin = await this.userService.isAdmin(userId);
if (!userIsAdmin) {
//非管理员用户,限制pipeline数量
const count = await this.repository.count({ where: { userId } });
const sysPublic = await this.sysSettingsService.getSetting<SysPublicSettings>(SysPublicSettings);
const limitUserPipelineCount = sysPublic.limitUserPipelineCount;
if (limitUserPipelineCount && limitUserPipelineCount > 0 && count >= limitUserPipelineCount) {
throw new NeedVIPException(`您最多只能创建${limitUserPipelineCount}条流水线`);
}
}
await this.checkMaxPipelineCount(bean, pipeline, domains);
}
if (!isUpdate) {
@@ -171,19 +179,51 @@ export class PipelineService extends BaseService<PipelineEntity> {
await this.addOrUpdate(bean);
}
await this.clearTriggers(bean.id);
if (bean.content) {
const pipeline = JSON.parse(bean.content);
if (pipeline.title) {
bean.title = pipeline.title;
}
pipeline.id = bean.id;
bean.content = JSON.stringify(pipeline);
if (pipeline.title) {
bean.title = pipeline.title;
}
pipeline.id = bean.id;
bean.content = JSON.stringify(pipeline);
await this.addOrUpdate(bean);
await this.registerTriggerById(bean.id);
//保存域名信息到certInfo表
await this.certInfoService.updateDomains(pipeline.id, domains);
return bean;
}
private async checkMaxPipelineCount(bean: PipelineEntity, pipeline: Pipeline, domains: string[]) {
// if (!isPlus()) {
// const count = await this.repository.count();
// if (count >= freeCount) {
// throw new NeedVIPException(`基础版最多只能创建${freeCount}条流水线`);
// }
// }
if (isComm()) {
//校验pipelineCount
const userSuite = await this.userSuiteService.getMySuiteDetail(bean.userId);
if (userSuite?.pipelineCount.used + 1 > userSuite?.pipelineCount.max) {
throw new NeedSuiteException(`对不起,您最多只能创建${userSuite?.pipelineCount.max}条流水线,请购买或升级套餐`);
}
if (userSuite.domainCount.used + domains.length > userSuite.domainCount.max) {
throw new NeedSuiteException(`对不起,您最多只能添加${userSuite.domainCount.max}个域名,请购买或升级套餐`);
}
}
const userId = bean.userId;
const userIsAdmin = await this.userService.isAdmin(userId);
if (!userIsAdmin) {
//非管理员用户,限制pipeline数量
const count = await this.repository.count({ where: { userId } });
const sysPublic = await this.sysSettingsService.getSetting<SysPublicSettings>(SysPublicSettings);
const limitUserPipelineCount = sysPublic.limitUserPipelineCount;
if (limitUserPipelineCount && limitUserPipelineCount > 0 && count >= limitUserPipelineCount) {
throw new NeedVIPException(`您最多只能创建${limitUserPipelineCount}条流水线`);
}
}
}
async foreachPipeline(callback: (pipeline: PipelineEntity) => void) {
const idEntityList = await this.repository.find({
select: {
@@ -578,4 +618,8 @@ export class PipelineService extends BaseService<PipelineEntity> {
{ groupId }
);
}
async getUserPipelineCount(userId) {
return await this.repository.count({ where: { userId } });
}
}
@@ -0,0 +1,31 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { PipelineService } from '../../pipeline/service/pipeline-service.js';
import { CertInfoService } from '../../monitor/service/cert-info-service.js';
import { IUsedCountService } from '@certd/commercial-core';
import { SiteInfoService } from '../../monitor/service/site-info-service.js';
@Provide('myCountService')
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class MyCountService implements IUsedCountService {
@Inject()
pipelineService: PipelineService;
@Inject()
certInfoService: CertInfoService;
@Inject()
siteInfoService: SiteInfoService;
async getUsedCount(userId: number) {
if (!userId) {
throw new Error('userId is required');
}
const pipelineCountUsed = await this.pipelineService.getUserPipelineCount(userId);
const domainCountUsed = await this.certInfoService.getUserDomainCount(userId);
const monitorCountUsed = await this.siteInfoService.getUserMonitorCount(userId);
return {
pipelineCountUsed,
domainCountUsed,
monitorCountUsed,
};
}
}
@@ -206,6 +206,9 @@ export class UserService extends BaseService<UserEntity> {
});
delete newUser.password;
utils.mitter.emit('register', { userId: newUser.id });
return newUser;
}