mirror of
https://github.com/certd/certd.git
synced 2026-04-23 19:57:27 +08:00
Merge remote-tracking branch 'origin/v2-dev' into v2-dev
This commit is contained in:
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 优化流水线列表页面、详情页面性能,精简返回数据 ([609ac9c](https://github.com/certd/certd/commit/609ac9c9a2dde605eb09834ae59693c1cb238765))
|
||||
* 支持自动选择校验方式申请证书 ([3f99432](https://github.com/certd/certd/commit/3f9943270cfb12946e38e6272bc5e8d95ad6ab9e))
|
||||
* OpenAPI支持autoApply参数 ([42f4d14](https://github.com/certd/certd/commit/42f4d1477dc791520a874aed56035abcbc8c433b))
|
||||
|
||||
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
DROP TABLE IF EXISTS `cd_domain`;
|
||||
CREATE TABLE `cd_domain`
|
||||
(
|
||||
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
|
||||
`user_id` bigint,
|
||||
`domain` varchar(512),
|
||||
challenge_type varchar(50),
|
||||
dns_provider_type varchar(50),
|
||||
dns_provider_access bigint,
|
||||
http_uploader_type varchar(50),
|
||||
http_uploader_access bigint,
|
||||
http_upload_root_dir varchar(512),
|
||||
`disabled` boolean NOT NULL DEFAULT false,
|
||||
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX `index_domain_user_id` ON `cd_domain` (`user_id`);
|
||||
CREATE INDEX `index_domain_domain` ON `cd_domain` (`domain`);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
DROP TABLE IF EXISTS "cd_domain";
|
||||
CREATE TABLE "cd_domain"
|
||||
(
|
||||
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||
"user_id" bigint,
|
||||
"domain" varchar(512),
|
||||
challenge_type varchar(50),
|
||||
dns_provider_type varchar(50),
|
||||
dns_provider_access bigint,
|
||||
http_uploader_type varchar(50),
|
||||
http_uploader_access bigint,
|
||||
http_upload_root_dir varchar(512),
|
||||
"disabled" boolean NOT NULL DEFAULT (false),
|
||||
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
CREATE INDEX "index_domain_user_id" ON "cd_domain" ("user_id");
|
||||
CREATE INDEX "index_domain_domain" ON "cd_domain" ("domain");
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
DROP TABLE IF EXISTS "cd_domain";
|
||||
CREATE TABLE "cd_domain"
|
||||
(
|
||||
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"user_id" integer,
|
||||
"domain" varchar(512),
|
||||
challenge_type varchar(50),
|
||||
dns_provider_type varchar(50),
|
||||
dns_provider_access bigint,
|
||||
http_uploader_type varchar(50),
|
||||
http_uploader_access bigint,
|
||||
http_upload_root_dir varchar(512),
|
||||
"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_domain_user_id" ON "cd_domain" ("user_id");
|
||||
CREATE INDEX "index_domain_domain" ON "cd_domain" ("domain");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-server",
|
||||
"version": "1.36.5",
|
||||
"version": "1.36.6",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -42,20 +42,20 @@
|
||||
"@aws-sdk/client-cloudfront": "^3.699.0",
|
||||
"@aws-sdk/client-iam": "^3.699.0",
|
||||
"@aws-sdk/client-s3": "^3.705.0",
|
||||
"@certd/acme-client": "^1.36.5",
|
||||
"@certd/basic": "^1.36.5",
|
||||
"@certd/commercial-core": "^1.36.5",
|
||||
"@certd/acme-client": "^1.36.6",
|
||||
"@certd/basic": "^1.36.6",
|
||||
"@certd/commercial-core": "^1.36.6",
|
||||
"@certd/cv4pve-api-javascript": "^8.4.1",
|
||||
"@certd/jdcloud": "^1.36.5",
|
||||
"@certd/lib-huawei": "^1.36.5",
|
||||
"@certd/lib-k8s": "^1.36.5",
|
||||
"@certd/lib-server": "^1.36.5",
|
||||
"@certd/midway-flyway-js": "^1.36.5",
|
||||
"@certd/pipeline": "^1.36.5",
|
||||
"@certd/plugin-cert": "^1.36.5",
|
||||
"@certd/plugin-lib": "^1.36.5",
|
||||
"@certd/plugin-plus": "^1.36.5",
|
||||
"@certd/plus-core": "^1.36.5",
|
||||
"@certd/jdcloud": "^1.36.6",
|
||||
"@certd/lib-huawei": "^1.36.6",
|
||||
"@certd/lib-k8s": "^1.36.6",
|
||||
"@certd/lib-server": "^1.36.6",
|
||||
"@certd/midway-flyway-js": "^1.36.6",
|
||||
"@certd/pipeline": "^1.36.6",
|
||||
"@certd/plugin-cert": "^1.36.6",
|
||||
"@certd/plugin-lib": "^1.36.6",
|
||||
"@certd/plugin-plus": "^1.36.6",
|
||||
"@certd/plus-core": "^1.36.6",
|
||||
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
|
||||
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
|
||||
"@koa/cors": "^5.0.0",
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||
import { CodeException, Constants, EncryptService } from '@certd/lib-server';
|
||||
import { CertInfoService } from '../../../modules/monitor/service/cert-info-service.js';
|
||||
import { CertInfo } from '@certd/plugin-cert';
|
||||
import { OpenKey } from '../../../modules/open/service/open-key-service.js';
|
||||
import { BaseOpenController } from '../base-open-controller.js';
|
||||
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
|
||||
import { CodeException, Constants, EncryptService } from "@certd/lib-server";
|
||||
import { CertInfo } from "@certd/plugin-cert";
|
||||
import { OpenKey } from "../../../modules/open/service/open-key-service.js";
|
||||
import { BaseOpenController } from "../base-open-controller.js";
|
||||
import { CertInfoFacade } from "../../../modules/monitor/facade/cert-info-facade.js";
|
||||
import { merge } from "lodash-es";
|
||||
|
||||
export type CertGetReq = {
|
||||
domains?: string;
|
||||
certId: number;
|
||||
autoApply?:boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -16,7 +18,7 @@ export type CertGetReq = {
|
||||
@Controller('/api/v1/cert')
|
||||
export class OpenCertController extends BaseOpenController {
|
||||
@Inject()
|
||||
certInfoService: CertInfoService;
|
||||
certInfoFacade: CertInfoFacade;
|
||||
@Inject()
|
||||
encryptService: EncryptService;
|
||||
|
||||
@@ -29,10 +31,13 @@ export class OpenCertController extends BaseOpenController {
|
||||
throw new CodeException(Constants.res.openKeyError);
|
||||
}
|
||||
|
||||
const res: CertInfo = await this.certInfoService.getCertInfo({
|
||||
const req = merge({}, bean, query)
|
||||
|
||||
const res: CertInfo = await this.certInfoFacade.getCertInfo({
|
||||
userId,
|
||||
domains: bean.domains || query.domains,
|
||||
certId: bean.certId || query.certId,
|
||||
domains: req.domains,
|
||||
certId: req.certId,
|
||||
autoApply: req.autoApply??false,
|
||||
});
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||
import { Constants, CrudController } from '@certd/lib-server';
|
||||
import {DomainService} from "../../../modules/cert/service/domain-service.js";
|
||||
|
||||
/**
|
||||
* 授权
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/cert/domain')
|
||||
export class DomainController extends CrudController<DomainService> {
|
||||
@Inject()
|
||||
service: DomainService;
|
||||
|
||||
getService(): DomainService {
|
||||
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 domain = body.query.domain;
|
||||
delete body.query.domain;
|
||||
|
||||
const bq = qb => {
|
||||
if (domain) {
|
||||
qb.andWhere('domain like :domain', { domain: `%${domain}%` });
|
||||
}
|
||||
};
|
||||
|
||||
const pageRet = await this.getService().page({
|
||||
query: body.query,
|
||||
page: body.page,
|
||||
sort: body.sort,
|
||||
buildQuery: bq,
|
||||
});
|
||||
return this.ok(pageRet);
|
||||
}
|
||||
|
||||
@Post('/list', { summary: Constants.per.authOnly })
|
||||
async list(@Body(ALL) body: any) {
|
||||
body.query = body.query ?? {};
|
||||
body.query.userId = this.getUserId();
|
||||
const list = await this.getService().list(body);
|
||||
return this.ok(list);
|
||||
}
|
||||
|
||||
@Post('/add', { summary: Constants.per.authOnly })
|
||||
async add(@Body(ALL) bean: any) {
|
||||
bean.userId = this.getUserId();
|
||||
return super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update', { summary: Constants.per.authOnly })
|
||||
async update(@Body(ALL) bean: any) {
|
||||
await this.service.checkUserId(bean.id, this.getUserId());
|
||||
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('/deleteByIds', { summary: Constants.per.authOnly })
|
||||
async deleteByIds(@Body(ALL) body: any) {
|
||||
await this.service.delete(body.ids, {
|
||||
userId: this.getUserId(),
|
||||
});
|
||||
return this.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,4 +101,10 @@ export class AccessController extends CrudController<AccessService> {
|
||||
const res = await this.service.getSimpleInfo(id);
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@Post('/getDictByIds', { summary: Constants.per.authOnly })
|
||||
async getDictByIds(@Body('ids') ids: number[]) {
|
||||
const res = await this.service.getSimpleByIds(ids, this.getUserId());
|
||||
return this.ok(res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import {EmailService} from '../../../modules/basic/service/email-service.js';
|
||||
import {http, HttpRequestConfig, logger, mergeUtils, utils} from '@certd/basic';
|
||||
import {NotificationService} from '../../../modules/pipeline/service/notification-service.js';
|
||||
import {TaskServiceBuilder} from "../../../modules/pipeline/service/task-service-getter.js";
|
||||
import {TaskServiceBuilder} from "../../../modules/pipeline/service/getter/task-service-getter.js";
|
||||
|
||||
@Provide()
|
||||
@Controller('/api/pi/handle')
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||
import { CommonException, Constants, CrudController, PermissionException } from '@certd/lib-server';
|
||||
import { PipelineEntity } from '../../../modules/pipeline/entity/pipeline.js';
|
||||
import { HistoryService } from '../../../modules/pipeline/service/history-service.js';
|
||||
import { HistoryLogService } from '../../../modules/pipeline/service/history-log-service.js';
|
||||
import { HistoryEntity } from '../../../modules/pipeline/entity/history.js';
|
||||
import { HistoryLogEntity } from '../../../modules/pipeline/entity/history-log.js';
|
||||
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
|
||||
import * as fs from 'fs';
|
||||
import { logger } from '@certd/basic';
|
||||
import { AuthService } from '../../../modules/sys/authority/service/auth-service.js';
|
||||
import { SysSettingsService } from '@certd/lib-server';
|
||||
import { In } from 'typeorm';
|
||||
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
|
||||
import { CommonException, Constants, CrudController, PermissionException, SysSettingsService } from "@certd/lib-server";
|
||||
import { PipelineEntity } from "../../../modules/pipeline/entity/pipeline.js";
|
||||
import { HistoryService } from "../../../modules/pipeline/service/history-service.js";
|
||||
import { HistoryLogService } from "../../../modules/pipeline/service/history-log-service.js";
|
||||
import { HistoryEntity } from "../../../modules/pipeline/entity/history.js";
|
||||
import { HistoryLogEntity } from "../../../modules/pipeline/entity/history-log.js";
|
||||
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
|
||||
import * as fs from "fs";
|
||||
import { logger } from "@certd/basic";
|
||||
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
|
||||
import { In } from "typeorm";
|
||||
|
||||
/**
|
||||
* 证书
|
||||
@@ -88,11 +87,30 @@ export class HistoryController extends CrudController<HistoryService> {
|
||||
const buildQuery = qb => {
|
||||
qb.limit(20);
|
||||
};
|
||||
const withDetail = body.withDetail;
|
||||
delete body.withDetail;
|
||||
let select:any = null
|
||||
if (!withDetail) {
|
||||
select = {
|
||||
pipeline: true, // 后面这里改成false
|
||||
id: true,
|
||||
userId: true,
|
||||
pipelineId: true,
|
||||
status: true,
|
||||
// startTime: true,
|
||||
triggerType: true,
|
||||
endTime: true,
|
||||
createTime: true,
|
||||
updateTime: true
|
||||
};
|
||||
}
|
||||
const listRet = await this.getService().list({
|
||||
query: body,
|
||||
sort: { prop: 'id', asc: false },
|
||||
buildQuery,
|
||||
select
|
||||
});
|
||||
|
||||
for (const item of listRet) {
|
||||
if (!item.pipeline) {
|
||||
continue;
|
||||
@@ -100,7 +118,13 @@ export class HistoryController extends CrudController<HistoryService> {
|
||||
const json = JSON.parse(item.pipeline);
|
||||
delete json.stages;
|
||||
item.pipeline = json;
|
||||
|
||||
//@ts-ignore
|
||||
item.version = json.version;
|
||||
item.status = json.status.result
|
||||
delete item.pipeline;
|
||||
}
|
||||
|
||||
return this.ok(listRet);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {ALL, Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core';
|
||||
import {Constants, CrudController} from '@certd/lib-server';
|
||||
import {SubDomainService, SubDomainsGetter} from "../../../modules/pipeline/service/sub-domain-service.js";
|
||||
import {SubDomainService} from "../../../modules/pipeline/service/sub-domain-service.js";
|
||||
import {DomainParser} from '@certd/plugin-cert/dist/dns-provider/domain-parser.js';
|
||||
import { SubDomainsGetter } from '../../../modules/pipeline/service/getter/sub-domain-getter.js';
|
||||
|
||||
/**
|
||||
* 子域名托管
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
/**
|
||||
* 域名管理
|
||||
*/
|
||||
@Entity('cd_domain')
|
||||
export class DomainEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ comment: '用户ID', name: 'user_id' })
|
||||
userId: number;
|
||||
|
||||
@Column({ comment: '主域名', length: 100 })
|
||||
domain: string;
|
||||
|
||||
@Column({ comment: '校验类型', name: 'challenge_type', length: 50 })
|
||||
challengeType : string;
|
||||
|
||||
@Column({ comment: 'DNS提供商', name: 'dns_provider_type', length: 50 })
|
||||
dnsProviderType: string;
|
||||
|
||||
@Column({ comment: 'DNS提供商授权', name: 'dns_provider_access' })
|
||||
dnsProviderAccess: number;
|
||||
|
||||
@Column({ comment: '是否禁用', name: 'disabled' })
|
||||
disabled: boolean;
|
||||
|
||||
|
||||
@Column({ comment: 'http上传类型', name: 'http_uploader_type', length: 50 })
|
||||
httpUploaderType: string;
|
||||
|
||||
@Column({ comment: 'http上传授权', name: 'http_uploader_access' })
|
||||
httpUploaderAccess: number;
|
||||
|
||||
@Column({ comment: 'http上传根目录', name: 'http_upload_root_dir', length: 512 })
|
||||
httpUploadRootDir: string;
|
||||
|
||||
@Column({
|
||||
comment: '创建时间',
|
||||
name: 'create_time',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
comment: '修改时间',
|
||||
name: 'update_time',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core';
|
||||
import {InjectEntityModel} from '@midwayjs/typeorm';
|
||||
import {In, Not, Repository} from 'typeorm';
|
||||
import {AccessService, BaseService} from '@certd/lib-server';
|
||||
import {DomainEntity} from '../entity/domain.js';
|
||||
import {SubDomainService} from "../../pipeline/service/sub-domain-service.js";
|
||||
import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
|
||||
import {DomainVerifiers} from "@certd/plugin-cert";
|
||||
import { SubDomainsGetter } from '../../pipeline/service/getter/sub-domain-getter.js';
|
||||
import { CnameRecordService } from '../../cname/service/cname-record-service.js';
|
||||
import { CnameRecordEntity } from "../../cname/entity/cname-record.js";
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, {allowDowngrade: true})
|
||||
export class DomainService extends BaseService<DomainEntity> {
|
||||
@InjectEntityModel(DomainEntity)
|
||||
repository: Repository<DomainEntity>;
|
||||
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
@Inject()
|
||||
subDomainService: SubDomainService;
|
||||
|
||||
@Inject()
|
||||
cnameRecordService: CnameRecordService;
|
||||
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async add(param) {
|
||||
if (param.userId == null ){
|
||||
throw new Error('userId 不能为空');
|
||||
}
|
||||
if (!param.domain) {
|
||||
throw new Error('domain 不能为空');
|
||||
}
|
||||
const old = await this.repository.findOne({
|
||||
where: {
|
||||
domain: param.domain,
|
||||
userId: param.userId
|
||||
}
|
||||
});
|
||||
if (old) {
|
||||
throw new Error(`域名(${param.domain})不能重复`);
|
||||
}
|
||||
return await super.add(param);
|
||||
}
|
||||
|
||||
async update(param) {
|
||||
if (!param.id) {
|
||||
throw new Error('id 不能为空');
|
||||
}
|
||||
const old = await this.info(param.id)
|
||||
if (!old) {
|
||||
throw new Error('domain记录不存在');
|
||||
}
|
||||
|
||||
const same = await this.repository.findOne({
|
||||
where: {
|
||||
domain: param.domain,
|
||||
userId: old.userId,
|
||||
id: Not(param.id)
|
||||
}
|
||||
});
|
||||
|
||||
if (same) {
|
||||
throw new Error(`域名(${param.domain})不能重复`);
|
||||
}
|
||||
delete param.userId
|
||||
return await super.update(param);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param domains //去除* 且去重之后的域名列表
|
||||
*/
|
||||
async getDomainVerifiers(userId: number, domains: string[]):Promise<DomainVerifiers> {
|
||||
|
||||
const mainDomainMap:Record<string, string> = {}
|
||||
const subDomainGetter = new SubDomainsGetter(userId, this.subDomainService)
|
||||
const domainParser = new DomainParser(subDomainGetter)
|
||||
|
||||
const mainDomains = []
|
||||
for (const domain of domains) {
|
||||
const mainDomain = await domainParser.parse(domain);
|
||||
mainDomainMap[domain] = mainDomain;
|
||||
mainDomains.push(mainDomain)
|
||||
}
|
||||
|
||||
//匹配DNS记录
|
||||
let allDomains = [...domains,...mainDomains]
|
||||
//去重
|
||||
allDomains = [...new Set(allDomains)]
|
||||
|
||||
//从 domain 表中获取配置
|
||||
const domainRecords = await this.find({
|
||||
where: {
|
||||
domain: In(allDomains),
|
||||
userId,
|
||||
disabled:false,
|
||||
}
|
||||
})
|
||||
|
||||
const dnsMap = domainRecords.filter(item=>item.challengeType === 'dns').reduce((pre, item) => {
|
||||
pre[item.domain] = item
|
||||
return pre
|
||||
}, {})
|
||||
|
||||
const httpMap = domainRecords.filter(item=>item.challengeType === 'http').reduce((pre, item) => {
|
||||
pre[item.domain] = item
|
||||
return pre
|
||||
}, {})
|
||||
|
||||
|
||||
//从cname record表中获取配置
|
||||
const cnameRecords = await this.cnameRecordService.find({
|
||||
where: {
|
||||
domain: In(allDomains),
|
||||
userId,
|
||||
status: "valid",
|
||||
}
|
||||
})
|
||||
|
||||
const cnameMap = cnameRecords.reduce((pre, item) => {
|
||||
pre[item.domain] = item
|
||||
return pre
|
||||
}, {})
|
||||
|
||||
//构建域名验证计划
|
||||
const domainVerifiers:DomainVerifiers = {}
|
||||
|
||||
for (const domain of domains) {
|
||||
const mainDomain = mainDomainMap[domain]
|
||||
|
||||
const dnsRecord = dnsMap[mainDomain]
|
||||
if (dnsRecord) {
|
||||
domainVerifiers[domain] = {
|
||||
domain,
|
||||
mainDomain,
|
||||
type: 'dns',
|
||||
dns: {
|
||||
dnsProviderType: dnsRecord.dnsProviderType,
|
||||
dnsProviderAccessId: dnsRecord.dnsProviderAccess
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
const cnameRecord:CnameRecordEntity = cnameMap[domain]
|
||||
if (cnameRecord) {
|
||||
domainVerifiers[domain] = {
|
||||
domain,
|
||||
mainDomain,
|
||||
type: 'cname',
|
||||
cname: {
|
||||
domain: cnameRecord.domain,
|
||||
hostRecord: cnameRecord.hostRecord,
|
||||
recordValue: cnameRecord.recordValue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
const httpRecord = httpMap[domain]
|
||||
if (httpRecord) {
|
||||
domainVerifiers[domain] = {
|
||||
domain,
|
||||
mainDomain,
|
||||
type: 'http',
|
||||
http: {
|
||||
httpUploaderType: httpRecord.httpUploaderType,
|
||||
httpUploaderAccess: httpRecord.httpUploaderAccess,
|
||||
httpUploadRootDir: httpRecord.httpUploadRootDir
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
domainVerifiers[domain] = null
|
||||
}
|
||||
|
||||
return domainVerifiers;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,27 @@
|
||||
import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core';
|
||||
import {InjectEntityModel} from '@midwayjs/typeorm';
|
||||
import {Repository} from 'typeorm';
|
||||
import {AccessService, BaseService, PlusService, ValidateException} from '@certd/lib-server';
|
||||
import {CnameRecordEntity, CnameRecordStatusType} from '../entity/cname-record.js';
|
||||
import {createDnsProvider, IDnsProvider} from '@certd/plugin-cert';
|
||||
import {CnameProvider, CnameRecord} from '@certd/pipeline';
|
||||
import {cache, http, isDev, logger, utils} from '@certd/basic';
|
||||
import {getAuthoritativeDnsResolver, walkTxtRecord} from '@certd/acme-client';
|
||||
import {CnameProviderService} from './cname-provider-service.js';
|
||||
import {CnameProviderEntity} from '../entity/cname-provider.js';
|
||||
import {CommonDnsProvider} from './common-provider.js';
|
||||
import {SubDomainService, SubDomainsGetter} from "../../pipeline/service/sub-domain-service.js";
|
||||
import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
|
||||
import punycode from 'punycode.js'
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import {
|
||||
AccessService,
|
||||
BaseService,
|
||||
PlusService,
|
||||
SysInstallInfo,
|
||||
SysSettingsService,
|
||||
ValidateException
|
||||
} from "@certd/lib-server";
|
||||
import { CnameRecordEntity, CnameRecordStatusType } from "../entity/cname-record.js";
|
||||
import { createDnsProvider, IDnsProvider } from "@certd/plugin-cert";
|
||||
import { CnameProvider, CnameRecord } from "@certd/pipeline";
|
||||
import { cache, http, isDev, logger, utils } from "@certd/basic";
|
||||
import { getAuthoritativeDnsResolver, walkTxtRecord } from "@certd/acme-client";
|
||||
import { CnameProviderService } from "./cname-provider-service.js";
|
||||
import { CnameProviderEntity } from "../entity/cname-provider.js";
|
||||
import { CommonDnsProvider } from "./common-provider.js";
|
||||
import { DomainParser } from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
|
||||
import punycode from "punycode.js";
|
||||
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
|
||||
import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js";
|
||||
|
||||
type CnameCheckCacheValue = {
|
||||
validating: boolean;
|
||||
pass: boolean;
|
||||
@@ -34,6 +43,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
||||
|
||||
@Inject()
|
||||
cnameProviderService: CnameProviderService;
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
@@ -100,7 +111,17 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
||||
}
|
||||
param.hostRecord = hostRecord;
|
||||
|
||||
const cnameKey = utils.id.simpleNanoId().toLowerCase();
|
||||
const randomKey = utils.id.simpleNanoId(6).toLowerCase();
|
||||
|
||||
let userIdHash = ""
|
||||
if(param.cnameProviderId < 0){
|
||||
//公共cname服务
|
||||
userIdHash = utils.hash.md5(`userId${userId}_${randomKey}`).substring(0, 10)
|
||||
}else{
|
||||
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo)
|
||||
userIdHash = utils.hash.md5(`${installInfo.siteId}_${randomKey}`).substring(0, 10)
|
||||
}
|
||||
const cnameKey = `${userIdHash}-${randomKey}`;
|
||||
const safeDomain = param.domain.replaceAll('.', '-');
|
||||
param.recordValue = `${safeDomain}.${cnameKey}.${cnameProvider.domain}`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { CodeException, Constants } from "@certd/lib-server";
|
||||
import { CertInfoEntity } from "../entity/cert-info.js";
|
||||
import { utils } from "@certd/basic";
|
||||
import { PipelineService } from "../../pipeline/service/pipeline-service.js";
|
||||
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
|
||||
import { UserEmailSetting } from "../../mine/service/models.js";
|
||||
import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
|
||||
import { CertInfoService } from "../service/cert-info-service.js";
|
||||
import { DomainService } from "../../cert/service/domain-service.js";
|
||||
import { DomainVerifierGetter } from "../../pipeline/service/getter/domain-verifier-getter.js";
|
||||
|
||||
|
||||
@Provide("CertInfoFacade")
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class CertInfoFacade {
|
||||
|
||||
@Inject()
|
||||
pipelineService: PipelineService;
|
||||
|
||||
@Inject()
|
||||
certInfoService: CertInfoService ;
|
||||
|
||||
@Inject()
|
||||
domainService: DomainService
|
||||
|
||||
@Inject()
|
||||
userSettingsService : UserSettingsService
|
||||
|
||||
async getCertInfo(req: { domains?: string; certId?: number; userId: number,autoApply?:boolean }) {
|
||||
const { domains, certId, userId } = req;
|
||||
if (certId) {
|
||||
return await this.certInfoService.getCertInfoById({ id: certId, userId });
|
||||
}
|
||||
const domainArr = domains.split(',');
|
||||
|
||||
const matchedList = await this.certInfoService.getMatchCertList({domains:domainArr,userId})
|
||||
let matched: CertInfoEntity = null
|
||||
if (matchedList.length === 0 ) {
|
||||
if(req.autoApply === true){
|
||||
//自动申请,先创建自动申请流水线
|
||||
const pipeline:PipelineEntity = await this.createAutoPipeline({domains:domainArr,userId})
|
||||
await this.triggerApplyPipeline({pipelineId:pipeline.id})
|
||||
}else{
|
||||
throw new CodeException(Constants.res.openCertNotFound);
|
||||
}
|
||||
}
|
||||
matched = null;
|
||||
for (const item of matchedList) {
|
||||
if (item.expiresTime>0 && item.expiresTime < new Date().getTime()) {
|
||||
matched = item;
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
if(req.autoApply === true){
|
||||
//如果没有找到有效期内的证书,则自动触发一次申请
|
||||
const first = matchedList[0]
|
||||
await this.triggerApplyPipeline({pipelineId:first.pipelineId})
|
||||
return
|
||||
}else{
|
||||
throw new CodeException(Constants.res.openCertNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
return await this.certInfoService.getCertInfoById({ id: matched.id, userId: userId });
|
||||
}
|
||||
|
||||
async createAutoPipeline(req:{domains:string[],userId:number}){
|
||||
|
||||
const verifierGetter = new DomainVerifierGetter(req.userId, this.domainService)
|
||||
|
||||
const allDomains = []
|
||||
for (const item of req.domains) {
|
||||
allDomains.push(item.replaceAll("*.",""))
|
||||
}
|
||||
const verifiers = await verifierGetter.getVerifiers(allDomains)
|
||||
for (const item of allDomains) {
|
||||
if (!verifiers[item]){
|
||||
throw new CodeException({
|
||||
...Constants.res.openDomainNoVerifier,
|
||||
message:`域名${item}没有配置校验方式,请先在域名管理页面配置`,
|
||||
data:{
|
||||
domain:item
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const userEmailSetting = await this.userSettingsService.getSetting<UserEmailSetting>(req.userId,UserEmailSetting)
|
||||
if(!userEmailSetting.list){
|
||||
throw new CodeException(Constants.res.openEmailNotFound)
|
||||
}
|
||||
const email = userEmailSetting.list[0]
|
||||
|
||||
return await this.pipelineService.createAutoPipeline({
|
||||
domains: req.domains,
|
||||
email,
|
||||
userId: req.userId,
|
||||
from:"OpenAPI"
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async triggerApplyPipeline(req:{pipelineId:number}){
|
||||
//查询流水线状态
|
||||
const status = await this.pipelineService.getStatus(req.pipelineId)
|
||||
if (status != 'running') {
|
||||
await this.pipelineService.trigger(req.pipelineId)
|
||||
await utils.sleep(1000)
|
||||
}
|
||||
throw new CodeException({
|
||||
...Constants.res.openCertApplying,
|
||||
data:{
|
||||
pipelineId:req.pipelineId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
||||
import {BaseService, CodeException, Constants, PageReq} from "@certd/lib-server";
|
||||
import {InjectEntityModel} from "@midwayjs/typeorm";
|
||||
import {MoreThan, Repository} from "typeorm";
|
||||
import {CertInfoEntity} from "../entity/cert-info.js";
|
||||
import {utils} from "@certd/basic";
|
||||
import {CertInfo, CertReader} from "@certd/plugin-cert";
|
||||
import { Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { BaseService, CodeException, Constants, PageReq } from "@certd/lib-server";
|
||||
import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { CertInfoEntity } from "../entity/cert-info.js";
|
||||
import { utils } from "@certd/basic";
|
||||
import { CertInfo, CertReader } from "@certd/plugin-cert";
|
||||
|
||||
export type UploadCertReq = {
|
||||
id?: number;
|
||||
@@ -71,6 +71,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
}
|
||||
|
||||
await this.addOrUpdate(bean);
|
||||
return bean.id
|
||||
}
|
||||
|
||||
async deleteByPipelineId(id: number) {
|
||||
@@ -82,44 +83,28 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
});
|
||||
}
|
||||
|
||||
async getCertInfo(params: { domains?: string; certId?: number; userId: number }) {
|
||||
const { domains, certId, userId } = params;
|
||||
if (certId) {
|
||||
return await this.getCertInfoById({ id: certId, userId });
|
||||
}
|
||||
return await this.getCertInfoByDomains({
|
||||
domains,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
|
||||
private async getCertInfoByDomains(params: { domains: string; userId: number }) {
|
||||
async getMatchCertList(params: { domains: string[]; userId: number }) {
|
||||
const { domains, userId } = params;
|
||||
if (!domains) {
|
||||
throw new CodeException(Constants.res.openCertNotFound);
|
||||
}
|
||||
const domainArr = domains.split(',');
|
||||
|
||||
const list = await this.find({
|
||||
select: {
|
||||
id: true,
|
||||
domains: true,
|
||||
expiresTime:true,
|
||||
pipelineId:true,
|
||||
},
|
||||
where: {
|
||||
userId,
|
||||
expiresTime: MoreThan(new Date().getTime())
|
||||
},
|
||||
});
|
||||
//遍历查找
|
||||
const matched = list.find(item => {
|
||||
return list.filter(item => {
|
||||
const itemDomains = item.domains.split(',');
|
||||
return utils.domain.match(domainArr, itemDomains);
|
||||
return utils.domain.match(domains, itemDomains);
|
||||
});
|
||||
if (!matched) {
|
||||
throw new CodeException(Constants.res.openCertNotFound);
|
||||
}
|
||||
|
||||
return await this.getCertInfoById({ id: matched.id, userId: userId });
|
||||
}
|
||||
|
||||
async getCertInfoById(req: { id: number; userId: number }) {
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import {DomainVerifiers, IDomainVerifierGetter} from "@certd/plugin-cert";
|
||||
import {DomainService} from "../../../cert/service/domain-service.js";
|
||||
|
||||
export class DomainVerifierGetter implements IDomainVerifierGetter {
|
||||
private userId: number;
|
||||
private domainService: DomainService;
|
||||
|
||||
constructor(userId: number, domainService: DomainService) {
|
||||
this.userId = userId;
|
||||
this.domainService = domainService;
|
||||
}
|
||||
|
||||
async getVerifiers(domains: string[]): Promise<DomainVerifiers>{
|
||||
return await this.domainService.getDomainVerifiers(this.userId,domains);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { INotificationService, NotificationSendReq } from '@certd/pipeline';
|
||||
import { NotificationService } from './notification-service.js';
|
||||
import {NotificationService} from "../notification-service.js";
|
||||
|
||||
export class NotificationGetter implements INotificationService {
|
||||
userId: number;
|
||||
@@ -0,0 +1,17 @@
|
||||
import {ISubDomainsGetter} from "@certd/plugin-cert";
|
||||
import {SubDomainService} from "../sub-domain-service.js";
|
||||
|
||||
export class SubDomainsGetter implements ISubDomainsGetter {
|
||||
userId: number;
|
||||
subDomainService: SubDomainService;
|
||||
|
||||
constructor(userId: number, subDomainService: SubDomainService) {
|
||||
this.userId = userId;
|
||||
this.subDomainService = subDomainService;
|
||||
}
|
||||
|
||||
async getSubDomains() {
|
||||
return await this.subDomainService.getListByUserId(this.userId)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { IServiceGetter } from "@certd/pipeline";
|
||||
import { ApplicationContext, IMidwayContainer, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { AccessGetter, AccessService } from "@certd/lib-server";
|
||||
import { CnameProxyService } from "./cname-proxy-service.js";
|
||||
import { NotificationGetter } from "./notification-getter.js";
|
||||
import { NotificationService } from "../notification-service.js";
|
||||
import { CnameRecordService } from "../../../cname/service/cname-record-service.js";
|
||||
import { SubDomainsGetter } from "./sub-domain-getter.js";
|
||||
import { DomainVerifierGetter } from "./domain-verifier-getter.js";
|
||||
import { DomainService } from "../../../cert/service/domain-service.js";
|
||||
import { SubDomainService } from "../sub-domain-service.js";
|
||||
|
||||
export class TaskServiceGetter implements IServiceGetter{
|
||||
private userId: number;
|
||||
private appCtx : IMidwayContainer;
|
||||
constructor(userId:number,appCtx:IMidwayContainer) {
|
||||
this.userId = userId;
|
||||
this.appCtx = appCtx
|
||||
}
|
||||
async get<T>(serviceName: string): Promise<T> {
|
||||
|
||||
if(serviceName === 'subDomainsGetter'){
|
||||
return await this.getSubDomainsGetter() as T
|
||||
} if (serviceName === 'accessService') {
|
||||
return await this.getAccessService() as T
|
||||
} else if (serviceName === 'cnameProxyService') {
|
||||
return await this.getCnameProxyService() as T
|
||||
} else if (serviceName === 'notificationService') {
|
||||
return await this.getNotificationService() as T
|
||||
} else if (serviceName === 'domainVerifierGetter') {
|
||||
return await this.getDomainVerifierGetter() as T
|
||||
}else{
|
||||
throw new Error(`service ${serviceName} not found`)
|
||||
}
|
||||
}
|
||||
|
||||
async getSubDomainsGetter(): Promise<SubDomainsGetter> {
|
||||
const subDomainsService:SubDomainService = await this.appCtx.getAsync("subDomainService")
|
||||
return new SubDomainsGetter(this.userId, subDomainsService)
|
||||
}
|
||||
|
||||
async getAccessService(): Promise<AccessGetter> {
|
||||
const accessService:AccessService = await this.appCtx.getAsync("accessService")
|
||||
return new AccessGetter(this.userId, accessService.getById.bind(accessService));
|
||||
}
|
||||
|
||||
async getCnameProxyService(): Promise<CnameProxyService> {
|
||||
const cnameRecordService:CnameRecordService = await this.appCtx.getAsync("cnameRecordService")
|
||||
return new CnameProxyService(this.userId, cnameRecordService.getWithAccessByDomain.bind(cnameRecordService));
|
||||
}
|
||||
|
||||
async getNotificationService(): Promise<NotificationGetter> {
|
||||
const notificationService:NotificationService = await this.appCtx.getAsync("notificationService")
|
||||
return new NotificationGetter(this.userId, notificationService);
|
||||
}
|
||||
|
||||
async getDomainVerifierGetter(): Promise<DomainVerifierGetter> {
|
||||
const domainService:DomainService = await this.appCtx.getAsync("domainService")
|
||||
return new DomainVerifierGetter(this.userId, domainService);
|
||||
}
|
||||
}
|
||||
export type TaskServiceCreateReq = {
|
||||
userId: number;
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class TaskServiceBuilder {
|
||||
@ApplicationContext()
|
||||
appCtx: IMidwayContainer;
|
||||
|
||||
create(req:TaskServiceCreateReq){
|
||||
const userId = req.userId;
|
||||
return new TaskServiceGetter(userId,this.appCtx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Config, Inject, Provide, Scope, ScopeEnum, sleep} from "@midwayjs/core";
|
||||
import {InjectEntityModel} from "@midwayjs/typeorm";
|
||||
import {In, MoreThan, Repository} from "typeorm";
|
||||
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from "@midwayjs/core";
|
||||
import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||
import { In, MoreThan, Repository } from "typeorm";
|
||||
import {
|
||||
AccessService,
|
||||
BaseService,
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
SysSettingsService,
|
||||
SysSiteInfo
|
||||
} from "@certd/lib-server";
|
||||
import {PipelineEntity} from "../entity/pipeline.js";
|
||||
import {PipelineDetail} from "../entity/vo/pipeline-detail.js";
|
||||
import { PipelineEntity } from "../entity/pipeline.js";
|
||||
import { PipelineDetail } from "../entity/vo/pipeline-detail.js";
|
||||
import {
|
||||
Executor,
|
||||
IAccessService,
|
||||
@@ -25,33 +25,32 @@ import {
|
||||
SysInfo,
|
||||
UserInfo
|
||||
} from "@certd/pipeline";
|
||||
import {DbStorage} from "./db-storage.js";
|
||||
import {StorageService} from "./storage-service.js";
|
||||
import {Cron} from "../../cron/cron.js";
|
||||
import {HistoryService} from "./history-service.js";
|
||||
import {HistoryEntity} from "../entity/history.js";
|
||||
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 {CnameRecordService} from "../../cname/service/cname-record-service.js";
|
||||
import {PluginConfigGetter} from "../../plugin/service/plugin-config-getter.js";
|
||||
import { DbStorage } from "./db-storage.js";
|
||||
import { StorageService } from "./storage-service.js";
|
||||
import { Cron } from "../../cron/cron.js";
|
||||
import { HistoryService } from "./history-service.js";
|
||||
import { HistoryEntity } from "../entity/history.js";
|
||||
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 { CnameRecordService } from "../../cname/service/cname-record-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 {logger} from "@certd/basic";
|
||||
import {UrlService} from "./url-service.js";
|
||||
import {NotificationService} from "./notification-service.js";
|
||||
import {UserSuiteEntity, UserSuiteService} from "@certd/commercial-core";
|
||||
import {CertInfoService} from "../../monitor/service/cert-info-service.js";
|
||||
import {TaskServiceBuilder} from "./task-service-getter.js";
|
||||
import {nanoid} from "nanoid";
|
||||
import {set} from "lodash-es";
|
||||
import { DbAdapter } from "../../db/index.js";
|
||||
import { isComm, isPlus } from "@certd/plus-core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { UrlService } from "./url-service.js";
|
||||
import { NotificationService } from "./notification-service.js";
|
||||
import { UserSuiteEntity, UserSuiteService } from "@certd/commercial-core";
|
||||
import { CertInfoService } from "../../monitor/service/cert-info-service.js";
|
||||
import { TaskServiceBuilder } from "./getter/task-service-getter.js";
|
||||
import { nanoid } from "nanoid";
|
||||
import { set } from "lodash-es";
|
||||
|
||||
const runningTasks: Map<string | number, Executor> = new Map();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 证书申请
|
||||
*/
|
||||
@@ -91,7 +90,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
@Inject()
|
||||
cron: Cron;
|
||||
|
||||
@Config('certd')
|
||||
@Config("certd")
|
||||
private certdConfig: any;
|
||||
|
||||
@Inject()
|
||||
@@ -112,17 +111,33 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
|
||||
async add(bean: PipelineEntity) {
|
||||
bean.status = ResultType.none
|
||||
bean.status = ResultType.none;
|
||||
await this.save(bean);
|
||||
return bean;
|
||||
}
|
||||
|
||||
async page(pageReq: PageReq<PipelineEntity>) {
|
||||
//模版流水线不要被查询出来
|
||||
set(pageReq,"query.isTemplate",false)
|
||||
set(pageReq, "query.isTemplate", false);
|
||||
const result = await super.page(pageReq);
|
||||
await this.fillLastVars(result.records);
|
||||
|
||||
for (const item of result.records) {
|
||||
if (!item.content) {
|
||||
continue;
|
||||
}
|
||||
const pipeline = JSON.parse(item.content);
|
||||
let stepCount = 0;
|
||||
RunnableCollection.each(pipeline.stages, (runnable: any) => {
|
||||
stepCount++;
|
||||
});
|
||||
// @ts-ignore
|
||||
item.stepCount = stepCount;
|
||||
// @ts-ignore
|
||||
item.triggerCount = pipeline.triggers.length;
|
||||
delete item.content;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -132,7 +147,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
for (const record of records) {
|
||||
pipelineIds.push(record.id);
|
||||
recordMap[record.id] = record;
|
||||
record.title = record.title + '';
|
||||
record.title = record.title + "";
|
||||
}
|
||||
if (pipelineIds?.length > 0) {
|
||||
const vars = await this.storageService.findPipelineVars(pipelineIds);
|
||||
@@ -153,17 +168,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
const info = await this.info(pipelineId);
|
||||
if (info && !info.disabled) {
|
||||
const pipeline = JSON.parse(info.content);
|
||||
this.registerTriggers(pipeline,false);
|
||||
this.registerTriggers(pipeline, false);
|
||||
}
|
||||
}
|
||||
|
||||
public async registerTrigger(info:PipelineEntity) {
|
||||
public async registerTrigger(info: PipelineEntity) {
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
if (info && !info.disabled) {
|
||||
const pipeline = JSON.parse(info.content);
|
||||
this.registerTriggers(pipeline,false);
|
||||
this.registerTriggers(pipeline, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +205,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
const isUpdate = bean.id > 0 && old != null;
|
||||
|
||||
|
||||
const pipeline = JSON.parse(bean.content || '{}');
|
||||
const pipeline = JSON.parse(bean.content || "{}");
|
||||
RunnableCollection.initPipelineRunnableType(pipeline);
|
||||
let domains = [];
|
||||
if (pipeline.stages) {
|
||||
RunnableCollection.each(pipeline.stages, (runnable: any) => {
|
||||
if (runnable.runnableType === 'step' && runnable.type.indexOf('CertApply')>=0) {
|
||||
if (runnable.runnableType === "step" && runnable.type.indexOf("CertApply") >= 0) {
|
||||
domains = runnable.input.domains || [];
|
||||
}
|
||||
});
|
||||
@@ -206,7 +221,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
await this.checkMaxPipelineCount(bean, pipeline, domains);
|
||||
}
|
||||
|
||||
if (!bean.status ){
|
||||
if (!bean.status) {
|
||||
bean.status = ResultType.none;
|
||||
}
|
||||
if (!isUpdate) {
|
||||
@@ -217,9 +232,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
await this.doUpdatePipelineJson(bean, pipeline);
|
||||
|
||||
//保存域名信息到certInfo表
|
||||
let fromType = 'pipeline';
|
||||
if (bean.type === 'cert_upload') {
|
||||
fromType = 'upload';
|
||||
let fromType = "pipeline";
|
||||
if (bean.type === "cert_upload") {
|
||||
fromType = "upload";
|
||||
}else if (bean.type === "cert_auto") {
|
||||
fromType = "auto";
|
||||
}
|
||||
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains, fromType);
|
||||
return bean;
|
||||
@@ -230,7 +247,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
* @param bean
|
||||
* @param pipeline
|
||||
*/
|
||||
async doUpdatePipelineJson(bean: PipelineEntity, pipeline:Pipeline) {
|
||||
async doUpdatePipelineJson(bean: PipelineEntity, pipeline: Pipeline) {
|
||||
await this.clearTriggers(bean);
|
||||
if (pipeline.title) {
|
||||
bean.title = pipeline.title;
|
||||
@@ -261,7 +278,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
throw new NeedSuiteException(`对不起,您最多只能添加${userSuite.domainCount.max}个域名,请购买或升级套餐`);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
//非商业版校验用户最大流水线数量
|
||||
const userId = bean.userId;
|
||||
const userIsAdmin = await this.userService.isAdmin(userId);
|
||||
@@ -280,12 +297,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
async foreachPipeline(callback: (pipeline: PipelineEntity) => void) {
|
||||
const idEntityList = await this.repository.find({
|
||||
select: {
|
||||
id: true,
|
||||
id: true
|
||||
},
|
||||
where: {
|
||||
disabled: false,
|
||||
templateId: 0,
|
||||
},
|
||||
templateId: 0
|
||||
}
|
||||
});
|
||||
const ids = idEntityList.map(item => {
|
||||
return item.id;
|
||||
@@ -305,7 +322,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
//分段加载记录
|
||||
for (const idArr of idsSpan) {
|
||||
const list = await this.repository.findBy({
|
||||
id: In(idArr),
|
||||
id: In(idArr)
|
||||
});
|
||||
|
||||
for (const entity of list) {
|
||||
@@ -330,14 +347,14 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
if (onlyAdminUser && entity.userId !== 1) {
|
||||
return;
|
||||
}
|
||||
const pipeline = JSON.parse(entity.content ?? '{}');
|
||||
const pipeline = JSON.parse(entity.content ?? "{}");
|
||||
try {
|
||||
await this.registerTriggers(pipeline, immediateTriggerOnce);
|
||||
} catch (e) {
|
||||
logger.error('加载定时trigger失败:', e);
|
||||
logger.error("加载定时trigger失败:", e);
|
||||
}
|
||||
});
|
||||
logger.info('定时器数量:', this.cron.getTaskSize());
|
||||
logger.info("定时器数量:", this.cron.getTaskSize());
|
||||
}
|
||||
|
||||
async registerTriggers(pipeline?: Pipeline, immediateTriggerOnce = false) {
|
||||
@@ -359,18 +376,18 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
if (isComm()) {
|
||||
await this.checkHasDeployCount(id, entity.userId);
|
||||
}
|
||||
await this.checkUserStatus(entity.userId)
|
||||
await this.checkUserStatus(entity.userId);
|
||||
this.cron.register({
|
||||
name: `pipeline.${id}.trigger.once`,
|
||||
cron: null,
|
||||
job: async () => {
|
||||
logger.info('用户手动启动job');
|
||||
logger.info("用户手动启动job");
|
||||
try {
|
||||
await this.doRun(entity, null, stepId);
|
||||
} catch (e) {
|
||||
logger.error('手动job执行失败:', e);
|
||||
logger.error("手动job执行失败:", e);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -382,7 +399,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
logger.error(e.message);
|
||||
await this.update({
|
||||
id: pipelineId,
|
||||
status: 'no_deploy_count',
|
||||
status: "no_deploy_count"
|
||||
});
|
||||
}
|
||||
throw e;
|
||||
@@ -390,7 +407,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
async delete(id:any) {
|
||||
async delete(id: any) {
|
||||
await this.clearTriggers(id);
|
||||
//TODO 删除storage
|
||||
// const storage = new DbStorage(pipeline.userId, this.storageService);
|
||||
@@ -405,11 +422,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
if (id == null) {
|
||||
return;
|
||||
}
|
||||
let pipeline:PipelineEntity = null
|
||||
if (typeof id === 'number') {
|
||||
let pipeline: PipelineEntity = null;
|
||||
if (typeof id === "number") {
|
||||
pipeline = await this.info(id);
|
||||
}else{
|
||||
pipeline = id
|
||||
} else {
|
||||
pipeline = id;
|
||||
}
|
||||
if (!pipeline) {
|
||||
return;
|
||||
@@ -429,7 +446,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
|
||||
registerCron(pipelineId, trigger) {
|
||||
if (pipelineId == null) {
|
||||
logger.warn('pipelineId为空,无法注册定时任务');
|
||||
logger.warn("pipelineId为空,无法注册定时任务");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -438,11 +455,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
return;
|
||||
}
|
||||
cron = cron.trim();
|
||||
if (cron.startsWith('* *')) {
|
||||
cron = cron.replace('* *', '0 0');
|
||||
if (cron.startsWith("* *")) {
|
||||
cron = cron.replace("* *", "0 0");
|
||||
}
|
||||
if (cron.startsWith('*')) {
|
||||
cron = cron.replace('*', '0');
|
||||
if (cron.startsWith("*")) {
|
||||
cron = cron.replace("*", "0");
|
||||
}
|
||||
const triggerId = trigger.id;
|
||||
const name = this.buildCronKey(pipelineId, triggerId);
|
||||
@@ -451,19 +468,19 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
name,
|
||||
cron,
|
||||
job: async () => {
|
||||
logger.info('定时任务触发:', pipelineId, triggerId);
|
||||
logger.info("定时任务触发:", pipelineId, triggerId);
|
||||
if (pipelineId == null) {
|
||||
logger.warn('pipelineId为空,无法执行');
|
||||
logger.warn("pipelineId为空,无法执行");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.run(pipelineId, triggerId);
|
||||
} catch (e) {
|
||||
logger.error('定时job执行失败:', e);
|
||||
logger.error("定时job执行失败:", e);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
logger.info('当前定时器数量:', this.cron.getTaskSize());
|
||||
logger.info("当前定时器数量:", this.cron.getTaskSize());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,11 +500,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
if (isComm()) {
|
||||
suite = await this.checkHasDeployCount(id, entity.userId);
|
||||
}
|
||||
try{
|
||||
await this.checkUserStatus(entity.userId)
|
||||
}catch (e) {
|
||||
logger.info(e.message)
|
||||
return
|
||||
try {
|
||||
await this.checkUserStatus(entity.userId);
|
||||
} catch (e) {
|
||||
logger.info(e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -505,7 +522,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggerType === 'timer') {
|
||||
if (triggerType === "timer") {
|
||||
if (entity.disabled) {
|
||||
return;
|
||||
}
|
||||
@@ -514,25 +531,25 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
const onChanged = async (history: RunHistory) => {
|
||||
//保存执行历史
|
||||
try {
|
||||
logger.info('保存执行历史:', history.id);
|
||||
logger.info("保存执行历史:", history.id);
|
||||
await this.saveHistory(history);
|
||||
} catch (e) {
|
||||
const pipelineEntity = new PipelineEntity();
|
||||
pipelineEntity.id = id;
|
||||
pipelineEntity.status = 'error';
|
||||
pipelineEntity.status = "error";
|
||||
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
|
||||
await this.update(pipelineEntity);
|
||||
logger.error('保存执行历史失败:', e);
|
||||
logger.error("保存执行历史失败:", e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
const userId = entity.userId;
|
||||
const historyId = await this.historyService.start(entity,triggerType);
|
||||
const historyId = await this.historyService.start(entity, triggerType);
|
||||
const userIsAdmin = await this.userService.isAdmin(userId);
|
||||
const user: UserInfo = {
|
||||
id: userId,
|
||||
role: userIsAdmin ? 'admin' : 'user',
|
||||
role: userIsAdmin ? "admin" : "user"
|
||||
};
|
||||
|
||||
|
||||
@@ -543,11 +560,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
|
||||
const taskServiceGetter = this.taskServiceBuilder.create({
|
||||
userId,
|
||||
})
|
||||
const accessGetter = await taskServiceGetter.get<IAccessService>("accessService")
|
||||
const notificationGetter =await taskServiceGetter.get<INotificationService>("notificationService")
|
||||
const cnameProxyService =await taskServiceGetter.get<ICnameProxyService>("cnameProxyService")
|
||||
userId
|
||||
});
|
||||
const accessGetter = await taskServiceGetter.get<IAccessService>("accessService");
|
||||
const notificationGetter = await taskServiceGetter.get<INotificationService>("notificationService");
|
||||
const cnameProxyService = await taskServiceGetter.get<ICnameProxyService>("cnameProxyService");
|
||||
const executor = new Executor({
|
||||
user,
|
||||
pipeline,
|
||||
@@ -561,7 +578,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
notificationService: notificationGetter,
|
||||
fileRootDir: this.certdConfig.fileRootDir,
|
||||
sysInfo,
|
||||
serviceGetter:taskServiceGetter
|
||||
serviceGetter: taskServiceGetter
|
||||
});
|
||||
try {
|
||||
runningTasks.set(historyId, executor);
|
||||
@@ -579,7 +596,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('执行失败:', e);
|
||||
logger.error("执行失败:", e);
|
||||
// throw e;
|
||||
} finally {
|
||||
runningTasks.delete(historyId);
|
||||
@@ -603,7 +620,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
|
||||
private getTriggerType(triggerId, pipeline) {
|
||||
let triggerType = 'user';
|
||||
let triggerType = "user";
|
||||
if (triggerId != null) {
|
||||
//如果不是手动触发
|
||||
//查找trigger
|
||||
@@ -613,8 +630,8 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
this.cron.remove(this.buildCronKey(pipeline.id, triggerId));
|
||||
triggerType = null;
|
||||
} else {
|
||||
logger.info('timer trigger:' + found.id, found.title, found.cron);
|
||||
triggerType = 'timer';
|
||||
logger.info("timer trigger:" + found.id, found.title, found.cron);
|
||||
triggerType = "timer";
|
||||
}
|
||||
}
|
||||
return triggerType;
|
||||
@@ -637,7 +654,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
//修改pipeline状态
|
||||
const pipelineEntity = new PipelineEntity();
|
||||
pipelineEntity.id = parseInt(history.pipeline.id);
|
||||
pipelineEntity.status = history.pipeline.status.status + '';
|
||||
pipelineEntity.status = history.pipeline.status.result + "";
|
||||
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
|
||||
await this.update(pipelineEntity);
|
||||
|
||||
@@ -661,8 +678,8 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
async count(param: { userId?: any }) {
|
||||
const count = await this.repository.count({
|
||||
where: {
|
||||
userId: param.userId,
|
||||
},
|
||||
userId: param.userId
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
@@ -670,12 +687,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
async statusCount(param: { userId?: any } = {}) {
|
||||
const statusCount = await this.repository
|
||||
.createQueryBuilder()
|
||||
.select('status')
|
||||
.addSelect('count(1)', 'count')
|
||||
.select("status")
|
||||
.addSelect("count(1)", "count")
|
||||
.where({
|
||||
userId: param.userId,
|
||||
userId: param.userId
|
||||
})
|
||||
.groupBy('status')
|
||||
.groupBy("status")
|
||||
.getRawMany();
|
||||
return statusCount;
|
||||
}
|
||||
@@ -685,11 +702,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
status: true,
|
||||
status: true
|
||||
},
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
userId
|
||||
}
|
||||
});
|
||||
await this.fillLastVars(list);
|
||||
list = list.filter(item => {
|
||||
@@ -703,16 +720,16 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
|
||||
async createCountPerDay(param: { days: number } = { days: 7 }) {
|
||||
const todayEnd = dayjs().endOf('day');
|
||||
const todayEnd = dayjs().endOf("day");
|
||||
const result = await this.getRepository()
|
||||
.createQueryBuilder('main')
|
||||
.select(`${this.dbAdapter.date('main.createTime')} AS date`) // 将UNIX时间戳转换为日期
|
||||
.addSelect('COUNT(1) AS count')
|
||||
.createQueryBuilder("main")
|
||||
.select(`${this.dbAdapter.date("main.createTime")} AS date`) // 将UNIX时间戳转换为日期
|
||||
.addSelect("COUNT(1) AS count")
|
||||
.where({
|
||||
// 0点
|
||||
createTime: MoreThan(todayEnd.add(-param.days, 'day').toDate()),
|
||||
createTime: MoreThan(todayEnd.add(-param.days, "day").toDate())
|
||||
})
|
||||
.groupBy('date')
|
||||
.groupBy("date")
|
||||
.getRawMany();
|
||||
|
||||
return result;
|
||||
@@ -729,48 +746,48 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
await this.repository.update(
|
||||
{
|
||||
id: In(ids),
|
||||
userId,
|
||||
userId
|
||||
},
|
||||
{ groupId }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async batchUpdateTrigger(ids: number[], trigger: any, userId: any){
|
||||
async batchUpdateTrigger(ids: number[], trigger: any, userId: any) {
|
||||
|
||||
const list = await this.find({
|
||||
where:{
|
||||
where: {
|
||||
id: In(ids),
|
||||
userId
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
for (const item of list) {
|
||||
const pipeline = JSON.parse(item.content);
|
||||
pipeline.triggers = [{
|
||||
id: nanoid(),
|
||||
title: '定时触发',
|
||||
title: "定时触发",
|
||||
...trigger
|
||||
}]
|
||||
await this.doUpdatePipelineJson(item,pipeline)
|
||||
}];
|
||||
await this.doUpdatePipelineJson(item, pipeline);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any){
|
||||
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any) {
|
||||
|
||||
const list = await this.find({
|
||||
where:{
|
||||
where: {
|
||||
id: In(ids),
|
||||
userId
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
for (const item of list) {
|
||||
const pipeline = JSON.parse(item.content);
|
||||
pipeline.notifications = [{
|
||||
id: nanoid(),
|
||||
title: '通知',
|
||||
title: "通知",
|
||||
/**
|
||||
* type: NotificationType;
|
||||
* when: NotificationWhen[];
|
||||
@@ -781,44 +798,44 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
*/
|
||||
type: "other",
|
||||
...notification
|
||||
}]
|
||||
await this.doUpdatePipelineJson(item,pipeline)
|
||||
}];
|
||||
await this.doUpdatePipelineJson(item, pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
async batchRerun(ids: number[], userId: any) {
|
||||
if (!isPlus()){
|
||||
throw new NeedVIPException("此功能需要升级专业版")
|
||||
if (!isPlus()) {
|
||||
throw new NeedVIPException("此功能需要升级专业版");
|
||||
}
|
||||
|
||||
if (!userId || ids.length === 0) {
|
||||
return;
|
||||
}
|
||||
const list = await this.repository.find({
|
||||
select:{
|
||||
id:true
|
||||
select: {
|
||||
id: true
|
||||
},
|
||||
where:{
|
||||
where: {
|
||||
id: In(ids),
|
||||
userId
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
ids = list.map(item=>item.id)
|
||||
ids = list.map(item => item.id);
|
||||
|
||||
//异步执行
|
||||
this.startBatchRerun(ids)
|
||||
this.startBatchRerun(ids);
|
||||
}
|
||||
|
||||
async startBatchRerun(ids: number[]){
|
||||
async startBatchRerun(ids: number[]) {
|
||||
//20条一批
|
||||
const batchSize = 20;
|
||||
for (let i = 0; i < ids.length; i += batchSize) {
|
||||
const batchIds = ids.slice(i, i + batchSize);
|
||||
const batchPromises = batchIds.map(async (id)=>{
|
||||
await this.run(id,null,"ALL")
|
||||
const batchPromises = batchIds.map(async (id) => {
|
||||
await this.run(id, null, "ALL");
|
||||
});
|
||||
await Promise.all(batchPromises)
|
||||
await Promise.all(batchPromises);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,35 +848,132 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
return await this.repository.find({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
title: true
|
||||
},
|
||||
where: {
|
||||
id: In(pipelineIds),
|
||||
userId,
|
||||
},
|
||||
userId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async checkUserStatus(userId: number) {
|
||||
const userEntity = await this.userService.info(userId);
|
||||
if(userEntity == null){
|
||||
throw new Error('用户不存在');
|
||||
if (userEntity == null) {
|
||||
throw new Error("用户不存在");
|
||||
}
|
||||
if(userEntity.status === 0){
|
||||
const message = `账户${userId}已被禁用,禁止运行流水线`
|
||||
throw new Error(message)
|
||||
if (userEntity.status === 0) {
|
||||
const message = `账户${userId}已被禁用,禁止运行流水线`;
|
||||
throw new Error(message);
|
||||
}
|
||||
const sysPublic = await this.sysSettingsService.getPublicSettings()
|
||||
if(isPlus() && sysPublic.userValidTimeEnabled === true){
|
||||
const sysPublic = await this.sysSettingsService.getPublicSettings();
|
||||
if (isPlus() && sysPublic.userValidTimeEnabled === true) {
|
||||
//校验用户有效期是否设置
|
||||
if(userEntity.validTime!= null && userEntity.validTime > 0){
|
||||
if(userEntity.validTime < new Date().getTime()){
|
||||
if (userEntity.validTime != null && userEntity.validTime > 0) {
|
||||
if (userEntity.validTime < new Date().getTime()) {
|
||||
//用户已过期
|
||||
const message = `账户${userId}已过有效期,禁止运行流水线`
|
||||
throw new Error(message)
|
||||
const message = `账户${userId}已过有效期,禁止运行流水线`;
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createAutoPipeline(req: { domains: string[]; email: string; userId: number ,from:string}) {
|
||||
|
||||
const randomHour = Math.floor(Math.random() * 6);
|
||||
const randomMin = Math.floor(Math.random() * 60);
|
||||
const randomCron = `0 ${randomMin} ${randomHour} * * *`;
|
||||
|
||||
let pipeline: any = {
|
||||
title: req.domains[0] + `证书自动申请【${req.from??"OpenAPI"}】`,
|
||||
runnableType: "pipeline",
|
||||
triggers: [
|
||||
{
|
||||
id: nanoid(),
|
||||
title: "定时触发",
|
||||
props:{
|
||||
cron: randomCron,
|
||||
},
|
||||
type: "timer"
|
||||
}
|
||||
],
|
||||
notifications: [
|
||||
{
|
||||
id: nanoid(),
|
||||
type: "custom",
|
||||
when: ["error", "turnToSuccess", "success"],
|
||||
notificationId: 0,
|
||||
title: "默认通知",
|
||||
}
|
||||
],
|
||||
stages: [
|
||||
{
|
||||
id: nanoid(),
|
||||
title: "证书申请阶段",
|
||||
maxTaskCount: 1,
|
||||
runnableType: "stage",
|
||||
tasks: [
|
||||
{
|
||||
id: nanoid(),
|
||||
title: "证书申请任务",
|
||||
runnableType: "task",
|
||||
steps: [
|
||||
{
|
||||
id: nanoid(),
|
||||
title: "申请证书",
|
||||
runnableType: "step",
|
||||
input: {
|
||||
renewDays: 35,
|
||||
domains: req.domains,
|
||||
email: req.email,
|
||||
"challengeType": "auto",
|
||||
"sslProvider": "letsencrypt",
|
||||
"privateKeyType": "rsa_2048",
|
||||
"certProfile": "classic",
|
||||
"useProxy": false,
|
||||
"skipLocalVerify": false,
|
||||
"maxCheckRetryCount": 20,
|
||||
"waitDnsDiffuseTime": 30,
|
||||
"pfxArgs": "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES",
|
||||
"successNotify": true
|
||||
},
|
||||
strategy: {
|
||||
runStrategy: 0 // 正常执行
|
||||
},
|
||||
type: "CertApply"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const bean = new PipelineEntity();
|
||||
bean.title = pipeline.title;
|
||||
bean.content = JSON.stringify(pipeline);
|
||||
bean.userId = req.userId;
|
||||
bean.status = "none";
|
||||
bean.type = "cert_auto";
|
||||
bean.disabled = false
|
||||
bean.keepHistoryCount = 30
|
||||
await this.save(bean)
|
||||
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
async getStatus(pipelineId: number) {
|
||||
const res = await this.repository.findOne({
|
||||
select: {
|
||||
status: true
|
||||
},
|
||||
where: {
|
||||
id: pipelineId
|
||||
}
|
||||
});
|
||||
return res?.status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {InjectEntityModel} from '@midwayjs/typeorm';
|
||||
import {Repository} from 'typeorm';
|
||||
import {SubDomainEntity} from '../entity/sub-domain.js';
|
||||
import {EmailService} from '../../basic/service/email-service.js';
|
||||
import {ISubDomainsGetter} from "@certd/plugin-cert";
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
@@ -38,21 +37,3 @@ export class SubDomainService extends BaseService<SubDomainEntity> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export class SubDomainsGetter implements ISubDomainsGetter {
|
||||
userId: number;
|
||||
subDomainService: SubDomainService;
|
||||
|
||||
constructor(userId: number, subDomainService: SubDomainService) {
|
||||
this.userId = userId;
|
||||
this.subDomainService = subDomainService;
|
||||
}
|
||||
|
||||
async getSubDomains() {
|
||||
return await this.subDomainService.getListByUserId(this.userId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import {IServiceGetter} from "@certd/pipeline";
|
||||
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
||||
import {SubDomainService, SubDomainsGetter} from "./sub-domain-service.js";
|
||||
import {AccessGetter, AccessService} from "@certd/lib-server";
|
||||
import {CnameProxyService} from "./cname-proxy-service.js";
|
||||
import {NotificationGetter} from "./notification-getter.js";
|
||||
import {NotificationService} from "./notification-service.js";
|
||||
import {CnameRecordService} from "../../cname/service/cname-record-service.js";
|
||||
|
||||
export class TaskServiceGetter implements IServiceGetter{
|
||||
serviceContainer:Record<string, any>;
|
||||
constructor(serviceContainer:Record<string, any>) {
|
||||
this.serviceContainer = serviceContainer;
|
||||
}
|
||||
async get<T>(serviceName: string): Promise<T> {
|
||||
const ret = this.serviceContainer[serviceName] as T;
|
||||
if(!ret){
|
||||
throw new Error(`service ${serviceName} not found`)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
export type TaskServiceCreateReq = {
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export type TaskServiceContainer = {
|
||||
subDomainsGetter:SubDomainsGetter;
|
||||
accessService: AccessGetter;
|
||||
cnameProxyService: CnameProxyService;
|
||||
notificationService: NotificationGetter;
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class TaskServiceBuilder {
|
||||
|
||||
@Inject()
|
||||
subDomainService: SubDomainService;
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
@Inject()
|
||||
cnameRecordService: CnameRecordService;
|
||||
@Inject()
|
||||
notificationService: NotificationService;
|
||||
|
||||
|
||||
create(req:TaskServiceCreateReq){
|
||||
|
||||
const userId = req.userId;
|
||||
const accessGetter = new AccessGetter(userId, this.accessService.getById.bind(this.accessService));
|
||||
const cnameProxyService = new CnameProxyService(userId, this.cnameRecordService.getWithAccessByDomain.bind(this.cnameRecordService));
|
||||
const notificationGetter = new NotificationGetter(userId, this.notificationService);
|
||||
|
||||
const serviceContainer:TaskServiceContainer = {
|
||||
subDomainsGetter:new SubDomainsGetter(req.userId, this.subDomainService),
|
||||
accessService: accessGetter,
|
||||
cnameProxyService:cnameProxyService,
|
||||
notificationService:notificationGetter
|
||||
}
|
||||
return new TaskServiceGetter(serviceContainer)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user