Compare commits

..

41 Commits

Author SHA1 Message Date
xiaojunnuo
f3a24ef6de chore: singleton 2025-12-28 23:46:55 +08:00
xiaojunnuo
1347355cb1 perf: 支持授权给管理员查看和下载用户证书 2025-12-28 23:36:53 +08:00
xiaojunnuo
f847c4a414 chore: 1 2025-12-28 01:05:48 +08:00
xiaojunnuo
776fa924e3 chore: 1 2025-12-28 00:49:41 +08:00
xiaojunnuo
8872466968 perf: 支持从阿里云商用证书订单中获取证书 2025-12-28 00:38:38 +08:00
xiaojunnuo
b620038d98 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-12-27 22:57:31 +08:00
xiaojunnuo
a248367b15 perf: 支持ucloud waf(未测试) 2025-12-27 22:57:26 +08:00
xiaojunnuo
c159ec4a9a Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-12-27 15:05:52 +08:00
xiaojunnuo
5359a7670f perf: 优化阿里云esa清理证书时机 2025-12-27 15:05:43 +08:00
xiaojunnuo
7e1c7a6de2 chore: 1 2025-12-27 02:23:29 +08:00
xiaojunnuo
91e19bbdd3 chore: 优化图标 2025-12-27 02:20:01 +08:00
xiaojunnuo
e61daaee2d perf: 支持ucloud,上传到ussl,部署到ucdn 2025-12-27 01:54:47 +08:00
xiaojunnuo
8caab1fd92 perf: aws route53 2025-12-26 23:20:14 +08:00
xiaojunnuo
cd944882c3 perf: 执行队列数量支持设置 2025-12-26 18:17:05 +08:00
xiaojunnuo
888d9591fe perf: 支持执行队列,避免同一时间触发流水线太多导致被限制 2025-12-26 16:30:03 +08:00
xiaojunnuo
833808c5de fix: 修复从模版创建的流水线不会自动执行的bug 2025-12-26 16:29:20 +08:00
xiaojunnuo
d731956b06 fix: 首页最快到期证书,不包含已禁用的流水线 2025-12-26 14:37:03 +08:00
xiaojunnuo
40449ae4de chore: docs 2025-12-26 14:26:27 +08:00
xiaojunnuo
44ad61f004 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-12-26 09:29:37 +08:00
xiaojunnuo
74865d53f8 chore: 修复aws route53 删除record失败的问题 2025-12-26 09:29:29 +08:00
xiaojunnuo
373415261e Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-12-25 22:20:59 +08:00
xiaojunnuo
d0f653da9a perf: 批量修改定时时间支持随机时间 2025-12-25 22:20:54 +08:00
xiaojunnuo
cbb8319cfa perf: 支持aws route53 dns 2025-12-25 18:56:27 +08:00
xiaojunnuo
0e467a6024 chore: docs 2025-12-22 16:36:32 +08:00
xiaojunnuo
e505916525 fix: 修复用户删除后,用相同的oauth授权登录报错用户不存在的问题
https://github.com/certd/certd/issues/603
2025-12-19 11:37:22 +08:00
xiaojunnuo
31f09ab117 chore: ip证书文档 2025-12-19 11:30:01 +08:00
xiaojunnuo
09e5e0f9b3 chore: 1 2025-12-19 10:49:10 +08:00
xiaojunnuo
773cada57a perf: ip证书校验方式提示 2025-12-19 10:08:28 +08:00
xiaojunnuo
403947ed6d chore: 开源地址 2025-12-18 19:07:27 +08:00
xiaojunnuo
d9d08a725c perf: 官方开源地址 2025-12-18 19:03:46 +08:00
xiaojunnuo
e2ed75af94 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-12-17 10:02:30 +08:00
xiaojunnuo
dd19afce92 fix: 修复部署到华为obs 报错的bug 2025-12-17 10:02:23 +08:00
xiaojunnuo
5b5deac7d9 perf: 腾讯云EO增加请求参数打印 2025-12-16 23:10:30 +08:00
xiaojunnuo
3f3ee3456e chore: 支持仅查询其他用户的流水线 2025-12-16 22:52:07 +08:00
xiaojunnuo
3e2f2fc02e chore: 批量修改流水线优化 2025-12-16 22:31:06 +08:00
xiaojunnuo
c5a3003cf7 fix: 发送证书到邮箱插件的邮件模版转为使用邮箱配置中的通用模版 2025-12-16 22:07:39 +08:00
xiaojunnuo
4c6dcddf11 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-12-16 10:02:31 +08:00
xiaojunnuo
b314e500cd build: release 2025-12-16 07:43:49 +08:00
xiaojunnuo
b83e6ad13f build: publish 2025-12-16 01:47:46 +08:00
xiaojunnuo
fee401cfdf build: trigger build image 2025-12-16 01:47:35 +08:00
xiaojunnuo
5f4469e306 fix: telegram 修复消息内存在横杠无法发出的bug 2025-12-15 15:12:07 +08:00
155 changed files with 3063 additions and 474 deletions

View File

