chore: license说明

This commit is contained in:
xiaojunnuo
2024-08-14 21:24:12 +08:00
parent 746bb9d385
commit db9d27468e
25 changed files with 483 additions and 114 deletions
@@ -1 +1,5 @@
alter table cd_access alter column setting type text using setting::text;
alter table sys_settings alter column setting type text using setting::text;
alter table user_settings alter column setting type text using setting::text;
alter table pi_history_log alter column logs type text using logs::text;
alter table pi_history alter column pipeline type text using pipeline::text;
@@ -31,6 +31,10 @@ export const Constants = {
code: 10,
message: '参数错误',
},
needvip: {
code: 88,
message: '需要VIP',
},
auth: {
code: 401,
message: '您还未登录或token已过期',
@@ -0,0 +1,10 @@
import { Constants } from '../constants.js';
import { BaseException } from './base-exception.js';
/**
* 需要vip异常
*/
export class NeedVIPException extends BaseException {
constructor(message) {
super('NeedVIPException', Constants.res.needvip.code, message ? message : Constants.res.needvip.message);
}
}
@@ -8,7 +8,6 @@ import * as staticFile from '@midwayjs/static-file';
import * as cron from './modules/plugin/cron/index.js';
import * as flyway from '@certd/midway-flyway-js';
import cors from '@koa/cors';
import { ReportMiddleware } from './middleware/report.js';
import { GlobalExceptionMiddleware } from './middleware/global-exception.js';
import { PreviewMiddleware } from './middleware/preview.js';
import { AuthorityMiddleware } from './middleware/authority.js';
@@ -60,7 +59,6 @@ export class MainConfiguration {
//this.app.use(bodyParser());
//请求日志打印
this.app.useMiddleware([
ReportMiddleware,
//统一异常处理
GlobalExceptionMiddleware,
//预览模式限制修改id<1000的数据
@@ -12,7 +12,7 @@ export class GlobalExceptionMiddleware implements IWebMiddleware {
logger.info('请求开始:', url);
try {
await next();
logger.info('请求完成', url, Date.now() - startTime + 'ms');
logger.info('请求完成:', url, Date.now() - startTime + 'ms');
} catch (err) {
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
ctx.status = 200;
@@ -1,27 +0,0 @@
import { Middleware, IMiddleware } from '@midwayjs/core';
import { NextFunction, Context } from '@midwayjs/koa';
@Middleware()
export class ReportMiddleware implements IMiddleware<Context, NextFunction> {
resolve() {
return async (ctx: Context, next: NextFunction) => {
// 控制器前执行的逻辑
const startTime = Date.now();
// 执行下一个 Web 中间件,最后执行到控制器
// 这里可以拿到下一个中间件或者控制器的返回值
const result = await next();
// 控制器之后执行的逻辑
ctx.logger.info(
`Report in "src/middleware/report.middleware.ts", rt = ${
Date.now() - startTime
}ms`
);
// 返回给上一个中间件的结果
return result;
};
}
static getName(): string {
return 'report';
}
}
@@ -1,24 +0,0 @@
import { Provide } from '@midwayjs/core';
import { IWebMiddleware, IMidwayKoaContext, NextFunction } from '@midwayjs/koa';
import { logger } from '../utils/logger.js';
@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');
};
}
}
@@ -2,6 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { BaseController } from '../../../basic/base-controller.js';
import { Constants } from '../../../basic/constants.js';
import { UserService } from '../../authority/service/user-service.js';
import { getPlusInfo } from '@certd/pipeline';
/**
*/
@@ -24,4 +25,12 @@ export class MineController extends BaseController {
await this.userService.changePassword(userId, body);
return this.ok({});
}
@Post('/plusInfo', { summary: Constants.per.authOnly })
async plusInfo(@Body(ALL) body) {
const info = getPlusInfo();
return this.ok({
...info,
});
}
}
@@ -4,7 +4,7 @@ import { In, Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service.js';
import { PipelineEntity } from '../entity/pipeline.js';
import { PipelineDetail } from '../entity/vo/pipeline-detail.js';
import { Executor, Pipeline, ResultType, RunHistory } from '@certd/pipeline';
import { Executor, isPlus, Pipeline, ResultType, RunHistory } from '@certd/pipeline';
import { AccessService } from './access-service.js';
import { DbStorage } from './db-storage.js';
import { StorageService } from './storage-service.js';
@@ -15,9 +15,10 @@ import { HistoryLogEntity } from '../entity/history-log.js';
import { HistoryLogService } from './history-log-service.js';
import { logger } from '../../../utils/logger.js';
import { EmailService } from '../../basic/service/email-service.js';
import { NeedVIPException } from '../../../basic/exception/vip-exception.js';
const runningTasks: Map<string | number, Executor> = new Map();
const freeCount = 10;
/**
* 证书申请
*/
@@ -47,6 +48,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
return this.repository;
}
async add(bean: PipelineEntity) {
if (!isPlus()) {
const count = await this.repository.count();
if (count >= freeCount) {
throw new NeedVIPException('免费版最多只能创建10个pipeline');
}
}
await super.add(bean);
return bean;
}
async page(query: any, page: { offset: number; limit: number }, order: any, buildQuery: any) {
const result = await super.page(query, page, order, buildQuery);
const pipelineIds: number[] = [];
@@ -93,6 +105,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
async save(bean: PipelineEntity) {
if (!isPlus()) {
const count = await this.repository.count();
if (count >= 10) {
throw new NeedVIPException('免费版最多只能创建10个pipeline');
}
}
await this.clearTriggers(bean.id);
if (bean.content) {
const pipeline = JSON.parse(bean.content);
@@ -0,0 +1,57 @@
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { SysSettingsService } from '../service/sys-settings-service.js';
import { BaseController } from '../../../basic/base-controller.js';
import { appKey, utils, verify } from '@certd/pipeline';
import { SysInstallInfo, SysLicenseInfo } from '../service/models.js';
import { logger } from '../../../utils/logger.js';
/**
*/
@Provide()
@Controller('/api/sys/plus')
export class SysPlusController extends BaseController {
@Inject()
sysSettingsService: SysSettingsService;
@Post('/active', { summary: 'sys:settings:edit' })
async active(@Body(ALL) body) {
const { code } = body;
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
const formData = {
appKey: appKey,
code,
subjectId: installInfo.siteId,
};
const res: any = await utils.http({
url: 'https://api.ai.handsfree.work/activation/active',
method: 'post',
data: formData,
});
if (res.code > 0) {
logger.error('激活失败', res.message);
return this.fail(res.message, 1);
}
const license = res.data.license;
let licenseInfo: SysLicenseInfo = await this.sysSettingsService.getSetting(SysLicenseInfo);
if (!licenseInfo) {
licenseInfo = new SysLicenseInfo();
}
licenseInfo.license = license;
await this.sysSettingsService.saveSetting(licenseInfo);
const verifyRes = await verify({
subjectId: installInfo.siteId,
license,
});
if (!verifyRes.isPlus) {
const message = verifyRes.message || '授权码校验失败';
logger.error(message);
return this.fail(message, 1);
}
return this.ok(res.data);
}
}
@@ -0,0 +1 @@
export * from './plugins/index.js';
@@ -0,0 +1 @@
export * from './plugin-k8s.js';
@@ -0,0 +1,109 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { K8sClient } from '@certd/lib-k8s';
import { K8sAccess } from '../access/index.js';
import { appendTimeSuffix } from '../../plugin-aliyun/utils/index.js';
@IsTaskPlugin({
name: 'DeployToK8SIngress',
title: 'K8S Ingress证书部署',
group: pluginGroups.other.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class K8STestPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '命名空间',
value: 'default',
component: {
placeholder: '命名空间',
},
required: true,
})
namespace!: string;
@TaskInput({
title: 'ingress名称',
value: '',
component: {
placeholder: 'ingress名称',
},
required: true,
helper: '可以传入一个数组',
})
ingressName!: string;
@TaskInput({
title: '保密字典Id',
component: {
placeholder: '保密字典Id',
},
required: true,
})
secretName!: string | string[];
@TaskInput({
title: 'k8s授权',
helper: 'kubeconfig',
component: {
name: 'pi-access-selector',
type: 'k8s',
},
required: true,
})
accessId!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'pi-output-selector',
},
required: true,
})
cert!: CertInfo;
async onInstance() {}
async execute(): Promise<void> {
const access: K8sAccess = await this.accessService.getById(this.accessId);
const k8sClient = new K8sClient({
kubeConfigStr: access.kubeconfig,
logger: this.logger,
});
await this.patchNginxCertSecret({ cert: this.cert, k8sClient });
await utils.sleep(3000); // 停留2秒,等待secret部署完成
}
async patchNginxCertSecret(options: { cert: CertInfo; k8sClient: K8sClient }) {
const { cert, k8sClient } = options;
const crt = cert.crt;
const key = cert.key;
const crtBase64 = Buffer.from(crt).toString('base64');
const keyBase64 = Buffer.from(key).toString('base64');
const { namespace, secretName } = this;
const body: any = {
data: {
'tls.crt': crtBase64,
'tls.key': keyBase64,
},
metadata: {
labels: {
certd: appendTimeSuffix('certd'),
},
},
};
let secretNames: any = secretName;
if (typeof secretName === 'string') {
secretNames = [secretName];
}
for (const secret of secretNames) {
await k8sClient.patchSecret({ namespace, secretName: secret, body });
this.logger.info(`ingress cert Secret已更新:${secret}`);
}
}
}
new K8STestPlugin();