mirror of
https://github.com/certd/certd.git
synced 2026-04-24 12:27:25 +08:00
Merge branch 'v2-dev' into v2
This commit is contained in:
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 服务器时间获取不准确的bug ([5d10cbf](https://github.com/certd/certd/commit/5d10cbf18daf94a90a7551641a3b13e3c5fec611))
|
||||||
|
* 修复复制流水线无效的bug ([3df20a9](https://github.com/certd/certd/commit/3df20a924f32970b052e2588ea20de095f0ea693))
|
||||||
|
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||||
|
* 修复token过期后,疯狂打印token过期信息的bug ([50a5fa1](https://github.com/certd/certd/commit/50a5fa15bb240a125bbc91d2ce1ff3c835888a77))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/certd/certd/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||||
|
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/certd/certd/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||||
|
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||||
|
* 支持阿里云中文域名申请 ([b3468cf](https://github.com/certd/certd/commit/b3468cf7f28228d7c9cf68de6b5a9bbeb67f2c6d))
|
||||||
|
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
|
||||||
|
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
22:32
|
01:58
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 服务器时间获取不准确的bug ([5d10cbf](https://github.com/certd/certd/commit/5d10cbf18daf94a90a7551641a3b13e3c5fec611))
|
||||||
|
* 修复复制流水线无效的bug ([3df20a9](https://github.com/certd/certd/commit/3df20a924f32970b052e2588ea20de095f0ea693))
|
||||||
|
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||||
|
* 修复token过期后,疯狂打印token过期信息的bug ([50a5fa1](https://github.com/certd/certd/commit/50a5fa15bb240a125bbc91d2ce1ff3c835888a77))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/certd/certd/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||||
|
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/certd/certd/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||||
|
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||||
|
* 支持阿里云中文域名申请 ([b3468cf](https://github.com/certd/certd/commit/b3468cf7f28228d7c9cf68de6b5a9bbeb67f2c6d))
|
||||||
|
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
|
||||||
|
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ https://your_server_ip:7002
|
|||||||
|
|
||||||
## 二、升级
|
## 二、升级
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
如果您是第一次升级certd版本,切记切记先备份一下数据
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
### 如果使用固定版本号
|
### 如果使用固定版本号
|
||||||
1. 修改`docker-compose.yaml`中的镜像版本号
|
1. 修改`docker-compose.yaml`中的镜像版本号
|
||||||
2. 运行`docker compose up -d` 即可
|
2. 运行`docker compose up -d` 即可
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ kill -9 $(lsof -t -i:7001)
|
|||||||
./start.sh
|
./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
升级certd版本前,切记切记先备份一下数据
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
## 三、数据备份
|
## 三、数据备份
|
||||||
> 数据默认保存在 `./packages/ui/certd-server/data` 目录下
|
> 数据默认保存在 `./packages/ui/certd-server/data` 目录下
|
||||||
> 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份
|
> 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份
|
||||||
|
|||||||
@@ -8,5 +8,9 @@
|
|||||||
3. [1Panel面板方式部署升级](./1panel/#三、升级)
|
3. [1Panel面板方式部署升级](./1panel/#三、升级)
|
||||||
4. [源码方式部署](./source/#二、升级)
|
4. [源码方式部署](./source/#二、升级)
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
如果您是第一次升级certd版本,切记切记先备份一下数据
|
||||||
|
:::
|
||||||
|
|
||||||
## 升级日志
|
## 升级日志
|
||||||
[CHANGELOG](../changelogs/CHANGELOG.md)
|
[CHANGELOG](../changelogs/CHANGELOG.md)
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmClient": "pnpm",
|
"npmClient": "pnpm",
|
||||||
"version": "1.33.7"
|
"version": "1.33.8"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/publishlab/node-acme-client/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/publishlab/node-acme-client/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/publishlab/node-acme-client/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||||
|
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/publishlab/node-acme-client/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||||
|
|
||||||
## [1.33.7](https://github.com/publishlab/node-acme-client/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/publishlab/node-acme-client/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/acme-client
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "Simple and unopinionated ACME client",
|
"description": "Simple and unopinionated ACME client",
|
||||||
"private": false,
|
"private": false,
|
||||||
"author": "nmorsman",
|
"author": "nmorsman",
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "scr/index.js",
|
"module": "scr/index.js",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.33.7",
|
"@certd/basic": "^1.33.8",
|
||||||
"@peculiar/x509": "^1.11.0",
|
"@peculiar/x509": "^1.11.0",
|
||||||
"asn1js": "^3.0.5",
|
"asn1js": "^3.0.5",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
@@ -26,7 +26,8 @@
|
|||||||
"http-proxy-agent": "^7.0.2",
|
"http-proxy-agent": "^7.0.2",
|
||||||
"https-proxy-agent": "^7.0.5",
|
"https-proxy-agent": "^7.0.5",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"node-forge": "^1.3.1"
|
"node-forge": "^1.3.1",
|
||||||
|
"punycode": "^2.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.14.10",
|
"@types/node": "^20.14.10",
|
||||||
@@ -67,5 +68,5 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,12 +117,12 @@ export default async (client, userOpts) => {
|
|||||||
|
|
||||||
log(`[auto] [${d}] Trigger challengeCreateFn()`);
|
log(`[auto] [${d}] Trigger challengeCreateFn()`);
|
||||||
try {
|
try {
|
||||||
const { recordReq, recordRes, dnsProvider, challenge, keyAuthorization } = await opts.challengeCreateFn(authz, keyAuthorizationGetter);
|
const { recordReq, recordRes, dnsProvider, challenge, keyAuthorization ,httpUploader} = await opts.challengeCreateFn(authz, keyAuthorizationGetter);
|
||||||
clearTasks.push(async () => {
|
clearTasks.push(async () => {
|
||||||
/* Trigger challengeRemoveFn(), suppress errors */
|
/* Trigger challengeRemoveFn(), suppress errors */
|
||||||
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
|
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
|
||||||
try {
|
try {
|
||||||
await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordReq, recordRes, dnsProvider);
|
await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordReq, recordRes, dnsProvider,httpUploader);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`);
|
log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,3 +46,5 @@ export * from './axios.js'
|
|||||||
export * from './logger.js'
|
export * from './logger.js'
|
||||||
export * from './verify.js'
|
export * from './verify.js'
|
||||||
export * from './error.js'
|
export * from './error.js'
|
||||||
|
|
||||||
|
export * from './util.js'
|
||||||
@@ -340,5 +340,6 @@ export {
|
|||||||
formatResponseError,
|
formatResponseError,
|
||||||
getAuthoritativeDnsResolver,
|
getAuthoritativeDnsResolver,
|
||||||
retrieveTlsAlpnCertificate,
|
retrieveTlsAlpnCertificate,
|
||||||
|
resolveDomainBySoaRecord
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -59,7 +59,7 @@ export interface ClientExternalAccountBindingOptions {
|
|||||||
export interface ClientAutoOptions {
|
export interface ClientAutoOptions {
|
||||||
csr: CsrBuffer | CsrString;
|
csr: CsrBuffer | CsrString;
|
||||||
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string}>;
|
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string}>;
|
||||||
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any) => Promise<any>;
|
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any,httpUploader:any) => Promise<any>;
|
||||||
email?: string;
|
email?: string;
|
||||||
termsOfServiceAgreed?: boolean;
|
termsOfServiceAgreed?: boolean;
|
||||||
skipChallengeVerification?: boolean;
|
skipChallengeVerification?: boolean;
|
||||||
@@ -204,4 +204,6 @@ export function setLogger(fn: (message: any, ...args: any[]) => void): void;
|
|||||||
|
|
||||||
export function walkTxtRecord(record: any): Promise<string[]>;
|
export function walkTxtRecord(record: any): Promise<string[]>;
|
||||||
|
|
||||||
export const CancelError: typeof CancelError;
|
export const CancelError: typeof CancelError;
|
||||||
|
|
||||||
|
export function resolveDomainBySoaRecord(domain: string): Promise<string>;
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
/**
|
|
||||||
* acme-client type definition tests
|
|
||||||
*/
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
||||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
||||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
||||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
||||||
function step(op) {
|
|
||||||
if (f) throw new TypeError("Generator is already executing.");
|
|
||||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
||||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
||||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
||||||
switch (op[0]) {
|
|
||||||
case 0: case 1: t = op; break;
|
|
||||||
case 4: _.label++; return { value: op[1], done: false };
|
|
||||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
||||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
||||||
default:
|
|
||||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
||||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
||||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
||||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
||||||
if (t[2]) _.ops.pop();
|
|
||||||
_.trys.pop(); continue;
|
|
||||||
}
|
|
||||||
op = body.call(thisArg, _);
|
|
||||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
||||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
var acme = require("acme-client");
|
|
||||||
(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
||||||
var accountKey, client, order, authorizations, authorization, challenge, _a, certKey, certCsr;
|
|
||||||
return __generator(this, function (_b) {
|
|
||||||
switch (_b.label) {
|
|
||||||
case 0: return [4 /*yield*/, acme.crypto.createPrivateKey()];
|
|
||||||
case 1:
|
|
||||||
accountKey = _b.sent();
|
|
||||||
client = new acme.Client({
|
|
||||||
accountKey: accountKey,
|
|
||||||
directoryUrl: acme.directory.letsencrypt.staging
|
|
||||||
});
|
|
||||||
/* Account */
|
|
||||||
return [4 /*yield*/, client.createAccount({
|
|
||||||
termsOfServiceAgreed: true,
|
|
||||||
contact: ['mailto:test@example.com']
|
|
||||||
})];
|
|
||||||
case 2:
|
|
||||||
/* Account */
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.createOrder({
|
|
||||||
identifiers: [
|
|
||||||
{ type: 'dns', value: 'example.com' },
|
|
||||||
{ type: 'dns', value: '*.example.com' },
|
|
||||||
]
|
|
||||||
})];
|
|
||||||
case 3:
|
|
||||||
order = _b.sent();
|
|
||||||
return [4 /*yield*/, client.getOrder(order)];
|
|
||||||
case 4:
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.getAuthorizations(order)];
|
|
||||||
case 5:
|
|
||||||
authorizations = _b.sent();
|
|
||||||
authorization = authorizations[0];
|
|
||||||
challenge = authorization.challenges[0];
|
|
||||||
return [4 /*yield*/, client.getChallengeKeyAuthorization(challenge)];
|
|
||||||
case 6:
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.verifyChallenge(authorization, challenge)];
|
|
||||||
case 7:
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.completeChallenge(challenge)];
|
|
||||||
case 8:
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.waitForValidStatus(challenge)];
|
|
||||||
case 9:
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, acme.crypto.createCsr({
|
|
||||||
commonName: 'example.com',
|
|
||||||
altNames: ['example.com', '*.example.com']
|
|
||||||
})];
|
|
||||||
case 10:
|
|
||||||
_a = _b.sent(), certKey = _a[0], certCsr = _a[1];
|
|
||||||
return [4 /*yield*/, client.finalizeOrder(order, certCsr)];
|
|
||||||
case 11:
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.getCertificate(order)];
|
|
||||||
case 12:
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.getCertificate(order, 'DST Root CA X3')];
|
|
||||||
case 13:
|
|
||||||
_b.sent();
|
|
||||||
/* Auto */
|
|
||||||
return [4 /*yield*/, client.auto({
|
|
||||||
csr: certCsr,
|
|
||||||
challengeCreateFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
||||||
return [2 /*return*/];
|
|
||||||
}); }); },
|
|
||||||
challengeRemoveFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
||||||
return [2 /*return*/];
|
|
||||||
}); }); }
|
|
||||||
})];
|
|
||||||
case 14:
|
|
||||||
/* Auto */
|
|
||||||
_b.sent();
|
|
||||||
return [4 /*yield*/, client.auto({
|
|
||||||
csr: certCsr,
|
|
||||||
email: 'test@example.com',
|
|
||||||
termsOfServiceAgreed: false,
|
|
||||||
skipChallengeVerification: false,
|
|
||||||
challengePriority: ['http-01', 'dns-01'],
|
|
||||||
preferredChain: 'DST Root CA X3',
|
|
||||||
challengeCreateFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
||||||
return [2 /*return*/];
|
|
||||||
}); }); },
|
|
||||||
challengeRemoveFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
||||||
return [2 /*return*/];
|
|
||||||
}); }); }
|
|
||||||
})];
|
|
||||||
case 15:
|
|
||||||
_b.sent();
|
|
||||||
return [2 /*return*/];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}); })();
|
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22:12
|
01:54
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/basic",
|
"name": "@certd/basic",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -44,5 +44,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/pipeline
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/pipeline",
|
"name": "@certd/pipeline",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
"test": "mocha --loader=ts-node/esm"
|
"test": "mocha --loader=ts-node/esm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.33.7",
|
"@certd/basic": "^1.33.8",
|
||||||
"@certd/plus-core": "^1.33.7",
|
"@certd/plus-core": "^1.33.8",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
@@ -43,5 +43,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-huawei
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-huawei",
|
"name": "@certd/lib-huawei",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
"types": "./dist/d/index.d.ts",
|
"types": "./dist/d/index.d.ts",
|
||||||
@@ -23,5 +23,5 @@
|
|||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-iframe
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-iframe",
|
"name": "@certd/lib-iframe",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -30,5 +30,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/jdcloud
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/jdcloud",
|
"name": "@certd/jdcloud",
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"description": "jdcloud openApi sdk",
|
"description": "jdcloud openApi sdk",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
@@ -60,5 +60,5 @@
|
|||||||
"fetch"
|
"fetch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-k8s
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-k8s",
|
"name": "@certd/lib-k8s",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.33.7",
|
"@certd/basic": "^1.33.8",
|
||||||
"@kubernetes/client-node": "0.21.0"
|
"@kubernetes/client-node": "0.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-server
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-server",
|
"name": "@certd/lib-server",
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -27,10 +27,10 @@
|
|||||||
],
|
],
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.33.7",
|
"@certd/acme-client": "^1.33.8",
|
||||||
"@certd/basic": "^1.33.7",
|
"@certd/basic": "^1.33.8",
|
||||||
"@certd/pipeline": "^1.33.7",
|
"@certd/pipeline": "^1.33.8",
|
||||||
"@certd/plus-core": "^1.33.7",
|
"@certd/plus-core": "^1.33.8",
|
||||||
"@midwayjs/cache": "~3.14.0",
|
"@midwayjs/cache": "~3.14.0",
|
||||||
"@midwayjs/core": "~3.20.3",
|
"@midwayjs/core": "~3.20.3",
|
||||||
"@midwayjs/i18n": "~3.20.3",
|
"@midwayjs/i18n": "~3.20.3",
|
||||||
@@ -61,5 +61,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/midway-flyway-js",
|
"name": "@certd/midway-flyway-js",
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -46,5 +46,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,19 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/certd/certd/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||||
|
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||||
|
* 支持阿里云中文域名申请 ([b3468cf](https://github.com/certd/certd/commit/b3468cf7f28228d7c9cf68de6b5a9bbeb67f2c6d))
|
||||||
|
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-cert
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-cert",
|
"name": "@certd/plugin-cert",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -15,15 +15,16 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.33.7",
|
"@certd/acme-client": "^1.33.8",
|
||||||
"@certd/basic": "^1.33.7",
|
"@certd/basic": "^1.33.8",
|
||||||
"@certd/pipeline": "^1.33.7",
|
"@certd/pipeline": "^1.33.8",
|
||||||
"@certd/plugin-lib": "^1.33.7",
|
"@certd/plugin-lib": "^1.33.8",
|
||||||
"@google-cloud/publicca": "^1.3.0",
|
"@google-cloud/publicca": "^1.3.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"psl": "^1.9.0",
|
"psl": "^1.9.0",
|
||||||
|
"punycode": "^2.3.1",
|
||||||
"rimraf": "^5.0.5"
|
"rimraf": "^5.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -41,5 +42,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export interface IDnsProvider<T = any> {
|
|||||||
createRecord(options: CreateRecordOptions): Promise<T>;
|
createRecord(options: CreateRecordOptions): Promise<T>;
|
||||||
removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
||||||
setCtx(ctx: DnsProviderContext): void;
|
setCtx(ctx: DnsProviderContext): void;
|
||||||
|
//中文域名是否需要punycode转码,如果返回True,则使用punycode来添加解析记录,否则使用中文域名添加解析记录
|
||||||
|
usePunyCode(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISubDomainsGetter {
|
export interface ISubDomainsGetter {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
|||||||
http!: HttpClient;
|
http!: HttpClient;
|
||||||
logger!: ILogger;
|
logger!: ILogger;
|
||||||
|
|
||||||
|
usePunyCode(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setCtx(ctx: DnsProviderContext) {
|
setCtx(ctx: DnsProviderContext) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.logger = ctx.logger;
|
this.logger = ctx.logger;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { IDomainParser, ISubDomainsGetter } from "./api";
|
import { IDomainParser, ISubDomainsGetter } from "./api";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import psl from "psl";
|
import psl from "psl";
|
||||||
|
import { resolveDomainBySoaRecord } from "@certd/acme-client";
|
||||||
|
import { logger, utils } from "@certd/basic";
|
||||||
|
|
||||||
export class DomainParser implements IDomainParser {
|
export class DomainParser implements IDomainParser {
|
||||||
subDomainsGetter: ISubDomainsGetter;
|
subDomainsGetter: ISubDomainsGetter;
|
||||||
@@ -17,16 +19,38 @@ export class DomainParser implements IDomainParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parse(fullDomain: string) {
|
async parse(fullDomain: string) {
|
||||||
const subDomains = await this.subDomainsGetter.getSubDomains();
|
logger.info(`查找主域名:${fullDomain}`);
|
||||||
if (subDomains && subDomains.length > 0) {
|
const cacheKey = `domain_parse:${fullDomain}`;
|
||||||
for (const subDomain of subDomains) {
|
const value = utils.cache.get(cacheKey);
|
||||||
if (fullDomain.endsWith(subDomain)) {
|
if (value) {
|
||||||
//找到子域名托管
|
logger.info(`从缓存获取到主域名:${fullDomain}->${value}`);
|
||||||
return subDomain;
|
return value;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
const mainDomain = await resolveDomainBySoaRecord(fullDomain);
|
||||||
|
if (mainDomain) {
|
||||||
|
utils.cache.set(cacheKey, mainDomain, {
|
||||||
|
ttl: 2 * 60 * 1000,
|
||||||
|
});
|
||||||
|
logger.info(`获取到主域名:${fullDomain}->${mainDomain}`);
|
||||||
|
return mainDomain;
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("从SOA获取主域名失败", e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.parseDomain(fullDomain);
|
// const subDomains = await this.subDomainsGetter.getSubDomains();
|
||||||
|
// if (subDomains && subDomains.length > 0) {
|
||||||
|
// for (const subDomain of subDomains) {
|
||||||
|
// if (fullDomain.endsWith(subDomain)) {
|
||||||
|
// //找到子域名托管
|
||||||
|
// return subDomain;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const res = this.parseDomain(fullDomain);
|
||||||
|
logger.info(`从psl获取主域名:${fullDomain}->${res}`);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { Challenge } from "@certd/acme-client/types/rfc8555";
|
|||||||
import { IContext } from "@certd/pipeline";
|
import { IContext } from "@certd/pipeline";
|
||||||
import { ILogger, utils } from "@certd/basic";
|
import { ILogger, utils } from "@certd/basic";
|
||||||
import { IDnsProvider, IDomainParser } from "../../dns-provider/index.js";
|
import { IDnsProvider, IDomainParser } from "../../dns-provider/index.js";
|
||||||
import { HttpChallengeUploader } from "./uploads/api.js";
|
import punycode from "node:punycode";
|
||||||
|
import { IOssClient } from "@certd/plugin-lib";
|
||||||
export type CnameVerifyPlan = {
|
export type CnameVerifyPlan = {
|
||||||
type?: string;
|
type?: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
@@ -18,7 +18,7 @@ export type CnameVerifyPlan = {
|
|||||||
export type HttpVerifyPlan = {
|
export type HttpVerifyPlan = {
|
||||||
type: string;
|
type: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
httpUploader: HttpChallengeUploader;
|
httpUploader: IOssClient;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DomainVerifyPlan = {
|
export type DomainVerifyPlan = {
|
||||||
@@ -35,7 +35,6 @@ export type DomainsVerifyPlan = {
|
|||||||
export type Providers = {
|
export type Providers = {
|
||||||
dnsProvider?: IDnsProvider;
|
dnsProvider?: IDnsProvider;
|
||||||
domainsVerifyPlan?: DomainsVerifyPlan;
|
domainsVerifyPlan?: DomainsVerifyPlan;
|
||||||
httpUploader?: HttpChallengeUploader;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CertInfo = {
|
export type CertInfo = {
|
||||||
@@ -184,7 +183,7 @@ export class AcmeService {
|
|||||||
return authz.challenges.find((c: any) => c.type === type);
|
return authz.challenges.find((c: any) => c.type === type);
|
||||||
};
|
};
|
||||||
|
|
||||||
const doHttpVerify = async (challenge: any, httpUploader: HttpChallengeUploader) => {
|
const doHttpVerify = async (challenge: any, httpUploader: IOssClient) => {
|
||||||
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
||||||
this.logger.info("http校验");
|
this.logger.info("http校验");
|
||||||
const filePath = `.well-known/acme-challenge/${challenge.token}`;
|
const filePath = `.well-known/acme-challenge/${challenge.token}`;
|
||||||
@@ -203,14 +202,16 @@ export class AcmeService {
|
|||||||
this.logger.info("dns校验");
|
this.logger.info("dns校验");
|
||||||
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
||||||
|
|
||||||
|
const mainDomain = dnsProvider.usePunyCode() ? domain : punycode.toUnicode(domain);
|
||||||
|
fullRecord = dnsProvider.usePunyCode() ? fullRecord : punycode.toUnicode(fullRecord);
|
||||||
const recordValue = keyAuthorization;
|
const recordValue = keyAuthorization;
|
||||||
let hostRecord = fullRecord.replace(`${domain}`, "");
|
let hostRecord = fullRecord.replace(`${mainDomain}`, "");
|
||||||
if (hostRecord.endsWith(".")) {
|
if (hostRecord.endsWith(".")) {
|
||||||
hostRecord = hostRecord.substring(0, hostRecord.length - 1);
|
hostRecord = hostRecord.substring(0, hostRecord.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const recordReq = {
|
const recordReq = {
|
||||||
domain,
|
domain: mainDomain,
|
||||||
fullRecord,
|
fullRecord,
|
||||||
hostRecord,
|
hostRecord,
|
||||||
type: "TXT",
|
type: "TXT",
|
||||||
@@ -286,7 +287,7 @@ export class AcmeService {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider?: IDnsProvider, httpUploader?: HttpChallengeUploader) {
|
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider?: IDnsProvider, httpUploader?: IOssClient) {
|
||||||
this.logger.info("执行清理");
|
this.logger.info("执行清理");
|
||||||
|
|
||||||
/* http-01 */
|
/* http-01 */
|
||||||
@@ -321,9 +322,16 @@ export class AcmeService {
|
|||||||
isTest?: boolean;
|
isTest?: boolean;
|
||||||
privateKeyType?: string;
|
privateKeyType?: string;
|
||||||
}): Promise<CertInfo> {
|
}): Promise<CertInfo> {
|
||||||
const { email, isTest, domains, csrInfo, dnsProvider, domainsVerifyPlan, httpUploader } = options;
|
const { email, isTest, csrInfo, dnsProvider, domainsVerifyPlan } = options;
|
||||||
const client: acme.Client = await this.getAcmeClient(email, isTest);
|
const client: acme.Client = await this.getAcmeClient(email, isTest);
|
||||||
|
|
||||||
|
let domains = options.domains;
|
||||||
|
const encodingDomains = [];
|
||||||
|
for (const domain of domains) {
|
||||||
|
encodingDomains.push(punycode.toASCII(domain));
|
||||||
|
}
|
||||||
|
domains = encodingDomains;
|
||||||
|
|
||||||
/* Create CSR */
|
/* Create CSR */
|
||||||
const { commonName, altNames } = this.buildCommonNameByDomains(domains);
|
const { commonName, altNames } = this.buildCommonNameByDomains(domains);
|
||||||
let privateKey = null;
|
let privateKey = null;
|
||||||
@@ -361,14 +369,13 @@ export class AcmeService {
|
|||||||
privateKey
|
privateKey
|
||||||
);
|
);
|
||||||
|
|
||||||
if (dnsProvider == null && domainsVerifyPlan == null && httpUploader == null) {
|
if (dnsProvider == null && domainsVerifyPlan == null) {
|
||||||
throw new Error("dnsProvider 、 domainsVerifyPlan 、 httpUploader不能都为空");
|
throw new Error("dnsProvider 、 domainsVerifyPlan不能都为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
const providers: Providers = {
|
const providers: Providers = {
|
||||||
dnsProvider,
|
dnsProvider,
|
||||||
domainsVerifyPlan,
|
domainsVerifyPlan,
|
||||||
httpUploader,
|
|
||||||
};
|
};
|
||||||
/* 自动申请证书 */
|
/* 自动申请证书 */
|
||||||
const crt = await client.auto({
|
const crt = await client.auto({
|
||||||
@@ -383,7 +390,7 @@ export class AcmeService {
|
|||||||
): Promise<{ recordReq?: any; recordRes?: any; dnsProvider?: any; challenge: Challenge; keyAuthorization: string }> => {
|
): Promise<{ recordReq?: any; recordRes?: any; dnsProvider?: any; challenge: Challenge; keyAuthorization: string }> => {
|
||||||
return await this.challengeCreateFn(authz, keyAuthorizationGetter, providers);
|
return await this.challengeCreateFn(authz, keyAuthorizationGetter, providers);
|
||||||
},
|
},
|
||||||
challengeRemoveFn: async (authz: acme.Authorization, challenge: Challenge, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider: IDnsProvider): Promise<any> => {
|
challengeRemoveFn: async (authz: acme.Authorization, challenge: Challenge, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider: IDnsProvider, httpUploader: IOssClient): Promise<any> => {
|
||||||
return await this.challengeRemoveFn(authz, challenge, keyAuthorization, recordReq, recordRes, dnsProvider, httpUploader);
|
return await this.challengeRemoveFn(authz, challenge, keyAuthorization, recordReq, recordRes, dnsProvider, httpUploader);
|
||||||
},
|
},
|
||||||
signal: this.options.signal,
|
signal: this.options.signal,
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
|||||||
"1、支持多个域名打到一个证书上,例如: foo.com,*.foo.com,*.bar.com\n" +
|
"1、支持多个域名打到一个证书上,例如: foo.com,*.foo.com,*.bar.com\n" +
|
||||||
"2、子域名被通配符包含的不要填写,例如:www.foo.com已经被*.foo.com包含,不要填写www.foo.com\n" +
|
"2、子域名被通配符包含的不要填写,例如:www.foo.com已经被*.foo.com包含,不要填写www.foo.com\n" +
|
||||||
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于xxx.yyy.foo.com、不能用于foo.com)\n" +
|
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于xxx.yyy.foo.com、不能用于foo.com)\n" +
|
||||||
"4、输入一个,空格之后,再输入下一个\n" +
|
"4、输入一个,空格之后,再输入下一个",
|
||||||
"5、如果你配置了子域托管解析,请先[设置托管子域名](#/certd/pipeline/subDomain)",
|
|
||||||
})
|
})
|
||||||
domains!: string[];
|
domains!: string[];
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { CertReader } from "./cert-reader.js";
|
|||||||
import { CertApplyBasePlugin } from "./base.js";
|
import { CertApplyBasePlugin } from "./base.js";
|
||||||
import { GoogleClient } from "../../libs/google.js";
|
import { GoogleClient } from "../../libs/google.js";
|
||||||
import { EabAccess } from "../../access";
|
import { EabAccess } from "../../access";
|
||||||
import { httpChallengeUploaderFactory } from "./uploads/factory.js";
|
|
||||||
import { DomainParser } from "../../dns-provider/domain-parser.js";
|
import { DomainParser } from "../../dns-provider/domain-parser.js";
|
||||||
|
import { ossClientFactory } from "@certd/plugin-lib";
|
||||||
export * from "./base.js";
|
export * from "./base.js";
|
||||||
export type { CertInfo };
|
export type { CertInfo };
|
||||||
export * from "./cert-reader.js";
|
export * from "./cert-reader.js";
|
||||||
@@ -115,6 +115,7 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
|||||||
})
|
})
|
||||||
dnsProviderType!: string;
|
dnsProviderType!: string;
|
||||||
|
|
||||||
|
// dns解析授权类型,勿删
|
||||||
dnsProviderAccessType!: string;
|
dnsProviderAccessType!: string;
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
@@ -446,7 +447,7 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
|||||||
rootDir = rootDir + "/";
|
rootDir = rootDir + "/";
|
||||||
}
|
}
|
||||||
this.logger.info("上传方式", httpRecord.httpUploaderType);
|
this.logger.info("上传方式", httpRecord.httpUploaderType);
|
||||||
const httpUploader = await httpChallengeUploaderFactory.createUploaderByType(httpRecord.httpUploaderType, {
|
const httpUploader = await ossClientFactory.createOssClientByType(httpRecord.httpUploaderType, {
|
||||||
access,
|
access,
|
||||||
rootDir: rootDir,
|
rootDir: rootDir,
|
||||||
ctx: httpUploaderContext,
|
ctx: httpUploaderContext,
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import { IAccessService } from "@certd/pipeline";
|
|
||||||
import { ILogger, utils } from "@certd/basic";
|
|
||||||
|
|
||||||
export type HttpChallengeUploader = {
|
|
||||||
upload: (fileName: string, fileContent: Buffer) => Promise<void>;
|
|
||||||
remove: (fileName: string) => Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HttpChallengeUploadContext = {
|
|
||||||
accessService: IAccessService;
|
|
||||||
logger: ILogger;
|
|
||||||
utils: typeof utils;
|
|
||||||
};
|
|
||||||
|
|
||||||
export abstract class BaseHttpChallengeUploader<A> implements HttpChallengeUploader {
|
|
||||||
rootDir: string;
|
|
||||||
access: A = null;
|
|
||||||
logger: ILogger;
|
|
||||||
utils: typeof utils;
|
|
||||||
ctx: HttpChallengeUploadContext;
|
|
||||||
protected constructor(opts: { rootDir: string; access: A }) {
|
|
||||||
this.rootDir = opts.rootDir;
|
|
||||||
this.access = opts.access;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setCtx(ctx: any) {
|
|
||||||
// set context
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.logger = ctx.logger;
|
|
||||||
this.utils = ctx.utils;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract remove(fileName: string): Promise<void>;
|
|
||||||
abstract upload(fileName: string, fileContent: Buffer): Promise<void>;
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { BaseHttpChallengeUploader } from "../api.js";
|
|
||||||
import { AliossAccess, AliyunAccess } from "@certd/plugin-lib";
|
|
||||||
import { AliossClient } from "@certd/plugin-lib";
|
|
||||||
|
|
||||||
export class AliossHttpChallengeUploader extends BaseHttpChallengeUploader<AliossAccess> {
|
|
||||||
async upload(filePath: string, fileContent: Buffer) {
|
|
||||||
const aliyunAccess = await this.ctx.accessService.getById<AliyunAccess>(this.access.accessId);
|
|
||||||
const client = new AliossClient({
|
|
||||||
access: aliyunAccess,
|
|
||||||
bucket: this.access.bucket,
|
|
||||||
region: this.access.region,
|
|
||||||
});
|
|
||||||
|
|
||||||
const key = this.rootDir + filePath;
|
|
||||||
this.logger.info(`开始上传文件: ${key}`);
|
|
||||||
await client.uploadFile(key, fileContent);
|
|
||||||
|
|
||||||
this.logger.info(`校验文件上传成功: ${filePath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(filePath: string) {
|
|
||||||
const key = this.rootDir + filePath;
|
|
||||||
// remove file from alioss
|
|
||||||
const client = await this.getAliossClient();
|
|
||||||
await client.removeFile(key);
|
|
||||||
this.logger.info(`文件删除成功: ${key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getAliossClient() {
|
|
||||||
const aliyunAccess = await this.ctx.accessService.getById<AliyunAccess>(this.access.accessId);
|
|
||||||
const client = new AliossClient({
|
|
||||||
access: aliyunAccess,
|
|
||||||
bucket: this.access.bucket,
|
|
||||||
region: this.access.region,
|
|
||||||
});
|
|
||||||
await client.init();
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { BaseHttpChallengeUploader } from "../api.js";
|
|
||||||
import { FtpAccess, FtpClient } from "@certd/plugin-lib";
|
|
||||||
import path from "path";
|
|
||||||
import os from "os";
|
|
||||||
import fs from "fs";
|
|
||||||
|
|
||||||
export class FtpHttpChallengeUploader extends BaseHttpChallengeUploader<FtpAccess> {
|
|
||||||
async upload(filePath: string, fileContent: Buffer) {
|
|
||||||
const client = new FtpClient({
|
|
||||||
access: this.access,
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
await client.connect(async (client) => {
|
|
||||||
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
|
||||||
const dir = path.dirname(tmpFilePath);
|
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
|
||||||
}
|
|
||||||
fs.writeFileSync(tmpFilePath, fileContent);
|
|
||||||
try {
|
|
||||||
// Write file to temp path
|
|
||||||
const path = this.rootDir + filePath;
|
|
||||||
await client.upload(path, tmpFilePath);
|
|
||||||
} finally {
|
|
||||||
// Remove temp file
|
|
||||||
fs.unlinkSync(tmpFilePath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(filePath: string) {
|
|
||||||
const client = new FtpClient({
|
|
||||||
access: this.access,
|
|
||||||
logger: this.logger,
|
|
||||||
});
|
|
||||||
await client.connect(async (client) => {
|
|
||||||
const path = this.rootDir + filePath;
|
|
||||||
await client.client.remove(path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { BaseHttpChallengeUploader } from "../api.js";
|
|
||||||
import { QiniuOssAccess, QiniuClient, QiniuAccess } from "@certd/plugin-lib";
|
|
||||||
|
|
||||||
export class QiniuOssHttpChallengeUploader extends BaseHttpChallengeUploader<QiniuOssAccess> {
|
|
||||||
async upload(filePath: string, fileContent: Buffer) {
|
|
||||||
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
|
|
||||||
const client = new QiniuClient({
|
|
||||||
access: qiniuAccess,
|
|
||||||
logger: this.logger,
|
|
||||||
http: this.ctx.utils.http,
|
|
||||||
});
|
|
||||||
if (this.rootDir.endsWith("/")) {
|
|
||||||
this.rootDir = this.rootDir.slice(0, -1);
|
|
||||||
}
|
|
||||||
await client.uploadFile(this.access.bucket, this.rootDir + filePath, fileContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(filePath: string) {
|
|
||||||
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
|
|
||||||
const client = new QiniuClient({
|
|
||||||
access: qiniuAccess,
|
|
||||||
logger: this.logger,
|
|
||||||
http: this.ctx.utils.http,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.rootDir.endsWith("/")) {
|
|
||||||
this.rootDir = this.rootDir.slice(0, -1);
|
|
||||||
}
|
|
||||||
await client.removeFile(this.access.bucket, this.rootDir + filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import { BaseHttpChallengeUploader } from "../api.js";
|
|
||||||
import { SshAccess, SshClient } from "@certd/plugin-lib";
|
|
||||||
import path from "path";
|
|
||||||
import os from "os";
|
|
||||||
import fs from "fs";
|
|
||||||
import { SftpAccess } from "@certd/plugin-lib";
|
|
||||||
|
|
||||||
export class SftpHttpChallengeUploader extends BaseHttpChallengeUploader<SftpAccess> {
|
|
||||||
async upload(filePath: string, fileContent: Buffer) {
|
|
||||||
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
|
||||||
|
|
||||||
// Write file to temp path
|
|
||||||
const dir = path.dirname(tmpFilePath);
|
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
|
||||||
}
|
|
||||||
fs.writeFileSync(tmpFilePath, fileContent);
|
|
||||||
|
|
||||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
|
||||||
const key = this.rootDir + filePath;
|
|
||||||
try {
|
|
||||||
const client = new SshClient(this.logger);
|
|
||||||
await client.uploadFiles({
|
|
||||||
connectConf: access,
|
|
||||||
mkdirs: true,
|
|
||||||
transports: [
|
|
||||||
{
|
|
||||||
localPath: tmpFilePath,
|
|
||||||
remotePath: key,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
opts: {
|
|
||||||
mode: this.access?.fileMode ?? undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
// Remove temp file
|
|
||||||
fs.unlinkSync(tmpFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(filePath: string) {
|
|
||||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
|
||||||
const client = new SshClient(this.logger);
|
|
||||||
const key = this.rootDir + filePath;
|
|
||||||
await client.removeFiles({
|
|
||||||
connectConf: access,
|
|
||||||
files: [key],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { BaseHttpChallengeUploader } from "../api.js";
|
|
||||||
import { TencentAccess, TencentCosAccess, TencentCosClient } from "@certd/plugin-lib";
|
|
||||||
|
|
||||||
export class TencentCosHttpChallengeUploader extends BaseHttpChallengeUploader<TencentCosAccess> {
|
|
||||||
async upload(filePath: string, fileContent: Buffer) {
|
|
||||||
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
|
|
||||||
const client = new TencentCosClient({
|
|
||||||
access: access,
|
|
||||||
logger: this.logger,
|
|
||||||
region: this.access.region,
|
|
||||||
bucket: this.access.bucket,
|
|
||||||
});
|
|
||||||
const key = this.rootDir + filePath;
|
|
||||||
await client.uploadFile(key, fileContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(filePath: string) {
|
|
||||||
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
|
|
||||||
const client = new TencentCosClient({
|
|
||||||
access: access,
|
|
||||||
logger: this.logger,
|
|
||||||
region: this.access.region,
|
|
||||||
bucket: this.access.bucket,
|
|
||||||
});
|
|
||||||
const key = this.rootDir + filePath;
|
|
||||||
await client.removeFile(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/certd/certd/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||||
|
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-lib",
|
"name": "@certd/plugin-lib",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -16,10 +16,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@certd/basic": "^1.33.7",
|
"@aws-sdk/client-s3": "^3.787.0",
|
||||||
"@certd/pipeline": "^1.33.7",
|
"@certd/basic": "^1.33.8",
|
||||||
|
"@certd/pipeline": "^1.33.8",
|
||||||
"@kubernetes/client-node": "0.21.0",
|
"@kubernetes/client-node": "0.21.0",
|
||||||
"ali-oss": "^6.21.0",
|
"ali-oss": "^6.22.0",
|
||||||
"basic-ftp": "^5.0.5",
|
"basic-ftp": "^5.0.5",
|
||||||
"cos-nodejs-sdk-v5": "^2.14.6",
|
"cos-nodejs-sdk-v5": "^2.14.6",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
@@ -48,5 +49,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
|
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AliyunAccess } from "../access";
|
import { AliyunAccess } from "../access/index.js";
|
||||||
|
|
||||||
export class AliossClient {
|
export class AliossClient {
|
||||||
access: AliyunAccess;
|
access: AliyunAccess;
|
||||||
@@ -52,7 +52,7 @@ export class AliossClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadFile(filePath: string, content: Buffer) {
|
async uploadFile(filePath: string, content: Buffer | string) {
|
||||||
await this.init();
|
await this.init();
|
||||||
return await this.client.put(filePath, content);
|
return await this.client.put(filePath, content);
|
||||||
}
|
}
|
||||||
@@ -61,4 +61,23 @@ export class AliossClient {
|
|||||||
await this.init();
|
await this.init();
|
||||||
return await this.client.delete(filePath);
|
return await this.client.delete(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async downloadFile(key: string, savePath: string) {
|
||||||
|
await this.init();
|
||||||
|
return await this.client.get(key, savePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listDir(dirKey: string) {
|
||||||
|
await this.init();
|
||||||
|
const res = await this.client.listV2({
|
||||||
|
prefix: dirKey,
|
||||||
|
// max-keys: 100,
|
||||||
|
// continuation-token: "token",
|
||||||
|
// delimiter: "/",
|
||||||
|
// marker: "marker",
|
||||||
|
// encoding-type: "url",
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.objects;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class FtpClient {
|
|||||||
this.logger = opts.logger;
|
this.logger = opts.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(callback: (client: FtpClient) => Promise<void>) {
|
async connect(callback: (client: FtpClient) => Promise<any>) {
|
||||||
const ftp = await import("basic-ftp");
|
const ftp = await import("basic-ftp");
|
||||||
const Client = ftp.Client;
|
const Client = ftp.Client;
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
@@ -21,7 +21,7 @@ export class FtpClient {
|
|||||||
this.logger.info("FTP连接成功");
|
this.logger.info("FTP连接成功");
|
||||||
this.client = client;
|
this.client = client;
|
||||||
try {
|
try {
|
||||||
await callback(this);
|
return await callback(this);
|
||||||
} finally {
|
} finally {
|
||||||
if (client) {
|
if (client) {
|
||||||
client.close();
|
client.close();
|
||||||
@@ -44,4 +44,20 @@ export class FtpClient {
|
|||||||
this.logger.info(`开始删除文件${filePath}`);
|
this.logger.info(`开始删除文件${filePath}`);
|
||||||
await this.client.remove(filePath, true);
|
await this.client.remove(filePath, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listDir(dir: string): Promise<any[]> {
|
||||||
|
if (!dir) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (!dir.endsWith("/")) {
|
||||||
|
dir = dir + "/";
|
||||||
|
}
|
||||||
|
this.logger.info(`开始列出目录${dir}`);
|
||||||
|
return await this.client.list(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
async download(filePath: string, savePath: string): Promise<void> {
|
||||||
|
this.logger.info(`开始下载文件${filePath} -> ${savePath}`);
|
||||||
|
await this.client.downloadTo(savePath, filePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ export * from "./ftp/index.js";
|
|||||||
export * from "./tencent/index.js";
|
export * from "./tencent/index.js";
|
||||||
export * from "./qiniu/index.js";
|
export * from "./qiniu/index.js";
|
||||||
export * from "./ctyun/index.js";
|
export * from "./ctyun/index.js";
|
||||||
|
export * from "./oss/index.js";
|
||||||
|
export * from "./s3/index.js";
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { IAccessService } from "@certd/pipeline";
|
||||||
|
import { ILogger, utils } from "@certd/basic";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export type OssClientRemoveByOpts = {
|
||||||
|
dir?: string;
|
||||||
|
//删除多少天前的文件
|
||||||
|
beforeDays?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OssFileItem = {
|
||||||
|
//文件全路径
|
||||||
|
path: string;
|
||||||
|
size: number;
|
||||||
|
//毫秒时间戳
|
||||||
|
lastModified: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IOssClient = {
|
||||||
|
upload: (fileName: string, fileContent: Buffer) => Promise<void>;
|
||||||
|
remove: (fileName: string, opts?: { joinRootDir?: boolean }) => Promise<void>;
|
||||||
|
|
||||||
|
download: (fileName: string, savePath: string) => Promise<void>;
|
||||||
|
|
||||||
|
removeBy: (removeByOpts: OssClientRemoveByOpts) => Promise<void>;
|
||||||
|
|
||||||
|
listDir: (dir: string) => Promise<OssFileItem[]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OssClientContext = {
|
||||||
|
accessService: IAccessService;
|
||||||
|
logger: ILogger;
|
||||||
|
utils: typeof utils;
|
||||||
|
};
|
||||||
|
|
||||||
|
export abstract class BaseOssClient<A> implements IOssClient {
|
||||||
|
rootDir: string = "";
|
||||||
|
access: A = null;
|
||||||
|
logger: ILogger;
|
||||||
|
utils: typeof utils;
|
||||||
|
ctx: OssClientContext;
|
||||||
|
|
||||||
|
protected constructor(opts: { rootDir?: string; access: A }) {
|
||||||
|
this.rootDir = opts.rootDir || "";
|
||||||
|
this.access = opts.access;
|
||||||
|
}
|
||||||
|
|
||||||
|
join(...strs: string[]) {
|
||||||
|
let res = "";
|
||||||
|
for (const item of strs) {
|
||||||
|
if (item) {
|
||||||
|
if (!res) {
|
||||||
|
res = item;
|
||||||
|
} else {
|
||||||
|
res += "/" + item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = res.replace(/[\\/]+/g, "/");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCtx(ctx: any) {
|
||||||
|
// set context
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.logger = ctx.logger;
|
||||||
|
this.utils = ctx.utils;
|
||||||
|
await this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract remove(fileName: string, opts?: { joinRootDir?: boolean }): Promise<void>;
|
||||||
|
abstract upload(fileName: string, fileContent: Buffer): Promise<void>;
|
||||||
|
abstract download(fileName: string, savePath: string): Promise<void>;
|
||||||
|
abstract listDir(dir: string): Promise<OssFileItem[]>;
|
||||||
|
|
||||||
|
async removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
|
||||||
|
const list = await this.listDir(removeByOpts.dir);
|
||||||
|
// removeByOpts.beforeDays = 0;
|
||||||
|
const beforeDate = dayjs().subtract(removeByOpts.beforeDays, "day");
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.lastModified && item.lastModified < beforeDate.valueOf()) {
|
||||||
|
await this.remove(item.path, { joinRootDir: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-10
@@ -1,30 +1,33 @@
|
|||||||
import { HttpChallengeUploadContext } from "./api";
|
import { OssClientContext } from "./api";
|
||||||
|
|
||||||
export class HttpChallengeUploaderFactory {
|
export class OssClientFactory {
|
||||||
async getClassByType(type: string) {
|
async getClassByType(type: string) {
|
||||||
if (type === "alioss") {
|
if (type === "alioss") {
|
||||||
const module = await import("./impls/alioss.js");
|
const module = await import("./impls/alioss.js");
|
||||||
return module.AliossHttpChallengeUploader;
|
return module.default;
|
||||||
} else if (type === "ssh") {
|
} else if (type === "ssh") {
|
||||||
const module = await import("./impls/ssh.js");
|
const module = await import("./impls/ssh.js");
|
||||||
return module.SshHttpChallengeUploader;
|
return module.default;
|
||||||
} else if (type === "sftp") {
|
} else if (type === "sftp") {
|
||||||
const module = await import("./impls/sftp.js");
|
const module = await import("./impls/sftp.js");
|
||||||
return module.SftpHttpChallengeUploader;
|
return module.default;
|
||||||
} else if (type === "ftp") {
|
} else if (type === "ftp") {
|
||||||
const module = await import("./impls/ftp.js");
|
const module = await import("./impls/ftp.js");
|
||||||
return module.FtpHttpChallengeUploader;
|
return module.default;
|
||||||
} else if (type === "tencentcos") {
|
} else if (type === "tencentcos") {
|
||||||
const module = await import("./impls/tencentcos.js");
|
const module = await import("./impls/tencentcos.js");
|
||||||
return module.TencentCosHttpChallengeUploader;
|
return module.default;
|
||||||
} else if (type === "qiniuoss") {
|
} else if (type === "qiniuoss") {
|
||||||
const module = await import("./impls/qiniuoss.js");
|
const module = await import("./impls/qiniuoss.js");
|
||||||
return module.QiniuOssHttpChallengeUploader;
|
return module.default;
|
||||||
|
} else if (type === "s3") {
|
||||||
|
const module = await import("./impls/s3.js");
|
||||||
|
return module.default;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`暂不支持此文件上传方式: ${type}`);
|
throw new Error(`暂不支持此文件上传方式: ${type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async createUploaderByType(type: string, opts: { rootDir: string; access: any; ctx: HttpChallengeUploadContext }) {
|
async createOssClientByType(type: string, opts: { rootDir?: string; access: any; ctx: OssClientContext }) {
|
||||||
const cls = await this.getClassByType(type);
|
const cls = await this.getClassByType(type);
|
||||||
if (cls) {
|
if (cls) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -35,4 +38,4 @@ export class HttpChallengeUploaderFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const httpChallengeUploaderFactory = new HttpChallengeUploaderFactory();
|
export const ossClientFactory = new OssClientFactory();
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||||
|
import { AliossAccess, AliossClient, AliyunAccess } from "../../aliyun/index.js";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export default class AliOssClientImpl extends BaseOssClient<AliossAccess> {
|
||||||
|
client: AliossClient;
|
||||||
|
join(...strs: string[]) {
|
||||||
|
const str = super.join(...strs);
|
||||||
|
if (str.startsWith("/")) {
|
||||||
|
return str.substring(1);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
async init() {
|
||||||
|
const aliyunAccess = await this.ctx.accessService.getById<AliyunAccess>(this.access.accessId);
|
||||||
|
const client = new AliossClient({
|
||||||
|
access: aliyunAccess,
|
||||||
|
bucket: this.access.bucket,
|
||||||
|
region: this.access.region,
|
||||||
|
});
|
||||||
|
await client.init();
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
async download(filePath: string, savePath: string): Promise<void> {
|
||||||
|
const key = this.join(this.rootDir, filePath);
|
||||||
|
await this.client.downloadFile(key, savePath);
|
||||||
|
}
|
||||||
|
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||||
|
const dirKey = this.join(this.rootDir, dir) + "/";
|
||||||
|
const list = await this.client.listDir(dirKey);
|
||||||
|
this.logger.info(`列出目录: ${dirKey},文件数:${list.length}`);
|
||||||
|
return list.map(item => {
|
||||||
|
return {
|
||||||
|
path: item.name,
|
||||||
|
lastModified: dayjs(item.lastModified).valueOf(),
|
||||||
|
size: item.size,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async upload(filePath: string, fileContent: Buffer | string) {
|
||||||
|
const key = this.join(this.rootDir, filePath);
|
||||||
|
this.logger.info(`开始上传文件: ${key}`);
|
||||||
|
await this.client.uploadFile(key, fileContent);
|
||||||
|
|
||||||
|
this.logger.info(`文件上传成功: ${filePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||||
|
if (opts?.joinRootDir !== false) {
|
||||||
|
filePath = this.join(this.rootDir, filePath);
|
||||||
|
}
|
||||||
|
const key = filePath;
|
||||||
|
// remove file from alioss
|
||||||
|
await this.client.removeFile(key);
|
||||||
|
this.logger.info(`文件删除成功: ${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { BaseOssClient } from "../api.js";
|
||||||
|
import path from "path";
|
||||||
|
import os from "os";
|
||||||
|
import fs from "fs";
|
||||||
|
import { FtpAccess, FtpClient } from "../../ftp/index.js";
|
||||||
|
|
||||||
|
export default class FtpOssClientImpl extends BaseOssClient<FtpAccess> {
|
||||||
|
join(...strs: string[]) {
|
||||||
|
const str = super.join(...strs);
|
||||||
|
if (!str.startsWith("/")) {
|
||||||
|
return "/" + str;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
async download(fileName: string, savePath: string) {
|
||||||
|
const client = this.getFtpClient();
|
||||||
|
await client.connect(async client => {
|
||||||
|
const path = this.join(this.rootDir, fileName);
|
||||||
|
await client.download(path, savePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async listDir(dir: string) {
|
||||||
|
const client = this.getFtpClient();
|
||||||
|
return await client.connect(async (client: FtpClient) => {
|
||||||
|
const path = this.join(this.rootDir, dir);
|
||||||
|
const res = await client.listDir(path);
|
||||||
|
return res.map(item => {
|
||||||
|
return {
|
||||||
|
path: this.join(path, item.name),
|
||||||
|
size: item.size,
|
||||||
|
lastModified: item.modifiedAt.getTime(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async upload(filePath: string, fileContent: Buffer | string) {
|
||||||
|
const client = this.getFtpClient();
|
||||||
|
await client.connect(async client => {
|
||||||
|
let tmpFilePath = fileContent as string;
|
||||||
|
if (typeof fileContent !== "string") {
|
||||||
|
tmpFilePath = path.join(os.tmpdir(), "cert", "oss", filePath);
|
||||||
|
const dir = path.dirname(tmpFilePath);
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
fs.writeFileSync(tmpFilePath, fileContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Write file to temp path
|
||||||
|
const path = this.join(this.rootDir, filePath);
|
||||||
|
await client.upload(tmpFilePath, path);
|
||||||
|
} finally {
|
||||||
|
// Remove temp file
|
||||||
|
fs.unlinkSync(tmpFilePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFtpClient() {
|
||||||
|
return new FtpClient({
|
||||||
|
access: this.access,
|
||||||
|
logger: this.logger,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||||
|
if (opts?.joinRootDir !== false) {
|
||||||
|
filePath = this.join(this.rootDir, filePath);
|
||||||
|
}
|
||||||
|
const client = this.getFtpClient();
|
||||||
|
await client.connect(async client => {
|
||||||
|
await client.client.remove(filePath);
|
||||||
|
this.logger.info(`删除文件成功: ${filePath}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { QiniuAccess, QiniuClient, QiniuOssAccess } from "../../qiniu/index.js";
|
||||||
|
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||||
|
|
||||||
|
export default class QiniuOssClientImpl extends BaseOssClient<QiniuOssAccess> {
|
||||||
|
client: QiniuClient;
|
||||||
|
|
||||||
|
join(...strs: string[]) {
|
||||||
|
const str = super.join(...strs);
|
||||||
|
if (str.startsWith("/")) {
|
||||||
|
return str.substring(1);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
async init() {
|
||||||
|
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
|
||||||
|
this.client = new QiniuClient({
|
||||||
|
access: qiniuAccess,
|
||||||
|
logger: this.logger,
|
||||||
|
http: this.ctx.utils.http,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async download(fileName: string, savePath: string): Promise<void> {
|
||||||
|
const path = this.join(this.rootDir, fileName);
|
||||||
|
await this.client.downloadFile(this.access.bucket, path, savePath);
|
||||||
|
}
|
||||||
|
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||||
|
const path = this.join(this.rootDir, dir);
|
||||||
|
const res = await this.client.listDir(this.access.bucket, path);
|
||||||
|
return res.items.map(item => {
|
||||||
|
return {
|
||||||
|
path: item.key,
|
||||||
|
size: item.fsize,
|
||||||
|
//ns ,纳秒,去掉低4位 为毫秒
|
||||||
|
lastModified: Math.floor(item.putTime / 10000),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async upload(filePath: string, fileContent: Buffer | string) {
|
||||||
|
const path = this.join(this.rootDir, filePath);
|
||||||
|
await this.client.uploadFile(this.access.bucket, path, fileContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||||
|
if (opts?.joinRootDir !== false) {
|
||||||
|
filePath = this.join(this.rootDir, filePath);
|
||||||
|
}
|
||||||
|
await this.client.removeFile(this.access.bucket, filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||||
|
import path from "node:path";
|
||||||
|
import { S3Access } from "../../s3/access.js";
|
||||||
|
import fs from "fs";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export default class S3OssClientImpl extends BaseOssClient<S3Access> {
|
||||||
|
client: any;
|
||||||
|
join(...strs: string[]) {
|
||||||
|
const str = super.join(...strs);
|
||||||
|
if (str.startsWith("/")) {
|
||||||
|
return str.substring(1);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
async init() {
|
||||||
|
// import { S3Client } from "@aws-sdk/client-s3";
|
||||||
|
//@ts-ignore
|
||||||
|
const { S3Client } = await import("@aws-sdk/client-s3");
|
||||||
|
this.client = new S3Client({
|
||||||
|
forcePathStyle: true,
|
||||||
|
//@ts-ignore
|
||||||
|
s3ForcePathStyle: true,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: this.access.accessKeyId, // 默认 MinIO 访问密钥
|
||||||
|
secretAccessKey: this.access.secretAccessKey, // 默认 MinIO 秘密密钥
|
||||||
|
},
|
||||||
|
region: "us-east-1",
|
||||||
|
endpoint: this.access.endpoint,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async download(filePath: string, savePath: string): Promise<void> {
|
||||||
|
// @ts-ignore
|
||||||
|
const { GetObjectCommand } = await import("@aws-sdk/client-s3");
|
||||||
|
const key = path.join(this.rootDir, filePath);
|
||||||
|
const params = {
|
||||||
|
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
|
||||||
|
Key: key, // The name of the object. For example, 'sample_upload.txt'.
|
||||||
|
};
|
||||||
|
const res = await this.client.send(new GetObjectCommand({ ...params }));
|
||||||
|
const fileContent = fs.createWriteStream(savePath);
|
||||||
|
res.Body.pipe(fileContent);
|
||||||
|
|
||||||
|
this.logger.info(`文件下载成功: ${savePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||||
|
// @ts-ignore
|
||||||
|
const { ListObjectsCommand } = await import("@aws-sdk/client-s3");
|
||||||
|
const dirKey = this.join(this.rootDir, dir);
|
||||||
|
const params = {
|
||||||
|
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
|
||||||
|
Prefix: dirKey, // The name of the object. For example, 'sample_upload.txt'.
|
||||||
|
};
|
||||||
|
const res = await this.client.send(new ListObjectsCommand({ ...params }));
|
||||||
|
return res.Contents.map(item => {
|
||||||
|
return {
|
||||||
|
path: item.Key,
|
||||||
|
size: item.Size,
|
||||||
|
lastModified: dayjs(item.LastModified).valueOf(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async upload(filePath: string, fileContent: Buffer | string) {
|
||||||
|
// @ts-ignore
|
||||||
|
const { PutObjectCommand } = await import("@aws-sdk/client-s3");
|
||||||
|
const key = path.join(this.rootDir, filePath);
|
||||||
|
this.logger.info(`开始上传文件: ${key}`);
|
||||||
|
const params = {
|
||||||
|
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
|
||||||
|
Key: key, // The name of the object. For example, 'sample_upload.txt'.
|
||||||
|
};
|
||||||
|
if (typeof fileContent === "string") {
|
||||||
|
fileContent = fs.createReadStream(fileContent) as any;
|
||||||
|
}
|
||||||
|
await this.client.send(new PutObjectCommand({ Body: fileContent, ...params }));
|
||||||
|
|
||||||
|
this.logger.info(`文件上传成功: ${filePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||||
|
if (opts?.joinRootDir !== false) {
|
||||||
|
filePath = this.join(this.rootDir, filePath);
|
||||||
|
}
|
||||||
|
const key = filePath;
|
||||||
|
// @ts-ignore
|
||||||
|
const { DeleteObjectCommand } = await import("@aws-sdk/client-s3");
|
||||||
|
await this.client.send(
|
||||||
|
new DeleteObjectCommand({
|
||||||
|
Bucket: this.access.bucket,
|
||||||
|
Key: key,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.info(`文件删除成功: ${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||||
|
import path from "path";
|
||||||
|
import os from "os";
|
||||||
|
import fs from "fs";
|
||||||
|
import { SftpAccess, SshAccess, SshClient } from "../../ssh/index.js";
|
||||||
|
|
||||||
|
export default class SftpOssClientImpl extends BaseOssClient<SftpAccess> {
|
||||||
|
async download(fileName: string, savePath: string): Promise<void> {
|
||||||
|
const path = this.join(this.rootDir, fileName);
|
||||||
|
const client = new SshClient(this.logger);
|
||||||
|
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||||
|
await client.download({
|
||||||
|
connectConf: access,
|
||||||
|
filePath: path,
|
||||||
|
savePath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||||
|
const path = this.join(this.rootDir, dir);
|
||||||
|
const client = new SshClient(this.logger);
|
||||||
|
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||||
|
const res = await client.listDir({
|
||||||
|
connectConf: access,
|
||||||
|
dir: path,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.map(item => {
|
||||||
|
return {
|
||||||
|
path: this.join(path, item.filename),
|
||||||
|
size: item.size,
|
||||||
|
lastModified: item.attrs.atime * 1000,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async upload(filePath: string, fileContent: Buffer | string) {
|
||||||
|
let tmpFilePath = fileContent as string;
|
||||||
|
if (typeof fileContent !== "string") {
|
||||||
|
tmpFilePath = path.join(os.tmpdir(), "cert", "oss", filePath);
|
||||||
|
const dir = path.dirname(tmpFilePath);
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
fs.writeFileSync(tmpFilePath, fileContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||||
|
const key = this.join(this.rootDir, filePath);
|
||||||
|
try {
|
||||||
|
const client = new SshClient(this.logger);
|
||||||
|
await client.uploadFiles({
|
||||||
|
connectConf: access,
|
||||||
|
mkdirs: true,
|
||||||
|
transports: [
|
||||||
|
{
|
||||||
|
localPath: tmpFilePath,
|
||||||
|
remotePath: key,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
uploadType: "sftp",
|
||||||
|
opts: {
|
||||||
|
mode: this.access?.fileMode ?? undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// Remove temp file
|
||||||
|
fs.unlinkSync(tmpFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||||
|
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||||
|
const client = new SshClient(this.logger);
|
||||||
|
if (opts?.joinRootDir !== false) {
|
||||||
|
filePath = this.join(this.rootDir, filePath);
|
||||||
|
}
|
||||||
|
await client.removeFiles({
|
||||||
|
connectConf: access,
|
||||||
|
files: [filePath],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-6
@@ -1,10 +1,20 @@
|
|||||||
import { BaseHttpChallengeUploader } from "../api.js";
|
import { BaseOssClient, OssClientRemoveByOpts, OssFileItem } from "../api.js";
|
||||||
import { SshAccess, SshClient } from "@certd/plugin-lib";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
import { SshAccess, SshClient } from "../../ssh/index.js";
|
||||||
|
|
||||||
export class SshHttpChallengeUploader extends BaseHttpChallengeUploader<SshAccess> {
|
//废弃
|
||||||
|
export default class SshOssClientImpl extends BaseOssClient<SshAccess> {
|
||||||
|
download(fileName: string, savePath: string): Promise<void> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
listDir(dir: string): Promise<OssFileItem[]> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
async upload(filePath: string, fileContent: Buffer) {
|
async upload(filePath: string, fileContent: Buffer) {
|
||||||
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
||||||
|
|
||||||
@@ -34,12 +44,14 @@ export class SshHttpChallengeUploader extends BaseHttpChallengeUploader<SshAcces
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(filePath: string) {
|
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||||
|
if (opts?.joinRootDir !== false) {
|
||||||
|
filePath = this.join(this.rootDir, filePath);
|
||||||
|
}
|
||||||
const client = new SshClient(this.logger);
|
const client = new SshClient(this.logger);
|
||||||
const key = this.rootDir + filePath;
|
|
||||||
await client.removeFiles({
|
await client.removeFiles({
|
||||||
connectConf: this.access,
|
connectConf: this.access,
|
||||||
files: [key],
|
files: [filePath],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
import { TencentAccess, TencentCosAccess, TencentCosClient } from "../../tencent/index.js";
|
||||||
|
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||||
|
|
||||||
|
export default class TencentOssClientImpl extends BaseOssClient<TencentCosAccess> {
|
||||||
|
client: TencentCosClient;
|
||||||
|
|
||||||
|
join(...strs: string[]) {
|
||||||
|
const str = super.join(...strs);
|
||||||
|
if (str.startsWith("/")) {
|
||||||
|
return str.substring(1);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
async init() {
|
||||||
|
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
|
||||||
|
this.client = new TencentCosClient({
|
||||||
|
access: access,
|
||||||
|
logger: this.logger,
|
||||||
|
region: this.access.region,
|
||||||
|
bucket: this.access.bucket,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async download(filePath: string, savePath: string): Promise<void> {
|
||||||
|
const key = this.join(this.rootDir, filePath);
|
||||||
|
await this.client.downloadFile(key, savePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||||
|
const dirKey = this.join(this.rootDir, dir) + "/";
|
||||||
|
// @ts-ignore
|
||||||
|
const res: any[] = await this.client.listDir(dirKey);
|
||||||
|
return res.map(item => {
|
||||||
|
return {
|
||||||
|
path: item.Key,
|
||||||
|
size: item.Size,
|
||||||
|
lastModified: dayjs(item.LastModified).valueOf(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async upload(filePath: string, fileContent: Buffer | string) {
|
||||||
|
const key = this.join(this.rootDir, filePath);
|
||||||
|
await this.client.uploadFile(key, fileContent);
|
||||||
|
this.logger.info(`文件上传成功: ${filePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||||
|
if (opts?.joinRootDir !== false) {
|
||||||
|
filePath = this.join(this.rootDir, filePath);
|
||||||
|
}
|
||||||
|
await this.client.removeFile(filePath);
|
||||||
|
this.logger.info(`文件删除成功: ${filePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./factory.js";
|
||||||
|
export * from "./api.js";
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { HttpClient, ILogger } from "@certd/basic";
|
import { HttpClient, ILogger, utils } from "@certd/basic";
|
||||||
import { QiniuAccess } from "../access.js";
|
import { QiniuAccess } from "../access.js";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
export type QiniuCertInfo = {
|
export type QiniuCertInfo = {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -98,7 +99,7 @@ export class QiniuClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadFile(bucket: string, key: string, content: Buffer) {
|
async uploadFile(bucket: string, key: string, content: Buffer | string) {
|
||||||
const sdk = await import("qiniu");
|
const sdk = await import("qiniu");
|
||||||
const qiniu = sdk.default;
|
const qiniu = sdk.default;
|
||||||
const mac = new qiniu.auth.digest.Mac(this.access.accessKey, this.access.secretKey);
|
const mac = new qiniu.auth.digest.Mac(this.access.accessKey, this.access.secretKey);
|
||||||
@@ -111,8 +112,15 @@ export class QiniuClient {
|
|||||||
const config = new qiniu.conf.Config();
|
const config = new qiniu.conf.Config();
|
||||||
const formUploader = new qiniu.form_up.FormUploader(config);
|
const formUploader = new qiniu.form_up.FormUploader(config);
|
||||||
const putExtra = new qiniu.form_up.PutExtra();
|
const putExtra = new qiniu.form_up.PutExtra();
|
||||||
// 文件上传
|
let res: any = {};
|
||||||
const { data, resp } = await formUploader.put(uploadToken, key, content, putExtra);
|
if (typeof content === "string") {
|
||||||
|
const readableStream = fs.createReadStream(content);
|
||||||
|
res = await formUploader.putStream(uploadToken, key, readableStream, putExtra);
|
||||||
|
} else {
|
||||||
|
// 文件上传
|
||||||
|
res = await formUploader.put(uploadToken, key, content, putExtra);
|
||||||
|
}
|
||||||
|
const { data, resp } = res;
|
||||||
if (resp.statusCode === 200) {
|
if (resp.statusCode === 200) {
|
||||||
this.logger.info("文件上传成功:" + key);
|
this.logger.info("文件上传成功:" + key);
|
||||||
return data;
|
return data;
|
||||||
@@ -123,12 +131,7 @@ export class QiniuClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeFile(bucket: string, key: string) {
|
async removeFile(bucket: string, key: string) {
|
||||||
const sdk = await import("qiniu");
|
const bucketManager = await this.getBucketManager();
|
||||||
const qiniu = sdk.default;
|
|
||||||
const mac = new qiniu.auth.digest.Mac(this.access.accessKey, this.access.secretKey);
|
|
||||||
const config = new qiniu.conf.Config();
|
|
||||||
config.useHttpsDomain = true;
|
|
||||||
const bucketManager = new qiniu.rs.BucketManager(mac, config);
|
|
||||||
|
|
||||||
const { resp } = await bucketManager.delete(bucket, key);
|
const { resp } = await bucketManager.delete(bucket, key);
|
||||||
|
|
||||||
@@ -139,4 +142,39 @@ export class QiniuClient {
|
|||||||
throw new Error("删除失败:" + JSON.stringify(resp));
|
throw new Error("删除失败:" + JSON.stringify(resp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async downloadFile(bucket: string, path: string, savePath: string) {
|
||||||
|
const bucketManager = await this.getBucketManager();
|
||||||
|
const privateBucketDomain = `http://${bucket}.qiniudn.com`;
|
||||||
|
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1小时过期
|
||||||
|
const privateDownloadUrl = bucketManager.privateDownloadUrl(privateBucketDomain, path, deadline);
|
||||||
|
|
||||||
|
await utils.request.download({
|
||||||
|
http: this.http,
|
||||||
|
logger: this.logger,
|
||||||
|
config: {
|
||||||
|
url: privateDownloadUrl,
|
||||||
|
method: "get",
|
||||||
|
},
|
||||||
|
savePath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBucketManager() {
|
||||||
|
const sdk = await import("qiniu");
|
||||||
|
const qiniu = sdk.default;
|
||||||
|
const mac = new qiniu.auth.digest.Mac(this.access.accessKey, this.access.secretKey);
|
||||||
|
const config = new qiniu.conf.Config();
|
||||||
|
config.useHttpsDomain = true;
|
||||||
|
return new qiniu.rs.BucketManager(mac, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listDir(bucket: string, path: string) {
|
||||||
|
const bucketManager = await this.getBucketManager();
|
||||||
|
const res = await bucketManager.listPrefix(bucket, {
|
||||||
|
prefix: path,
|
||||||
|
limit: 1000,
|
||||||
|
});
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个注解将注册一个授权配置
|
||||||
|
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
|
||||||
|
*/
|
||||||
|
@IsAccess({
|
||||||
|
name: "s3",
|
||||||
|
title: "s3/minio授权",
|
||||||
|
desc: "S3/minio oss授权",
|
||||||
|
icon: "mdi:folder-upload-outline",
|
||||||
|
})
|
||||||
|
export class S3Access extends BaseAccess {
|
||||||
|
@AccessInput({
|
||||||
|
title: "endpoint",
|
||||||
|
component: {
|
||||||
|
placeholder: "http://xxxxxx:9000",
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
helper: "Minio的地址,如果是aws s3 则无需填写",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
endpoint!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* const minioClient = new S3Client({
|
||||||
|
* endpoint: "http://localhost:9000",
|
||||||
|
* forcePathStyle: true,
|
||||||
|
* credentials: {
|
||||||
|
* accessKeyId: "minioadmin", // 默认 MinIO 访问密钥
|
||||||
|
* secretAccessKey: "minioadmin", // 默认 MinIO 秘密密钥
|
||||||
|
* },
|
||||||
|
* region: "us-east-1",
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "accessKeyId",
|
||||||
|
component: {
|
||||||
|
placeholder: "accessKeyId",
|
||||||
|
},
|
||||||
|
helper: "accessKeyId",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessKeyId!: string;
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "secretAccessKey",
|
||||||
|
component: {
|
||||||
|
placeholder: "secretAccessKey",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
helper: "secretAccessKey",
|
||||||
|
encrypt: true,
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
secretAccessKey!: string;
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "地区",
|
||||||
|
value: "us-east-1",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
helper: "region",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
region!: string;
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "存储桶",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
helper: "bucket 名称",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
bucket!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
new S3Access();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./access.js";
|
||||||
@@ -136,6 +136,20 @@ export class AsyncSsh2Client {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listDir(options: { sftp: any; remotePath: string }) {
|
||||||
|
const { sftp, remotePath } = options;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.logger.info(`listDir:${remotePath}`);
|
||||||
|
sftp.readdir(remotePath, (err: Error, list: any) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(list);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async unlink(options: { sftp: any; remotePath: string }) {
|
async unlink(options: { sftp: any; remotePath: string }) {
|
||||||
const { sftp, remotePath } = options;
|
const { sftp, remotePath } = options;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -283,6 +297,28 @@ export class AsyncSsh2Client {
|
|||||||
}
|
}
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async download(param: { remotePath: string; savePath: string; sftp: any }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const { remotePath, savePath, sftp } = param;
|
||||||
|
sftp.fastGet(
|
||||||
|
remotePath,
|
||||||
|
savePath,
|
||||||
|
{
|
||||||
|
step: (transferred: any, chunk: any, total: any) => {
|
||||||
|
this.logger.info(`${transferred} / ${total}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(err: any) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SshClient {
|
export class SshClient {
|
||||||
@@ -329,17 +365,17 @@ export class SshClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.uploadType === "sftp") {
|
if (options.uploadType === "scp") {
|
||||||
const sftp = await conn.getSftp();
|
|
||||||
for (const transport of transports) {
|
|
||||||
await conn.fastPut({ sftp, ...transport, opts });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//scp
|
//scp
|
||||||
for (const transport of transports) {
|
for (const transport of transports) {
|
||||||
await this.scpUpload({ conn, ...transport, opts });
|
await this.scpUpload({ conn, ...transport, opts });
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const sftp = await conn.getSftp();
|
||||||
|
for (const transport of transports) {
|
||||||
|
await conn.fastPut({ sftp, ...transport, opts });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info("文件全部上传成功");
|
this.logger.info("文件全部上传成功");
|
||||||
@@ -359,25 +395,29 @@ export class SshClient {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
// 准备 SCP 协议头
|
try {
|
||||||
const fileStats = fs.statSync(localPath);
|
// 准备 SCP 协议头
|
||||||
const fileName = path.basename(localPath);
|
const fileStats = fs.statSync(localPath);
|
||||||
|
const fileName = path.basename(localPath);
|
||||||
|
|
||||||
// SCP 协议格式:C[权限] [文件大小] [文件名]\n
|
// SCP 协议格式:C[权限] [文件大小] [文件名]\n
|
||||||
stream.write(`C0644 ${fileStats.size} ${fileName}\n`);
|
stream.write(`C0644 ${fileStats.size} ${fileName}\n`);
|
||||||
|
|
||||||
// 通过管道传输文件
|
// 通过管道传输文件
|
||||||
fs.createReadStream(localPath)
|
fs.createReadStream(localPath)
|
||||||
.on("error", e => {
|
.on("error", e => {
|
||||||
this.logger.info("read stream error", e);
|
this.logger.info("read stream error", e);
|
||||||
reject(e);
|
reject(e);
|
||||||
})
|
})
|
||||||
.pipe(stream)
|
.pipe(stream)
|
||||||
.on("finish", async () => {
|
.on("finish", async () => {
|
||||||
this.logger.info(`上传完成:${localPath} => ${remotePath}`);
|
this.logger.info(`上传完成:${localPath} => ${remotePath}`);
|
||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
})
|
||||||
.on("error", reject);
|
.on("error", reject);
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -526,4 +566,31 @@ export class SshClient {
|
|||||||
conn.end();
|
conn.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listDir(param: { connectConf: any; dir: string }) {
|
||||||
|
return await this._call<any>({
|
||||||
|
connectConf: param.connectConf,
|
||||||
|
callable: async (conn: AsyncSsh2Client) => {
|
||||||
|
const sftp = await conn.getSftp();
|
||||||
|
return await conn.listDir({
|
||||||
|
sftp,
|
||||||
|
remotePath: param.dir,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async download(param: { connectConf: any; filePath: string; savePath: string }) {
|
||||||
|
return await this._call<any>({
|
||||||
|
connectConf: param.connectConf,
|
||||||
|
callable: async (conn: AsyncSsh2Client) => {
|
||||||
|
const sftp = await conn.getSftp();
|
||||||
|
return await conn.download({
|
||||||
|
sftp,
|
||||||
|
remotePath: param.filePath,
|
||||||
|
savePath: param.savePath,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { TencentAccess } from "../access.js";
|
import { TencentAccess } from "../access.js";
|
||||||
import { ILogger } from "@certd/basic";
|
import { ILogger } from "@certd/basic";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
export class TencentCosClient {
|
export class TencentCosClient {
|
||||||
access: TencentAccess;
|
access: TencentAccess;
|
||||||
@@ -23,15 +24,19 @@ export class TencentCosClient {
|
|||||||
return new sdk.default(clientConfig);
|
return new sdk.default(clientConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadFile(key: string, file: Buffer) {
|
async uploadFile(key: string, file: Buffer | string) {
|
||||||
const cos = await this.getCosClient();
|
const cos = await this.getCosClient();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
let readableStream = file as any;
|
||||||
|
if (typeof file === "string") {
|
||||||
|
readableStream = fs.createReadStream(file);
|
||||||
|
}
|
||||||
cos.putObject(
|
cos.putObject(
|
||||||
{
|
{
|
||||||
Bucket: this.bucket /* 必须 */,
|
Bucket: this.bucket /* 必须 */,
|
||||||
Region: this.region /* 必须 */,
|
Region: this.region /* 必须 */,
|
||||||
Key: key /* 必须 */,
|
Key: key /* 必须 */,
|
||||||
Body: file, // 上传文件对象
|
Body: readableStream, // 上传文件对象
|
||||||
onProgress: function (progressData) {
|
onProgress: function (progressData) {
|
||||||
console.log(JSON.stringify(progressData));
|
console.log(JSON.stringify(progressData));
|
||||||
},
|
},
|
||||||
@@ -66,4 +71,47 @@ export class TencentCosClient {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async downloadFile(key: string, savePath: string) {
|
||||||
|
const cos = await this.getCosClient();
|
||||||
|
const writeStream = fs.createWriteStream(savePath);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
cos.getObject(
|
||||||
|
{
|
||||||
|
Bucket: this.bucket,
|
||||||
|
Region: this.region,
|
||||||
|
Key: key,
|
||||||
|
Output: writeStream,
|
||||||
|
},
|
||||||
|
function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async listDir(dirKey: string) {
|
||||||
|
const cos = await this.getCosClient();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
cos.getBucket(
|
||||||
|
{
|
||||||
|
Bucket: this.bucket,
|
||||||
|
Region: this.region,
|
||||||
|
Prefix: dirKey,
|
||||||
|
MaxKeys: 1000,
|
||||||
|
},
|
||||||
|
function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(data.Contents);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
VITE_APP_API=/api
|
VITE_APP_API=api
|
||||||
#登录与权限关闭
|
#登录与权限关闭
|
||||||
VITE_APP_PM_ENABLED=true
|
VITE_APP_PM_ENABLED=true
|
||||||
VITE_APP_TITLE=Certd
|
VITE_APP_TITLE=Certd
|
||||||
VITE_APP_SLOGAN=让你的证书永不过期
|
VITE_APP_SLOGAN=让你的证书永不过期
|
||||||
VITE_APP_COPYRIGHT_YEAR=2021-2024
|
VITE_APP_COPYRIGHT_YEAR=2021-2025
|
||||||
VITE_APP_COPYRIGHT_NAME=handsfree.work
|
VITE_APP_COPYRIGHT_NAME=handsfree.work
|
||||||
VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
|
VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
|
||||||
VITE_APP_LOGO=/static/images/logo/logo.svg
|
VITE_APP_LOGO=static/images/logo/logo.svg
|
||||||
VITE_APP_LOGIN_LOGO=/static/images/logo/rect-black.svg
|
VITE_APP_LOGIN_LOGO=static/images/logo/rect-black.svg
|
||||||
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
||||||
VITE_APP_NAMESPACE=fs
|
VITE_APP_NAMESPACE=fs
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
VITE_APP_API=/api
|
VITE_APP_API=api
|
||||||
#登录与权限开启
|
#登录与权限开启
|
||||||
VITE_APP_PM_ENABLED=true
|
VITE_APP_PM_ENABLED=true
|
||||||
|
|||||||
@@ -3,6 +3,19 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 服务器时间获取不准确的bug ([5d10cbf](https://github.com/certd/certd/commit/5d10cbf18daf94a90a7551641a3b13e3c5fec611))
|
||||||
|
* 修复复制流水线无效的bug ([3df20a9](https://github.com/certd/certd/commit/3df20a924f32970b052e2588ea20de095f0ea693))
|
||||||
|
* 修复token过期后,疯狂打印token过期信息的bug ([50a5fa1](https://github.com/certd/certd/commit/50a5fa15bb240a125bbc91d2ce1ff3c835888a77))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||||
|
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<link rel="icon" href="/api/app/favicon"/>
|
<link rel="icon" href="api/app/favicon"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>Loading</title>
|
<title>Loading</title>
|
||||||
<script src="/static/icons/iconfont.js"></script>
|
<script src="static/icons/iconfont.js?v=<%=version%>"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/index.css"/>
|
<link rel="stylesheet" type="text/css" href="static/index.css?v=<%=version%>"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-client",
|
"name": "@certd/ui-client",
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --open",
|
"dev": "vite --open",
|
||||||
@@ -101,8 +101,8 @@
|
|||||||
"zod-defaults": "^0.1.3"
|
"zod-defaults": "^0.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@certd/lib-iframe": "^1.33.7",
|
"@certd/lib-iframe": "^1.33.8",
|
||||||
"@certd/pipeline": "^1.33.7",
|
"@certd/pipeline": "^1.33.8",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/chai": "^4.3.12",
|
"@types/chai": "^4.3.12",
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ function createService() {
|
|||||||
errorLog(error, error?.response?.config?.showErrorNotify);
|
errorLog(error, error?.response?.config?.showErrorNotify);
|
||||||
if (status === 401) {
|
if (status === 401) {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
userStore.logout();
|
userStore.logout(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error?.config?.onError) {
|
if (error?.config?.onError) {
|
||||||
|
|||||||
+1
-1
@@ -21,6 +21,6 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const nslookupCmd = computed(() => {
|
const nslookupCmd = computed(() => {
|
||||||
return `nslookup -qa=txt _acme-challenge.${props.record.domain}`;
|
return `nslookup -q=txt _acme-challenge.${props.record.domain}`;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+1
@@ -75,6 +75,7 @@ const uploaderTypeDict = dict({
|
|||||||
{ label: "阿里云OSS", value: "alioss" },
|
{ label: "阿里云OSS", value: "alioss" },
|
||||||
{ label: "腾讯云COS", value: "tencentcos" },
|
{ label: "腾讯云COS", value: "tencentcos" },
|
||||||
{ label: "七牛OSS", value: "qiniuoss" },
|
{ label: "七牛OSS", value: "qiniuoss" },
|
||||||
|
{ label: "S3/Minio", value: "s3" },
|
||||||
{ label: "SSH(已废弃,请选择SFTP方式)", value: "ssh", disabled: true },
|
{ label: "SSH(已废弃,请选择SFTP方式)", value: "ssh", disabled: true },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -98,17 +98,17 @@ export const certdResources = [
|
|||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: "子域名托管设置",
|
// title: "子域名托管设置",
|
||||||
name: "SubDomain",
|
// name: "SubDomain",
|
||||||
path: "/certd/pipeline/subDomain",
|
// path: "/certd/pipeline/subDomain",
|
||||||
component: "/certd/pipeline/sub-domain/index.vue",
|
// component: "/certd/pipeline/sub-domain/index.vue",
|
||||||
meta: {
|
// meta: {
|
||||||
icon: "material-symbols:approval-delegation-outline",
|
// icon: "material-symbols:approval-delegation-outline",
|
||||||
auth: true,
|
// auth: true,
|
||||||
keepAlive: true,
|
// keepAlive: true,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: "流水线分组管理",
|
title: "流水线分组管理",
|
||||||
name: "PipelineGroupManager",
|
name: "PipelineGroupManager",
|
||||||
|
|||||||
@@ -161,10 +161,10 @@ export const useSettingStore = defineStore({
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
if (this.isComm) {
|
if (this.isComm) {
|
||||||
if (siteInfo.logo) {
|
if (siteInfo.logo) {
|
||||||
siteInfo.logo = `/api/basic/file/download?key=${siteInfo.logo}`;
|
siteInfo.logo = `api/basic/file/download?key=${siteInfo.logo}`;
|
||||||
}
|
}
|
||||||
if (siteInfo.loginLogo) {
|
if (siteInfo.loginLogo) {
|
||||||
siteInfo.loginLogo = `/api/basic/file/download?key=${siteInfo.loginLogo}`;
|
siteInfo.loginLogo = `api/basic/file/download?key=${siteInfo.loginLogo}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.siteInfo = _.merge({}, defaultSiteInfo, siteInfo);
|
this.siteInfo = _.merge({}, defaultSiteInfo, siteInfo);
|
||||||
|
|||||||
@@ -108,10 +108,12 @@ export const useUserStore = defineStore({
|
|||||||
/**
|
/**
|
||||||
* @description: logout
|
* @description: logout
|
||||||
*/
|
*/
|
||||||
async logout(goLogin = true) {
|
async logout(goLogin = true, from401 = false) {
|
||||||
this.resetState();
|
this.resetState();
|
||||||
resetAllStores();
|
resetAllStores();
|
||||||
await UserApi.logout(); //主要是清空cookie
|
if (!from401) {
|
||||||
|
await UserApi.logout(); //主要是清空cookie
|
||||||
|
}
|
||||||
goLogin && router.push("/login");
|
goLogin && router.push("/login");
|
||||||
mitter.emit("app.logout");
|
mitter.emit("app.logout");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<a-descriptions title="" bordered>
|
<a-descriptions title="" bordered>
|
||||||
<a-descriptions-item label="用户名">{{ userInfo.username }}</a-descriptions-item>
|
<a-descriptions-item label="用户名">{{ userInfo.username }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="头像">
|
<a-descriptions-item label="头像">
|
||||||
<a-avatar v-if="userInfo.avatar" size="large" :src="'/api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
|
<a-avatar v-if="userInfo.avatar" size="large" :src="'api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
|
||||||
<a-avatar v-else size="large" style="background-color: #00b4f5">
|
<a-avatar v-else size="large" style="background-color: #00b4f5">
|
||||||
{{ userInfo.username }}
|
{{ userInfo.username }}
|
||||||
</a-avatar>
|
</a-avatar>
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
|
|||||||
function onDialogOpen(opt: any) {
|
function onDialogOpen(opt: any) {
|
||||||
const searchForm = crudExpose.getSearchValidatedFormData();
|
const searchForm = crudExpose.getSearchValidatedFormData();
|
||||||
opt.initialForm = {
|
opt.initialForm = {
|
||||||
|
...opt.initialForm,
|
||||||
groupId: searchForm.groupId,
|
groupId: searchForm.groupId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="header-profile flex-wrap bg-white dark:bg-black">
|
<div class="header-profile flex-wrap bg-white dark:bg-black">
|
||||||
<div class="flex flex-1">
|
<div class="flex flex-1">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a-avatar v-if="userInfo.avatar" size="large" :src="'/api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
|
<a-avatar v-if="userInfo.avatar" size="large" :src="'api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
|
||||||
<a-avatar v-else size="large" style="background-color: #00b4f5">
|
<a-avatar v-else size="large" style="background-color: #00b4f5">
|
||||||
{{ userInfo.username }}
|
{{ userInfo.username }}
|
||||||
</a-avatar>
|
</a-avatar>
|
||||||
@@ -182,7 +182,7 @@ const userInfo: ComputedRef<UserInfoRes> = computed(() => {
|
|||||||
return userStore.getUserInfo;
|
return userStore.getUserInfo;
|
||||||
});
|
});
|
||||||
const now = computed(() => {
|
const now = computed(() => {
|
||||||
const serverTime = settingStore.app.deltaTime + Date.now();
|
const serverTime = Date.now() - settingStore.app.deltaTime;
|
||||||
return dayjs(serverTime).format("YYYY-MM-DD HH:mm:ss");
|
return dayjs(serverTime).format("YYYY-MM-DD HH:mm:ss");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function onChange(value: string) {
|
|||||||
const imageCodeUrl = ref();
|
const imageCodeUrl = ref();
|
||||||
function resetImageCode() {
|
function resetImageCode() {
|
||||||
const randomStr = nanoid(10);
|
const randomStr = nanoid(10);
|
||||||
let url = "/api/basic/code/captcha";
|
let url = "api/basic/code/captcha";
|
||||||
imageCodeUrl.value = url + "?randomStr=" + randomStr;
|
imageCodeUrl.value = url + "?randomStr=" + randomStr;
|
||||||
emit("update:randomStr", randomStr);
|
emit("update:randomStr", randomStr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
width: "auto",
|
width: "auto",
|
||||||
},
|
},
|
||||||
buildUrl(key: string) {
|
buildUrl(key: string) {
|
||||||
return `/api/basic/file/download?&key=` + key;
|
return `api/basic/file/download?&key=` + key;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -190,7 +190,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
buildUrl(key: string) {
|
buildUrl(key: string) {
|
||||||
return `/api/basic/file/download?&key=` + key;
|
return `api/basic/file/download?&key=` + key;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ export default ({ command, mode }) => {
|
|||||||
const env = loadEnv(mode, process.cwd());
|
const env = loadEnv(mode, process.cwd());
|
||||||
const devServerFs: any = {};
|
const devServerFs: any = {};
|
||||||
const devAlias: any[] = [];
|
const devAlias: any[] = [];
|
||||||
const base = "/";
|
const base = "./";
|
||||||
// if (mode.startsWith("dev")) {
|
// if (mode.startsWith("dev")) {
|
||||||
// base = "/dev";
|
// base = "./";
|
||||||
// }
|
// }
|
||||||
return {
|
return {
|
||||||
base: base,
|
base: base,
|
||||||
@@ -36,6 +36,7 @@ export default ({ command, mode }) => {
|
|||||||
data: {
|
data: {
|
||||||
title: env.VITE_APP_TITLE,
|
title: env.VITE_APP_TITLE,
|
||||||
projectPath: env.VITE_APP_PROJECT_PATH,
|
projectPath: env.VITE_APP_PROJECT_PATH,
|
||||||
|
version: env.VITE_APP_VERSION,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||||
|
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
|
||||||
|
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
|
||||||
|
|
||||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-server",
|
"name": "@certd/ui-server",
|
||||||
"version": "1.33.7",
|
"version": "1.33.8",
|
||||||
"description": "fast-server base midway",
|
"description": "fast-server base midway",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -38,19 +38,19 @@
|
|||||||
"@aws-sdk/client-acm": "^3.699.0",
|
"@aws-sdk/client-acm": "^3.699.0",
|
||||||
"@aws-sdk/client-cloudfront": "^3.699.0",
|
"@aws-sdk/client-cloudfront": "^3.699.0",
|
||||||
"@aws-sdk/client-s3": "^3.705.0",
|
"@aws-sdk/client-s3": "^3.705.0",
|
||||||
"@certd/acme-client": "^1.33.7",
|
"@certd/acme-client": "^1.33.8",
|
||||||
"@certd/basic": "^1.33.7",
|
"@certd/basic": "^1.33.8",
|
||||||
"@certd/commercial-core": "^1.33.7",
|
"@certd/commercial-core": "^1.33.8",
|
||||||
"@certd/jdcloud": "^1.33.7",
|
"@certd/jdcloud": "^1.33.8",
|
||||||
"@certd/lib-huawei": "^1.33.7",
|
"@certd/lib-huawei": "^1.33.8",
|
||||||
"@certd/lib-k8s": "^1.33.7",
|
"@certd/lib-k8s": "^1.33.8",
|
||||||
"@certd/lib-server": "^1.33.7",
|
"@certd/lib-server": "^1.33.8",
|
||||||
"@certd/midway-flyway-js": "^1.33.7",
|
"@certd/midway-flyway-js": "^1.33.8",
|
||||||
"@certd/pipeline": "^1.33.7",
|
"@certd/pipeline": "^1.33.8",
|
||||||
"@certd/plugin-cert": "^1.33.7",
|
"@certd/plugin-cert": "^1.33.8",
|
||||||
"@certd/plugin-lib": "^1.33.7",
|
"@certd/plugin-lib": "^1.33.8",
|
||||||
"@certd/plugin-plus": "^1.33.7",
|
"@certd/plugin-plus": "^1.33.8",
|
||||||
"@certd/plus-core": "^1.33.7",
|
"@certd/plus-core": "^1.33.8",
|
||||||
"@corsinvest/cv4pve-api-javascript": "^8.3.0",
|
"@corsinvest/cv4pve-api-javascript": "^8.3.0",
|
||||||
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
|
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
|
||||||
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
|
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CreateRecordOptions, DnsProviderContext, IDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
|
import {CreateRecordOptions, DnsProviderContext, IDnsProvider, RemoveRecordOptions} from '@certd/plugin-cert';
|
||||||
import { PlusService } from '@certd/lib-server';
|
import {PlusService} from '@certd/lib-server';
|
||||||
|
|
||||||
export type CommonCnameProvider = {
|
export type CommonCnameProvider = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -24,7 +24,13 @@ export class CommonDnsProvider implements IDnsProvider {
|
|||||||
this.plusService = opts.plusService;
|
this.plusService = opts.plusService;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onInstance() {}
|
usePunyCode(): boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async onInstance() {
|
||||||
|
}
|
||||||
|
|
||||||
async createRecord(options: CreateRecordOptions) {
|
async createRecord(options: CreateRecordOptions) {
|
||||||
if (!this.config.domain.endsWith(options.domain)) {
|
if (!this.config.domain.endsWith(options.domain)) {
|
||||||
throw new Error('cname服务域名不匹配');
|
throw new Error('cname服务域名不匹配');
|
||||||
@@ -45,6 +51,7 @@ export class CommonDnsProvider implements IDnsProvider {
|
|||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeRecord(options: RemoveRecordOptions<any>) {
|
async removeRecord(options: RemoveRecordOptions<any>) {
|
||||||
const res = await this.plusService.requestWithToken({
|
const res = await this.plusService.requestWithToken({
|
||||||
url: '/activation/certd/cname/recordRemove',
|
url: '/activation/certd/cname/recordRemove',
|
||||||
@@ -60,6 +67,7 @@ export class CommonDnsProvider implements IDnsProvider {
|
|||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCtx(ctx: DnsProviderContext): void {
|
setCtx(ctx: DnsProviderContext): void {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -87,15 +87,13 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
async createRecord(options: CreateRecordOptions): Promise<any> {
|
async createRecord(options: CreateRecordOptions): Promise<any> {
|
||||||
const { fullRecord, value, type, domain } = options;
|
const { fullRecord,hostRecord, value, type, domain } = options;
|
||||||
this.logger.info('添加域名解析:', fullRecord, value, domain);
|
this.logger.info('添加域名解析:', fullRecord, value, domain);
|
||||||
// const domain = await this.matchDomain(fullRecord);
|
// const domain = await this.matchDomain(fullRecord);
|
||||||
const rr = fullRecord.replace('.' + domain, '');
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
RegionId: 'cn-hangzhou',
|
RegionId: 'cn-hangzhou',
|
||||||
DomainName: domain,
|
DomainName: domain,
|
||||||
RR: rr,
|
RR: hostRecord,
|
||||||
Type: type,
|
Type: type,
|
||||||
Value: value,
|
Value: value,
|
||||||
// Line: 'oversea' // 海外
|
// Line: 'oversea' // 海外
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
|
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import dayjs from 'dayjs';
|
import dayjs from "dayjs";
|
||||||
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
|
import { AbstractPlusTaskPlugin } from "@certd/plugin-plus";
|
||||||
import JSZip from 'jszip';
|
import JSZip from "jszip";
|
||||||
import * as os from 'node:os';
|
import * as os from "node:os";
|
||||||
import { SshAccess, SshClient } from '@certd/plugin-lib';
|
import { OssClientContext, ossClientFactory, OssClientRemoveByOpts, SshAccess, SshClient } from "@certd/plugin-lib";
|
||||||
|
|
||||||
const defaultBackupDir = 'certd_backup';
|
const defaultBackupDir = 'certd_backup';
|
||||||
const defaultFilePrefix = 'db-backup';
|
const defaultFilePrefix = 'db_backup';
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: 'DBBackupPlugin',
|
name: 'DBBackupPlugin',
|
||||||
title: '数据库备份',
|
title: '数据库备份',
|
||||||
@@ -30,8 +31,9 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
component: {
|
component: {
|
||||||
name: 'a-select',
|
name: 'a-select',
|
||||||
options: [
|
options: [
|
||||||
{ label: '本地复制', value: 'local' },
|
{label: '本地复制', value: 'local'},
|
||||||
{ label: 'ssh上传', value: 'ssh' },
|
{label: 'ssh上传', value: 'ssh'},
|
||||||
|
{label: 'oss上传', value: 'oss'},
|
||||||
],
|
],
|
||||||
placeholder: '',
|
placeholder: '',
|
||||||
},
|
},
|
||||||
@@ -57,6 +59,53 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
})
|
})
|
||||||
sshAccessId!: number;
|
sshAccessId!: number;
|
||||||
|
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: 'OSS类型',
|
||||||
|
component: {
|
||||||
|
name: 'a-select',
|
||||||
|
options: [
|
||||||
|
{value: "alioss", label: "阿里云OSS"},
|
||||||
|
{value: "s3", label: "MinIO/S3"},
|
||||||
|
{value: "qiniuoss", label: "七牛云"},
|
||||||
|
{value: "tencentcos", label: "腾讯云COS"},
|
||||||
|
{value: "ftp", label: "Ftp"},
|
||||||
|
{value: "sftp", label: "Sftp"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show:ctx.compute(({form})=>{
|
||||||
|
return form.backupMode === 'oss';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
ossType!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: 'OSS授权',
|
||||||
|
component: {
|
||||||
|
name: 'access-selector',
|
||||||
|
},
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show:ctx.compute(({form})=>{
|
||||||
|
return form.backupMode === 'oss';
|
||||||
|
}),
|
||||||
|
component:{
|
||||||
|
type: ctx.compute(({form})=>{
|
||||||
|
return form.ossType;
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
ossAccessId!: number;
|
||||||
|
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: '备份保存目录',
|
title: '备份保存目录',
|
||||||
component: {
|
component: {
|
||||||
@@ -104,7 +153,9 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
})
|
})
|
||||||
retainDays!: number;
|
retainDays!: number;
|
||||||
|
|
||||||
async onInstance() {}
|
async onInstance() {
|
||||||
|
}
|
||||||
|
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
this.logger.info('开始备份数据库');
|
this.logger.info('开始备份数据库');
|
||||||
|
|
||||||
@@ -114,11 +165,11 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
this.logger.error('数据库文件不存在:', dbPath);
|
this.logger.error('数据库文件不存在:', dbPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dbTmpFilename = `${this.filePrefix}.${dayjs().format('YYYYMMDD.HHmmss')}.sqlite`;
|
const dbTmpFilename = `${this.filePrefix}_${dayjs().format('YYYYMMDD_HHmmss')}_sqlite`;
|
||||||
const dbZipFilename = `${dbTmpFilename}.zip`;
|
const dbZipFilename = `${dbTmpFilename}.zip`;
|
||||||
const tempDir = path.resolve(os.tmpdir(), 'certd_backup');
|
const tempDir = path.resolve(os.tmpdir(), 'certd_backup');
|
||||||
if (!fs.existsSync(tempDir)) {
|
if (!fs.existsSync(tempDir)) {
|
||||||
await fs.promises.mkdir(tempDir, { recursive: true });
|
await fs.promises.mkdir(tempDir, {recursive: true});
|
||||||
}
|
}
|
||||||
const dbTmpPath = path.resolve(tempDir, dbTmpFilename);
|
const dbTmpPath = path.resolve(tempDir, dbTmpFilename);
|
||||||
const dbZipPath = path.resolve(tempDir, dbZipFilename);
|
const dbZipPath = path.resolve(tempDir, dbZipFilename);
|
||||||
@@ -129,14 +180,14 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
const stream = fs.createReadStream(dbTmpPath);
|
const stream = fs.createReadStream(dbTmpPath);
|
||||||
// 使用流的方式添加文件内容
|
// 使用流的方式添加文件内容
|
||||||
zip.file(dbTmpFilename, stream, { binary: true, compression: 'DEFLATE' });
|
zip.file(dbTmpFilename, stream, {binary: true, compression: 'DEFLATE'});
|
||||||
|
|
||||||
const uploadDir = path.resolve('data', 'upload');
|
const uploadDir = path.resolve('data', 'upload');
|
||||||
if (this.withUpload && fs.existsSync(uploadDir)) {
|
if (this.withUpload && fs.existsSync(uploadDir)) {
|
||||||
zip.folder(uploadDir);
|
zip.folder(uploadDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await zip.generateAsync({ type: 'nodebuffer' });
|
const content = await zip.generateAsync({type: 'nodebuffer'});
|
||||||
|
|
||||||
await fs.promises.writeFile(dbZipPath, content);
|
await fs.promises.writeFile(dbZipPath, content);
|
||||||
this.logger.info(`数据库文件压缩完成:${dbZipPath}`);
|
this.logger.info(`数据库文件压缩完成:${dbZipPath}`);
|
||||||
@@ -164,7 +215,7 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
}
|
}
|
||||||
const dir = path.dirname(backupPath);
|
const dir = path.dirname(backupPath);
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
await fs.promises.mkdir(dir, { recursive: true });
|
await fs.promises.mkdir(dir, {recursive: true});
|
||||||
}
|
}
|
||||||
backupPath = path.resolve(backupPath);
|
backupPath = path.resolve(backupPath);
|
||||||
await fs.promises.copyFile(dbPath, backupPath);
|
await fs.promises.copyFile(dbPath, backupPath);
|
||||||
@@ -195,7 +246,7 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
this.logger.info('备份目录:', backupPath);
|
this.logger.info('备份目录:', backupPath);
|
||||||
await sshClient.uploadFiles({
|
await sshClient.uploadFiles({
|
||||||
connectConf: access,
|
connectConf: access,
|
||||||
transports: [{ localPath: dbPath, remotePath: backupPath }],
|
transports: [{localPath: dbPath, remotePath: backupPath}],
|
||||||
mkdirs: true,
|
mkdirs: true,
|
||||||
});
|
});
|
||||||
this.logger.info('备份文件上传完成');
|
this.logger.info('备份文件上传完成');
|
||||||
@@ -221,7 +272,39 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async ossBackup(dbPath: string, backupDir: string, backupPath: string) {
|
private async ossBackup(dbPath: string, backupDir: string, backupPath: string) {
|
||||||
// TODO
|
if (!this.ossAccessId) {
|
||||||
|
throw new Error('未配置ossAccessId');
|
||||||
|
}
|
||||||
|
const access = await this.getAccess(this.ossAccessId);
|
||||||
|
const ossType = this.ossType
|
||||||
|
|
||||||
|
const ctx: OssClientContext = {
|
||||||
|
logger: this.logger,
|
||||||
|
utils: this.ctx.utils,
|
||||||
|
accessService:this.accessService
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info(`开始备份文件到:${ossType}`);
|
||||||
|
const client = await ossClientFactory.createOssClientByType(ossType, {
|
||||||
|
access,
|
||||||
|
ctx,
|
||||||
|
})
|
||||||
|
|
||||||
|
await client.upload(backupPath, dbPath);
|
||||||
|
|
||||||
|
if (this.retainDays > 0) {
|
||||||
|
// 删除过期备份
|
||||||
|
this.logger.info('开始删除过期备份文件');
|
||||||
|
const removeByOpts: OssClientRemoveByOpts = {
|
||||||
|
dir: backupDir,
|
||||||
|
beforeDays: this.retainDays,
|
||||||
|
};
|
||||||
|
await client.removeBy(removeByOpts);
|
||||||
|
this.logger.info('删除过期备份文件完成');
|
||||||
|
}else{
|
||||||
|
this.logger.info('已禁止删除过期文件');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new DBBackupPlugin();
|
new DBBackupPlugin();
|
||||||
|
|||||||
Generated
+1173
-70
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user