@@ -17,6 +17,13 @@ Certd® 是一个免费的全自动证书管理系统,让你的网站证书永
> 流水线数量现已调整为无限制,欢迎大家使用 > 流水线数量现已调整为无限制,欢迎大家使用
|官方开源地址: | |
| ---- | ---- |
| [Github](https://github.com/certd/certd)| ![](https://img.shields.io/github/stars/certd/certd?logo=github) |
| [Gitee](https://gitee.com/certd/certd) | ![](https://gitee.com/certd/certd/badge/star.svg?theme=dark) |
| [AtomGit](https://atomgit.com/certd/certd) |![](https://atomgit.com/certd/certd/star/badge.svg) |
## 一、特性 ## 一、特性
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。 本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。

View File

@@ -13,6 +13,15 @@ Certd® is a free, fully automated certificate management system that ensures yo
> The number of pipelines is now unlimited. Welcome to use it. > The number of pipelines is now unlimited. Welcome to use it.
Official Open Source Address:
[Github](https://github.com/certd/certd) ![](https://img.shields.io/github/stars/certd/certd?logo=github)
[Gitee](https://gitee.com/certd/certd) ![](https://gitee.com/certd/certd/badge/star.svg?theme=dark)
[AtomGit](https://atomgit.com/certd/certd) ![](https://atomgit.com/certd/certd/star/badge.svg)
## 1. Features ## 1. Features
This project not only supports automated certificate application but also automated certificate deployment and updates, ensuring your certificates never expire. This project not only supports automated certificate application but also automated certificate deployment and updates, ensuring your certificates never expire.

View File

@@ -122,6 +122,7 @@ export default defineConfig({
{text: "宝塔动态IP白名单", link: "/guide/use/baota/white_list.md"}, {text: "宝塔动态IP白名单", link: "/guide/use/baota/white_list.md"},
{text: "子域名托管", link: "/guide/use/cert/subdomain.md"}, {text: "子域名托管", link: "/guide/use/cert/subdomain.md"},
{text: "流水线有效期", link: "/guide/use/pipeline/valid.md"}, {text: "流水线有效期", link: "/guide/use/pipeline/valid.md"},
{text: "IP证书申请", link: "/guide/use/cert/ip.md"},
] ]
}, },
{ {

View File

@@ -3,6 +3,21 @@
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.37.16](https://github.com/certd/certd/compare/v1.37.15...v1.37.16) (2025-12-15)
### Bug Fixes
* 修复ipv6作为证书域名申请证书校验失败的bug ([e4e16bc](https://github.com/certd/certd/commit/e4e16bc6a65bb082c18ca0590226f0987a47d477))
* 优化西部数据 500 already exists 的问题 ([2bfad9f](https://github.com/certd/certd/commit/2bfad9fc651da208b610abd921fbfb2fbc04203f))
### Performance Improvements
* 批量设置定时,支持清除定时 ([63d8bcf](https://github.com/certd/certd/commit/63d8bcf8823f713365042d3c7aee3cf31d44b044))
* 新增数据库迁移doc说明文档优化datetime字段平滑迁移 ([45fbce0](https://github.com/certd/certd/commit/45fbce0c2af5fb3ead6d3dd12a42f8cc1714262f))
* 支持彩虹聚合登录 ([6f18693](https://github.com/certd/certd/commit/6f186932ccad4becfdc0087c0539f7b2d0069844))
* 支持邮件模版设置 ([a6c0d2c](https://github.com/certd/certd/commit/a6c0d2c6f1fd6b60e6d7af290487c94564fd91ea))
* oidc支持使用第三方昵称或账号作为certd用户的用户名 ([b6fea0c](https://github.com/certd/certd/commit/b6fea0c8562abf912daa7d72958ceb2e93575d31))
## [1.37.15](https://github.com/certd/certd/compare/v1.37.14...v1.37.15) (2025-12-06) ## [1.37.15](https://github.com/certd/certd/compare/v1.37.14...v1.37.15) (2025-12-06)
### Bug Fixes ### Bug Fixes

View File

@@ -6,6 +6,13 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具 关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
| 官方开源地址: | |
| ---- | ---- |
| [Github](https://github.com/certd/certd)| ![](https://img.shields.io/github/stars/certd/certd?logo=github) |
| [Gitee](https://gitee.com/certd/certd) | ![](https://gitee.com/certd/certd/badge/star.svg?theme=dark) |
| [AtomGit](https://atomgit.com/certd/certd) |![](https://atomgit.com/certd/certd/star/badge.svg) |
![首页](../images/start/home.png) ![首页](../images/start/home.png)
## 1、关于证书续期 ## 1、关于证书续期

View File

@@ -40,32 +40,33 @@
| 36.| **括彩云cdn授权** | 括彩云CDN每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) | | 36.| **括彩云cdn授权** | 括彩云CDN每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
| 37.| **uniCloud** | unicloud授权 | | 37.| **uniCloud** | unicloud授权 |
| 38.| **猫云授权** | | | 38.| **猫云授权** | |
| 39.| **西部数码授权** | | | 39.| **授权插件示例** | |
| 40.| **多吉云** | | | 40.| **西部数码授权** | |
| 41.| **我爱云授权** | 我爱云CDN | | 41.| **多吉云** | |
| 42.| **CacheFly** | CacheFly | | 42.| **我爱云授权** | 我爱云CDN |
| 43.| **Gcore** | Gcore | | 43.| **CacheFly** | CacheFly |
| 44.| **亚马逊云aws授权** | | | 44.| **Gcore** | Gcore |
| 45.| **亚马逊云科技(国区)授权** | | | 45.| **亚马逊云aws授权** | |
| 46.| **dns.la授权** | | | 46.| **亚马逊云科技(国区)授权** | |
| 47.| **又拍云** | | | 47.| **dns.la授权** | |
| 48.| **51dns授权** | | | 48.| **又拍云** | |
| 49.| **FlexCDN授权** | | | 49.| **51dns授权** | |
| 50.| **farcdn授权** | | | 50.| **FlexCDN授权** | |
| 51.| **cloudflare授权** | | | 51.| **farcdn授权** | |
| 52.| **Github授权** | | | 52.| **cloudflare授权** | |
| 53.| **namesilo授权** | | | 53.| **Github授权** | |
| 54.| **proxmox** | | | 54.| **namesilo授权** | |
| 55.| **网宿授权** | | | 55.| **proxmox** | |
| 56.| **金山云授权** | | | 56.| **网宿授权** | |
| 57.| **APISIX授权** | | | 57.| **金山云授权** | |
| 58.| **Dokploy授权** | | | 58.| **APISIX授权** | |
| 59.| **godaddy授权** | | | 59.| **Dokploy授权** | |
| 60.| **新网授权** | | | 60.| **godaddy授权** | |
| 61.| **新网授权(代理方式)** | | | 61.| **新网授权** | |
| 62.| **新网互联授权** | 仅支持代理账号ip需要加入白名单 | | 62.| **新网授权(代理方式)** | |
| 63.| **中国移动CND授权** | | | 63.| **新网互联授权** | 仅支持代理账号ip需要加入白名单 |
| 64.| **雨云授权** | https://app.rainyun.com/ | | 64.| **中国移动CND授权** | |
| 65.| **雨云授权** | https://app.rainyun.com/ |
<style module> <style module>
table th:first-of-type { table th:first-of-type {

View File

@@ -17,8 +17,9 @@
| 13.| **cloudflare** | cloudflare dns provider | | 13.| **cloudflare** | cloudflare dns provider |
| 14.| **namesilo** | namesilo dns provider | | 14.| **namesilo** | namesilo dns provider |
| 15.| **godaddy** | GoDaddy | | 15.| **godaddy** | GoDaddy |
| 16.| **51dns** | 51DNS | | 16.| **Dns提供商Demo** | dns provider示例 |
| 17.| **新网互联** | 新网互联 | | 17.| **51dns** | 51DNS |
| 18.| **新网互联** | 新网互联 |
<style module> <style module>
table th:first-of-type { table th:first-of-type {

View File

@@ -43,4 +43,12 @@ service:
certd_koa_hostname: 0.0.0.0 certd_koa_hostname: 0.0.0.0
``` ```
## 6. DNS记录问题
1. DNS 不要设置CAA记录删除即可
2. DNSSEC相关报错DNSSEC管理中删除即可
3. DNS 有其他平台申请过的_acme-challenge记录删除即可

View File

@@ -17,7 +17,7 @@
> 如果出现过: 100.25.1.5 100.25.4.8 > 如果出现过: 100.25.1.5 100.25.4.8
> >
> 可以尝试配置 100.25.*.5 > 可以尝试配置 100.25.*.*
## 二、nginx代理方案 ## 二、nginx代理方案

View File

@@ -1,10 +0,0 @@
# 证书申请失败情况
## DNS记录问题
1. DNS 不要设置CAA记录删除即可
2. DNSSEC相关报错DNSSEC管理中删除即可
3. DNS 有其他平台申请过的_acme-challenge记录删除即可

11
docs/guide/use/cert/ip.md Normal file
View File

@@ -0,0 +1,11 @@
# IP证书申请
certd已支持IP证书申请
> 注意IP证书有效期只有7天。
## 申请方式
相比普通的域名证书申请方式区别在于:
1. 域名栏填写IP
2. 校验方式选择HTTP只能HTTP
3. 证书颁发机构选择默认的Let's Encrypt
4. 过期更新天数改成2天

View File

@@ -7,13 +7,14 @@
services: services:
certd: certd:
environment: # 环境变量 environment: # 环境变量
- certd_system_resetAdminPasswd=false - certd_system_resetAdminPasswd=true
``` ```
## 2. 重启容器 ## 2. 重启容器
```shell ```shell
docker compose up -d docker compose up -d
docker logs -f --tail 500 certd docker logs -f --tail 500 certd
# 观察日志当日志中输出“重置1号管理员用户密码完成”,即可操作下一步 # 观察日志当日志中输出“重置1号管理员用户密码完成”即可操作下一步
# 这里会打印1号管理员记录的用户名如果你修改过管理员用户名请注意查看此条日志
``` ```
## 3. 恢复环境变量 ## 3. 恢复环境变量
修改docker-compose.yaml`certd_system_resetAdminPasswd`改回`false` 修改docker-compose.yaml`certd_system_resetAdminPasswd`改回`false`
@@ -23,4 +24,6 @@ docker logs -f --tail 500 certd
docker compose up -d docker compose up -d
``` ```
## 5. 默认密码登录 ## 5. 默认密码登录
使用`admin/123456`登录系统,请及时修改管理员密码 使用`原管理员账号/123456`登录系统,请及时修改管理员密码
> 默认管理员账号: admin
> 如果忘记管理员账号,请查看修改密码时的启动日志,会打印管理员账号名

View File

@@ -70,5 +70,5 @@
"bugs": { "bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues" "url": "https://github.com/publishlab/node-acme-client/issues"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -247,7 +247,7 @@ async function getAuthoritativeDnsResolver(recordName, logger = log) {
try { try {
/* Resolve root domain by SOA */ /* Resolve root domain by SOA */
const domain = await resolveDomainBySoaRecord(recordNam,logger); const domain = await resolveDomainBySoaRecord(recordName,logger);
/* Resolve authoritative NS addresses */ /* Resolve authoritative NS addresses */
logger(`获取到权威NS服务器name: ${domain}`); logger(`获取到权威NS服务器name: ${domain}`);

View File

@@ -47,5 +47,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -45,5 +45,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -473,9 +473,7 @@ export class Executor {
await this.options.emailService?.sendByTemplate({ await this.options.emailService?.sendByTemplate({
type: "pipelineResult", type: "pipelineResult",
data: templateData, data: templateData,
email: { receivers: notification.options?.receivers,
receivers: notification.options?.receivers,
},
}); });
} catch (e) { } catch (e) {
logger.error("send email error", e); logger.error("send email error", e);

View File

@@ -185,6 +185,8 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
if (res == null) { if (res == null) {
throw new Error("授权不存在,可能已被删除,请前往任务配置里面重新选择授权"); throw new Error("授权不存在,可能已被删除,请前往任务配置里面重新选择授权");
} }
res.ctx.logger = this.logger;
res.ctx.http = this.http;
// @ts-ignore // @ts-ignore
if (this.logger?.addSecret) { if (this.logger?.addSecret) {
// 隐藏加密信息,不在日志中输出 // 隐藏加密信息,不在日志中输出

View File

@@ -27,6 +27,7 @@ export const pluginGroups = {
tencent: new PluginGroup("tencent", "腾讯云", 4, "svg:icon-tencentcloud"), tencent: new PluginGroup("tencent", "腾讯云", 4, "svg:icon-tencentcloud"),
volcengine: new PluginGroup("volcengine", "火山引擎", 4, "svg:icon-volcengine"), volcengine: new PluginGroup("volcengine", "火山引擎", 4, "svg:icon-volcengine"),
jdcloud: new PluginGroup("jdcloud", "京东云", 4, "svg:icon-jdcloud"), jdcloud: new PluginGroup("jdcloud", "京东云", 4, "svg:icon-jdcloud"),
ucloud: new PluginGroup("ucloud", "UCloud", 4, "svg:icon-ucloud"),
baidu: new PluginGroup("baidu", "百度云", 4, "ant-design:baidu-outlined"), baidu: new PluginGroup("baidu", "百度云", 4, "ant-design:baidu-outlined"),
qiniu: new PluginGroup("qiniu", "七牛云", 5, "svg:icon-qiniuyun"), qiniu: new PluginGroup("qiniu", "七牛云", 5, "svg:icon-qiniuyun"),
aws: new PluginGroup("aws", "亚马逊云", 6, "svg:icon-aws"), aws: new PluginGroup("aws", "亚马逊云", 6, "svg:icon-aws"),

View File

@@ -9,7 +9,8 @@ export type EmailSend = {
export type EmailSendByTemplateReq = { export type EmailSendByTemplateReq = {
type: string; type: string;
data: any; data: any;
email: { receivers: string[]; attachments?: any[] }; receivers: string[];
attachments?: any[];
}; };
export interface IEmailService { export interface IEmailService {

View File

@@ -24,5 +24,5 @@
"prettier": "^2.8.8", "prettier": "^2.8.8",
"tslib": "^2.8.1" "tslib": "^2.8.1"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -31,5 +31,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -56,5 +56,5 @@
"fetch" "fetch"
] ]
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -32,5 +32,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -64,5 +64,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -2,3 +2,4 @@ export * from './service/plus-service.js';
export * from './service/file-service.js'; export * from './service/file-service.js';
export * from './service/encryptor.js'; export * from './service/encryptor.js';
export * from './service/ocr-service.js'; export * from './service/ocr-service.js';
export * from './service/executor-queue.js';

View File

@@ -0,0 +1,79 @@
import { logger } from "@certd/basic";
export type TaskItem = {
task: ()=>Promise<void>;
}
export class UserTaskQueue{
userId: number;
pendingQueue: TaskItem[] = [];
runningQueue: TaskItem[] = [];
getMaxRunningCount: ()=>number ;
constructor(req: { userId: number ,getMaxRunningCount: ()=>number }) {
this.userId = req.userId;
this.getMaxRunningCount = req.getMaxRunningCount ;
}
addTask(task: TaskItem) {
this.pendingQueue.push(task);
this.runTask();
}
runTask() {
logger.info(`[user_${this.userId}]当前运行队列:${this.runningQueue.length}, 等待队列:${this.pendingQueue.length},最大运行队列:${this.getMaxRunningCount()}`);
if (this.runningQueue.length >= this.getMaxRunningCount()) {
return;
}
if (this.pendingQueue.length === 0) {
return;
}
const task = this.pendingQueue.shift();
if (!task) {
return;
}
// 执行任务
this.runningQueue.push(task);
const call = async ()=>{
try{
await task.task();
}finally{
// 任务执行完成,从运行队列中移除
const index = this.runningQueue.indexOf(task);
if (index > -1) {
this.runningQueue.splice(index, 1);
}
// 继续执行下一个任务
this.runTask();
}
}
logger.info(`[user_${this.userId}]执行任务,当前运行队列:${this.runningQueue.length}, 等待队列:${this.pendingQueue.length}`);
call()
}
}
export class ExecutorQueue{
queues: Record<number, UserTaskQueue> = {};
maxRunningCount: number = 8;
setMaxRunningCount(count: number) {
this.maxRunningCount = count;
}
getUserQueue(userId: number) {
const userQueue = this.queues[userId];
if (!userQueue) {
this.queues[userId] = new UserTaskQueue({ userId, getMaxRunningCount: ()=>this.maxRunningCount });
}
return this.queues[userId];
}
addTask(userId: number, task: TaskItem) {
const userQueue = this.getUserQueue(userId);
userQueue.addTask(task);
}
}
export const executorQueue = new ExecutorQueue();

View File

@@ -14,7 +14,7 @@ export const uploadTmpFileCacheKey = 'tmpfile_key_';
/** /**
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class FileService { export class FileService {
async saveFile(userId: number, tmpCacheKey: any, permission: 'public' | 'private') { async saveFile(userId: number, tmpCacheKey: any, permission: 'public' | 'private') {
if (tmpCacheKey.startsWith(`/${permission}`)) { if (tmpCacheKey.startsWith(`/${permission}`)) {

View File

@@ -5,7 +5,7 @@ import { IOcrService } from "@certd/plugin-lib";
/** /**
*/ */
@Provide("ocrService") @Provide("ocrService")
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class OcrService implements IOcrService { export class OcrService implements IOcrService {
@Inject() @Inject()
plusService: PlusService; plusService: PlusService;

View File

@@ -5,7 +5,7 @@ import { SysInstallInfo, SysLicenseInfo, SysSettingsService } from '../../settin
import { merge } from 'lodash-es'; import { merge } from 'lodash-es';
import fs from 'fs'; import fs from 'fs';
@Provide("plusService") @Provide("plusService")
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class PlusService { export class PlusService {
@Inject() @Inject()
sysSettingsService: SysSettingsService; sysSettingsService: SysSettingsService;

View File

@@ -43,12 +43,16 @@ export class SysPublicSettings extends BaseSettings {
//流水线是否启用有效期 //流水线是否启用有效期
pipelineValidTimeEnabled?: boolean = false; pipelineValidTimeEnabled?: boolean = false;
//证书域名添加到监控 //证书域名添加到监控
certDomainAddToMonitorEnabled?: boolean = false; certDomainAddToMonitorEnabled?: boolean = false;
// 固定证书有效期天数0表示不固定 // 固定证书有效期天数0表示不固定
fixedCertExpireDays?: number; fixedCertExpireDays?: number;
//默认到期前更新天数
defaultCertRenewDays?: number;
// 第三方OAuth配置 // 第三方OAuth配置
oauthEnabled?: boolean = false; oauthEnabled?: boolean = false;
oauthProviders: Record<string, { oauthProviders: Record<string, {
@@ -73,6 +77,8 @@ export class SysPrivateSettings extends BaseSettings {
httpRequestTimeout?: number = 30; httpRequestTimeout?: number = 30;
pipelineMaxRunningCount?: number;
sms?: { sms?: {
type?: string; type?: string;
config?: any; config?: any;

View File

@@ -8,6 +8,7 @@ import { BaseService } from '../../../basic/index.js';
import { cache, logger, setGlobalProxy } from '@certd/basic'; import { cache, logger, setGlobalProxy } from '@certd/basic';
import * as dns from 'node:dns'; import * as dns from 'node:dns';
import {mergeUtils} from "@certd/basic"; import {mergeUtils} from "@certd/basic";
import { executorQueue } from '../../basic/service/executor-queue.js';
const {merge} = mergeUtils; const {merge} = mergeUtils;
/** /**
* 设置 * 设置
@@ -140,6 +141,10 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
if (bean.dnsResultOrder) { if (bean.dnsResultOrder) {
dns.setDefaultResultOrder(bean.dnsResultOrder as any); dns.setDefaultResultOrder(bean.dnsResultOrder as any);
} }
if (bean.pipelineMaxRunningCount){
executorQueue.setMaxRunningCount(bean.pipelineMaxRunningCount);
}
} }
async updateByKey(key: string, setting: any) { async updateByKey(key: string, setting: any) {

View File

@@ -10,7 +10,7 @@ import {EncryptService} from './encrypt-service.js';
* 授权 * 授权
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, {allowDowngrade: true}) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AccessService extends BaseService<AccessEntity> { export class AccessService extends BaseService<AccessEntity> {
@InjectEntityModel(AccessEntity) @InjectEntityModel(AccessEntity)
repository: Repository<AccessEntity>; repository: Repository<AccessEntity>;

View File

@@ -9,7 +9,7 @@ import { AddonEntity } from "../entity/addon.js";
* Addon * Addon
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AddonService extends BaseService<AddonEntity> { export class AddonService extends BaseService<AddonEntity> {
@InjectEntityModel(AddonEntity) @InjectEntityModel(AddonEntity)
repository: Repository<AddonEntity>; repository: Repository<AddonEntity>;

View File

@@ -46,5 +46,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -43,5 +43,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -233,13 +233,22 @@ export class AcmeService {
// const origDomain = punycode.toUnicode(domain); // const origDomain = punycode.toUnicode(domain);
const origFullDomain = punycode.toUnicode(fullDomain); const origFullDomain = punycode.toUnicode(fullDomain);
const isIp = utils.domain.isIp(origFullDomain);
function checkIpChallenge(type: string) {
if (isIp) {
throw new Error(`IP证书不支持${type}校验方式请选择HTTP方式校验`);
}
}
if (providers.domainsVerifyPlan) { if (providers.domainsVerifyPlan) {
//按照计划执行 //按照计划执行
const domainVerifyPlan = providers.domainsVerifyPlan[origFullDomain]; const domainVerifyPlan = providers.domainsVerifyPlan[origFullDomain];
if (domainVerifyPlan) { if (domainVerifyPlan) {
if (domainVerifyPlan.type === "dns") { if (domainVerifyPlan.type === "dns") {
checkIpChallenge("dns");
dnsProvider = domainVerifyPlan.dnsProvider; dnsProvider = domainVerifyPlan.dnsProvider;
} else if (domainVerifyPlan.type === "cname") { } else if (domainVerifyPlan.type === "cname") {
checkIpChallenge("cname");
const cname: CnameVerifyPlan = domainVerifyPlan.cnameVerifyPlan; const cname: CnameVerifyPlan = domainVerifyPlan.cnameVerifyPlan;
if (cname) { if (cname) {
dnsProvider = cname.dnsProvider; dnsProvider = cname.dnsProvider;
@@ -274,6 +283,7 @@ export class AcmeService {
} }
const dnsChallenge = getChallenge("dns-01"); const dnsChallenge = getChallenge("dns-01");
checkIpChallenge("dns");
return await doDnsVerify(dnsChallenge, fullRecord, dnsProvider); return await doDnsVerify(dnsChallenge, fullRecord, dnsProvider);
} }

View File

@@ -20,7 +20,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
@TaskInput({ @TaskInput({
title: "更新天数", title: "更新天数",
value: 35, value: 18,
component: { component: {
name: "a-input-number", name: "a-input-number",
vModel: "value", vModel: "value",

View File

@@ -0,0 +1,165 @@
import { IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { AliyunAccess, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import type { CertInfo } from "../acme.js";
import { CertApplyBasePlugin } from "../base.js";
import { CertReader } from "../cert-reader.js";
import dayjs from "dayjs";
export { CertReader };
export type { CertInfo };
@IsTaskPlugin({
name: "CertApplyGetFormAliyun",
icon: "ph:certificate",
title: "获取阿里云订阅证书",
group: pluginGroups.cert.key,
desc: "从阿里云拉取订阅模式的商用证书",
default: {
strategy: {
runStrategy: RunStrategy.AlwaysRun,
},
},
})
export class CertApplyGetFormAliyunPlugin extends CertApplyBasePlugin {
@TaskInput({
title: "Access授权",
helper: "阿里云授权AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "aliyun",
},
required: true,
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "证书订单ID",
helper: "订阅模式的证书订单Id",
typeName: "CertApplyGetFormAliyun",
component: {
name: "RemoteAutoComplete",
vModel: "value",
},
action: CertApplyGetFormAliyunPlugin.prototype.onGetOrderList.name,
})
)
orderId!: string;
async onInit(): Promise<void> {}
async doCertApply(): Promise<CertReader> {
const access = await this.getAccess<AliyunAccess>(this.accessId);
const client = await access.getClient("cas.aliyuncs.com");
this.logger.info(`开始获取证书,orderId:${this.orderId}`);
let orderId: any = this.orderId;
if (!orderId) {
throw new Error("请先输入证书订单ID");
}
if (typeof orderId !== "string") {
orderId = parseInt(orderId);
}
const certState = await this.getCertificateState(client, orderId);
this.logger.info(`获取到证书Id:${JSON.stringify(certState.CertId)}`);
const certDetail = await this.getCertDetail(client, certState.CertId);
this.logger.info(`获取到证书:${certDetail.getAllDomains()}, 过期时间:${dayjs(certDetail.expires).format("YYYY-MM-DD HH:mm:ss")}`);
return certDetail;
}
async getCertDetail(client: any, certId: any) {
const res = await client.doRequest({
// 接口名称
// 接口名称
action: "GetUserCertificateDetail",
// 接口版本
version: "2020-04-07",
// 接口协议
protocol: "HTTPS",
// 接口 HTTP 方法
method: "POST",
authType: "AK",
style: "RPC",
// 接口 PATH
pathname: `/`,
data: {
query: {
CertId: certId,
},
},
});
const crt = res.Cert;
const key = res.Key;
return new CertReader({
crt,
key,
csr: "",
});
}
async getCertificateState(client: any, orderId: any): Promise<{ CertId: string; Type: string; Domain: string }> {
const res = await client.doRequest({
// 接口名称
action: "DescribeCertificateState",
// 接口版本
version: "2020-04-07",
// 接口协议
protocol: "HTTPS",
// 接口 HTTP 方法
method: "POST",
authType: "AK",
style: "RPC",
// 接口 PATH
pathname: `/`,
data: {
query: {
OrderId: orderId,
},
},
});
return res;
}
async onGetOrderList(req: PageSearch) {
if (!this.accessId) {
throw new Error("请先选择Access授权");
}
const access = await this.getAccess<AliyunAccess>(this.accessId);
const client = await access.getClient("cas.aliyuncs.com");
const res = await client.doRequest({
// 接口名称
action: "ListUserCertificateOrder",
// 接口版本
version: "2020-04-07",
method: "POST",
authType: "AK",
style: "RPC",
// 接口 PATH
pathname: `/`,
data: {
query: {
Status: "ISSUED",
},
},
});
const list = res?.CertificateOrderList || [];
if (!list || list.length === 0) {
throw new Error("没有找到已签发的证书订单");
}
return list.map((item: any) => {
const label = `${item.Domain}<${item.OrderId}>`;
return {
label: label,
value: item.OrderId,
Domain: item.Domain,
};
});
}
}
new CertApplyGetFormAliyunPlugin();

View File

@@ -93,7 +93,7 @@ const preferredChainMergeScript = (() => {
desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上", desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上",
default: { default: {
input: { input: {
renewDays: 35, renewDays: 18,
forceUpdate: false, forceUpdate: false,
}, },
strategy: { strategy: {
@@ -111,7 +111,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
options: [ options: [
{ value: "dns", label: "DNS直接验证" }, { value: "dns", label: "DNS直接验证" },
{ value: "cname", label: "CNAME代理验证" }, { value: "cname", label: "CNAME代理验证" },
{ value: "http", label: "HTTP文件验证" }, { value: "http", label: "HTTP文件验证IP证书只能选它" },
{ value: "dnses", label: "多DNS提供商" }, { value: "dnses", label: "多DNS提供商" },
{ value: "auto", label: "自动匹配" }, { value: "auto", label: "自动匹配" },
], ],
@@ -119,7 +119,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
required: true, required: true,
helper: `1. <b>DNS直接验证</b>当域名dns解析已被本系统支持时即下方DNS解析服务商选项中可选推荐选择此方式 helper: `1. <b>DNS直接验证</b>当域名dns解析已被本系统支持时即下方DNS解析服务商选项中可选推荐选择此方式
2. <b>CNAME代理验证</b>:支持任何注册商的域名,第一次需要手动添加[CNAME记录](#/certd/cname/record)如果经常申请失败建议将DNS服务器修改为阿里云/腾讯云的然后使用DNS直接验证 2. <b>CNAME代理验证</b>:支持任何注册商的域名,第一次需要手动添加[CNAME记录](#/certd/cname/record)如果经常申请失败建议将DNS服务器修改为阿里云/腾讯云的然后使用DNS直接验证
3. <b>HTTP文件验证</b>:不支持泛域名,需要配置网站文件上传 3. <b>HTTP文件验证</b>:不支持泛域名,需要配置网站文件上传IP证书必须选它
4. <b>多DNS提供商</b>每个域名可以选择独立的DNS提供商 4. <b>多DNS提供商</b>每个域名可以选择独立的DNS提供商
5. <b>自动匹配</b>:此处无需选择校验方式,需要在[域名管理](#/certd/cert/domain)中提前配置好校验方式 5. <b>自动匹配</b>:此处无需选择校验方式,需要在[域名管理](#/certd/cert/domain)中提前配置好校验方式
`, `,
@@ -133,12 +133,12 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
name: "icon-select", name: "icon-select",
vModel: "value", vModel: "value",
options: [ options: [
{ value: "letsencrypt", label: "Let's Encrypt免费新手推荐", icon: "simple-icons:letsencrypt" }, { value: "letsencrypt", label: "Let's Encrypt免费新手推荐支持IP证书", icon: "simple-icons:letsencrypt" },
{ value: "google", label: "Google免费", icon: "flat-color-icons:google" }, { value: "google", label: "Google免费", icon: "flat-color-icons:google" },
{ value: "zerossl", label: "ZeroSSL免费", icon: "emojione:digit-zero" }, { value: "zerossl", label: "ZeroSSL免费", icon: "emojione:digit-zero" },
{ value: "litessl", label: "litessl免费", icon: "roentgen:free" }, { value: "litessl", label: "litessl免费", icon: "roentgen:free" },
{ value: "sslcom", label: "SSL.com仅主域名和www免费", icon: "la:expeditedssl" }, { value: "sslcom", label: "SSL.com仅主域名和www免费", icon: "la:expeditedssl" },
{ value: "letsencrypt_staging", label: "Let's Encrypt测试环境IP证书", icon: "simple-icons:letsencrypt" }, { value: "letsencrypt_staging", label: "Let's Encrypt测试环境仅供测试", icon: "simple-icons:letsencrypt" },
], ],
}, },
helper: "Let's Encrypt申请最简单\nGoogle大厂光环兼容性好仅首次需要翻墙获取EAB授权\nZeroSSL需要EAB授权无需翻墙\nSSL.com仅主域名和www免费,必须设置CAA记录", helper: "Let's Encrypt申请最简单\nGoogle大厂光环兼容性好仅首次需要翻墙获取EAB授权\nZeroSSL需要EAB授权无需翻墙\nSSL.com仅主域名和www免费,必须设置CAA记录",

View File

@@ -3,4 +3,5 @@ export { EVENT_CERT_APPLY_SUCCESS } from "./cert-plugin/base-convert.js";
export * from "./cert-plugin/index.js"; export * from "./cert-plugin/index.js";
export * from "./cert-plugin/lego/index.js"; export * from "./cert-plugin/lego/index.js";
export * from "./cert-plugin/custom/index.js"; export * from "./cert-plugin/custom/index.js";
export * from "./cert-plugin/getter/aliyun.js";
export const CertApplyPluginNames = [":cert:"]; export const CertApplyPluginNames = [":cert:"];

View File

@@ -53,5 +53,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "f2e4e59f8d1b54b835d5da43aef8b527d6a4ba0b" "gitHead": "fa14f6219810ddbfcf1dde7b69963ee8a36c80c4"
} }

View File

@@ -55,15 +55,39 @@
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe8fb;</span> <span class="icon iconfont">&#xe60a;</span>
<div class="name">social-foursquare</div> <div class="name">飞牛</div>
<div class="code-name">&amp;#xe8fb;</div> <div class="code-name">&amp;#xe60a;</div>
</li> </li>
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe65a;</span> <span class="icon iconfont">&#xe8ad;</span>
<div class="name">ksyun-logo</div> <div class="name">金山云logo</div>
<div class="code-name">&amp;#xe65a;</div> <div class="code-name">&amp;#xe8ad;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe989;</span>
<div class="name">中国移动</div>
<div class="code-name">&amp;#xe989;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe643;</span>
<div class="name">xinnet</div>
<div class="code-name">&amp;#xe643;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe717;</span>
<div class="name">ucloud</div>
<div class="code-name">&amp;#xe717;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe8fb;</span>
<div class="name">social-foursquare</div>
<div class="code-name">&amp;#xe8fb;</div>
</li> </li>
<li class="dib"> <li class="dib">
@@ -228,7 +252,7 @@
<pre><code class="language-css" <pre><code class="language-css"
>@font-face { >@font-face {
font-family: 'iconfont'; font-family: 'iconfont';
src: url('iconfont.svg?t=1754884110189#iconfont') format('svg'); src: url('iconfont.svg?t=1766772710945#iconfont') format('svg');
} }
</code></pre> </code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3> <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -255,23 +279,59 @@
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-four"></span> <span class="icon iconfont icon-fnos"></span>
<div class="name"> <div class="name">
social-foursquare 飞牛
</div> </div>
<div class="code-name">.icon-four <div class="code-name">.icon-fnos
</div> </div>
</li> </li>
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-ksyun"></span> <span class="icon iconfont icon-ksyun"></span>
<div class="name"> <div class="name">
ksyun-logo 金山云logo
</div> </div>
<div class="code-name">.icon-ksyun <div class="code-name">.icon-ksyun
</div> </div>
</li> </li>
<li class="dib">
<span class="icon iconfont icon-cmcc"></span>
<div class="name">
中国移动
</div>
<div class="code-name">.icon-cmcc
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-xinnet"></span>
<div class="name">
xinnet
</div>
<div class="code-name">.icon-xinnet
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-ucloud"></span>
<div class="name">
ucloud
</div>
<div class="code-name">.icon-ucloud
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-four"></span>
<div class="name">
social-foursquare
</div>
<div class="code-name">.icon-four
</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-rainyun"></span> <span class="icon iconfont icon-rainyun"></span>
<div class="name"> <div class="name">
@@ -517,20 +577,52 @@
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-four"></use> <use xlink:href="#icon-fnos"></use>
</svg> </svg>
<div class="name">social-foursquare</div> <div class="name">飞牛</div>
<div class="code-name">#icon-four</div> <div class="code-name">#icon-fnos</div>
</li> </li>
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-ksyun"></use> <use xlink:href="#icon-ksyun"></use>
</svg> </svg>
<div class="name">ksyun-logo</div> <div class="name">金山云logo</div>
<div class="code-name">#icon-ksyun</div> <div class="code-name">#icon-ksyun</div>
</li> </li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-cmcc"></use>
</svg>
<div class="name">中国移动</div>
<div class="code-name">#icon-cmcc</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-xinnet"></use>
</svg>
<div class="name">xinnet</div>
<div class="code-name">#icon-xinnet</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-ucloud"></use>
</svg>
<div class="name">ucloud</div>
<div class="code-name">#icon-ucloud</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-four"></use>
</svg>
<div class="name">social-foursquare</div>
<div class="code-name">#icon-four</div>
</li>
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rainyun"></use> <use xlink:href="#icon-rainyun"></use>

View File

@@ -1,6 +1,6 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4688792 */ font-family: "iconfont"; /* Project id 4688792 */
src: url('iconfont.svg?t=1754884110189#iconfont') format('svg'); src: url('iconfont.svg?t=1766772710945#iconfont') format('svg');
} }
.iconfont { .iconfont {
@@ -11,12 +11,28 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-four:before { .icon-fnos:before {
content: "\e8fb"; content: "\e60a";
} }
.icon-ksyun:before { .icon-ksyun:before {
content: "\e65a"; content: "\e8ad";
}
.icon-cmcc:before {
content: "\e989";
}
.icon-xinnet:before {
content: "\e643";
}
.icon-ucloud:before {
content: "\e717";
}
.icon-four:before {
content: "\e8fb";
} }
.icon-rainyun:before { .icon-rainyun:before {

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,41 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "45984300",
"name": "飞牛",
"font_class": "fnos",
"unicode": "e60a",
"unicode_decimal": 58890
},
{
"icon_id": "21785199",
"name": "金山云logo",
"font_class": "ksyun",
"unicode": "e8ad",
"unicode_decimal": 59565
},
{
"icon_id": "17262195",
"name": "中国移动",
"font_class": "cmcc",
"unicode": "e989",
"unicode_decimal": 59785
},
{
"icon_id": "3445787",
"name": "xinnet",
"font_class": "xinnet",
"unicode": "e643",
"unicode_decimal": 58947
},
{
"icon_id": "41854093",
"name": "ucloud",
"font_class": "ucloud",
"unicode": "e717",
"unicode_decimal": 59159
},
{ {
"icon_id": "544964", "icon_id": "544964",
"name": "social-foursquare", "name": "social-foursquare",
@@ -12,13 +47,6 @@
"unicode": "e8fb", "unicode": "e8fb",
"unicode_decimal": 59643 "unicode_decimal": 59643
}, },
{
"icon_id": "8567079",
"name": "ksyun-logo",
"font_class": "ksyun",
"unicode": "e65a",
"unicode_decimal": 58970
},
{ {
"icon_id": "42174864", "icon_id": "42174864",
"name": "雨-copy", "name": "雨-copy",

View File

@@ -14,9 +14,17 @@
/> />
<missing-glyph /> <missing-glyph />
<glyph glyph-name="four" unicode="&#59643;" d="M769.629867 861.866667c0 0-438.0864 0-508.209067 0C191.287467 861.866667 170.666667 808.964267 170.666667 775.650133c0-33.3376 0-809.898667 0-809.898666 0-37.527467 20.106667-51.4496 31.3984-56.036267 11.304533-4.597333 42.487467-8.471467 61.169066 13.1584 0 0 239.933867 279.242667 244.053334 283.3792 6.231467 6.250667 6.231467 6.250667 12.4672 6.250667 12.4672 0 104.942933 0 155.236266 0 65.224533 0 75.712 46.653867 82.525867 74.1376 5.672533 23.016533 69.309867 349.7856 90.564267 453.4592C864.305067 819.214933 844.256 861.866667 769.629867 861.866667zM757.5168 286.641067c5.672533 23.016533 69.309867 349.7856 90.564267 453.4592M739.6096 723.904l-21.3312-110.365867c-2.549333-12.053333-17.678933-24.736-31.707733-24.736-14.026667 0-204.622933 0-204.622934 0C459.675733 588.8 443.733333 575.7056 443.733333 553.3824l0-28.689067c0-22.3424 16.040533-38.173867 38.325334-38.173866 0 0 158.894933 0 174.609066 0 15.7312 0 31.168-17.2992 27.767467-34.144-3.4176-16.8704-19.383467-99.3472-21.2992-108.565334-1.924267-9.233067-12.469333-25.013333-31.170133-25.013333-15.761067 0-137.1072 0-137.1072 0-24.970667 0-32.520533-3.2704-49.224534-24.091733-16.718933-20.842667-166.946133-201.796267-166.946133-201.796267-1.521067-1.7536-3.008-1.245867-3.008 0.6656L275.68 725.5552c0 14.2592 12.3584 30.9824 30.888533 30.9824 0 0 391.921067 0 407.835734 0C729.412267 756.539733 743.441067 742.391467 739.6096 723.904z" horiz-adv-x="1024" /> <glyph glyph-name="fnos" unicode="&#58890;" d="M144.482434 896h730.821577c67.983402-28.323585 116.141145-76.481328 144.46473-144.46473v-730.821577c-28.323585-67.983402-76.481328-116.141145-144.46473-144.46473h-730.821577c-67.983402 28.323585-116.141145 76.481328-144.46473 144.46473v730.821577c28.323585 67.983402 76.481328 116.141145 144.46473 144.46473zM229.461687 632.564315c-1.665593-32.445079 1.164216-66.43678 8.497926-101.975103 17.990108-19.800166 40.654075-29.717245 67.983402-29.742739-18.63595-17.208299-27.133876-38.453112-25.493776-63.73444-84.486373 1.053743-117.058921 43.543369-97.726141 127.46888a431.320697 431.320697 0 0 0 46.738589 67.983402zM781.826833 632.564315c66.938158-53.536929 75.436083-114.44156 25.493776-182.705394a3226.917178 3226.917178 0 0 0-399.40249-12.746888v-237.941909h50.987552c-9.883087 77.900481 24.108614 109.062373 101.975104 93.477179a275.647203 275.647203 0 0 0 4.248962 67.983402 80.458357 80.458357 0 0 0 21.244813 12.746888 571.281527 571.281527 0 0 0 169.958507-4.248962c-32.436581-40.280166-74.926207-60.105826-127.46888-59.485477 11.149278-79.387618-22.842423-107.719701-101.975104-84.979254a347.871071 347.871071 0 0 0-4.248962-76.481327 59.273029 59.273029 0 0 0-29.742739-21.244814 361.14483 361.14483 0 0 0-110.473029 0 59.273029 59.273029 0 0 0-29.742739 21.244814 814.381676 814.381676 0 0 0-12.746888 161.460581 814.381676 814.381676 0 0 0 12.746888 161.46058 305.415436 305.415436 0 0 0 38.240664 29.742739l365.410789 8.497925c15.576697 4.248963 25.493776 14.166041 29.742738 29.742739 5.761593 31.62078 4.34244 62.782672-4.248962 93.477178z" horiz-adv-x="1024" />
<glyph glyph-name="ksyun" unicode="&#58970;" d="M765.457 75.317c-3.34 0-6.63 0.215-9.937 0.354h5.055v0.086c-51.304 1.425-97.07 24.545-128.508 60.633l-0.591-0.035-3.1 4.473c-0.674 0.82-1.386 1.614-2.042 2.451l-9.393 13.59 0.185 0.173-6.552 9.444c-0.056-0.069-0.1-0.138-0.151-0.198l-89.26 129.086-0.221-0.026c-11.154 14.712-28.757 24.264-48.64 24.264-10.982 0-21.26-2.94-30.16-8.016a104.014 104.014 0 0 0 7.117-11.767l144.994-221.015 0.302 0.181c14.453-23.991 40.672-40.084 70.72-40.084 11.496 0 22.434 2.357 32.38 6.604-23.15-22.93-54.986-37.115-90.145-37.115-46.033 0-86.278 24.355-108.867 60.805h-0.289l-64.475 98.628c-35.82-55.668-98.126-92.628-169.227-92.628-1.675 0-3.294 0.208-4.957 0.251v0.111c-125.861 2.66-227.127 105.328-227.127 231.834 0 119.944 91 218.623 207.729 230.8 27.415 126.561 139.986 221.4 274.757 221.4 139.196 0 254.699-101.167 277.171-233.97 112.178-13.248 199.207-108.604 199.207-224.343 0-124.805-101.17-225.971-225.975-225.971z m0 360.781c-26.715 0-51.55-7.873-72.5-21.28a186.8 186.8 0 0 1 11.13 63.584c0 104.403-84.634 189.033-189.033 189.033s-189.032-84.63-189.032-189.033c0-16.603 2.261-32.652 6.324-47.967-20.082 11.072-43.142 17.409-67.695 17.409-77.566 0-140.448-62.882-140.448-140.444 0-77.571 62.882-140.448 140.448-140.448v-0.027c54.52 0 99.642 39.723 108.31 91.783 0.48 2.866 0.864 5.767 1.145 8.702 0.267 3.125 0.479 6.26 0.479 9.445 0 32.116-16.469 60.321-41.346 76.682 7.524 1.74 15.333 2.728 23.392 2.728 32.271 0 60.896-14.794 79.988-37.759 21.117 23.108 51.351 37.68 85.043 37.68 41.367 0 77.553-21.897 97.963-54.709h0.086l61.375-91.04c18.833-26.296 49.556-43.512 84.37-43.512v-0.44c74.454 0 134.811 60.348 134.811 134.802s-60.356 134.811-134.81 134.811z" horiz-adv-x="1024" /> <glyph glyph-name="ksyun" unicode="&#59565;" d="M718.5408 442.1632c-4.1984 0-8.3968 0.7168-12.5952 0.7168a103.8336 103.8336 0 0 1-67.8912-25.088 139.6736 139.6736 0 0 1 13.6192 60.3136 133.12 133.12 0 0 1-2.4576 25.7024 139.5712 139.5712 0 0 1-274.432 0 133.12 133.12 0 0 1-2.4576-25.7024 139.6736 139.6736 0 0 1 13.6192-60.3136 103.8336 103.8336 0 0 1-67.8912 25.088c-4.1984 0-8.3968 0-12.5952-0.7168a104.5504 104.5504 0 1 1 101.0688-159.4368 116.736 116.736 0 0 1 6.144 11.0592 105.1648 105.1648 0 0 1 4.9152 76.6976 62.6688 62.6688 0 0 0 27.3408-20.48l2.6624-3.8912a83.2512 83.2512 0 0 0 133.9392 3.3792l4.7104-6.8608 51.2-73.9328a84.0704 84.0704 0 0 1 62.464-34.6112h5.5296a104.5504 104.5504 0 0 1 12.5952 208.384zM512 896a512 512 0 1 1 512-512A512 512 0 0 1 512 896z m200.8064-732.3648h-6.8608a152.4736 152.4736 0 0 0-120.5248 58.9824S509.2352 331.3664 508.0064 332.8a42.1888 42.1888 0 0 1-32.4608 15.2576 40.96 40.96 0 0 1-24.064-7.5776l122.88-175.4112a62.5664 62.5664 0 0 1 77.0048-20.48A97.5872 97.5872 0 0 0 593.92 112.4352a97.0752 97.0752 0 0 0-95.5392 39.1168l-49.4592 70.656a174.8992 174.8992 0 1 0-143.36 290.5088 209.7152 209.7152 0 0 0 413.9008 0 174.7968 174.7968 0 0 0-6.144-349.0816z" horiz-adv-x="1024" />
<glyph glyph-name="cmcc" unicode="&#59785;" d="M922.8575 285.75C845.75 386.25 730.625 489.45 698.8925 514.575 676.25 532.5 654.4625 541.5 632.6975 541.5a95.3025 95.3025 0 0 1-61.6575-25.125c-3.63-3.5925-9.0675-8.0775-15.4125-13.4625-45.3375-37.695-186.795-166.0275-190.4175-169.6125a58.9425 58.9425 0 0 0-39.9-16.155 69.2175 69.2175 0 0 0-43.5 20.64A1055.0475 1055.0475 0 0 0 166.625 447.27c-34.455 38.5875-63.4725 72.69-63.4725 72.69s-6.345-12.5625-9.975-22.4325a17.025 17.025 0 0 1 0.9075-14.3625C113.1275 456.24 199.25 354.8325 317.15 253.425A100.2675 100.2675 0 0 1 383.3375 226.5a95.3025 95.3025 0 0 1 61.6575 25.125c3.63 3.5925 9.0675 8.0775 15.4125 13.4625 45.3375 37.695 131.4825 115.77 171.375 152.565 9.0675 8.0775 15.4125 13.4625 18.135 16.155a58.9425 58.9425 0 0 0 39.9 16.155 74.295 74.295 0 0 0 43.5-20.64c25.3875-21.54 66.195-59.25 115.155-109.5 35.3625-37.695 63.4725-71.7975 63.4725-72.69 0 0 6.345 13.4625 9.975 22.4325a16.125 16.125 0 0 1 0.9375 16.185zM469.61 660.9525c-39.8025-28.95-202.635-180.0225-234.3-216.21a550.5 550.5 0 0 1 57-53.4 19.8 19.8 0 0 1 28.95 3.615c62.4225 63.3225 190.8825 176.4075 208.065 189.975 47.9475 40.71 104.94 44.325 155.595 6.33 124.8375-94.05 236.1075-241.5075 255.105-268.65 0 0 2.715 13.5675 4.5 25.3275a28.5975 28.5975 0 0 1-3.615 20.805c-117.6 169.1775-242.4225 274.11-290.37 300.345-54.2775 28.0425-117.6 38.0025-180.93-8.1375zM337.505 748.7025c-80.4825-65.13-184.5-161.025-210.75-189.975a706.5 706.5 0 0 1 50.6625-54.2775 11.1225 11.1225 0 0 1 17.19 1.8075c79.605 76.8975 165.5475 155.6025 216.2025 194.505 84.135 65.1375 183.645 71.4675 280.44-5.43a1192.665 1192.665 0 0 0 114.8925-104.94 1623 1623 0 0 0 141.12-180 206.505 206.505 0 0 1-4.5 37.995 57.8325 57.8325 0 0 1-5.43 14.475 857.025 857.025 0 0 1-100.4325 145.59C728.3375 735.135 660.4925 773.13 616.1675 796.65 557.36 826.5 444.2825 835.5525 337.505 748.7025zM554.6375 113.64c39.8025 28.95 202.6425 180 234.3 216.21a615.465 615.465 0 0 1-57 54.2775 19.8 19.8 0 0 1-28.95-3.615c-62.3925-63.3225-190.8525-176.4075-208.0425-189.975-47.9475-40.71-104.94-44.325-156.5025-6.33C213.605 278.2875 103.235 425.745 84.2375 453.7875c0 0-2.715-13.5675-4.5-25.3275a28.5975 28.5975 0 0 1 3.6-20.8125c117.6-169.1625 242.4375-274.11 290.385-300.36 54.2775-29.8275 117.6075-39.75 180.915 6.3525zM686.75 25.8975c80.5125 65.1375 184.545 161.025 210.75 189.975a706.5 706.5 0 0 1-50.6625 54.2775 11.1225 11.1225 0 0 1-17.19-1.8075C750.05 191.445 664.1075 112.74 613.4525 73.8375c-84.135-65.1375-183.645-71.4675-280.44 5.43a1192.6725 1192.6725 0 0 0-114.8925 104.9625A1623 1623 0 0 0 77 364.23a214.4325 214.4325 0 0 1 4.5-37.995 57.8325 57.8325 0 0 1 5.4525-14.505 857.025 857.025 0 0 1 100.4175-145.65c108.555-126.6 176.4-164.61 220.725-188.1 58.8075-29.8875 171.885-38.9325 278.655 47.9175z" horiz-adv-x="1024" />
<glyph glyph-name="xinnet" unicode="&#58947;" d="M449.695 233.805h-88.57v26.492l74.139 5.63 9.618 22.795-127.737-8.113-0.617-2.771v-177.932l28.119 8.282 13.398 3.923v0.03l3.08 0.91v99.226h23.988v-112.394l44.58 13.168v99.226h8.25zM304.441 276.603h-48.809s-6.702 11.83-28.222 11.83c-21.525 0-27.389-11.83-27.389-11.83h-48.383v-21.643l141.504 0.102 11.299 21.541zM304.513 233.38h-20.7l7.837 14.765h-43.566l-11.614-14.934h-18.218l-10.865 14.861h-45.663l8.036-14.692h-18.233v-21.33h141.42zM161.927 164.311l-13.801-57.507h40.163l11.728 57.507zM256.84 164.311l9.477-55.332h38.3l-10.506 55.332zM304.733 194.389h-53.886v10.74h-44.438v-10.74h-54.771v-21.446h54.771v-48.318h-8.643v-0.111h-0.188v-24.631h13.742l39.527 24.954v48.106h42.296zM864.869 288.799H593.381v-188.916l44.496 12.932V265.246h188.049zM877.851 288.577l-44.705-26.382v-138.056h-36.295v-24.18h36.975l7.072 4.223 36.953 20zM734.63 248.512h-36.384l-8.753-21.764-9.535 24.053h-36.402l26.135-64.327-25.837-62.403h36.394l8.878 22.154 8.792-22.154h36.403l-25.344 62.403zM826.948 248.512H790.55l-9.685-24.121-9.843 23.793H734.63l26.974-62.699-25.413-61.414h36.394l9.023 22.51 7.39-18.144H825.4l-24.559 57.048zM380.676 634.375c0-18.175-13.885-32.909-30.973-32.909-17.115 0-30.994 14.734-30.994 32.909 0 18.184 13.879 32.903 30.994 32.903 17.088 0 30.973-14.719 30.973-32.903M708.995 555.937c-17.526 18.575-35.742 16.512-35.742 16.512h-101.77v-188.863h44.27V525.21h35.131s11.535 0.583 19.521-7.881c7.973-8.491 7.723-19.026 7.723-19.026v-114.718h44.496V519.638c-0.001 0 0.293 21.58-13.629 36.299M952.059 619.991h44.539v-214.982l-44.539-40.773zM923.821 565.444h100.994v-47.234H923.821zM327.123 502.904v-119.319h44.508V528.96c-11.426-8.281-26.541-17.127-44.508-26.056M208.431 456.805c-49.177-15.085-95.271-24.986-131.765-29.172l-27.514-44.048h100.25l26.299 53.23 23.829-53.23h100.255l-50.09 86.893c-13.222-4.714-26.999-9.32-41.264-13.673M384.299 570.887c-4.249 15.558-46.788 17.206-106.439 6.753 32.997 4.075 55.276 1.879 57.824-7.445 3.938-14.56-41.703-41.739-106.182-64.657l-4.818 8.344 75.1 135.905H199.53l-23.831-53.233-26.297 53.233H49.151l81.398-135.905-24.658-39.493c-33.66-4.29-56.458-2.133-59.02 7.278-2.77 10.262 18.963 26.743 54.58 43.6C37.942 498.585-3.402 470.228 1.158 453.44c6.298-23.116 97.141-15.564 202.949 16.861 105.786 32.439 186.461 77.47 180.192 100.586M873.015 443.356c-10.421-13.998-26.54-22.961-44.674-22.961-25.423 0-46.964 17.714-54.271 42.163h144.285a106.258 106.258 0 0 1 1.601 18.312c0 53.732-41.021 97.293-91.614 97.293-50.556 0-91.57-43.561-91.57-97.293 0-53.725 41.015-97.285 91.57-97.285 38.085 0 70.728 24.677 84.548 59.771h-39.875z m-44.674 98.009c25.456 0 47.013-17.729 54.296-42.172H774.069c7.308 24.443 28.849 42.172 54.272 42.172M533.527 555.937c-17.575 18.575-35.783 16.512-35.783 16.512h-101.78v-188.863h44.301V525.21h35.108s11.542 0.583 19.518-7.881c8.002-8.491 7.725-19.026 7.725-19.026v-114.718h44.495V519.638c-0.001 0 0.289 21.58-13.584 36.299M974.67 115.784c-22.534 0-40.867 18.328-40.867 40.857 0 22.528 18.333 40.856 40.867 40.856 22.529 0 40.857-18.328 40.857-40.856 0-22.529-18.328-40.857-40.857-40.857z m0 78.714c-20.88 0-37.867-16.982-37.867-37.856 0-20.875 16.987-37.857 37.867-37.857 20.875 0 37.857 16.982 37.857 37.857 0 20.874-16.982 37.856-37.857 37.856zM993.532 133.594h-6.949l-7.567 12.662c-1.512 2.532-2.896 4.255-4.152 5.167-1.256 0.913-2.758 1.37-4.505 1.37h-4.24v-19.199h-5.889v45.611h13.898c4.436 0 7.91-1.069 10.424-3.209 2.512-2.14 3.769-5.134 3.769-8.981 0-6.321-3.377-10.424-10.129-12.309v-0.146a9.343 9.343 0 0 0 3.253-2.268c0.913-0.981 2.096-2.66 3.549-5.035l8.538-13.663z m-27.414 40.459v-16.107h6.949c2.689 0 4.873 0.785 6.552 2.355s2.518 3.632 2.518 6.184c0 2.375-0.766 4.23-2.297 5.565s-3.759 2.003-6.684 2.003h-7.038z" horiz-adv-x="1024" />
<glyph glyph-name="ucloud" unicode="&#59159;" d="M945.883 829.294v-592.457c0-191.927-150.162-342.09-342.089-342.09h-108.47c-191.927 0-342.09 150.163-342.09 342.09V562.249h100.06v91.794h83.456v83.383h66.78v-542.28A149.504 149.504 0 0 1 553.69 44.91h16.823a149.723 149.723 0 0 1 150.163 150.235V829.294h225.28z m-734.28-183.589v-58.441h-58.369v58.441h58.368z m83.455 91.721v-58.368h-50.03v58.368h50.03z m-83.383 0v-41.691h-50.102v41.691h50.03z m-91.794-8.265v-33.426H78.117v33.5h41.691z m275.31 100.133v-58.515H336.75V829.294h58.514z m-100.133-8.412v-41.691h-50.03V820.882h50.03z m-91.794 0v-33.353h-41.691V820.882h41.691z m-91.794-8.338v-25.015H78.117V812.544h33.353zM278.382 896v-25.015h-25.088V896h25.088z m-75.118 0v-25.015h-33.353V896h33.353z" horiz-adv-x="1024" />
<glyph glyph-name="four" unicode="&#59643;" d="M769.629867 861.866667c0 0-438.0864 0-508.209067 0C191.287467 861.866667 170.666667 808.964267 170.666667 775.650133c0-33.3376 0-809.898667 0-809.898666 0-37.527467 20.106667-51.4496 31.3984-56.036267 11.304533-4.597333 42.487467-8.471467 61.169066 13.1584 0 0 239.933867 279.242667 244.053334 283.3792 6.231467 6.250667 6.231467 6.250667 12.4672 6.250667 12.4672 0 104.942933 0 155.236266 0 65.224533 0 75.712 46.653867 82.525867 74.1376 5.672533 23.016533 69.309867 349.7856 90.564267 453.4592C864.305067 819.214933 844.256 861.866667 769.629867 861.866667zM757.5168 286.641067c5.672533 23.016533 69.309867 349.7856 90.564267 453.4592M739.6096 723.904l-21.3312-110.365867c-2.549333-12.053333-17.678933-24.736-31.707733-24.736-14.026667 0-204.622933 0-204.622934 0C459.675733 588.8 443.733333 575.7056 443.733333 553.3824l0-28.689067c0-22.3424 16.040533-38.173867 38.325334-38.173866 0 0 158.894933 0 174.609066 0 15.7312 0 31.168-17.2992 27.767467-34.144-3.4176-16.8704-19.383467-99.3472-21.2992-108.565334-1.924267-9.233067-12.469333-25.013333-31.170133-25.013333-15.761067 0-137.1072 0-137.1072 0-24.970667 0-32.520533-3.2704-49.224534-24.091733-16.718933-20.842667-166.946133-201.796267-166.946133-201.796267-1.521067-1.7536-3.008-1.245867-3.008 0.6656L275.68 725.5552c0 14.2592 12.3584 30.9824 30.888533 30.9824 0 0 391.921067 0 407.835734 0C729.412267 756.539733 743.441067 742.391467 739.6096 723.904z" horiz-adv-x="1024" />
<glyph glyph-name="rainyun" unicode="&#58888;" d="M718.134993 776.973274c-70.87597 0-133.984711-37.986607-175.005393-97.211733-25.971674 39.685689-67.113719 65.414637-113.595733 65.414637-70.390519 0-128.766104-58.861037-140.05286-135.926519h-54.49197c-83.13363 0-150.368711-77.308207-150.368711-172.699496 0-95.391289 67.356444-172.699496 150.368711-172.699496h483.145956c123.426133 0 223.429215 114.809363 223.429214 256.43994 0 141.873304-100.003081 256.682667-223.429214 256.682667zM718.134993 805.49357c-66.264178 0-128.644741-29.855289-175.005393-82.76954-30.70483 32.768-70.754607 50.972444-113.595733 50.972444-74.759585 0-139.081956-55.948326-160.077748-135.926518h-34.467082c-96.847644 0-175.612207-90.294044-175.612207-201.219793s78.764563-201.09843 175.612207-201.09843h483.145956c137.140148 0 248.672711 127.7952 248.672711 284.960237 0 157.2864-111.532563 285.0816-248.672711 285.0816z m0-513.001244H234.989037c-69.055526 0-125.246578 64.686459-125.246578 144.1792s56.191052 144.1792 125.246578 144.1792h54.49197c12.379022 0 22.816237 10.073126 24.879408 23.787141 9.466311 64.929185 58.011496 112.139378 115.173452 112.139377 37.015704 0 70.997333-19.6608 93.328118-53.885155 4.733156-7.160415 12.014933-11.408119 19.903526-11.529482 7.403141 0 15.291733 3.883615 20.146252 10.922667 37.986607 54.856059 94.420385 86.289067 155.101867 86.289067 109.34803 0 198.307081-102.308978 198.307081-228.041008 0.121363-125.73203-88.837689-228.041007-198.185718-228.041007zM216.056415 169.915733L66.658607-1.084681c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292504 4.854519-5.461333 11.286756-8.252681 17.718993-8.252682 6.5536 0 12.985837 2.791348 17.961718 8.495408l149.397808 171.000415c9.8304 11.165393 9.709037 29.248474-0.242726 40.292503-9.951763 11.165393-25.971674 11.04403-35.680711-0.242726zM448.223763 170.158459c-9.951763 11.04403-25.850311 10.922667-35.680711-0.242726l-74.638222-85.439526c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292503 4.854519-5.461333 11.286756-8.252681 17.718992-8.252682 6.5536 0 12.985837 2.791348 17.961719 8.495408l74.638222 85.439526c9.8304 11.286756 9.709037 29.248474-0.242726 40.292503zM808.793126 169.915733l-74.638222-85.439526c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292503 4.854519-5.461333 11.286756-8.252681 17.718992-8.252682 6.5536 0 12.985837 2.791348 17.961719 8.495408l74.638222 85.439526c9.8304 11.165393 9.709037 29.248474-0.242726 40.292503-9.951763 11.165393-25.850311 11.04403-35.680711-0.242726zM612.427852 169.915733L463.030044-1.084681c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292504 4.854519-5.461333 11.286756-8.252681 17.718993-8.252682 6.5536 0 12.985837 2.791348 17.961718 8.495408l149.276445 171.000415c9.8304 11.165393 9.709037 29.248474-0.242726 40.292503-9.8304 11.165393-25.850311 11.04403-35.559348-0.242726z" horiz-adv-x="1024" /> <glyph glyph-name="rainyun" unicode="&#58888;" d="M718.134993 776.973274c-70.87597 0-133.984711-37.986607-175.005393-97.211733-25.971674 39.685689-67.113719 65.414637-113.595733 65.414637-70.390519 0-128.766104-58.861037-140.05286-135.926519h-54.49197c-83.13363 0-150.368711-77.308207-150.368711-172.699496 0-95.391289 67.356444-172.699496 150.368711-172.699496h483.145956c123.426133 0 223.429215 114.809363 223.429214 256.43994 0 141.873304-100.003081 256.682667-223.429214 256.682667zM718.134993 805.49357c-66.264178 0-128.644741-29.855289-175.005393-82.76954-30.70483 32.768-70.754607 50.972444-113.595733 50.972444-74.759585 0-139.081956-55.948326-160.077748-135.926518h-34.467082c-96.847644 0-175.612207-90.294044-175.612207-201.219793s78.764563-201.09843 175.612207-201.09843h483.145956c137.140148 0 248.672711 127.7952 248.672711 284.960237 0 157.2864-111.532563 285.0816-248.672711 285.0816z m0-513.001244H234.989037c-69.055526 0-125.246578 64.686459-125.246578 144.1792s56.191052 144.1792 125.246578 144.1792h54.49197c12.379022 0 22.816237 10.073126 24.879408 23.787141 9.466311 64.929185 58.011496 112.139378 115.173452 112.139377 37.015704 0 70.997333-19.6608 93.328118-53.885155 4.733156-7.160415 12.014933-11.408119 19.903526-11.529482 7.403141 0 15.291733 3.883615 20.146252 10.922667 37.986607 54.856059 94.420385 86.289067 155.101867 86.289067 109.34803 0 198.307081-102.308978 198.307081-228.041008 0.121363-125.73203-88.837689-228.041007-198.185718-228.041007zM216.056415 169.915733L66.658607-1.084681c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292504 4.854519-5.461333 11.286756-8.252681 17.718993-8.252682 6.5536 0 12.985837 2.791348 17.961718 8.495408l149.397808 171.000415c9.8304 11.165393 9.709037 29.248474-0.242726 40.292503-9.951763 11.165393-25.971674 11.04403-35.680711-0.242726zM448.223763 170.158459c-9.951763 11.04403-25.850311 10.922667-35.680711-0.242726l-74.638222-85.439526c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292503 4.854519-5.461333 11.286756-8.252681 17.718992-8.252682 6.5536 0 12.985837 2.791348 17.961719 8.495408l74.638222 85.439526c9.8304 11.286756 9.709037 29.248474-0.242726 40.292503zM808.793126 169.915733l-74.638222-85.439526c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292503 4.854519-5.461333 11.286756-8.252681 17.718992-8.252682 6.5536 0 12.985837 2.791348 17.961719 8.495408l74.638222 85.439526c9.8304 11.165393 9.709037 29.248474-0.242726 40.292503-9.951763 11.165393-25.850311 11.04403-35.680711-0.242726zM612.427852 169.915733L463.030044-1.084681c-9.8304-11.165393-9.709037-29.248474 0.242726-40.292504 4.854519-5.461333 11.286756-8.252681 17.718993-8.252682 6.5536 0 12.985837 2.791348 17.961718 8.495408l149.276445 171.000415c9.8304 11.165393 9.709037 29.248474-0.242726 40.292503-9.8304 11.165393-25.850311 11.04403-35.559348-0.242726z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -71,7 +71,7 @@ function createService() {
// @ts-ignore // @ts-ignore
response.config.onError(err); response.config.onError(err);
} }
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios); errorCreate(`${errorMessage} (请求接口: ${response.config.url}`, showErrorNotify, dataAxios);
} }
}, },
error => { error => {
@@ -113,7 +113,7 @@ function createService() {
default: default:
break; break;
} }
error.message += `: ${error.response?.config?.url}`; error.message += ` 请求接口:${error.response?.config?.url}`;
errorLog(error, error?.response?.config?.showErrorNotify); errorLog(error, error?.response?.config?.showErrorNotify);
if (status === 401) { if (status === 401) {
const userStore = useUserStore(); const userStore = useUserStore();

View File

@@ -52,6 +52,7 @@ export default {
siteMonitorSetting: "Site Monitor Settings", siteMonitorSetting: "Site Monitor Settings",
userSecurity: "Security Settings", userSecurity: "Security Settings",
userProfile: "Account Info", userProfile: "Account Info",
userGrant: "Grant Delegation",
suite: "Suite", suite: "Suite",
mySuite: "My Suite", mySuite: "My Suite",
suiteBuy: "Suite Purchase", suiteBuy: "Suite Purchase",
@@ -62,6 +63,12 @@ export default {
greeting: "Hello", greeting: "Hello",
profile: "Account Info", profile: "Account Info",
logout: "Logout", logout: "Logout",
setting: {
grantSetting: "Grant Settings",
saveSuccess: "Save Success",
allowAdminViewCerts: "Allow Admin view and download Certs",
allowAdminViewCertsHelper: "Allow admin view and download all certificates",
},
}, },
dashboard: { dashboard: {
greeting: "Hello, {name}, welcome to 【{site}】", greeting: "Hello, {name}, welcome to 【{site}】",
@@ -220,6 +227,7 @@ export default {
selectTitle: "Certificate Apply Plugin", selectTitle: "Certificate Apply Plugin",
jsAcme: "JS-ACME: Easy to use, powerful features [Recommended]", jsAcme: "JS-ACME: Easy to use, powerful features [Recommended]",
legoAcme: "Lego-ACME: Based on Lego, supports a wide range of DNS providers, suitable for users familiar with Lego", legoAcme: "Lego-ACME: Based on Lego, supports a wide range of DNS providers, suitable for users familiar with Lego",
aliyunOrder: "Aliyun-Order: Get certificate from Aliyun certificate order",
}, },
pipelineForm: { pipelineForm: {
createTitle: "Create Certificate Pipeline", createTitle: "Create Certificate Pipeline",

View File

@@ -61,7 +61,7 @@ export default {
description: "Automatic running", description: "Automatic running",
setSchedule: "Set Scheduled Execution", setSchedule: "Set Scheduled Execution",
pipelineSuccessThenSchedule: "Pipeline tests succeed, then configure scheduled triggers so it runs automatically daily", pipelineSuccessThenSchedule: "Pipeline tests succeed, then configure scheduled triggers so it runs automatically daily",
recommendDailyRun: "Recommend configuring to run once daily; new certs requested 35 days before expiry and auto-skipped otherwise", recommendDailyRun: "Recommend configuring to run once daily; new certs requested 18 days before expiry and auto-skipped otherwise",
setEmailNotification: "Set Email Notifications", setEmailNotification: "Set Email Notifications",
suggestErrorAndRecoveryEmails: "Suggest listening for 'On Error' and 'Error to Success' to quickly troubleshoot failures (basic version requires mail server setup)", suggestErrorAndRecoveryEmails: "Suggest listening for 'On Error' and 'Error to Success' to quickly troubleshoot failures (basic version requires mail server setup)",
tutorialEndTitle: "Tutorial End", tutorialEndTitle: "Tutorial End",

View File

@@ -57,6 +57,7 @@ export default {
siteMonitorSetting: "站点监控设置", siteMonitorSetting: "站点监控设置",
userSecurity: "认证安全设置", userSecurity: "认证安全设置",
userProfile: "账号信息", userProfile: "账号信息",
userGrant: "授权委托",
suite: "套餐", suite: "套餐",
mySuite: "我的套餐", mySuite: "我的套餐",
suiteBuy: "套餐购买", suiteBuy: "套餐购买",
@@ -68,6 +69,13 @@ export default {
greeting: "您好", greeting: "您好",
profile: "账号信息", profile: "账号信息",
logout: "注销登录", logout: "注销登录",
setting: {
grantSetting: "授权委托设置",
saveSuccess: "保存成功",
allowAdminViewCerts: "授权管理员查看和下载证书",
allowAdminViewCertsHelper: "允许管理员查看和下载我的所有证书",
},
}, },
dashboard: { dashboard: {
greeting: "您好,{name},欢迎使用 【{site}】", greeting: "您好,{name},欢迎使用 【{site}】",
@@ -225,6 +233,7 @@ export default {
selectTitle: "证书申请插件", selectTitle: "证书申请插件",
jsAcme: "JS-ACME使用简单方便功能强大【推荐】", jsAcme: "JS-ACME使用简单方便功能强大【推荐】",
legoAcme: "Lego-ACME基于Lego实现支持海量DNS提供商熟悉LEGO的用户可以使用", legoAcme: "Lego-ACME基于Lego实现支持海量DNS提供商熟悉LEGO的用户可以使用",
aliyunOrder: "Aliyun-Order从阿里云证书订单中获取证书更新",
}, },
pipelineForm: { pipelineForm: {
createTitle: "创建证书流水线", createTitle: "创建证书流水线",
@@ -759,6 +768,14 @@ export default {
certDomainAddToMonitorEnabled: "证书域名添加到证书监控", certDomainAddToMonitorEnabled: "证书域名添加到证书监控",
certDomainAddToMonitorEnabledHelper: "创建证书流水线时是否可以选择将域名添加到证书监控", certDomainAddToMonitorEnabledHelper: "创建证书流水线时是否可以选择将域名添加到证书监控",
defaultCertRenewDays: "默认到期前更新天数",
defaultCertRenewDaysHelper: "创建证书流水线时,默认的证书到期前更新天数",
defaultCertRenewDaysRecommend: "默认值15",
pipelineMaxRunningCount: "同时最大运行流水线数量",
pipelineMaxRunningCountHelper: "同一个用户同时运行的最大流水线数量避免同时触发太多导致ACME账户被限制",
pipelineMaxRunningCountRecommend: "推荐5-10",
fixedCertExpireDays: "固定证书有效期天数", fixedCertExpireDays: "固定证书有效期天数",
fixedCertExpireDaysHelper: "固定证书有效期天数,有助于列表进度条整齐显示", fixedCertExpireDaysHelper: "固定证书有效期天数,有助于列表进度条整齐显示",
fixedCertExpireDaysRecommend: "推荐90", fixedCertExpireDaysRecommend: "推荐90",

View File

@@ -61,7 +61,7 @@ export default {
description: "自动运行", description: "自动运行",
setSchedule: "设置定时执行", setSchedule: "设置定时执行",
pipelineSuccessThenSchedule: "流水线测试成功,接下来配置定时触发,以后每天定时执行就不用管了", pipelineSuccessThenSchedule: "流水线测试成功,接下来配置定时触发,以后每天定时执行就不用管了",
recommendDailyRun: "推荐配置每天运行一次,默认到期前35天会重新申请新证书并部署,没到期前会自动跳过,不会重复申请。", recommendDailyRun: "推荐配置每天运行一次,默认到期前18天会重新申请新证书并部署,没到期前会自动跳过,不会重复申请。",
setEmailNotification: "设置邮件通知", setEmailNotification: "设置邮件通知",
suggestErrorAndRecoveryEmails: "建议选择监听'错误时'和'错误转成功'两种即可,在意外失败时可以尽快去排查问题", suggestErrorAndRecoveryEmails: "建议选择监听'错误时'和'错误转成功'两种即可,在意外失败时可以尽快去排查问题",
tutorialEndTitle: "教程结束", tutorialEndTitle: "教程结束",

View File

@@ -204,6 +204,17 @@ export const certdResources = [
isMenu: true, isMenu: true,
}, },
}, },
{
title: "certd.userGrant",
name: "UserGrantSetting",
path: "/certd/mine/grant",
component: "/certd/mine/grant/index.vue",
meta: {
icon: "mi:user-check",
auth: true,
isMenu: true,
},
},
{ {
title: "certd.userProfile", title: "certd.userProfile",
name: "UserProfile", name: "UserProfile",

View File

@@ -54,6 +54,9 @@ export type SysPublicSetting = {
//流水线是否启用有效期 //流水线是否启用有效期
pipelineValidTimeEnabled?: boolean; pipelineValidTimeEnabled?: boolean;
// 默认到期前更新天数
defaultCertRenewDays?: number;
//证书域名添加到监控 //证书域名添加到监控
certDomainAddToMonitorEnabled?: boolean; certDomainAddToMonitorEnabled?: boolean;
@@ -86,6 +89,9 @@ export type SysPrivateSetting = {
httpsProxy?: string; httpsProxy?: string;
dnsResultOrder?: string; dnsResultOrder?: string;
commonCnameEnabled?: boolean; commonCnameEnabled?: boolean;
// 同一个用户同时最大运行流水线数量
pipelineMaxRunningCount?: number;
sms?: { sms?: {
type?: string; type?: string;
config?: any; config?: any;

View File

@@ -1,6 +1,6 @@
<template> <template>
<a-button v-if="showButton" type="primary" @click="open"> <a-button v-if="showButton" type="primary" @click="open">
{{ $t("authentication.changePasswordButton") }} {{ t("authentication.changePasswordButton") }}
</a-button> </a-button>
</template> </template>

View File

@@ -0,0 +1,25 @@
// @ts-ignore
import { request } from "/@/api/service";
const apiPrefix = "/user/settings";
export type UserGrantSetting = {
allowAdminViewCerts: boolean;
};
export async function GrantSettingsGet() {
const res = await request({
url: apiPrefix + "/grant/get",
method: "post",
});
if (!res) {
return {};
}
return res as UserGrantSetting;
}
export async function UserSettingSave(req: any) {
return await request({
url: apiPrefix + "/grant/save",
method: "post",
data: req,
});
}

View File

@@ -0,0 +1,67 @@
<template>
<fs-page class="page-user-settings page-grant">
<template #header>
<div class="title">{{ t("certd.user.setting.grantSetting") }}</div>
</template>
<div class="user-settings-form settings-form">
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off">
<a-form-item :label="t('certd.user.setting.allowAdminViewCerts')" :name="['allowAdminViewCerts']">
<div class="flex mt-5">
<a-switch v-model:checked="formState.allowAdminViewCerts" :disabled="!settingsStore.isPlus" />
<vip-button class="ml-5" mode="button"></vip-button>
</div>
<div class="helper">{{ t("certd.user.setting.allowAdminViewCertsHelper") }}</div>
</a-form-item>
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
<loading-button type="primary" html-type="button" :click="doSave">{{ t("certd.confirm") }}</loading-button>
</a-form-item>
</a-form>
</div>
</fs-page>
</template>
<script setup lang="tsx">
import { computed, reactive, watch } from "vue";
import * as api from "./api";
import { UserGrantSetting } from "./api";
import { Modal, notification } from "ant-design-vue";
import { merge } from "lodash-es";
import { useSettingStore } from "/@/store/settings";
import { useI18n } from "/src/locales";
const { t } = useI18n();
const settingsStore = useSettingStore();
defineOptions({
name: "UserSecurity",
});
const formState = reactive<Partial<UserGrantSetting>>({
allowAdminViewCerts: false,
});
async function loadUserSettings() {
const data: any = await api.GrantSettingsGet();
merge(formState, data);
}
loadUserSettings();
const doSave = async () => {
await api.UserSettingSave({
...formState,
});
notification.success({
message: t("certd.user.setting.saveSuccess"),
});
};
</script>
<style lang="less">
.page-user-settings {
.user-settings-form {
width: 600px;
margin: 20px;
}
}
</style>

View File

@@ -163,6 +163,7 @@ export function useCertPipelineCreator() {
data: [ data: [
{ value: "CertApply", label: "JS-ACME" }, { value: "CertApply", label: "JS-ACME" },
{ value: "CertApplyLego", label: "Lego-ACME" }, { value: "CertApplyLego", label: "Lego-ACME" },
{ value: "CertApplyGetFormAliyun", label: "Aliyun-Order" },
], ],
}), }),
form: { form: {
@@ -174,6 +175,7 @@ export function useCertPipelineCreator() {
<ul> <ul>
<li>{t("certd.plugin.jsAcme")}</li> <li>{t("certd.plugin.jsAcme")}</li>
<li>{t("certd.plugin.legoAcme")}</li> <li>{t("certd.plugin.legoAcme")}</li>
<li>{t("certd.plugin.aliyunOrder")}</li>
</ul> </ul>
); );
}, },
@@ -201,7 +203,7 @@ export function useCertPipelineCreator() {
component: { component: {
name: "cron-editor", name: "cron-editor",
vModel: "modelValue", vModel: "modelValue",
placeholder: "0 0 4 * * *", placeholder: "0 0 4 * * * (表示凌晨4点执行)",
}, },
helper: t("certd.pipelineForm.triggerCronHelper"), helper: t("certd.pipelineForm.triggerCronHelper"),
order: 100, order: 100,

View File

@@ -22,6 +22,8 @@ async function batchUpdateRequest(form: any) {
title: "定时触发", title: "定时触发",
type: "timer", type: "timer",
props: form.clear ? false : form.props, props: form.clear ? false : form.props,
random: form.random,
randomRange: form.randomRange,
}); });
emit("change"); emit("change");
} }
@@ -56,6 +58,49 @@ async function openFormDialog() {
}, },
}, },
}, },
random: {
title: "随机时间",
form: {
value: true,
helper: "是否给流水线随机设置一个时间",
show: compute(({ form }) => {
return form.clear !== true;
}),
component: {
name: "fs-dict-switch",
vModel: "checked",
dict: dict({
data: [
{
label: "随机时间",
value: true,
},
{
label: "固定时间",
value: false,
},
],
}),
},
},
},
randomRange: {
title: "随机时间范围",
form: {
value: ["00:00:00", "08:00:00"],
helper: "随机时间范围,单位秒",
component: {
// <a-time-range-picker :bordered="false" />
name: "a-time-range-picker",
vModel: "value",
valueFormat: "HH:mm:ss",
},
show: compute(({ form }) => {
return form.clear !== true && form.random === true;
}),
rules: [{ required: true, message: "请选择随机时间范围" }],
},
},
"props.cron": { "props.cron": {
title: t("certd.schedule"), title: t("certd.schedule"),
form: { form: {
@@ -64,7 +109,7 @@ async function openFormDialog() {
vModel: "modelValue", vModel: "modelValue",
}, },
show: compute(({ form }) => { show: compute(({ form }) => {
return form.clear !== true; return form.clear !== true && form?.random !== true;
}), }),
rules: [{ required: true, message: t("certd.selectCron") }], rules: [{ required: true, message: t("certd.selectCron") }],
}, },

View File

@@ -145,6 +145,9 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
}, },
}, },
}, },
search: {
col: { span: 3 },
},
form: { form: {
afterSubmit({ form, res, mode }) { afterSubmit({ form, res, mode }) {
if (mode === "add") { if (mode === "add") {
@@ -262,6 +265,7 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
type: "number", type: "number",
search: { search: {
show: true, show: true,
col: { span: 3 },
}, },
column: { column: {
width: 100, width: 100,
@@ -277,6 +281,7 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
show: computed(() => { show: computed(() => {
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
}), }),
col: { span: 3 },
}, },
form: { form: {
show: false, show: false,
@@ -297,6 +302,7 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
component: { component: {
name: "a-input", name: "a-input",
}, },
col: { span: 3 },
}, },
form: { form: {
rules: [{ required: true, message: t("certd.fields.required") }], rules: [{ required: true, message: t("certd.fields.required") }],

View File

@@ -26,12 +26,26 @@
<a-form-item :label="t('certd.sys.setting.fixedCertExpireDays')" :name="['public', 'fixedCertExpireDays']"> <a-form-item :label="t('certd.sys.setting.fixedCertExpireDays')" :name="['public', 'fixedCertExpireDays']">
<div class="flex items-center"> <div class="flex items-center">
<a-input-number v-model:value="formState.public.fixedCertExpireDays" :placeholder="t('certd.sys.setting.fixedCertExpireDaysRecommend')" /> <a-input-number v-model:value="formState.public.fixedCertExpireDays" :disabled="!settingsStore.isPlus" :placeholder="t('certd.sys.setting.fixedCertExpireDaysRecommend')" />
<vip-button class="ml-5" mode="button"></vip-button> <vip-button class="ml-5" mode="button"></vip-button>
</div> </div>
<div class="helper">{{ t("certd.sys.setting.fixedCertExpireDaysHelper") }}</div> <div class="helper">{{ t("certd.sys.setting.fixedCertExpireDaysHelper") }}</div>
</a-form-item> </a-form-item>
<a-form-item :label="t('certd.sys.setting.defaultCertRenewDays')" :name="['public', 'defaultCertRenewDays']">
<div class="flex items-center">
<a-input-number v-model:value="formState.public.defaultCertRenewDays" :placeholder="t('certd.sys.setting.defaultCertRenewDaysRecommend')" />
</div>
<div class="helper">{{ t("certd.sys.setting.defaultCertRenewDaysHelper") }}</div>
</a-form-item>
<a-form-item :label="t('certd.sys.setting.pipelineMaxRunningCount')" :name="['private', 'pipelineMaxRunningCount']">
<div class="flex items-center">
<a-input-number v-model:value="formState.private.pipelineMaxRunningCount" :placeholder="t('certd.sys.setting.pipelineMaxRunningCountRecommend')" />
</div>
<div class="helper">{{ t("certd.sys.setting.pipelineMaxRunningCountHelper") }}</div>
</a-form-item>
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }"> <a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }">
<a-button :loading="saveLoading" type="primary" html-type="submit">{{ t("certd.saveButton") }}</a-button> <a-button :loading="saveLoading" type="primary" html-type="submit">{{ t("certd.saveButton") }}</a-button>
</a-form-item> </a-form-item>

View File

@@ -6,7 +6,7 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "cross-env NODE_ENV=production node --optimize-for-size ./bootstrap.js", "start": "cross-env NODE_ENV=production node --optimize-for-size ./bootstrap.js",
"dev-start": "cross-env NODE_ENV=dev & mwtsc --watch --run @midwayjs/mock/app", "dev-start": "cross-env NODE_ENV=dev & mwtsc --watch --run @midwayjs/mock/app",
"dc": "cd ../../../ && pnpm run dev", "dc": "cd ../../../ && pnpm run dev",
"dev": "cross-env NODE_ENV=local & pnpm run dev-start", "dev": "cross-env NODE_ENV=local & pnpm run dev-start",
"dev-commlocal": "cross-env NODE_ENV=dev-commlocal mwtsc --watch --run @midwayjs/mock/app", "dev-commlocal": "cross-env NODE_ENV=dev-commlocal mwtsc --watch --run @midwayjs/mock/app",
@@ -44,6 +44,7 @@
"@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-iam": "^3.699.0", "@aws-sdk/client-iam": "^3.699.0",
"@aws-sdk/client-route-53": "^3.957.0",
"@aws-sdk/client-s3": "^3.705.0", "@aws-sdk/client-s3": "^3.705.0",
"@certd/acme-client": "^1.37.16", "@certd/acme-client": "^1.37.16",
"@certd/basic": "^1.37.16", "@certd/basic": "^1.37.16",
@@ -74,6 +75,7 @@
"@midwayjs/upload": "3.20.13", "@midwayjs/upload": "3.20.13",
"@midwayjs/validate": "3.20.13", "@midwayjs/validate": "3.20.13",
"@peculiar/x509": "^1.11.0", "@peculiar/x509": "^1.11.0",
"@ucloud-sdks/ucloud-sdk-js": "^0.2.4",
"@volcengine/openapi": "^1.28.1", "@volcengine/openapi": "^1.28.1",
"ali-oss": "^6.21.0", "ali-oss": "^6.21.0",
"axios": "^1.7.2", "axios": "^1.7.2",

View File

@@ -31,6 +31,20 @@ process.on('uncaughtException', error => {
} }
}); });
// function startHeapLog() {
// function format(bytes: any) {
// return (bytes / 1024 / 1024).toFixed(2) + ' MB';
// }
// function log() {
// const mu = process.memoryUsage();
// logger.info(`rss:${format(mu.rss)},heapUsed: ${format(mu.heapUsed)},heapTotal: ${format(mu.heapTotal)},external: ${format(mu.external)}`);
// }
// setInterval(log, 200);
// log()
// }
// startHeapLog();
@Configuration({ @Configuration({
detectorOptions: { detectorOptions: {
ignore: [ ignore: [
@@ -64,6 +78,9 @@ export class MainConfiguration {
app: koa.Application; app: koa.Application;
async onReady() { async onReady() {
// add middleware // add middleware
// this.app.useMiddleware([ReportMiddleware]); // this.app.useMiddleware([ReportMiddleware]);
// add filter // add filter

View File

@@ -2,6 +2,9 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { Constants, CrudController } from "@certd/lib-server"; import { Constants, CrudController } from "@certd/lib-server";
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js"; import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
import { UserSettingsEntity } from "../../../modules/mine/entity/user-settings.js"; import { UserSettingsEntity } from "../../../modules/mine/entity/user-settings.js";
import { UserGrantSetting } from "../../../modules/mine/service/models.js";
import { isPlus } from "@certd/plus-core";
import { merge } from "lodash-es";
/** /**
*/ */
@@ -65,6 +68,26 @@ export class UserSettingsController extends CrudController<UserSettingsService>
const entity = await this.service.getByKey(key, this.getUserId()); const entity = await this.service.getByKey(key, this.getUserId());
return this.ok(entity); return this.ok(entity);
} }
@Post("/grant/get", { summary: Constants.per.authOnly })
async grantSettingsGet() {
const userId = this.getUserId();
const setting = await this.service.getSetting<UserGrantSetting>(userId, UserGrantSetting);
return this.ok(setting);
}
@Post("/grant/save", { summary: Constants.per.authOnly })
async grantSettingsSave(@Body(ALL) bean: UserGrantSetting) {
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
}
const userId = this.getUserId();
const setting = new UserGrantSetting();
merge(setting, bean);
await this.service.saveSetting(userId, setting);
return this.ok({});
}
} }

View File

@@ -1,8 +1,10 @@
import {Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core'; import { Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js'; import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
import { BaseController, Constants } from '@certd/lib-server'; import { BaseController, Constants, PermissionException } from '@certd/lib-server';
import { StorageService } from '../../../modules/pipeline/service/storage-service.js'; import { StorageService } from '../../../modules/pipeline/service/storage-service.js';
import {CertReader} from "@certd/plugin-cert"; import { CertReader } from "@certd/plugin-cert";
import { UserSettingsService } from '../../../modules/mine/service/user-settings-service.js';
import { UserGrantSetting } from '../../../modules/mine/service/models.js';
@Provide() @Provide()
@Controller('/api/pi/cert') @Controller('/api/pi/cert')
@@ -12,10 +14,32 @@ export class CertController extends BaseController {
@Inject() @Inject()
storeService: StorageService; storeService: StorageService;
@Inject()
userSettingsService: UserSettingsService;
@Post('/get', { summary: Constants.per.authOnly }) @Post('/get', { summary: Constants.per.authOnly })
async getCert(@Query('id') id: number) { async getCert(@Query('id') id: number) {
const userId = this.getUserId(); const userId = this.getUserId();
await this.pipelineService.checkUserId(id, userId);
const pipleinUserId = await this.pipelineService.getPipelineUserId(id);
if (pipleinUserId !== userId) {
// 如果是管理员,检查用户是否有授权管理员查看
const isAdmin = await this.isAdmin()
if (!isAdmin) {
throw new PermissionException();
}
// 是否允许管理员查看
const setting = await this.userSettingsService.getSetting<UserGrantSetting>(pipleinUserId, UserGrantSetting, false);
if (setting?.allowAdminViewCerts !== true) {
//不允许管理员查看
throw new PermissionException("该流水线的用户还未授权管理员查看证书,请先让用户在”设置->授权委托“中打开开关");
}
}
const privateVars = await this.storeService.getPipelinePrivateVars(id); const privateVars = await this.storeService.getPipelinePrivateVars(id);
return this.ok(privateVars.cert); return this.ok(privateVars.cert);
} }
@@ -24,7 +48,7 @@ export class CertController extends BaseController {
@Post('/readCertDetail', { summary: Constants.per.authOnly }) @Post('/readCertDetail', { summary: Constants.per.authOnly })
async readCertDetail(@Body('crt') crt: string) { async readCertDetail(@Body('crt') crt: string) {
if (!crt) { if (!crt) {
throw new Error('crt is required'); throw new Error('crt is required');
} }
const certDetail = CertReader.readCertDetail(crt) const certDetail = CertReader.readCertDetail(crt)
return this.ok(certDetail); return this.ok(certDetail);

View File

@@ -10,6 +10,8 @@ import * as fs from "fs";
import { logger } from "@certd/basic"; import { logger } from "@certd/basic";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js"; import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { In } from "typeorm"; import { In } from "typeorm";
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
import { UserGrantSetting } from "../../../modules/mine/service/models.js";
/** /**
* 证书 * 证书
@@ -30,6 +32,9 @@ export class HistoryController extends CrudController<HistoryService> {
@Inject() @Inject()
sysSettingsService: SysSettingsService; sysSettingsService: SysSettingsService;
@Inject()
userSettingsService: UserSettingsService;
getService(): HistoryService { getService(): HistoryService {
return this.service; return this.service;
} }
@@ -77,7 +82,7 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/list', { summary: Constants.per.authOnly }) @Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body) { async list(@Body(ALL) body) {
const isAdmin = await this.authService.isAdmin(this.ctx); const isAdmin = this.authService.isAdmin(this.ctx);
if (!isAdmin) { if (!isAdmin) {
body.userId = this.getUserId(); body.userId = this.getUserId();
} }
@@ -89,7 +94,7 @@ export class HistoryController extends CrudController<HistoryService> {
}; };
const withDetail = body.withDetail; const withDetail = body.withDetail;
delete body.withDetail; delete body.withDetail;
let select:any = null let select: any = null
if (!withDetail) { if (!withDetail) {
select = { select = {
pipeline: true, // 后面这里改成false pipeline: true, // 后面这里改成false
@@ -193,7 +198,6 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/files', { summary: Constants.per.authOnly }) @Post('/files', { summary: Constants.per.authOnly })
async files(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number) { async files(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number) {
await this.authService.checkEntityUserId(this.ctx, this.service, historyId);
const files = await this.getFiles(historyId, pipelineId); const files = await this.getFiles(historyId, pipelineId);
return this.ok(files); return this.ok(files);
} }
@@ -210,14 +214,24 @@ export class HistoryController extends CrudController<HistoryService> {
throw new CommonException('historyId is null'); throw new CommonException('historyId is null');
} }
if (history.userId !== this.getUserId()) { if (history.userId !== this.getUserId()) {
throw new PermissionException(); // 如果是管理员,检查用户是否有授权管理员查看
const isAdmin = await this.isAdmin()
if (!isAdmin) {
throw new PermissionException();
}
// 是否允许管理员查看
const setting = await this.userSettingsService.getSetting<UserGrantSetting>(history.userId, UserGrantSetting, false);
if (setting?.allowAdminViewCerts!==true) {
//不允许管理员查看
throw new PermissionException("该流水线的用户还未授权管理员下载证书,请先让用户在”设置->授权委托“中打开开关");
}
//允许管理员查看
} }
return await this.service.getFiles(history); return await this.service.getFiles(history);
} }
@Get('/download', { summary: Constants.per.authOnly }) @Get('/download', { summary: Constants.per.authOnly })
async download(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number, @Query('fileId') fileId: string) { async download(@Query('pipelineId') pipelineId: number, @Query('historyId') historyId: number, @Query('fileId') fileId: string) {
await this.authService.checkEntityUserId(this.ctx, this.service, historyId);
const files = await this.getFiles(historyId, pipelineId); const files = await this.getFiles(historyId, pipelineId);
const file = files.find(f => f.id === fileId); const file = files.find(f => f.id === fileId);
if (file == null) { if (file == null) {

View File

@@ -33,7 +33,20 @@ export class PipelineController extends CrudController<PipelineService> {
async page(@Body(ALL) body) { async page(@Body(ALL) body) {
const isAdmin = await this.authService.isAdmin(this.ctx); const isAdmin = await this.authService.isAdmin(this.ctx);
const publicSettings = await this.sysSettingsService.getPublicSettings(); const publicSettings = await this.sysSettingsService.getPublicSettings();
if (!(publicSettings.managerOtherUserPipeline && isAdmin)) {
let onlyOther = false
if(isAdmin){
if(publicSettings.managerOtherUserPipeline){
//管理员管理 其他用户
if( body.query.userId === -1){
//如果只查询其他用户
onlyOther = true;
delete body.query.userId;
}
}else{
body.query.userId = this.getUserId();
}
}else{
body.query.userId = this.getUserId(); body.query.userId = this.getUserId();
} }
@@ -44,6 +57,9 @@ export class PipelineController extends CrudController<PipelineService> {
if (title) { if (title) {
qb.andWhere('(title like :title or content like :content)', { title: `%${title}%`, content: `%${title}%` }); qb.andWhere('(title like :title or content like :content)', { title: `%${title}%`, content: `%${title}%` });
} }
if(onlyOther){
qb.andWhere('user_id != :userId', { userId: this.getUserId() });
}
}; };
if (!body.sort || !body.sort?.prop) { if (!body.sort || !body.sort?.prop) {
body.sort = { prop: 'order', asc: false }; body.sort = { prop: 'order', asc: false };
@@ -142,26 +158,36 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/batchDelete', { summary: Constants.per.authOnly }) @Post('/batchDelete', { summary: Constants.per.authOnly })
async batchDelete(@Body('ids') ids: number[]) { async batchDelete(@Body('ids') ids: number[]) {
await this.service.batchDelete(ids, this.getUserId()); const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchDelete(ids, userId);
return this.ok({}); return this.ok({});
} }
@Post('/batchUpdateGroup', { summary: Constants.per.authOnly }) @Post('/batchUpdateGroup', { summary: Constants.per.authOnly })
async batchUpdateGroup(@Body('ids') ids: number[], @Body('groupId') groupId: number) { async batchUpdateGroup(@Body('ids') ids: number[], @Body('groupId') groupId: number) {
await this.service.batchUpdateGroup(ids, groupId, this.getUserId()); const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchUpdateGroup(ids, groupId, userId);
return this.ok({}); return this.ok({});
} }
@Post('/batchUpdateTrigger', { summary: Constants.per.authOnly }) @Post('/batchUpdateTrigger', { summary: Constants.per.authOnly })
async batchUpdateTrigger(@Body('ids') ids: number[], @Body('trigger') trigger: any) { async batchUpdateTrigger(@Body('ids') ids: number[], @Body('trigger') trigger: any) {
await this.service.batchUpdateTrigger(ids, trigger, this.getUserId()); const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchUpdateTrigger(ids, trigger, userId);
return this.ok({}); return this.ok({});
} }
@Post('/batchUpdateNotification', { summary: Constants.per.authOnly }) @Post('/batchUpdateNotification', { summary: Constants.per.authOnly })
async batchUpdateNotification(@Body('ids') ids: number[], @Body('notification') notification: any) { async batchUpdateNotification(@Body('ids') ids: number[], @Body('notification') notification: any) {
await this.service.batchUpdateNotifications(ids, notification, this.getUserId()); const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchUpdateNotifications(ids, notification, userId);
return this.ok({}); return this.ok({});
} }

View File

@@ -7,7 +7,7 @@ import crypto from 'crypto';
import {SafeService} from "../sys/settings/safe-service.js"; import {SafeService} from "../sys/settings/safe-service.js";
@Autoload() @Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AutoAInitSite { export class AutoAInitSite {
@Inject() @Inject()
userService: UserService; userService: UserService;

View File

@@ -3,7 +3,7 @@ import { logger } from "@certd/basic";
import { PluginService } from "../plugin/service/plugin-service.js"; import { PluginService } from "../plugin/service/plugin-service.js";
@Autoload() @Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AutoBLoadPlugins { export class AutoBLoadPlugins {
@Inject() @Inject()
pluginService: PluginService; pluginService: PluginService;

View File

@@ -13,7 +13,7 @@ import {UserService} from "../sys/authority/service/user-service.js";
import {Between} from "typeorm"; import {Between} from "typeorm";
@Autoload() @Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AutoCRegisterCron { export class AutoCRegisterCron {
@Inject() @Inject()
pipelineService: PipelineService; pipelineService: PipelineService;

View File

@@ -3,7 +3,7 @@ import { UserSuiteService } from '@certd/commercial-core';
import { Autoload, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core'; import { Autoload, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
@Autoload() @Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AutoDMitterRegister { export class AutoDMitterRegister {
@Inject() @Inject()
userSuiteService: UserSuiteService; userSuiteService: UserSuiteService;

View File

@@ -5,7 +5,7 @@ import { CertInfo, EVENT_CERT_APPLY_SUCCESS } from "@certd/plugin-cert";
import { PipelineEvent } from "@certd/pipeline"; import { PipelineEvent } from "@certd/pipeline";
@Autoload() @Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AutoEPipelineEmitterRegister { export class AutoEPipelineEmitterRegister {
@Inject() @Inject()
certInfoService: CertInfoService; certInfoService: CertInfoService;

View File

@@ -9,7 +9,7 @@ import { Application } from '@midwayjs/koa';
import { httpsServer, HttpsServerOptions } from './https/server.js'; import { httpsServer, HttpsServerOptions } from './https/server.js';
@Autoload() @Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AutoZPrint { export class AutoZPrint {
@Inject() @Inject()
sysSettingsService: SysSettingsService; sysSettingsService: SysSettingsService;

View File

@@ -5,7 +5,7 @@ import { ICaptchaAddon } from "../../../plugins/plugin-captcha/api.js";
import { AddonGetterService } from "../../pipeline/service/addon-getter-service.js"; import { AddonGetterService } from "../../pipeline/service/addon-getter-service.js";
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class CaptchaService { export class CaptchaService {
@Inject() @Inject()
sysSettingsService: SysSettingsService; sysSettingsService: SysSettingsService;

View File

@@ -10,7 +10,7 @@ import { EmailService } from './email-service.js';
/** /**
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class CodeService { export class CodeService {
@Inject() @Inject()
sysSettingsService: SysSettingsService; sysSettingsService: SysSettingsService;
@@ -108,9 +108,7 @@ export class CodeService {
await this.emailService.sendByTemplate({ await this.emailService.sendByTemplate({
type: templateData.notificationType, type: templateData.notificationType,
data: templateData, data: templateData,
email:{ receivers: [email],
receivers: [email],
},
}); });
const key = this.buildEmailCodeKey(email,opts?.verificationType); const key = this.buildEmailCodeKey(email,opts?.verificationType);

View File

@@ -30,7 +30,7 @@ export type EmailConfig = {
usePlus?: boolean; usePlus?: boolean;
} & SendMailOptions; } & SendMailOptions;
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class EmailService implements IEmailService { export class EmailService implements IEmailService {
@Inject() @Inject()
settingsService: UserSettingsService; settingsService: UserSettingsService;
@@ -123,9 +123,7 @@ export class EmailService implements IEmailService {
content: '测试邮件,from certd', content: '测试邮件,from certd',
url:"https://certd.handfree.work", url:"https://certd.handfree.work",
}, },
email: { receivers: [receiver],
receivers: [receiver],
},
}); });
} }
@@ -186,8 +184,9 @@ export class EmailService implements IEmailService {
content = await addon.buildDefaultContent({ data: req.data }) content = await addon.buildDefaultContent({ data: req.data })
} }
return await this.send({ return await this.send({
...req.email, ...content,
...content receivers: req.receivers,
attachments: req.attachments,
}) })
} }
} }

View File

@@ -6,7 +6,7 @@ import { merge } from 'lodash-es';
import { GroupEntity } from '../entity/group.js'; import { GroupEntity } from '../entity/group.js';
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class GroupService extends BaseService<GroupEntity> { export class GroupService extends BaseService<GroupEntity> {
@InjectEntityModel(GroupEntity) @InjectEntityModel(GroupEntity)
repository: Repository<GroupEntity>; repository: Repository<GroupEntity>;

View File

@@ -15,7 +15,7 @@ import { CnameRecordEntity } from "../../cname/entity/cname-record.js";
* *
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, {allowDowngrade: true}) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class DomainService extends BaseService<DomainEntity> { export class DomainService extends BaseService<DomainEntity> {
@InjectEntityModel(DomainEntity) @InjectEntityModel(DomainEntity)
repository: Repository<DomainEntity>; repository: Repository<DomainEntity>;

View File

@@ -9,7 +9,7 @@ import { CommonProviders } from './common-provider.js';
* 授权 * 授权
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class CnameProviderService extends BaseService<CnameProviderEntity> { export class CnameProviderService extends BaseService<CnameProviderEntity> {
@InjectEntityModel(CnameProviderEntity) @InjectEntityModel(CnameProviderEntity)
repository: Repository<CnameProviderEntity>; repository: Repository<CnameProviderEntity>;

View File

@@ -37,7 +37,7 @@ type CnameCheckCacheValue = {
* 授权 * 授权
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class CnameRecordService extends BaseService<CnameRecordEntity> { export class CnameRecordService extends BaseService<CnameRecordEntity> {
@InjectEntityModel(CnameRecordEntity) @InjectEntityModel(CnameRecordEntity)
repository: Repository<CnameRecordEntity>; repository: Repository<CnameRecordEntity>;

View File

@@ -5,7 +5,7 @@ import { SqlAdapter } from './d.js';
import { MysqlAdapter } from './mysql.js'; import { MysqlAdapter } from './mysql.js';
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class DbAdapter implements SqlAdapter { export class DbAdapter implements SqlAdapter {
adapter: SqlAdapter; adapter: SqlAdapter;
@Config('typeorm.dataSource.default.type') @Config('typeorm.dataSource.default.type')

View File

@@ -22,7 +22,7 @@ import { OauthBoundService } from "./oauth-bound-service.js";
/** /**
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, {allowDowngrade: true}) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class LoginService { export class LoginService {
@Inject() @Inject()
userService: UserService; userService: UserService;
@@ -242,7 +242,9 @@ export class LoginService {
} }
const info = await this.userService.findOne({id: oauthBound.userId}); const info = await this.userService.findOne({id: oauthBound.userId});
if (info == null) { if (info == null) {
throw new CommonException('用户不存在'); // 用户已被删除删除此oauth绑定
await this.oauthBoundService.delete([oauthBound.id]);
return null
} }
return this.generateToken(info); return this.generateToken(info);
} }

View File

@@ -6,7 +6,7 @@ import { OauthBoundEntity } from "../entity/oauth-bound.js";
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class OauthBoundService extends BaseService<OauthBoundEntity> { export class OauthBoundService extends BaseService<OauthBoundEntity> {
@InjectEntityModel(OauthBoundEntity) @InjectEntityModel(OauthBoundEntity)

View File

@@ -37,3 +37,12 @@ export class UserEmailSetting extends BaseSettings {
list:string[] = []; list:string[] = [];
} }
export class UserGrantSetting extends BaseSettings {
static __title__ = "用户授权设置";
static __key__ = "user.grant";
allowAdminViewCerts:boolean = false;
}

View File

@@ -7,7 +7,7 @@ import { UserService } from "../../sys/authority/service/user-service.js";
* 授权 * 授权
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class TwoFactorService { export class TwoFactorService {
@Inject() @Inject()
userSettingsService: UserSettingsService; userSettingsService: UserSettingsService;

View File

@@ -14,7 +14,7 @@ const UserSettingCache = new LocalCache({
* 授权 * 授权
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class UserSettingsService extends BaseService<UserSettingsEntity> { export class UserSettingsService extends BaseService<UserSettingsEntity> {
@InjectEntityModel(UserSettingsEntity) @InjectEntityModel(UserSettingsEntity)
repository: Repository<UserSettingsEntity>; repository: Repository<UserSettingsEntity>;

View File

@@ -12,7 +12,7 @@ import { DomainVerifierGetter } from "../../pipeline/service/getter/domain-verif
@Provide("CertInfoFacade") @Provide("CertInfoFacade")
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class CertInfoFacade { export class CertInfoFacade {
@Inject() @Inject()

View File

@@ -16,7 +16,7 @@ export type UploadCertReq = {
@Provide("CertInfoService") @Provide("CertInfoService")
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class CertInfoService extends BaseService<CertInfoEntity> { export class CertInfoService extends BaseService<CertInfoEntity> {
@InjectEntityModel(CertInfoEntity) @InjectEntityModel(CertInfoEntity)
repository: Repository<CertInfoEntity>; repository: Repository<CertInfoEntity>;

View File

@@ -18,7 +18,7 @@ import {Cron} from "../../cron/cron.js";
import { dnsContainer } from "./dns-custom.js"; import { dnsContainer } from "./dns-custom.js";
@Provide() @Provide()
@Scope(ScopeEnum.Request, {allowDowngrade: true}) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class SiteInfoService extends BaseService<SiteInfoEntity> { export class SiteInfoService extends BaseService<SiteInfoEntity> {
@InjectEntityModel(SiteInfoEntity) @InjectEntityModel(SiteInfoEntity)
repository: Repository<SiteInfoEntity>; repository: Repository<SiteInfoEntity>;

View File

@@ -18,7 +18,7 @@ import { dnsContainer } from "./dns-custom.js";
const dns = dnsSdk.promises; const dns = dnsSdk.promises;
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class SiteIpService extends BaseService<SiteIpEntity> { export class SiteIpService extends BaseService<SiteIpEntity> {
@InjectEntityModel(SiteIpEntity) @InjectEntityModel(SiteIpEntity)
repository: Repository<SiteIpEntity>; repository: Repository<SiteIpEntity>;

View File

@@ -15,7 +15,7 @@ export type OpenKey = {
scope: string; scope: string;
}; };
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class OpenKeyService extends BaseService<OpenKeyEntity> { export class OpenKeyService extends BaseService<OpenKeyEntity> {
@InjectEntityModel(OpenKeyEntity) @InjectEntityModel(OpenKeyEntity)
repository: Repository<OpenKeyEntity>; repository: Repository<OpenKeyEntity>;

View File

@@ -7,7 +7,7 @@ import { AddonService, newAddon, PermissionException, ValidateException } from "
* Addon * Addon
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class AddonGetterService { export class AddonGetterService {
@Inject() @Inject()

View File

@@ -3,7 +3,7 @@ import { pluginGroups, pluginRegistry } from '@certd/pipeline';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class BuiltInPluginService { export class BuiltInPluginService {
getList() { getList() {
const collection = pluginRegistry.storage; const collection = pluginRegistry.storage;

View File

@@ -3,7 +3,7 @@ import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { SiteInfo ,ISiteInfoGetter} from "@certd/plugin-lib"; import { SiteInfo ,ISiteInfoGetter} from "@certd/plugin-lib";
@Provide("siteInfoGetter") @Provide("siteInfoGetter")
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class SiteInfoGetter implements ISiteInfoGetter{ export class SiteInfoGetter implements ISiteInfoGetter{
@Inject() @Inject()
sysSettingsService: SysSettingsService; sysSettingsService: SysSettingsService;

View File

@@ -70,7 +70,7 @@ export class TaskServiceGetter implements IServiceGetter{
} }
} }
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class TaskServiceBuilder { export class TaskServiceBuilder {
@ApplicationContext() @ApplicationContext()
appCtx: IMidwayContainer; appCtx: IMidwayContainer;

View File

@@ -8,7 +8,7 @@ import { HistoryLogEntity } from '../entity/history-log.js';
* 证书申请 * 证书申请
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class HistoryLogService extends BaseService<HistoryLogEntity> { export class HistoryLogService extends BaseService<HistoryLogEntity> {
@InjectEntityModel(HistoryLogEntity) @InjectEntityModel(HistoryLogEntity)
repository: Repository<HistoryLogEntity>; repository: Repository<HistoryLogEntity>;

View File

@@ -16,7 +16,7 @@ import { logger } from '@certd/basic';
* 证书申请 * 证书申请
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class HistoryService extends BaseService<HistoryEntity> { export class HistoryService extends BaseService<HistoryEntity> {
@InjectEntityModel(HistoryEntity) @InjectEntityModel(HistoryEntity)
repository: Repository<HistoryEntity>; repository: Repository<HistoryEntity>;

View File

@@ -16,7 +16,7 @@ import { EmailService } from '../../basic/service/email-service.js';
import { isComm, isPlus } from '@certd/plus-core'; import { isComm, isPlus } from '@certd/plus-core';
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class NotificationService extends BaseService<NotificationEntity> { export class NotificationService extends BaseService<NotificationEntity> {
@InjectEntityModel(NotificationEntity) @InjectEntityModel(NotificationEntity)
repository: Repository<NotificationEntity>; repository: Repository<NotificationEntity>;

View File

@@ -6,7 +6,7 @@ import { PipelineGroupEntity } from '../entity/pipeline-group.js';
import { merge } from 'lodash-es'; import { merge } from 'lodash-es';
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class PipelineGroupService extends BaseService<PipelineGroupEntity> { export class PipelineGroupService extends BaseService<PipelineGroupEntity> {
@InjectEntityModel(PipelineGroupEntity) @InjectEntityModel(PipelineGroupEntity)
repository: Repository<PipelineGroupEntity>; repository: Repository<PipelineGroupEntity>;

View File

@@ -47,6 +47,7 @@ import { CertInfoService } from "../../monitor/service/cert-info-service.js";
import { TaskServiceBuilder } from "./getter/task-service-getter.js"; import { TaskServiceBuilder } from "./getter/task-service-getter.js";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { set } from "lodash-es"; import { set } from "lodash-es";
import { executorQueue } from "@certd/lib-server";
const runningTasks: Map<string | number, Executor> = new Map(); const runningTasks: Map<string | number, Executor> = new Map();
@@ -55,7 +56,7 @@ const runningTasks: Map<string | number, Executor> = new Map();
* 证书申请 * 证书申请
*/ */
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Singleton, { allowDowngrade: true })
export class PipelineService extends BaseService<PipelineEntity> { export class PipelineService extends BaseService<PipelineEntity> {
@InjectEntityModel(PipelineEntity) @InjectEntityModel(PipelineEntity)
repository: Repository<PipelineEntity>; repository: Repository<PipelineEntity>;
@@ -311,7 +312,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
}, },
where: { where: {
disabled: false, disabled: false,
templateId: 0 isTemplate: false
} }
}); });
const ids = idEntityList.map(item => { const ids = idEntityList.map(item => {
@@ -372,7 +373,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
return; return;
} }
for (const trigger of pipeline.triggers) { for (const trigger of pipeline.triggers) {
this.registerCron(pipeline.id, trigger); this.registerCron(pipeline.id, pipeline.userId, trigger);
} }
if (immediateTriggerOnce) { if (immediateTriggerOnce) {
@@ -460,7 +461,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
logger.info("当前定时器数量:", this.cron.getTaskSize()); logger.info("当前定时器数量:", this.cron.getTaskSize());
} }
registerCron(pipelineId, trigger) { registerCron(pipelineId: number, userId: number, trigger) {
if (pipelineId == null) { if (pipelineId == null) {
logger.warn("pipelineId为空无法注册定时任务"); logger.warn("pipelineId为空无法注册定时任务");
return; return;
@@ -489,11 +490,16 @@ export class PipelineService extends BaseService<PipelineEntity> {
logger.warn("pipelineId为空,无法执行"); logger.warn("pipelineId为空,无法执行");
return; return;
} }
try { //加入执行队列
await this.run(pipelineId, triggerId); executorQueue.addTask(userId, {
} catch (e) { task: async () => {
logger.error("定时job执行失败", e); try {
} await this.run(pipelineId, triggerId);
} catch (e) {
logger.error("定时job执行失败", e);
}
}
});
} }
}); });
logger.info("当前定时器数量:", this.cron.getTaskSize()); logger.info("当前定时器数量:", this.cron.getTaskSize());
@@ -666,12 +672,13 @@ export class PipelineService extends BaseService<PipelineEntity> {
//如果不是手动触发 //如果不是手动触发
//查找trigger //查找trigger
const found = this.findTrigger(pipeline, triggerId); const found = this.findTrigger(pipeline, triggerId);
const key = this.buildCronKey(pipeline.id, triggerId);
if (!found) { if (!found) {
//如果没有找到triggerId说明被用户删掉了这里再删除一次 //如果没有找到triggerId说明被用户删掉了这里再删除一次
this.cron.remove(this.buildCronKey(pipeline.id, triggerId)); this.cron.remove(key);
triggerType = null; triggerType = null;
} else { } else {
logger.info("timer trigger:" + found.id, found.title, found.cron); logger.info(`timer trigger:${key}, ${found.title}, ${JSON.stringify(found.props)}`);
triggerType = "timer"; triggerType = "timer";
} }
} }
@@ -746,7 +753,8 @@ export class PipelineService extends BaseService<PipelineEntity> {
status: true status: true
}, },
where: { where: {
userId userId,
disabled: false
} }
}); });
await this.fillLastVars(list); await this.fillLastVars(list);
@@ -777,17 +785,29 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
async batchDelete(ids: number[], userId: number) { async batchDelete(ids: number[], userId: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
for (const id of ids) { for (const id of ids) {
await this.checkUserId(id, userId); if (userId) {
await this.checkUserId(id, userId);
}
await this.delete(id); await this.delete(id);
} }
} }
async batchUpdateGroup(ids: number[], groupId: number, userId: any) { async batchUpdateGroup(ids: number[], groupId: number, userId: any) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
const query :any = {}
if(userId && userId>0){
query.userId = userId;
}
await this.repository.update( await this.repository.update(
{ {
id: In(ids), id: In(ids),
userId ...query
}, },
{ groupId } { groupId }
); );
@@ -795,11 +815,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
async batchUpdateTrigger(ids: number[], trigger: any, userId: any) { async batchUpdateTrigger(ids: number[], trigger: any, userId: any) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
const query :any = {}
if(userId && userId>0){
query.userId = userId;
}
const list = await this.find({ const list = await this.find({
where: { where: {
id: In(ids), id: In(ids),
userId ...query
} }
}); });
@@ -809,6 +835,22 @@ export class PipelineService extends BaseService<PipelineEntity> {
//清除trigger //清除trigger
pipeline.triggers = [] pipeline.triggers = []
} else { } else {
if(trigger.random === true){
//随机时间
const start = dayjs().format("YYYY-MM-DD") + " " + trigger.randomRange[0];
let end = dayjs().format("YYYY-MM-DD") + " " + trigger.randomRange[1];
if(trigger.randomRange[1]<trigger.randomRange[0]){
//跨天
end = dayjs().add(1, "day").format("YYYY-MM-DD") + " " + trigger.randomRange[1];
}
const startTime = dayjs(start).valueOf();
const endTime = dayjs(end).valueOf();
const randomTime = Math.floor(Math.random() * (endTime - startTime)) + startTime;
const time = dayjs(randomTime).format(" ss:mm:HH").replaceAll(":", " ").replaceAll(" 0", " ").trim();
set(trigger,"props.cron", `${time} * * *`)
}
delete trigger.random
delete trigger.randomRange;
pipeline.triggers = [{ pipeline.triggers = [{
id: nanoid(), id: nanoid(),
title: "定时触发", title: "定时触发",
@@ -822,11 +864,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any) { async batchUpdateNotifications(ids: number[], notification: Notification, userId: any) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
const query :any = {}
if(userId && userId>0){
query.userId = userId;
}
const list = await this.find({ const list = await this.find({
where: { where: {
id: In(ids), id: In(ids),
userId ...query
} }
}); });
@@ -972,7 +1020,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
title: "申请证书", title: "申请证书",
runnableType: "step", runnableType: "step",
input: { input: {
renewDays: 35, renewDays: 18,
domains: req.domains, domains: req.domains,
email: req.email, email: req.email,
"challengeType": "auto", "challengeType": "auto",
@@ -1024,4 +1072,16 @@ export class PipelineService extends BaseService<PipelineEntity> {
}); });
return res?.status; return res?.status;
} }
async getPipelineUserId(pipelineId: number) {
const res = await this.repository.findOne({
select: {
userId: true
},
where: {
id: pipelineId
}
});
return res?.userId;
}
} }

Some files were not shown because too many files have changed in this diff Show More