Merge branch 'certd:v2' into v2

This commit is contained in:
origami
2024-11-16 23:32:28 +08:00
committed by GitHub
289 changed files with 2673 additions and 7088 deletions
+1
View File
@@ -0,0 +1 @@
LEGO_VERSION=4.19.2
+2 -9
View File
@@ -22,18 +22,11 @@ typeorm:
default:
database: './data/db-comm.sqlite'
#plus:
# server:
# baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
#
#account:
# server:
# baseUrl: 'https://ai.handsfree.work/subject'
#PLUS_SERVER_BASE_URL=http://127.0.0.1:11007
plus:
server:
baseUrls: ['http://127.0.0.1:11007']
account:
server:
@@ -22,13 +22,6 @@ typeorm:
default:
database: './data/db-comm-pro.sqlite'
plus:
server:
baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
account:
server:
baseUrl: 'https://ai.handsfree.work/subject'
#
#plus:
+1 -11
View File
@@ -9,19 +9,9 @@
# dataSource:
# default:
# database: './data/db.sqlite'
plus:
server:
baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
account:
server:
baseUrl: 'https://ai.handsfree.work/subject'
baseUrl: 'https://app.handfree.work/subject'
#plus:
# server:
# baseUrls: ['http://127.0.0.1:11007']
#
#account:
# server:
# baseUrl: 'http://127.0.0.1:1017/subject'
+20
View File
@@ -0,0 +1,20 @@
# key: ./data/ssl/cert.key
# cert: ./data/ssl/cert.crt
#plus:
# server:
# baseUrl: 'http://127.0.0.1:11007'
typeorm:
dataSource:
default:
database: './data/db-new.sqlite'
#plus:
# server:
# baseUrls: ['http://127.0.0.1:11007']
#
#account:
# server:
# baseUrl: 'http://127.0.0.1:1017/subject'
@@ -0,0 +1,29 @@
# key: ./data/ssl/cert.key
# cert: ./data/ssl/cert.crt
#plus:
# server:
# baseUrl: 'http://127.0.0.1:11007'
#flyway:
# scriptDir: './db/migration-pg'
#typeorm:
# dataSource:
# default:
# type: postgres
# host: localhost
# port: 5433
# username: postgres
# password: root
# database: postgres
typeorm:
dataSource:
default:
database: './data/db-plus-dev-1.sqlite'
# plus server: 'http://127.0.0.1:11007'
account:
server:
baseUrl: 'http://127.0.0.1:1017/subject'
@@ -7,9 +7,6 @@ typeorm:
logging: false
plus:
server:
baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
account:
server:
baseUrl: 'https://ai.handsfree.work/subject'
baseUrl: 'https://app.handfree.work/subject'
+23
View File
@@ -0,0 +1,23 @@
{
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"prettier"
],
"env": {
"mocha": true
},
"rules": {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
tools/** filter=lfs diff=lfs merge=lfs -text
+2 -3
View File
@@ -19,6 +19,5 @@ run/
.clinic
.env.pgpl.yaml
tools/windows/*
!tools/windows/*.zip
tools/lego/*
!tools/lego/readme.md
+38
View File
@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.4](https://github.com/certd/certd/compare/v1.27.3...v1.27.4) (2024-11-14)
### Performance Improvements
* 公共cname服务支持关闭 ([f4ae512](https://github.com/certd/certd/commit/f4ae5125dc4cd97816976779cb3586b5ee78947e))
## [1.27.3](https://github.com/certd/certd/compare/v1.27.2...v1.27.3) (2024-11-13)
### Bug Fixes
* 修复偶发性cname一直验证超时的bug ([d2ce72e](https://github.com/certd/certd/commit/d2ce72e4aaacdf726ba8b91fcd71db40a27714ba))
* 修复邮件配置,忽略证书校验设置不生效的bug ([66a9690](https://github.com/certd/certd/commit/66a9690dc958732e1b3c672d965db502296446f9))
### Performance Improvements
* 优化上传到主机插 路径选择,根据证书格式显示 ([8c3f86c](https://github.com/certd/certd/commit/8c3f86c6909ed91f48bb2880e78834e22f6f6a29))
## [1.27.2](https://github.com/certd/certd/compare/v1.27.1...v1.27.2) (2024-11-08)
### Bug Fixes
* 修复删除腾讯云过期证书时间判断上的bug,导致已过期仍然没有删除证书 ([1ba1007](https://github.com/certd/certd/commit/1ba10072615015d91b81fc56a3b01dae6a2ae9d1))
### Performance Improvements
* 优化部署到阿里云CDN插件,支持多域名,更易用 ([80c500f](https://github.com/certd/certd/commit/80c500f618b169a1f64c57fe442242a4d0d9d833))
* 优化流水线页面切换回来不丢失查询条件 ([4dcf6e8](https://github.com/certd/certd/commit/4dcf6e87bc5f7657ce8a56c5331e8723a0fee8ee))
* 支持公共cname服务 ([3c919ee](https://github.com/certd/certd/commit/3c919ee5d1aef5d26cf3620a7c49d920786bc941))
* 执行历史支持点击查看流水线详情 ([8968639](https://github.com/certd/certd/commit/89686399f90058835435b92872fc236fac990148))
* 专业版7天试用 ([c58250e](https://github.com/certd/certd/commit/c58250e1f065a9bd8b4e82acc1df754504c0010c))
## [1.27.1](https://github.com/certd/certd/compare/v1.27.0...v1.27.1) (2024-11-04)
### Performance Improvements
* 优化时间选择器,自动填写分钟和秒钟 ([396dc34](https://github.com/certd/certd/commit/396dc34a841c7d016b033736afdba8366fb2d211))
* cname 域名映射记录可读性优化 ([b1117ed](https://github.com/certd/certd/commit/b1117ed54a3ef015752999324ff72b821ef5e4b9))
# [1.27.0](https://github.com/certd/certd/compare/v1.26.16...v1.27.0) (2024-10-31)
### Bug Fixes
+19 -17
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.27.0",
"version": "1.27.4",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -10,8 +10,9 @@
"commdev": "cross-env NODE_ENV=commdev mwtsc --watch --run @midwayjs/mock/app",
"commpro": "cross-env NODE_ENV=commpro mwtsc --watch --run @midwayjs/mock/app",
"pgdev": "cross-env NODE_ENV=pgdev mwtsc --watch --run @midwayjs/mock/app",
"local-plus": "cross-env NODE_ENV=localplus mwtsc --watch --run @midwayjs/mock/app",
"pgpl": "cross-env NODE_ENV=pgpl mwtsc --watch --run @midwayjs/mock/app",
"dev-new": "npm run rm-db-new && cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app",
"dev-new": "cross-env NODE_ENV=devnew mwtsc --watch --run @midwayjs/mock/app",
"rm-db-new": "rimraf ./data/db-new.sqlite",
"test": "cross-env NODE_ENV=unittest mocha",
"cov": "cross-env c8 --all --reporter=text --reporter=lcovonly npm run test",
@@ -23,21 +24,22 @@
"build-on-docker": "node ./before-build.js && npm run build",
"up-mw-deps": "npx midway-version -u -w",
"heap": "cross-env NODE_ENV=pgpl clinic heapprofiler -- node ./bootstrap.js",
"flame": "clinic flame -- node ./bootstrap.js"
"flame": "clinic flame -- node ./bootstrap.js",
"tsc": "tsc --skipLibCheck"
},
"dependencies": {
"@alicloud/pop-core": "^1.7.10",
"@certd/acme-client": "^1.27.0",
"@certd/commercial-core": "^1.27.0",
"@certd/lib-huawei": "^1.27.0",
"@certd/lib-jdcloud": "^1.27.0",
"@certd/lib-k8s": "^1.27.0",
"@certd/lib-server": "^1.27.0",
"@certd/midway-flyway-js": "^1.27.0",
"@certd/pipeline": "^1.27.0",
"@certd/plugin-cert": "^1.27.0",
"@certd/plugin-plus": "^1.27.0",
"@certd/plus-core": "^1.27.0",
"@certd/acme-client": "^1.27.4",
"@certd/basic": "^1.27.4",
"@certd/commercial-core": "^1.27.4",
"@certd/lib-huawei": "^1.27.4",
"@certd/lib-k8s": "^1.27.4",
"@certd/lib-server": "^1.27.4",
"@certd/midway-flyway-js": "^1.27.4",
"@certd/pipeline": "^1.27.4",
"@certd/plugin-cert": "^1.27.4",
"@certd/plugin-plus": "^1.27.4",
"@certd/plus-core": "^1.27.4",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
"@koa/cors": "^5.0.0",
@@ -84,7 +86,7 @@
"qiniu": "^7.12.0",
"querystring": "^0.2.1",
"reflect-metadata": "^0.2.2",
"rimraf": "^6.0.1",
"rimraf": "^5.0.5",
"socks": "^2.8.3",
"socks-proxy-agent": "^8.0.4",
"ssh2": "^1.15.0",
@@ -107,9 +109,9 @@
"@types/ssh2": "^1.15.0",
"c8": "^10.1.2",
"mocha": "^10.2.0",
"prettier": "^3.3.3",
"prettier": "^2.8.8",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"engines": {
@@ -10,7 +10,7 @@ import { UserEntity } from '../modules/sys/authority/entity/user.js';
import { PipelineEntity } from '../modules/pipeline/entity/pipeline.js';
//import { logger } from '../utils/logger';
// load .env file in process.cwd
import { mergeConfig } from './loader.js';
import { loadDotEnv, mergeConfig } from './loader.js';
import { libServerEntities } from '@certd/lib-server';
import { commercialEntities } from '@certd/commercial-core';
import { tmpdir } from 'node:os';
@@ -123,6 +123,8 @@ const development = {
contactLink: '',
},
} as MidwayConfig;
loadDotEnv();
mergeConfig(development, 'development');
mergeConfig(development, env);
+12 -1
View File
@@ -2,7 +2,7 @@ import path from 'path';
import * as _ from 'lodash-es';
import yaml from 'js-yaml';
import fs from 'fs';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
function parseEnv(defaultConfig: any) {
const config = {};
@@ -46,3 +46,14 @@ export function mergeConfig(config: any, envType: string) {
}
return config;
}
export function loadDotEnv() {
const envStr = fs.readFileSync('.env').toString();
envStr.split('\n').forEach(line => {
const [key, value] = line.trim().split('=');
const oldValue = process.env[key];
if (!oldValue) {
process.env[key] = value;
}
});
}
@@ -12,7 +12,7 @@ import cors from '@koa/cors';
import { GlobalExceptionMiddleware } from './middleware/global-exception.js';
import { PreviewMiddleware } from './middleware/preview.js';
import { AuthorityMiddleware } from './middleware/authority.js';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import { ResetPasswdMiddleware } from './middleware/reset-passwd/middleware.js';
import DefaultConfig from './config/config.default.js';
import * as libServer from '@certd/lib-server';
@@ -1,6 +1,6 @@
import { Controller, Get, Provide } from '@midwayjs/core';
import { BaseController, Constants } from '@certd/lib-server';
import { http, logger } from '@certd/pipeline';
import { http, logger } from '@certd/basic';
/**
*/
@Provide()
@@ -2,7 +2,7 @@ import { Controller, Fields, Files, Get, Inject, Post, Provide, Query } from '@m
import { BaseController, Constants, FileService, UploadFileItem, uploadTmpFileCacheKey } from '@certd/lib-server';
import send from 'koa-send';
import { nanoid } from 'nanoid';
import { cache } from '@certd/pipeline';
import { cache } from '@certd/basic';
import { UploadFileInfo } from '@midwayjs/upload';
/**
@@ -41,6 +41,7 @@ export class FileController extends BaseController {
}
const filePath = this.fileService.getFile(key, userId);
this.ctx.response.attachment(filePath);
this.ctx.response.set('Cache-Control', 'public,max-age=2592000');
await send(this.ctx, filePath);
}
}
@@ -1,5 +1,5 @@
import { Controller, Get, Inject, MidwayEnvironmentService, Provide } from '@midwayjs/core';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import { Constants } from '@certd/lib-server';
@Provide()
@@ -1,6 +1,6 @@
import { Config, Controller, Get, Inject, Provide } from '@midwayjs/core';
import { BaseController, Constants, SysHeaderMenus, SysInstallInfo, SysPublicSettings, SysSettingsService, SysSiteEnv, SysSiteInfo } from '@certd/lib-server';
import { AppKey, getPlusInfo } from '@certd/pipeline';
import { AppKey, getPlusInfo } from '@certd/plus-core';
/**
*/
@@ -21,7 +21,7 @@ export class CnameProviderController extends BaseController {
@Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body: any) {
body.userId = this.getUserId();
const res = await this.providerService.find({});
const res = await this.providerService.list({});
return this.ok(res);
}
}
@@ -40,7 +40,8 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
@Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body: any) {
body.userId = this.getUserId();
return super.list(body);
const list = await this.getService().list(body);
return this.ok(list);
}
@Post('/add', { summary: Constants.per.authOnly })
@@ -1,21 +1,11 @@
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { Constants } from '@certd/lib-server';
import {
AccessRequestHandleReq,
http,
ITaskPlugin,
logger,
mergeUtils,
newAccess,
pluginRegistry,
PluginRequestHandleReq,
TaskInstanceContext,
utils,
} from '@certd/pipeline';
import { AccessRequestHandleReq, ITaskPlugin, newAccess, pluginRegistry, PluginRequestHandleReq, TaskInstanceContext } from '@certd/pipeline';
import { BaseController } from '@certd/lib-server';
import { AccessService } from '../../modules/pipeline/service/access-service.js';
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';
@Provide()
@Controller('/api/pi/handle')
@@ -64,12 +54,21 @@ export class HandleController extends BaseController {
const accessGetter = new AccessGetter(userId, this.accessService.getById.bind(this.accessService));
const download = async (config: HttpRequestConfig, savePath: string) => {
await utils.download({
http,
logger,
config,
savePath,
});
};
//@ts-ignore
const taskCtx: TaskInstanceContext = {
pipeline: undefined,
step: undefined,
lastStatus: undefined,
http,
download,
logger: logger,
inputChanged: true,
accessService: accessGetter,
@@ -7,7 +7,7 @@ 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/pipeline';
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';
@@ -37,7 +37,7 @@ export class PipelineController extends CrudController<PipelineService> {
const buildQuery = qb => {
if (title) {
qb.andWhere('title like :title', { title: `%${title}%` });
qb.andWhere('title like :title', { title: `%${title}%` }).orWhere('content like :content', { content: `%${title}%` });
}
};
if (!body.sort || !body.sort?.prop) {
@@ -1,7 +1,7 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { AccessService } from '../../../modules/pipeline/service/access-service.js';
import { AccessController } from '../../pipeline/access-controller.js';
import { checkComm } from '@certd/pipeline';
import { checkComm } from '@certd/plus-core';
/**
* 授权
@@ -1,8 +1,5 @@
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { BaseController, PlusService } from '@certd/lib-server';
import { AppKey } from '@certd/pipeline';
import { SysSettingsService } from '@certd/lib-server';
import { SysInstallInfo } from '@certd/lib-server';
import { BaseController, PlusService, SysInstallInfo, SysSettingsService } from '@certd/lib-server';
export type PreBindUserReq = {
userId: number;
@@ -23,18 +20,8 @@ export class BasicController extends BaseController {
@Post('/preBindUser', { summary: 'sys:settings:edit' })
public async preBindUser(@Body(ALL) body: PreBindUserReq) {
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
// 设置缓存内容
await this.plusService.requestWithoutSign({
url: '/activation/subject/preBind',
method: 'POST',
data: {
userId: body.userId,
appKey: AppKey,
subjectId: installInfo.siteId,
},
});
await this.plusService.userPreBind(body.userId);
return this.ok({});
}
@@ -1,6 +1,5 @@
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { BaseController, PlusService, SysInstallInfo, SysSettingsService } from '@certd/lib-server';
import { AppKey, logger } from '@certd/pipeline';
/**
*/
@@ -16,23 +15,8 @@ export class SysPlusController extends BaseController {
@Post('/active', { summary: 'sys:settings:edit' })
async active(@Body(ALL) body) {
const { code } = body;
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
const siteId = installInfo.siteId;
const formData = {
appKey: AppKey,
code,
subjectId: siteId,
};
const res: any = await this.plusService.active(formData);
if (res.code > 0) {
logger.error('激活失败', res.message);
return this.fail(res.message, 1);
}
const license = res.data.license;
await this.plusService.updateLicense(license);
await this.plusService.active(code);
return this.ok(true);
}
@@ -41,7 +25,7 @@ export class SysPlusController extends BaseController {
const { url } = body;
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
await this.plusService.bindUrl(installInfo.siteId, url);
await this.plusService.bindUrl(url);
installInfo.bindUrl = url;
await this.sysSettingsService.saveSetting(installInfo);
@@ -4,7 +4,8 @@ import * as _ from 'lodash-es';
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
import { UserSettingsService } from '../../../modules/mine/service/user-settings-service.js';
import { getEmailSettings } from '../../../modules/sys/settings/fix.js';
import { http, logger } from '@certd/pipeline';
import { http, logger } from '@certd/basic';
import { merge } from 'lodash-es';
/**
*/
@@ -77,6 +78,14 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
return this.ok(conf);
}
@Post('/saveEmailSettings', { summary: 'sys:settings:edit' })
async saveEmailSettings(@Body(ALL) body) {
const conf = await getEmailSettings(this.service, this.userSettingsService);
merge(conf, body);
await this.service.saveSetting(conf);
return this.ok(conf);
}
@Post('/getSysSettings', { summary: 'sys:settings:edit' })
async getSysSettings() {
const publicSettings = await this.service.getPublicSettings();
@@ -2,7 +2,7 @@ import { Init, Inject, MidwayWebRouterService, Provide, Scope, ScopeEnum } from
import { IMidwayKoaContext, IWebMiddleware, NextFunction } from '@midwayjs/koa';
import jwt from 'jsonwebtoken';
import { Constants } from '@certd/lib-server';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import { AuthService } from '../modules/sys/authority/service/auth-service.js';
import { SysSettingsService } from '@certd/lib-server';
import { SysPrivateSettings } from '@certd/lib-server';
@@ -1,6 +1,6 @@
import { Provide } from '@midwayjs/core';
import { IWebMiddleware, IMidwayKoaContext, NextFunction } from '@midwayjs/koa';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import { Result } from '@certd/lib-server';
@Provide()
@@ -2,7 +2,7 @@ import { Autoload, Config, Init, Inject, Provide, Scope, ScopeEnum } from '@midw
import { IMidwayKoaContext, IWebMiddleware, NextFunction } from '@midwayjs/koa';
import { CommonException } from '@certd/lib-server';
import { UserService } from '../../modules/sys/authority/service/user-service.js';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
/**
* 重置密码模式
@@ -1,5 +1,5 @@
import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import { UserService } from '../sys/authority/service/user-service.js';
import { PlusService, SysInstallInfo, SysPrivateSettings, SysSettingsService } from '@certd/lib-server';
import { nanoid } from 'nanoid';
@@ -1,6 +1,6 @@
import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
import { PipelineService } from '../pipeline/service/pipeline-service.js';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import { SysSettingsService } from '@certd/lib-server';
@Autoload()
@@ -1,5 +1,7 @@
import { App, Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
import { getPlusInfo, isPlus, logger } from '@certd/pipeline';
import { getPlusInfo, isPlus } from '@certd/plus-core';
import { logger } from '@certd/basic';
import { SysInstallInfo, SysSettingsService } from '@certd/lib-server';
import { getVersion } from '../../utils/version.js';
import dayjs from 'dayjs';
@@ -1,4 +1,4 @@
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import fs from 'fs';
// @ts-ignore
import forge from 'node-forge';
@@ -2,7 +2,7 @@ import https from 'node:https';
import fs from 'fs';
import { Application } from '@midwayjs/koa';
import { createSelfCertificate } from './self-certificate.js';
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
export type HttpsServerOptions = {
enabled: boolean;
@@ -1,6 +1,10 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import type { EmailSend } from '@certd/pipeline';
import { IEmailService, isPlus, logger } from '@certd/pipeline';
import { IEmailService } from '@certd/pipeline';
import { logger } from '@certd/basic';
import { isPlus } from '@certd/plus-core';
import nodemailer from 'nodemailer';
import type SMTPConnection from 'nodemailer/lib/smtp-connection';
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
@@ -45,14 +49,7 @@ export class EmailService implements IEmailService {
* receivers: string[];
*/
await this.plusService.request({
url: '/activation/emailSend',
data: {
subject: email.subject,
text: email.content,
to: email.receivers,
},
});
await this.plusService.sendEmail(email);
}
/**
@@ -34,4 +34,6 @@ export class CnameProviderEntity {
default: () => 'CURRENT_TIMESTAMP',
})
updateTime: Date;
title: string;
}
@@ -1,5 +1,5 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
export type CnameRecordStatusType = 'cname' | 'validating' | 'valid' | 'error';
export type CnameRecordStatusType = 'cname' | 'validating' | 'valid' | 'error' | 'timeout';
/**
* cname record配置
*/
@@ -1,8 +1,9 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService, ValidateException } from '@certd/lib-server';
import { CnameProviderEntity } from '../entity/cname_provider.js';
import { BaseService, ListReq, SysPrivateSettings, SysSettingsService, ValidateException } from '@certd/lib-server';
import { CnameProviderEntity } from '../entity/cname-provider.js';
import { CommonProviders } from './common-provider.js';
/**
* 授权
@@ -13,13 +14,16 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
@InjectEntityModel(CnameProviderEntity)
repository: Repository<CnameProviderEntity>;
@Inject()
settingsService: SysSettingsService;
//@ts-ignore
getRepository() {
return this.repository;
}
async getDefault() {
return await this.repository.findOne({ where: { isDefault: true } });
return await this.repository.findOne({ where: { isDefault: true, disabled: false } });
}
/**
* 新增
@@ -80,10 +84,34 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
if (def) {
return def;
}
const founds = await this.repository.find({ take: 1, order: { createTime: 'DESC' } });
const founds = await this.repository.find({ take: 1, order: { createTime: 'DESC' }, where: { disabled: false } });
if (founds && founds.length > 0) {
return founds[0];
}
const sysPrivateSettings = await this.settingsService.getSetting<SysPrivateSettings>(SysPrivateSettings);
if (sysPrivateSettings.commonCnameEnabled !== false && CommonProviders.length > 0) {
return CommonProviders[0] as CnameProviderEntity;
}
return null;
}
async list(req: ListReq): Promise<any[]> {
const list = await super.list(req);
const sysPrivateSettings = await this.settingsService.getSetting<SysPrivateSettings>(SysPrivateSettings);
if (sysPrivateSettings.commonCnameEnabled !== false) {
return [...list, ...CommonProviders];
}
return list;
}
async info(id: any, infoIgnoreProperty?: any): Promise<any | null> {
if (id < 0) {
//使用公共provider
return CommonProviders.find(p => p.id === id);
}
return await super.info(id, infoIgnoreProperty);
}
}
@@ -1,16 +1,18 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService, ValidateException } from '@certd/lib-server';
import { BaseService, PlusService, ValidateException } from '@certd/lib-server';
import { CnameRecordEntity, CnameRecordStatusType } from '../entity/cname-record.js';
import { v4 as uuidv4 } from 'uuid';
import { createDnsProvider, IDnsProvider, parseDomain } from '@certd/plugin-cert';
import { cache, CnameProvider, http, logger, utils } from '@certd/pipeline';
import { CnameProvider, CnameRecord } from '@certd/pipeline';
import { cache, http, logger, utils } from '@certd/basic';
import { AccessService } from '../../pipeline/service/access-service.js';
import { isDev } from '../../../utils/env.js';
import { isDev } from '@certd/basic';
import { walkTxtRecord } from '@certd/acme-client';
import { CnameProviderService } from './cname-provider-service.js';
import { CnameProviderEntity } from '../entity/cname_provider.js';
import { CnameProviderEntity } from '../entity/cname-provider.js';
import { CommonDnsProvider } from './common-provider.js';
type CnameCheckCacheValue = {
validating: boolean;
@@ -34,6 +36,10 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
@Inject()
accessService: AccessService;
@Inject()
plusService: PlusService;
//@ts-ignore
getRepository() {
return this.repository;
@@ -85,8 +91,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
}
param.hostRecord = hostRecord;
const cnameKey = uuidv4().replaceAll('-', '');
param.recordValue = `${cnameKey}.${cnameProvider.domain}`;
const cnameKey = utils.id.simpleNanoId();
param.recordValue = `${param.domain}.${cnameKey}.${cnameProvider.domain}`;
}
async update(param: any) {
@@ -122,8 +128,17 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
// }
async getWithAccessByDomain(domain: string, userId: number) {
const record = await this.getByDomain(domain, userId);
record.cnameProvider.access = await this.accessService.getAccessById(record.cnameProvider.accessId, false);
const record: CnameRecord = await this.getByDomain(domain, userId);
if (record.cnameProvider.id > 0) {
//自定义cname服务
record.cnameProvider.access = await this.accessService.getAccessById(record.cnameProvider.accessId, false);
} else {
record.commonDnsProvider = new CommonDnsProvider({
config: record.cnameProvider,
plusService: this.plusService,
});
}
return record;
}
@@ -152,7 +167,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
cnameProvider: {
...provider,
} as CnameProvider,
};
} as CnameRecord;
}
/**
@@ -178,17 +193,29 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
startTime: new Date().getTime(),
};
}
let ttl = 60 * 60 * 15 * 1000;
let ttl = 5 * 60 * 1000;
if (isDev()) {
ttl = 30 * 1000;
}
const recordValue = bean.recordValue.substring(0, bean.recordValue.indexOf('.'));
const testRecordValue = 'certd-cname-verify';
const buildDnsProvider = async () => {
const cnameProvider = await this.cnameProviderService.info(bean.cnameProviderId);
if (cnameProvider == null) {
throw new ValidateException(`CNAME服务:${bean.cnameProviderId} 已被删除,请修改CNAME记录,重新选择CNAME服务`);
}
if (cnameProvider.disabled === true) {
throw new Error(`CNAME服务:${bean.cnameProviderId} 已被禁用`);
}
if (cnameProvider.id < 0) {
//公共CNAME
return new CommonDnsProvider({
config: cnameProvider,
plusService: this.plusService,
});
}
const access = await this.accessService.getById(cnameProvider.accessId, cnameProvider.userId);
const context = { access, logger, http, utils };
const dnsProvider: IDnsProvider = await createDnsProvider({
@@ -203,16 +230,17 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
return true;
}
if (value.startTime + ttl < new Date().getTime()) {
logger.warn(`cname验证超时,停止检查,${bean.domain} ${recordValue}`);
logger.warn(`cname验证超时,停止检查,${bean.domain} ${testRecordValue}`);
clearInterval(value.intervalId);
await this.updateStatus(bean.id, 'cname');
await this.updateStatus(bean.id, 'timeout');
cache.delete(cacheKey);
return false;
}
const originDomain = parseDomain(bean.domain);
const fullDomain = `${bean.hostRecord}.${originDomain}`;
logger.info(`检查CNAME配置 ${fullDomain} ${recordValue}`);
logger.info(`检查CNAME配置 ${fullDomain} ${testRecordValue}`);
// const txtRecords = await dns.promises.resolveTxt(fullDomain);
// if (txtRecords.length) {
@@ -225,10 +253,10 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
logger.error(`获取TXT记录失败,${e.message}`);
}
logger.info(`检查到TXT记录 ${JSON.stringify(records)}`);
const success = records.includes(recordValue);
const success = records.includes(testRecordValue);
if (success) {
clearInterval(value.intervalId);
logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${recordValue}`);
logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${testRecordValue}`);
await this.updateStatus(bean.id, 'valid');
value.pass = true;
cache.delete(cacheKey);
@@ -242,8 +270,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
} catch (e) {
logger.error(`删除CNAME的校验DNS记录失败, ${e.message}req:${JSON.stringify(value.recordReq)}recordRes:${JSON.stringify(value.recordRes)}`, e);
}
return success;
}
return success;
};
if (value.validating) {
@@ -263,7 +291,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
fullRecord: fullRecord,
hostRecord: hostRecord,
type: 'TXT',
value: recordValue,
value: testRecordValue,
};
const dnsProvider = await buildDnsProvider();
const recordRes = await dnsProvider.createRecord(req);
@@ -0,0 +1,66 @@
import { CreateRecordOptions, DnsProviderContext, IDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { PlusService } from '@certd/lib-server';
export type CommonCnameProvider = {
id: number;
domain: string;
title?: string;
};
export const CommonProviders = [
{
id: -1,
domain: 'cname.certd.com.cn',
title: '公共CNAME服务',
},
];
export class CommonDnsProvider implements IDnsProvider {
ctx: DnsProviderContext;
config: CommonCnameProvider;
plusService: PlusService;
constructor(opts: { config: CommonCnameProvider; plusService: PlusService }) {
this.config = opts.config;
this.plusService = opts.plusService;
}
async onInstance() {}
async createRecord(options: CreateRecordOptions) {
if (!this.config.domain.endsWith(options.domain)) {
throw new Error('cname服务域名不匹配');
}
await this.plusService.register();
const res = await this.plusService.requestWithToken({
url: '/activation/certd/cname/recordCreate',
method: 'post',
data: {
subjectId: await this.plusService.getSubjectId(),
domain: options.domain,
hostRecord: options.hostRecord,
recordValue: options.value,
providerId: this.config.id,
},
});
return res;
}
async removeRecord(options: RemoveRecordOptions<any>) {
const res = await this.plusService.requestWithToken({
url: '/activation/certd/cname/recordRemove',
method: 'post',
data: {
subjectId: await this.plusService.getSubjectId(),
domain: options.recordReq.domain,
hostRecord: options.recordReq.hostRecord,
recordValue: options.recordReq.value,
recordId: options.recordRes.recordId,
providerId: this.config.id,
},
});
return res;
}
setCtx(ctx: DnsProviderContext): void {
this.ctx = ctx;
}
}
@@ -1,5 +1,5 @@
import parser from 'cron-parser';
import { ILogger } from '@certd/pipeline';
import { ILogger } from '@certd/basic';
export type CronTaskReq = {
/**
@@ -6,9 +6,11 @@ import { HistoryEntity } from '../entity/history.js';
import { PipelineEntity } from '../entity/pipeline.js';
import { HistoryDetail } from '../entity/vo/history-detail.js';
import { HistoryLogService } from './history-log-service.js';
import { FileItem, FileStore, logger, Pipeline, RunnableCollection } from '@certd/pipeline';
import { FileItem, FileStore, Pipeline, RunnableCollection } from '@certd/pipeline';
import dayjs from 'dayjs';
import { DbAdapter } from '../../db/index.js';
import { logger } from '@certd/basic';
/**
* 证书申请
@@ -4,7 +4,7 @@ import { In, MoreThan, Repository } from 'typeorm';
import { BaseService, NeedVIPException, PageReq, SysPublicSettings, SysSettingsService } from '@certd/lib-server';
import { PipelineEntity } from '../entity/pipeline.js';
import { PipelineDetail } from '../entity/vo/pipeline-detail.js';
import { Executor, isPlus, logger, Pipeline, ResultType, RunHistory, UserInfo } from '@certd/pipeline';
import { Executor, Pipeline, ResultType, RunHistory, UserInfo } from '@certd/pipeline';
import { AccessService } from './access-service.js';
import { DbStorage } from './db-storage.js';
import { StorageService } from './storage-service.js';
@@ -21,6 +21,8 @@ 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 { isPlus } from '@certd/plus-core';
import { logger } from '@certd/basic';
const runningTasks: Map<string | number, Executor> = new Map();
const freeCount = 10;
@@ -306,8 +308,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
return;
}
cron = cron.trim();
if (cron.startsWith('* *')) {
cron = cron.replace('* *', '0 0');
}
if (cron.startsWith('*')) {
cron = '0' + cron.substring(1, cron.length);
cron = cron.replace('*', '0');
}
const triggerId = trigger.id;
const name = this.buildCronKey(pipelineId, triggerId);
@@ -3,7 +3,7 @@ import { BaseService, PageReq } from '@certd/lib-server';
import { PluginEntity } from '../entity/plugin.js';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { isComm } from '@certd/pipeline';
import { isComm } from '@certd/plus-core';
import { BuiltInPluginService } from '../../pipeline/service/builtin-plugin-service.js';
import { merge } from 'lodash-es';
@@ -135,5 +135,6 @@ export class RoleService extends BaseService<RoleEntity> {
if (urs.length > 0) {
throw new Error('该角色已被用户使用,无法删除');
}
await this.deleteWhere({ id: In(idArr) });
}
}
@@ -12,7 +12,7 @@ import bcrypt from 'bcryptjs';
import { RandomUtil } from '../../../../utils/random.js';
import dayjs from 'dayjs';
import { DbAdapter } from '../../../db/index.js';
import { utils } from '@certd/pipeline';
import { utils } from '@certd/basic';
/**
* 系统用户
*/
@@ -9,7 +9,6 @@ export * from './plugin-other/index.js';
export * from './plugin-west/index.js';
export * from './plugin-doge/index.js';
export * from './plugin-qiniu/index.js';
export * from './plugin-jdcloud/index.js';
export * from './plugin-woai/index.js';
export * from './plugin-cachefly/index.js';
export * from './plugin-gcore/index.js';
@@ -1,5 +1,7 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, ILogger } from '@certd/pipeline';
import { Autowire } from '@certd/pipeline';
import { ILogger } from '@certd/basic';
import { AliyunAccess, AliyunClient } from '@certd/plugin-plus';
@IsDnsProvider({
@@ -1,6 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import dayjs from 'dayjs';
import { AliyunAccess, AliyunClient } from "@certd/plugin-plus";
import { AliyunAccess, AliyunClient, AliyunSslClient, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-plus';
import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
@IsTaskPlugin({
name: 'DeployCertToAliyunCDN',
title: '部署证书至阿里云CDN',
@@ -15,29 +16,35 @@ import { AliyunAccess, AliyunClient } from "@certd/plugin-plus";
})
export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
@TaskInput({
title: 'CDN加速域名',
helper: '你在阿里云上配置的CDN加速域名,比如:certd.docmirror.cn',
title: '证书服务接入点',
helper: '不会选就按默认',
value: 'cas.aliyuncs.com',
component: {
name: 'a-select',
options: [
{ value: 'cas.aliyuncs.com', label: '中国大陆' },
{ value: 'cas.ap-southeast-1.aliyuncs.com', label: '新加坡' },
{ value: 'cas.eu-central-1.aliyuncs.com', label: '德国(法兰克福)' },
],
},
required: true,
})
domainName!: string;
@TaskInput({
title: '证书名称',
helper: '上传后将以此名称作为前缀备注',
})
certName!: string;
endpoint!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'],
},
required: true,
})
cert!: string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: 'Access授权',
helper: '阿里云授权AccessKeyId、AccessKeySecret',
@@ -49,47 +56,84 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: 'CDN加速域名',
helper: '你在阿里云上配置的CDN加速域名,比如:certd.docmirror.cn',
typeName: 'DeployCertToAliyunCDN',
action: DeployCertToAliyunCDN.prototype.onGetDomainList.name,
watches: ['certDomains', 'accessId'],
required: true,
})
)
domainName!: string | string[];
@TaskInput({
title: '证书名称',
helper: '上传后将以此名称作为前缀备注',
})
certName!: string;
async onInstance() {}
async execute(): Promise<void> {
this.logger.info('开始部署证书到阿里云cdn');
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
const access = await this.accessService.getById<AliyunAccess>(this.accessId);
const sslClient = new AliyunSslClient({
access,
logger: this.logger,
endpoint: this.endpoint || 'cas.aliyuncs.com',
});
let certId: any = this.cert;
if (typeof this.cert === 'object') {
certId = await sslClient.uploadCert({
name: this.appendTimeSuffix('certd'),
cert: this.cert,
});
}
const client = await this.getClient(access);
const params = await this.buildParams();
await this.doRequest(client, params);
if (typeof this.domainName === 'string') {
this.domainName = [this.domainName];
}
for (const domain of this.domainName) {
await this.SetCdnDomainSSLCertificate(client, {
CertId: certId,
DomainName: domain,
});
}
this.logger.info('部署完成');
}
async getClient(access: AliyunAccess) {
const client = new AliyunClient({logger:this.logger})
const client = new AliyunClient({ logger: this.logger });
await client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
endpoint: 'https://cdn.aliyuncs.com',
apiVersion: '2018-05-10',
})
return client
});
return client;
}
async buildParams() {
const CertName = (this.certName ?? 'certd') + '-' + dayjs().format('YYYYMMDDHHmmss');
const cert: any = this.cert;
return {
DomainName: this.domainName,
SSLProtocol: 'on',
CertName: CertName,
CertType: 'upload',
SSLPub: cert.crt,
SSLPri: cert.key,
};
}
async doRequest(client: any, params: any) {
async SetCdnDomainSSLCertificate(client: any, params: { CertId: number; DomainName: string }) {
const requestOption = {
method: 'POST',
formatParams: false,
};
const ret: any = await client.request('SetCdnDomainSSLCertificate', params, requestOption);
const ret: any = await client.request(
'SetCdnDomainSSLCertificate',
{
SSLProtocol: 'on',
...params,
},
requestOption
);
this.checkRet(ret);
this.logger.info('设置cdn证书成功:', ret.RequestId);
this.logger.info(`设置CDN: ${params.DomainName} 证书成功:`, ret.RequestId);
}
checkRet(ret: any) {
@@ -97,5 +141,39 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
throw new Error('执行失败:' + ret.Message);
}
}
async onGetDomainList(data: any) {
if (!this.accessId) {
throw new Error('请选择Access授权');
}
const access = await this.accessService.getById<AliyunAccess>(this.accessId);
const client = await this.getClient(access);
const params = {
// 'DomainName': 'aaa',
PageSize: 500,
};
const requestOption = {
method: 'POST',
formatParams: false,
};
const res = await client.request('DescribeUserDomains', params, requestOption);
this.checkRet(res);
const pageData = res?.Domains?.PageData;
if (!pageData || pageData.length === 0) {
throw new Error('找不到CDN域名,您可以手动输入');
}
const options = pageData.map((item: any) => {
return {
value: item.DomainName,
label: item.DomainName,
domain: item.DomainName,
};
});
return optionsUtils.buildGroupOptions(options, this.certDomains);
}
}
new DeployCertToAliyunCDN();
@@ -0,0 +1,154 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AliyunAccess } from '@certd/plugin-plus';
import { CertInfo } from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToAliyunOSS',
title: '部署证书至阿里云OSS',
icon: 'ant-design:aliyun-outlined',
group: pluginGroups.aliyun.key,
desc: '自动部署域名证书至阿里云OSS',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DeployCertToAliyunOSS extends AbstractTaskPlugin {
@TaskInput({
title: '大区',
component: {
name: 'a-auto-complete',
vModel: 'value',
options: [
{ value: 'oss-cn-hangzhou', label: '华东1(杭州)' },
{ value: 'oss-cn-shanghai', label: '华东2(上海)' },
{ value: 'oss-cn-nanjing', label: '华东5(南京-本地地域)' },
{ value: 'oss-cn-fuzhou', label: '华东6(福州-本地地域)' },
{ value: 'oss-cn-wuhan-lr', label: '华中1(武汉-本地地域)' },
{ value: 'oss-cn-qingdao', label: '华北1(青岛)' },
{ value: 'oss-cn-beijing', label: '华北2(北京)' },
{ value: 'oss-cn-zhangjiakou', label: '华北 3(张家口)' },
{ value: 'oss-cn-huhehaote', label: '华北5(呼和浩特)' },
{ value: 'oss-cn-wulanchabu', label: '华北6(乌兰察布)' },
{ value: 'oss-cn-shenzhen', label: '华南1(深圳)' },
{ value: 'oss-cn-heyuan', label: '华南2(河源)' },
{ value: 'oss-cn-guangzhou', label: '华南3(广州)' },
{ value: 'oss-cn-chengdu', label: '西南1(成都)' },
{ value: 'oss-cn-hongkong', label: '中国香港' },
{ value: 'oss-us-west-1', label: '美国(硅谷)①' },
{ value: 'oss-us-east-1', label: '美国(弗吉尼亚)①' },
{ value: 'oss-ap-northeast-1', label: '日本(东京)①' },
{ value: 'oss-ap-northeast-2', label: '韩国(首尔)' },
{ value: 'oss-ap-southeast-1', label: '新加坡①' },
{ value: 'oss-ap-southeast-2', label: '澳大利亚(悉尼)①' },
{ value: 'oss-ap-southeast-3', label: '马来西亚(吉隆坡)①' },
{ value: 'oss-ap-southeast-5', label: '印度尼西亚(雅加达)①' },
{ value: 'oss-ap-southeast-6', label: '菲律宾(马尼拉)' },
{ value: 'oss-ap-southeast-7', label: '泰国(曼谷)' },
{ value: 'oss-eu-central-1', label: '德国(法兰克福)①' },
{ value: 'oss-eu-west-1', label: '英国(伦敦)' },
{ value: 'oss-me-east-1', label: '阿联酋(迪拜)①' },
{ value: 'oss-rg-china-mainland', label: '无地域属性(中国内地)' },
],
},
required: true,
})
region!: string;
@TaskInput({
title: 'Bucket',
helper: '存储桶名称',
required: true,
})
bucket!: string;
@TaskInput({
title: '绑定的域名',
helper: '你在阿里云OSS上绑定的域名,比如:certd.docmirror.cn',
required: true,
})
domainName!: string;
@TaskInput({
title: '证书名称',
helper: '上传后将以此名称作为前缀备注',
})
certName!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: 'Access授权',
helper: '阿里云授权AccessKeyId、AccessKeySecret',
component: {
name: 'access-selector',
type: 'aliyun',
},
required: true,
})
accessId!: string;
async onInstance() {}
async execute(): Promise<void> {
this.logger.info('开始部署证书到阿里云OSS');
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
this.logger.info(`bucket: ${this.bucket}, region: ${this.region}, domainName: ${this.domainName}`);
const client = await this.getClient(access);
await this.doRequest(client, {});
this.logger.info('部署完成');
}
async getClient(access: AliyunAccess) {
// @ts-ignore
const OSS = await import('ali-oss');
return new OSS.default({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: this.region,
//@ts-ignore
authorizationV4: true,
// yourBucketName填写Bucket名称。
bucket: this.bucket,
});
}
async doRequest(client: any, params: any) {
params = client._bucketRequestParams('POST', this.bucket, {
cname: '',
comp: 'add',
});
const xml = `
<BucketCnameConfiguration>
<Cname>
<Domain>${this.domainName}</Domain>
<CertificateConfiguration>
<PrivateKey>${this.cert.key}</PrivateKey>
<Certificate>${this.cert.crt}</Certificate>
</CertificateConfiguration>
</Cname>
</BucketCnameConfiguration>`;
params.content = xml;
params.mime = 'xml';
params.successStatuses = [200];
const res = await client.request(params);
this.checkRet(res);
return res;
}
checkRet(ret: any) {
if (ret.code != null) {
throw new Error('执行失败:' + ret.Message);
}
}
}
new DeployCertToAliyunOSS();
@@ -1,3 +1,4 @@
export * from './deploy-to-cdn/index.js';
export * from './deploy-to-dcdn/index.js';
export * from './deploy-to-oss/index.js';
export * from './upload-to-aliyun/index.js';
@@ -1,5 +1,7 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, HttpClient, ILogger } from '@certd/pipeline';
import { Autowire } from '@certd/pipeline';
import { HttpClient, ILogger } from '@certd/basic';
import { CloudflareAccess } from './access.js';
export type CloudflareRecord = {
@@ -1,7 +1,9 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, HttpClient, ILogger } from '@certd/pipeline';
import { Autowire } from '@certd/pipeline';
import { HttpClient, ILogger } from '@certd/basic';
import { DemoAccess } from './access.js';
import { isDev } from "../../utils/env.js";
import { isDev } from '../../utils/env.js';
type DemoRecord = {
// 这里定义Record记录的数据结构,跟对应云平台接口返回值一样即可,一般是拿到id就行,用于删除txt解析记录,清理申请痕迹
@@ -1,22 +1,26 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo, CertReader } from '@certd/plugin-cert';
import { isDev } from '../../../utils/env.js';
import { isDev } from '@certd/basic';
@IsTaskPlugin({
name: 'demoTest',
title: 'Demo测试插件',
icon: 'clarity:plugin-line',
//插件分组
group: pluginGroups.other.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
// 你开发的插件要删除此项,否则不会在生产环墋中显示
deprecated: isDev() ? '测试插件,生产环境不显示' : undefined,
})
export class DemoTestPlugin extends AbstractTaskPlugin {
//测试参数
@TaskInput({
title: '属性示例',
value: '默认值',
component: {
//前端组件配置,具体配置见组件文档 https://www.antdv.com/components/input-cn
name: 'a-input',
@@ -57,7 +61,7 @@ export class DemoTestPlugin extends AbstractTaskPlugin {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
},
// required: true,
// required: true, // 必填
})
cert!: CertInfo;
@@ -70,6 +74,7 @@ export class DemoTestPlugin extends AbstractTaskPlugin {
type: 'demo', //固定授权类型
},
// rules: [{ required: true, message: '此项必填' }],
// required: true, //必填
})
accessId!: string;
@@ -98,8 +103,5 @@ export class DemoTestPlugin extends AbstractTaskPlugin {
this.logger.info('授权id:', accessId);
}
}
//TODO 这里实例化插件,进行注册
if (isDev()) {
//你的实现 要去掉这个if,不然生产环境将不会显示
new DemoTestPlugin();
}
//实例化一下,注册插件
new DemoTestPlugin();
@@ -1,7 +1,7 @@
import crypto from 'crypto';
import querystring from 'querystring';
import { DogeCloudAccess } from '../access.js';
import { HttpClient } from '@certd/pipeline';
import { HttpClient } from '@certd/basic';
export class DogeClient {
accessKey: string;
@@ -1,28 +0,0 @@
import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline';
/**
* 这个注解将注册一个授权配置
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
*/
@IsAccess({
name: 'dynadot',
title: 'dynadot授权',
desc: '************\n注意:申请证书时会覆盖已有的域名解析配置,慎用\n************\n待优化,主要是dynadot的接口一言难尽',
})
export class DynadotAccess extends BaseAccess {
/**
* 授权属性配置
*/
@AccessInput({
title: 'API Production Key',
component: {
placeholder: '授权key',
},
helper: '前往 [Dynadot API](https://www.dynadot.com/account/domain/setting/api.html) 获取 API Production Key',
required: true,
encrypt: true,
})
apiProductionKey = '';
}
new DynadotAccess();
@@ -1,94 +0,0 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, ILogger } from '@certd/pipeline';
import { DynadotAccess } from './access.js';
import querystring from 'querystring';
// 这里通过IsDnsProvider注册一个dnsProvider
@IsDnsProvider({
name: 'dynadot',
title: 'dynadot',
desc: 'dynadot dns provider',
// 这里是对应的 cloudflare的access类型名称
accessType: 'dynadot',
deprecated: '暂不支持',
})
export class DynadotDnsProvider extends AbstractDnsProvider {
// 通过Autowire传递context
@Autowire()
logger!: ILogger;
access!: DynadotAccess;
async onInstance() {
this.access = this.ctx.access as DynadotAccess;
}
private async doRequest(command: string, query: any) {
const baseUrl = 'https://api.dynadot.com/api3.json?key=' + this.access.apiProductionKey;
const qs = querystring.stringify(query);
const url = `${baseUrl}&command=${command}&${qs}`;
const res = await this.ctx.http.request<any, any>({
url,
method: 'get',
headers: {
'Content-Type': 'application/json',
},
});
/*
"SetDnsResponse": {
"ResponseCode": 0,
"Status": "success"
}
*/
for (const resKey in res) {
if (res[resKey].ResponseCode != null && res[resKey].ResponseCode !== 0) {
throw new Error(`请求失败:${res[resKey].Status}`);
}
}
return res;
}
/**
* 创建dns解析记录,用于验证域名所有权
*/
async createRecord(options: CreateRecordOptions) {
/**
* fullRecord: '_acme-challenge.test.example.com',
* value: 一串uuid
* type: 'TXT',
* domain: 'example.com'
*/
const { fullRecord, value, type, domain } = options;
this.logger.info('添加域名解析:', fullRecord, value, type, domain);
//先获取域名原始解析记录
//https://api.dynadot.com/api3.xml?key=[API Key]&command=domain_info&domain=domain1.com
const res1 = await this.doRequest('domain_info', {
domain: domain,
});
// this.logger.info(`域名信息:${JSON.stringify(res1)}`);
// "DomainInfoResponse.NameServerSettings":{"Type":"Dynadot DNS","SubDomains":[{"Subhost":"_acme-challenge","RecordType":"TXT","Value":"43XrhFA6pJpE7a-20y7BmC6CsN20TMt5l-Zl-CL_-4I"}],"TTL":"300"}
this.logger.info('原始域名解析记录:', JSON.stringify(res1.DomainInfoResponse?.DomainInfo?.NameServerSettings));
const prefix = fullRecord.replace(`.${domain}`, '');
// 给domain下创建txt类型的dns解析记录,fullRecord
const res = await this.doRequest('set_dns2', {
domain: domain,
subdomain0: prefix,
sub_record_type0: 'TXT',
sub_record0: value,
});
this.logger.info(`添加域名解析成功:fullRecord=${fullRecord},value=${value}`);
this.logger.info(`请求结果:${JSON.stringify(res)}`);
//本接口需要返回本次创建的dns解析记录,这个记录会在删除的时候用到
return {};
}
/**
* 删除dns解析记录,清理申请痕迹
* @param options
*/
async removeRecord(options: RemoveRecordOptions<any>): Promise<void> {}
}
//实例化这个provider,将其自动注册到系统中
new DynadotDnsProvider();
@@ -1,3 +0,0 @@
export * from './dns-provider.js';
export * from './plugins/index.js';
export * from './access.js';
@@ -2,7 +2,7 @@
import ssh2, { ConnectConfig, ExecOptions } from 'ssh2';
import path from 'path';
import * as _ from 'lodash-es';
import { ILogger } from '@certd/pipeline';
import { ILogger } from '@certd/basic';
import { SshAccess } from '../access/index.js';
import stripAnsi from 'strip-ansi';
import { SocksClient } from 'socks';
@@ -86,6 +86,7 @@ export class AsyncSsh2Client {
sftp.fastPut(localPath, remotePath, (err: Error) => {
if (err) {
reject(err);
this.logger.error('请确认路径是否包含文件名,路径本身不能是目录,路径不能有*?之类的特殊符号,要有写入权限');
return;
}
this.logger.info(`上传文件成功:${localPath} => ${remotePath}`);
@@ -17,12 +17,47 @@ import path from 'path';
},
})
export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: '证书类型',
helper: '要部署的证书格式,支持pem、pfx、der、jks格式',
component: {
name: 'a-select',
options: [
{ value: 'pem', label: 'pem,用于Nginx等大部分应用' },
{ value: 'pfx', label: 'pfx,一般用于IIS' },
{ value: 'der', label: 'der,一般用于Apache' },
{ value: 'jks', label: 'jks,一般用于JAVA应用' },
],
},
required: true,
})
certType!: string;
@TaskInput({
title: '证书保存路径',
helper: '全链证书,路径要包含文件名' + '\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:tmp/cert.pem',
component: {
placeholder: 'tmp/full_chain.pem',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
crtPath!: string;
@@ -32,6 +67,14 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
component: {
placeholder: 'tmp/cert.key',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
keyPath!: string;
@@ -42,6 +85,13 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
component: {
placeholder: '/root/deploy/nginx/intermediate.pem',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
rules: [{ type: 'filepath' }],
})
icPath!: string;
@@ -52,6 +102,14 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
component: {
placeholder: 'tmp/cert.pfx',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pfx';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
pfxPath!: string;
@@ -63,30 +121,35 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
component: {
placeholder: 'tmp/cert.der 或 tmp/cert.cer',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'der';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
derPath!: string;
// @TaskInput({
// title: 'jks证书保存路径',
// helper: '用于java,路径要包含文件名,例如:tmp/cert.jks',
// component: {
// placeholder: 'tmp/cert.jks',
// },
// rules: [{ type: 'filepath' }],
// })
jksPath!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
title: 'jks证书保存路径',
helper: '用于java,路径要包含文件名,例如:tmp/cert.jks',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
placeholder: 'tmp/cert.jks',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'jks';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
cert!: CertInfo;
jksPath!: string;
@TaskOutput({
title: '证书保存路径',
@@ -18,12 +18,47 @@ import dayjs from 'dayjs';
},
})
export class UploadCertToHostPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: '证书格式',
helper: '要部署的证书格式,支持pem、pfx、der、jks',
component: {
name: 'a-select',
options: [
{ value: 'pem', label: 'pemNginx等大部分应用' },
{ value: 'pfx', label: 'pfx,一般用于IIS' },
{ value: 'der', label: 'der,一般用于Apache' },
{ value: 'jks', label: 'jks,一般用于JAVA应用' },
],
},
required: true,
})
certType!: string;
@TaskInput({
title: '证书保存路径',
helper: '全链证书,需要有写入权限,路径要包含证书文件名,例如:/tmp/cert.pem',
helper: '填写应用原本的证书保存路径,路径要包含证书文件名,例如:/tmp/cert.pem',
component: {
placeholder: '/root/deploy/nginx/full_chain.pem',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
crtPath!: string;
@@ -33,6 +68,14 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
component: {
placeholder: '/root/deploy/nginx/cert.key',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
keyPath!: string;
@@ -43,50 +86,70 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
component: {
placeholder: '/root/deploy/nginx/intermediate.pem',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
rules: [{ type: 'filepath' }],
})
icPath!: string;
@TaskInput({
title: 'PFX证书保存路径',
helper: '用于IIS证书部署,需要有写入权限,路径要包含证书文件名,例如:/tmp/cert.pfx',
helper: '填写应用原本的证书保存路径,路径要包含证书文件名,例如:D:\\iis\\cert.pfx',
component: {
placeholder: '/root/deploy/nginx/cert.pfx',
placeholder: 'D:\\iis\\cert.pfx',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pfx';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
pfxPath!: string;
@TaskInput({
title: 'DER证书保存路径',
helper: '用于Apache证书部署,需要有写入权限,路径要包含证书文件名,例如:/tmp/cert.der',
helper: '填写应用原本的证书保存路径,路径要包含证书文件名,例如:/tmp/cert.der',
component: {
placeholder: '/root/deploy/nginx/cert.der',
placeholder: '/root/deploy/apache/cert.der',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'der';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
derPath!: string;
// @TaskInput({
// title: 'jks证书保存路径',
// helper: '需要有写入权限,路径要包含证书文件名,例如:/tmp/cert.jks',
// component: {
// placeholder: '/root/deploy/nginx/cert.jks',
// },
// rules: [{ type: 'filepath' }],
// })
jksPath!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
title: 'jks证书保存路径',
helper: '填写应用原本的证书保存路径,路径要包含证书文件名,例如:/tmp/cert.jks',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
placeholder: '/root/deploy/java_app/cert.jks',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'jks';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
cert!: CertInfo;
jksPath!: string;
@TaskInput({
title: '主机登录配置',
@@ -1,6 +1,8 @@
import * as _ from 'lodash-es';
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, ILogger } from '@certd/pipeline';
import { Autowire } from '@certd/pipeline';
import { ILogger } from '@certd/basic';
import { HuaweiAccess } from '../access/index.js';
import { ApiRequestOptions, HuaweiYunClient } from '@certd/lib-huawei';
@@ -1,7 +1,8 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, resetLogConfigure, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { HuaweiAccess } from '../../access/index.js';
import { CertInfo } from '@certd/plugin-cert';
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-plus';
import { resetLogConfigure } from '@certd/basic';
@IsTaskPlugin({
name: 'HauweiDeployCertToCDN',
@@ -1,39 +0,0 @@
import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline';
/**
* 这个注解将注册一个授权配置
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
*/
@IsAccess({
name: 'jdcloud',
title: '京东云授权',
desc: '暂时无法成功申请,还没测试通过',
})
export class JDCloudAccess extends BaseAccess {
/**
* 授权属性配置
*/
@AccessInput({
title: 'AccessKeyId',
component: {
placeholder: 'AK',
},
helper: '前往 [AccessKey管理](https://uc.jdcloud.com/account/accesskey) 获取 API Production Key',
required: true,
encrypt: true,
})
accessKeyId = '';
@AccessInput({
title: 'AccessKeySecret',
component: {
placeholder: 'SK',
},
helper: 'SK',
required: true,
encrypt: true,
})
accessKeySecret = '';
}
new JDCloudAccess();
@@ -1,124 +0,0 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, ILogger } from '@certd/pipeline';
import { JDCloudAccess } from './access.js';
function promisfy(func: any) {
return (params: any, regionId: string) => {
return new Promise((resolve, reject) => {
try {
func(params, regionId, (err, result) => {
if (err) {
reject(err.error || err);
return;
}
resolve(result);
});
} catch (e) {
reject(e);
}
});
};
}
// 这里通过IsDnsProvider注册一个dnsProvider
@IsDnsProvider({
name: 'jdcloud',
title: '京东云',
desc: '京东云 dns provider',
// 这里是对应的 cloudflare的access类型名称
accessType: 'jdcloud',
deprecated: '暂不支持',
})
export class JDCloudDnsProvider extends AbstractDnsProvider {
// 通过Autowire传递context
@Autowire()
logger!: ILogger;
access!: JDCloudAccess;
service!: any;
regionId: string;
async onInstance() {
this.access = this.ctx.access as JDCloudAccess;
const { DomainService } = await import('@certd/lib-jdcloud');
// @ts-ignore
this.regionId = 'cn-north-1';
this.service = new DomainService({
credentials: {
accessKeyId: this.access.accessKeyId,
secretAccessKey: this.access.accessKeySecret,
},
regionId: this.regionId,
});
}
/**
* 创建dns解析记录,用于验证域名所有权
*/
async createRecord(options: CreateRecordOptions) {
/**
* fullRecord: '_acme-challenge.test.example.com',
* value: 一串uuid
* type: 'TXT',
* domain: 'example.com'
*/
const { fullRecord, value, type, domain } = options;
this.logger.info('添加域名解析:', fullRecord, value, type, domain);
const describeDomains = promisfy((a, b, c) => {
this.service.describeDomains(a, b, c);
});
const res: any = await describeDomains({ domainName: domain, pageNumber: 1, pageSize: 10 }, this.regionId);
this.logger.info('请求成功:', JSON.stringify(res.result));
const dataList = res.result.dataList;
if (dataList.length === 0) {
throw new Error('账号下找不到域名:' + domain);
}
const domainId = dataList[0].id;
this.logger.info('domainId:', domainId);
//开始创建解析记录
const createResourceRecord = promisfy((a, b, c) => {
this.service.createResourceRecord(a, b, c);
});
const res2: any = await createResourceRecord(
{
domainId,
req: {
hostRecord: fullRecord,
hostValue: value,
type: 'TXT',
},
},
this.regionId
);
this.logger.info('请求成功:', JSON.stringify(res.result));
const recordList = res2.result.dataList;
const recordId = recordList[0].id;
this.logger.info(`添加域名解析成功:fullRecord=${fullRecord},value=${value}`);
this.logger.info(`请求结果:recordId:${recordId}`);
//本接口需要返回本次创建的dns解析记录,这个记录会在删除的时候用到
return { id: recordId, domainId };
}
/**
* 删除dns解析记录,清理申请痕迹
* @param options
*/
async removeRecord(options: RemoveRecordOptions<any>): Promise<void> {
// const { fullRecord, value, domain } = options.recordReq;
const record = options.recordRes;
const deleteResourceRecord = promisfy(this.service.deleteResourceRecord);
const res: any = await deleteResourceRecord(
{
domainId: record.domainId,
resourceRecordId: record.id,
},
this.regionId
);
this.logger.info(`删除dns解析记录成功:${JSON.stringify(res)}`);
}
}
//实例化这个provider,将其自动注册到系统中
new JDCloudDnsProvider();
@@ -1,3 +0,0 @@
export * from './dns-provider.js';
export * from './plugins/index.js';
export * from './access.js';
@@ -21,7 +21,7 @@ export type CustomScriptContext = {
export class CustomScriptPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '脚本',
helper: '自定义js脚本',
helper: '自定义js脚本[脚本编写帮助文档](https://certd.docmirror.cn/guide/use/custom-script/)',
component: {
name: 'a-textarea',
vModel: 'value',
@@ -1,4 +1,6 @@
import { Autowire, HttpClient, ILogger } from '@certd/pipeline';
import { Autowire } from '@certd/pipeline';
import { HttpClient, ILogger } from '@certd/basic';
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import * as _ from 'lodash-es';
import { DnspodAccess } from '../access/index.js';
@@ -1,4 +1,6 @@
import { Autowire, HttpClient, ILogger } from '@certd/pipeline';
import { Autowire } from '@certd/pipeline';
import { HttpClient, ILogger } from '@certd/basic';
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { TencentAccess } from '@certd/plugin-plus';
@@ -1,6 +1,6 @@
import { TencentAccess } from '@certd/plugin-plus';
import { CertInfo } from '@certd/plugin-cert';
import { ILogger } from '@certd/pipeline';
import { ILogger } from '@certd/basic';
export class TencentSslClient {
access: TencentAccess;
logger: ILogger;
@@ -94,14 +94,15 @@ export class TencentDeleteExpiringCert extends AbstractPlusTaskPlugin {
};
const res = await sslClient.DescribeCertificates(params);
let certificates = res?.Certificates;
if (!certificates && !certificates.length) {
if (!certificates && certificates.length === 0) {
this.logger.info('没有找到证书');
return;
}
const lastDay = dayjs().add(this.expiringDays, 'day');
certificates = certificates.filter((item: any) => {
const endTime = item.CertEndTime;
return dayjs(endTime).add(this.expiringDays, 'day').isBefore(dayjs());
return dayjs(endTime).isBefore(lastDay);
});
for (const certificate of certificates) {
this.logger.info(`证书ID:${certificate.CertificateId}, 过期时间:${certificate.CertEndTime}Alias:${certificate.Alias},证书域名:${certificate.Domain}`);
@@ -1,4 +1,4 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { TencentAccess } from '@certd/plugin-plus';
import dayjs from 'dayjs';
@@ -131,10 +131,10 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
}
try {
await utils.sleep(2000);
await this.ctx.utils.sleep(2000);
let newCertId = await this.getCertIdFromProps(client);
if ((lastCertId && newCertId === lastCertId) || (!lastCertId && !newCertId)) {
await utils.sleep(2000);
await this.ctx.utils.sleep(2000);
newCertId = await this.getCertIdFromProps(client);
}
if (newCertId === lastCertId) {
@@ -1,12 +1,12 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AbstractPlusTaskPlugin, createRemoteSelectInputDefine } from '@certd/plugin-plus';
import { createRemoteSelectInputDefine } from '@certd/plugin-plus';
import { TencentSslClient } from '../../lib/index.js';
@IsTaskPlugin({
name: 'DeployCertToTencentCosPlugin',
title: '部署证书到腾讯云COS',
needPlus: true,
needPlus: false,
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '部署到腾讯云COS源站域名证书【注意:很不稳定,需要重试很多次偶尔才能成功一次】',
@@ -16,7 +16,7 @@ import { TencentSslClient } from '../../lib/index.js';
},
},
})
export class DeployCertToTencentCosPlugin extends AbstractPlusTaskPlugin {
export class DeployCertToTencentCosPlugin extends AbstractTaskPlugin {
/**
* AccessProvider的id
*/
@@ -1,5 +1,8 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, HttpClient, ILogger } from '@certd/pipeline';
import { Autowire } from '@certd/pipeline';
import { HttpClient, ILogger } from '@certd/basic';
import { WestAccess } from './access.js';
type westRecord = {
@@ -1,6 +1,7 @@
import { AbstractTaskPlugin, HttpClient, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { WoaiAccess } from '../access.js';
import { HttpClient } from '@certd/basic';
@IsTaskPlugin({
name: 'WoaiCDN',
+1 -1
View File
@@ -1 +1 @@
export { isDev } from '@certd/pipeline';
export { isDev } from '@certd/basic';
+1 -1
View File
@@ -1,4 +1,4 @@
import { utils } from '@certd/pipeline';
import { utils } from '@certd/basic';
export async function request(config: any): Promise<any> {
try {
@@ -1,4 +1,4 @@
import { logger } from '@certd/pipeline';
import { logger } from '@certd/basic';
import fs from 'fs';
export async function getVersion() {
@@ -0,0 +1 @@
lego workspace