Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f148240bcf | ||
|
|
f914931bc9 | ||
|
|
8c1033634d | ||
|
|
781b79f529 | ||
|
|
ad91703492 | ||
|
|
a007c81e9a | ||
|
|
39bffe3389 | ||
|
|
3f2767b28b | ||
|
|
312c6e685a | ||
|
|
d2b6ab75b7 | ||
|
|
dc16294b3d | ||
|
|
77dfcef168 | ||
|
|
30ef5841d6 | ||
|
|
217ba85ff8 | ||
|
|
71e2555391 | ||
|
|
f036eb1cf2 | ||
|
|
1347066549 | ||
|
|
7fc149f67d | ||
|
|
dfba5ee638 | ||
|
|
9ba79f996f | ||
|
|
cd85000908 | ||
|
|
995349ab3e | ||
|
|
4fa8031318 | ||
|
|
3f45bb1629 | ||
|
|
0e139e6284 | ||
|
|
82dbfc6de3 | ||
|
|
9b2937d601 | ||
|
|
3375839a40 | ||
|
|
7f5ff6fab5 | ||
|
|
5160b4c3d9 | ||
|
|
85234b21c7 | ||
|
|
223af9e09d | ||
|
|
49fdf8213a | ||
|
|
7a48101015 | ||
|
|
6b85b4a0c9 | ||
|
|
3c56a53e91 | ||
|
|
9797a0835d | ||
|
|
b6dc57f3e4 | ||
|
|
78ac21c767 | ||
|
|
1e2d8fa027 | ||
|
|
e7e2e4786d | ||
|
|
9acdd15c1e | ||
|
|
fcc0dd93fd | ||
|
|
5eba437732 | ||
|
|
0d0fcfccf3 | ||
|
|
993ef7bf57 | ||
|
|
46080b311a | ||
|
|
1fbe6b55c1 | ||
|
|
07795568bf | ||
|
|
e820e5599b | ||
|
|
cb8636faec | ||
|
|
aa1046c39a | ||
|
|
7e94ba0875 | ||
|
|
077b365458 | ||
|
|
b3f1e1e444 | ||
|
|
ead8e1fec5 | ||
|
|
5be1139c1a | ||
|
|
45e218dd5b | ||
|
|
0abb030889 | ||
|
|
85df8eb09d | ||
|
|
c6291b42fc | ||
|
|
2634789769 | ||
|
|
253075e7c0 | ||
|
|
363fbdee00 | ||
|
|
a9fdceca6f | ||
|
|
f9cb605eb4 | ||
|
|
d5867d0971 | ||
|
|
f0faee34a4 |
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -14,18 +14,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Node.js
|
# - name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
# uses: actions/setup-node@v4
|
||||||
with:
|
# with:
|
||||||
node-version: 20.11.0
|
# node-version: 20.11.0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ">=1.22.5"
|
go-version: ">=1.22.5"
|
||||||
|
|
||||||
- name: Build Admin dashboard UI
|
# - name: Build Admin dashboard UI
|
||||||
run: npm --prefix=./ui ci && npm --prefix=./ui run build
|
# run: npm --prefix=./ui ci && npm --prefix=./ui run build
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v3
|
uses: goreleaser/goreleaser-action@v3
|
||||||
|
|||||||
72
CONTRIBUTING.md
Normal file
72
CONTRIBUTING.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# 向 Certimate 贡献代码
|
||||||
|
|
||||||
|
感谢你抽出时间来改进 Certimate!以下是向 Certimate 主仓库提交 PR(Pull Request)时的操作指南。
|
||||||
|
|
||||||
|
- [向 Certimate 贡献代码](#向-certimate-贡献代码)
|
||||||
|
- [前提条件](#前提条件)
|
||||||
|
- [修改 Go 代码](#修改-go-代码)
|
||||||
|
- [修改管理页面 (Admin UI)](#修改管理页面-admin-ui)
|
||||||
|
|
||||||
|
## 前提条件
|
||||||
|
|
||||||
|
- Go 1.22+ (用于修改 Go 代码)
|
||||||
|
- Node 20+ (用于修改 UI)
|
||||||
|
|
||||||
|
如果还没有这样做,你可以 fork Certimate 的主仓库,并克隆到本地以便进行修改:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/your_username/certimate.git
|
||||||
|
```
|
||||||
|
|
||||||
|
> **重要提示:**
|
||||||
|
> 建议为每个 bug 修复或新功能创建一个从 `main` 分支派生的新分支。如果你计划提交多个 PR,请保持不同的改动在独立分支中,以便更容易进行代码审查并最终合并。
|
||||||
|
> 保持一个 pr 只实现一个功能。
|
||||||
|
|
||||||
|
## 修改 Go 代码
|
||||||
|
|
||||||
|
假设你已经对 Certimate 的 Go 代码做了一些修改,现在你想要运行它:
|
||||||
|
|
||||||
|
1. 进入根目录
|
||||||
|
2. 运行以下命令启动服务:
|
||||||
|
```bash
|
||||||
|
go run main.go serve
|
||||||
|
```
|
||||||
|
|
||||||
|
这将启动一个 Web 服务器,默认运行在 `http://localhost:8090`,并使用来自 `ui/dist` 的预构建管理页面。
|
||||||
|
|
||||||
|
**在向主仓库提交 PR 之前,建议你:**
|
||||||
|
|
||||||
|
- 为你的改动添加单元测试或集成测试(Certimate 使用 Go 的标准 `testing` 包)。你可以通过以下命令运行测试(在项目根目录下):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 修改管理页面 (Admin UI)
|
||||||
|
|
||||||
|
Certimate 的管理页面是一个基于 React 和 Vite 构建的单页应用(SPA)。
|
||||||
|
|
||||||
|
要启动 Admin UI:
|
||||||
|
|
||||||
|
1. 进入 `ui` 项目目录
|
||||||
|
2. 运行 `npm install` 安装依赖
|
||||||
|
3. 启动 Vite 开发服务器:
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
你可以通过浏览器访问 `http://localhost:5173` 来查看运行中的管理页面。
|
||||||
|
|
||||||
|
由于 Admin UI 只是一个客户端应用,运行时需要 Certimate 的后端服务作为支撑。你可以手动运行Certimate,或者使用预构建的可执行文件。
|
||||||
|
|
||||||
|
|
||||||
|
所有对 Admin UI 的修改将会自动反映在浏览器中,无需手动刷新页面。
|
||||||
|
|
||||||
|
完成修改后,运行以下命令来构建 Admin UI,以便它能被嵌入到 Go 包中:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
完成所有步骤后,你可以将改动提交 PR 到 Certimate 主仓库。
|
||||||
73
CONTRIBUTING_EN.md
Normal file
73
CONTRIBUTING_EN.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Contributing to Certimate
|
||||||
|
|
||||||
|
Thank you for taking the time to improve Certimate! Below is a guide for submitting a PR (Pull Request) to the main Certimate repository.
|
||||||
|
|
||||||
|
- [Contributing to Certimate](#contributing-to-certimate)
|
||||||
|
- [Prerequisites](#prerequisites)
|
||||||
|
- [Making Changes in the Go Code](#making-changes-in-the-go-code)
|
||||||
|
- [Making Changes in the Admin UI](#making-changes-in-the-admin-ui)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Go 1.22+ (for Go code changes)
|
||||||
|
- Node 20+ (for Admin UI changes)
|
||||||
|
|
||||||
|
If you haven't done so already, you can fork the Certimate repository and clone your fork to work locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/your_username/certimate.git
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Important:**
|
||||||
|
> It is recommended to create a new branch from `main` for each bug fix or feature. If you plan to submit multiple PRs, ensure the changes are in separate branches for easier review and eventual merge.
|
||||||
|
> Keep each PR focused on a single feature or fix.
|
||||||
|
|
||||||
|
## Making Changes in the Go Code
|
||||||
|
|
||||||
|
Once you have made changes to the Go code in Certimate, follow these steps to run the project:
|
||||||
|
|
||||||
|
1. Navigate to the root directory.
|
||||||
|
2. Start the service by running:
|
||||||
|
```bash
|
||||||
|
go run main.go serve
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start a web server at `http://localhost:8090` using the prebuilt Admin UI located in `ui/dist`.
|
||||||
|
|
||||||
|
**Before submitting a PR to the main repository, consider:**
|
||||||
|
|
||||||
|
- Adding unit or integration tests for your changes. Certimate uses Go’s standard `testing` package. You can run tests using the following command (while in the root project directory):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Making Changes in the Admin UI
|
||||||
|
|
||||||
|
Certimate’s Admin UI is a single-page application (SPA) built using React and Vite.
|
||||||
|
|
||||||
|
To start the Admin UI:
|
||||||
|
|
||||||
|
1. Navigate to the `ui` project directory.
|
||||||
|
2. Install the necessary dependencies by running:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
3. Start the Vite development server:
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now access the running Admin UI at `http://localhost:5173` in your browser.
|
||||||
|
|
||||||
|
Since the Admin UI is a client-side application, you will also need to have the Certimate backend running. You can either manually run Certimate or use a prebuilt executable.
|
||||||
|
|
||||||
|
Any changes you make in the Admin UI will be automatically reflected in the browser without requiring a page reload.
|
||||||
|
|
||||||
|
After completing your changes, build the Admin UI so it can be embedded into the Go package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Once all steps are completed, you are ready to submit a PR to the main Certimate repository.
|
||||||
62
README.md
62
README.md
@@ -1,4 +1,4 @@
|
|||||||
|
[中文](README.md) | [English](README_EN.md)
|
||||||
|
|
||||||
# 🔒Certimate
|
# 🔒Certimate
|
||||||
|
|
||||||
@@ -18,27 +18,8 @@ Certimate 就是为了解决上述问题而产生的,它具有以下特点:
|
|||||||
* [Why Certimate?](https://docs.certimate.me/blog/why-certimate)
|
* [Why Certimate?](https://docs.certimate.me/blog/why-certimate)
|
||||||
* [域名变量及部署授权组介绍](https://docs.certimate.me/blog/multi-deployer)
|
* [域名变量及部署授权组介绍](https://docs.certimate.me/blog/multi-deployer)
|
||||||
|
|
||||||
|
|
||||||
Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决方案。使用文档请访问[https://docs.certimate.me](https://docs.certimate.me)
|
Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决方案。使用文档请访问[https://docs.certimate.me](https://docs.certimate.me)
|
||||||
|
|
||||||
- [🔒Certimate](#certimate)
|
|
||||||
- [一、安装](#一安装)
|
|
||||||
- [1. 二进制文件](#1-二进制文件)
|
|
||||||
- [2. Docker 安装](#2-docker-安装)
|
|
||||||
- [3. 源代码安装](#3-源代码安装)
|
|
||||||
- [二、使用](#二使用)
|
|
||||||
- [三、支持的服务商列表](#三支持的服务商列表)
|
|
||||||
- [四、系统截图](#四系统截图)
|
|
||||||
- [五、概念](#五概念)
|
|
||||||
- [1. 域名](#1-域名)
|
|
||||||
- [2. dns 服务商授权信息](#2-dns-服务商授权信息)
|
|
||||||
- [3. 部署服务商授权信息](#3-部署服务商授权信息)
|
|
||||||
- [六、常见问题](#六常见问题)
|
|
||||||
- [七、贡献](#七贡献)
|
|
||||||
- [八、加入社区](#八加入社区)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 一、安装
|
## 一、安装
|
||||||
|
|
||||||
安装 Certimate 非常简单,你可以选择以下方式之一进行安装:
|
安装 Certimate 非常简单,你可以选择以下方式之一进行安装:
|
||||||
@@ -51,15 +32,19 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
|
|||||||
./certimate serve
|
./certimate serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
或运行以下命令自动给 Certimate 自身添加证书
|
||||||
|
```bash
|
||||||
|
./certimate serve 你的域名
|
||||||
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> MacOS 在执行二进制文件时会提示:无法打开“certimate”,因为Apple无法检查其是否包含恶意软件。可在系统设置> 隐私与安全性> 安全性 中点击 "仍然允许",然后再次尝试执行二进制文件。
|
> MacOS 在执行二进制文件时会提示:无法打开“certimate”,因为Apple无法检查其是否包含恶意软件。可在系统设置> 隐私与安全性> 安全性 中点击 "仍然允许",然后再次尝试执行二进制文件。
|
||||||
|
|
||||||
|
|
||||||
### 2. Docker 安装
|
### 2. Docker 安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
git clone git@github.com:usual2970/certimate.git && cd certimate/docker && docker compose up -d
|
mkdir -p ~/.certimate && cd ~/.certimate && curl -O https://raw.githubusercontent.com/usual2970/certimate/refs/heads/main/docker/docker-compose.yml && docker compose up -d
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -68,10 +53,10 @@ git clone git@github.com:usual2970/certimate.git && cd certimate/docker && docke
|
|||||||
```bash
|
```bash
|
||||||
git clone EMAIL:usual2970/certimate.git
|
git clone EMAIL:usual2970/certimate.git
|
||||||
cd certimate
|
cd certimate
|
||||||
|
go mod vendor
|
||||||
go run main.go serve
|
go run main.go serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 二、使用
|
## 二、使用
|
||||||
|
|
||||||
执行完上述安装操作后,在浏览器中访问 `http://127.0.0.1:8090` 即可访问 Certimate 管理页面。
|
执行完上述安装操作后,在浏览器中访问 `http://127.0.0.1:8090` 即可访问 Certimate 管理页面。
|
||||||
@@ -85,17 +70,14 @@ go run main.go serve
|
|||||||
|
|
||||||
## 三、支持的服务商列表
|
## 三、支持的服务商列表
|
||||||
|
|
||||||
| 服务商 | 是否域名服务商 | 是否部署服务 | 备注 |
|
| 服务商 | 是否域名服务商 | 是否部署服务 | 备注 |
|
||||||
|------|------|-----|------|
|
| ---------- | -------------- | ------------ | ------------------------------------------------------ |
|
||||||
| 阿里云| 是 | 是 | 支持阿里云注册的域名,支持部署到阿里云 CDN,OSS |
|
| 阿里云 | 是 | 是 | 支持阿里云注册的域名,支持部署到阿里云 CDN,OSS |
|
||||||
| 腾讯云| 是 | 是 | 支持腾讯云注册的域名,支持部署到腾讯云 CDN |
|
| 腾讯云 | 是 | 是 | 支持腾讯云注册的域名,支持部署到腾讯云 CDN |
|
||||||
| 七牛云| 否 | 是 | 七牛云没有注册域名服务,支持部署到七牛云 CDN |
|
| 七牛云 | 否 | 是 | 七牛云没有注册域名服务,支持部署到七牛云 CDN |
|
||||||
|CloudFlare| 是 | 否 | 支持 CloudFlare 注册的域名,CloudFlare 服务自带SSL证书 |
|
| CloudFlare | 是 | 否 | 支持 CloudFlare 注册的域名,CloudFlare 服务自带SSL证书 |
|
||||||
|SSH| 否 | 是 | 支持部署到 SSH 服务器 |
|
| SSH | 否 | 是 | 支持部署到 SSH 服务器 |
|
||||||
|WEBHOOK| 否 | 是 | 支持回调到 WEBHOOK |
|
| WEBHOOK | 否 | 是 | 支持回调到 WEBHOOK |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 四、系统截图
|
## 四、系统截图
|
||||||
|
|
||||||
@@ -109,7 +91,6 @@ go run main.go serve
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## 五、概念
|
## 五、概念
|
||||||
|
|
||||||
Certimate 的工作流程如下:
|
Certimate 的工作流程如下:
|
||||||
@@ -153,7 +134,6 @@ Certimate 申请证书后,会自动将证书部署到你指定的目标上,
|
|||||||
|
|
||||||
## 六、常见问题
|
## 六、常见问题
|
||||||
|
|
||||||
|
|
||||||
Q: 提供saas服务吗?
|
Q: 提供saas服务吗?
|
||||||
|
|
||||||
> A: 不提供,目前仅支持self-hosted(私有部署)。
|
> A: 不提供,目前仅支持self-hosted(私有部署)。
|
||||||
@@ -166,15 +146,13 @@ Q: 自动续期证书?
|
|||||||
|
|
||||||
> A: 已经申请的证书会在过期前10天自动续期。每天会检查一次证书是否快要过期,快要过期时会自动重新申请证书并部署到目标服务上。
|
> A: 已经申请的证书会在过期前10天自动续期。每天会检查一次证书是否快要过期,快要过期时会自动重新申请证书并部署到目标服务上。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 七、贡献
|
## 七、贡献
|
||||||
|
|
||||||
Certimate 是一个免费且开源的项目,采用 [MIT 开源协议](LICENSE.md)。你可以使用它做任何你想做的事,甚至把它当作一个付费服务提供给用户。
|
Certimate 是一个免费且开源的项目,采用 [MIT 开源协议](LICENSE.md)。你可以使用它做任何你想做的事,甚至把它当作一个付费服务提供给用户。
|
||||||
|
|
||||||
你可以通过以下方式来支持 Certimate 的开发:
|
你可以通过以下方式来支持 Certimate 的开发:
|
||||||
|
|
||||||
* 提交代码:如果你发现了 bug 或有新的功能需求,而你又有相关经验,可以提交代码给我们。
|
* [提交代码:如果你发现了 bug 或有新的功能需求,而你又有相关经验,可以提交代码给我们](CONTRIBUTING.md)。
|
||||||
* 提交 issue:功能建议或者 bug 可以[提交 issue](https://github.com/usual2970/certimate/issues) 给我们。
|
* 提交 issue:功能建议或者 bug 可以[提交 issue](https://github.com/usual2970/certimate/issues) 给我们。
|
||||||
|
|
||||||
支持更多服务商、UI 的优化改进、BUG 修复、文档完善等,欢迎大家提交 PR。
|
支持更多服务商、UI 的优化改进、BUG 修复、文档完善等,欢迎大家提交 PR。
|
||||||
@@ -182,7 +160,9 @@ Certimate 是一个免费且开源的项目,采用 [MIT 开源协议](LICENSE.
|
|||||||
## 八、加入社区
|
## 八、加入社区
|
||||||
|
|
||||||
* [Telegram-a new era of messaging](https://t.me/+ZXphsppxUg41YmVl)
|
* [Telegram-a new era of messaging](https://t.me/+ZXphsppxUg41YmVl)
|
||||||
|
* 微信群聊(超200人需邀请入群,可先加作者好友)
|
||||||
|
|
||||||
* 微信群聊
|
<img src="https://i.imgur.com/8xwsLTA.png" width="400"/>
|
||||||
|
|
||||||
<img src="https://i.imgur.com/lJUfTeD.png" width="400"/>
|
## 九、Star History
|
||||||
|
[](https://starchart.cc/usual2970/certimate)
|
||||||
|
|||||||
166
README_EN.md
Normal file
166
README_EN.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
[中文](README.md) | [English](README_EN.md)
|
||||||
|
|
||||||
|
# 🔒Certimate
|
||||||
|
|
||||||
|
For individuals managing personal projects or those responsible for IT operations in small businesses who need to manage multiple domain names, applying for certificates manually comes with several drawbacks:
|
||||||
|
|
||||||
|
1. 😱Troublesome: Applying for and deploying certificates isn’t difficult, but it can be quite a hassle, especially when managing multiple domains.
|
||||||
|
2. 😭Easily forgotten: The current free certificate has a validity period of only 90 days, requiring regular renewal operations. This increases the workload and makes it easy to forget, which can result in the website becoming inaccessible.
|
||||||
|
|
||||||
|
Certimate was created to solve the above-mentioned issues and has the following features:
|
||||||
|
|
||||||
|
1. Simple operation: Automatically apply, deploy, and renew SSL certificates without any manual intervention.
|
||||||
|
2. Support for self-hosted deployment: The deployment method is simple; you only need to download the binary file and execute it. Both the binary files and Docker images are generated using GitHub Actions, ensuring a transparent process that can be audited independently.
|
||||||
|
3. Data security: Since it is a self-hosted deployment, all data is stored locally and will not be saved on the service provider’s servers, ensuring the security of the data.
|
||||||
|
|
||||||
|
Related articles:
|
||||||
|
|
||||||
|
* [Why Certimate?](https://docs.certimate.me/blog/why-certimate)
|
||||||
|
* [Introduction to Domain Variables and Deployment Authorization Groups](https://docs.certimate.me/blog/multi-deployer)
|
||||||
|
|
||||||
|
Certimate aims to provide users with a secure and user-friendly SSL certificate management solution. For usage documentation, please visit.[https://docs.certimate.me](https://docs.certimate.me)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Installing Certimate is very simple, you can choose one of the following methods for installation:
|
||||||
|
|
||||||
|
### 1. Binary File
|
||||||
|
|
||||||
|
You can download the precompiled binary files directly from the [Releases page](https://github.com/usual2970/certimate/releases), and after extracting them, execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./certimate serve
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run the following command to automatically add a certificate to Certimate itself.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./certimate serve yourDomain
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> When executing the binary file on macOS, you may see a prompt saying: “Cannot open ‘certimate’ because Apple cannot check it for malicious software.” You can go to System Preferences > Security & Privacy > General, then click “Allow Anyway,” and try executing the binary file again.
|
||||||
|
|
||||||
|
### 2. Docker Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
mkdir -p ~/.certimate && cd ~/.certimate && curl -O https://raw.githubusercontent.com/usual2970/certimate/refs/heads/main/docker/docker-compose.yml && docker compose up -d
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Source Code Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone EMAIL:usual2970/certimate.git
|
||||||
|
cd certimate
|
||||||
|
go mod vendor
|
||||||
|
go run main.go serve
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
After completing the installation steps above, you can access the Certimate management page by visiting http://127.0.0.1:8090 in your browser.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
username:admin@certimate.fun
|
||||||
|
password:1234567890
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## List of Supported Providers
|
||||||
|
|
||||||
|
| Provider | Domain Registrar | Deployment Service | Remarks |
|
||||||
|
| ------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
|
||||||
|
| Alibaba Cloud | Yes | Yes | Supports domains registered with Alibaba Cloud; supports deployment to Alibaba Cloud CDN and OSS. |
|
||||||
|
| Tencent Cloud | Yes | Yes | Supports domains registered with Tencent Cloud; supports deployment to Tencent Cloud CDN. |
|
||||||
|
| Qiniu Cloud | No | Yes | Qiniu Cloud does not offer domain registration services; supports deployment to Qiniu Cloud CDN. |
|
||||||
|
| Cloudflare | Yes | No | Supports domains registered with Cloudflare; Cloudflare services come with SSL certificates. |
|
||||||
|
| SSH | No | Yes | Supports deployment to SSH servers. |
|
||||||
|
| WEBHOOK | No | Yes | Supports callbacks to WEBHOOK. |
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Concepts
|
||||||
|
|
||||||
|
The workflow of Certimate is as follows:
|
||||||
|
|
||||||
|
* Users fill in the certificate application information on the Certimate management page, including domain name, authorization information for the DNS provider, and authorization information for the service provider to deploy to.
|
||||||
|
* Certimate sends a request to the certificate vendor's API to apply for an SSL certificate.
|
||||||
|
* Certimate stores the certificate information, including the certificate content, private key, validity period, etc., and automatically renews the certificate when it is about to expire.
|
||||||
|
* Certimate sends a deployment request to the service provider's API to deploy the certificate to the service provider's servers.
|
||||||
|
|
||||||
|
This involves authorization information for the domain, DNS provider, and deployment service provider.
|
||||||
|
|
||||||
|
### 1. Domain
|
||||||
|
|
||||||
|
It involves the domain name for which the certificate is being requested.
|
||||||
|
|
||||||
|
### 2. Authorization Information for the DNS Provider
|
||||||
|
|
||||||
|
To apply for a certificate for a domain, you need to prove that the domain belongs to you. Therefore, when manually applying for a certificate, you typically need to add a TXT record to the DNS records in the domain provider's control panel.
|
||||||
|
|
||||||
|
Certimate will automatically add a TXT record for you; you only need to fill in the authorization information for your DNS provider in the Certimate backend.
|
||||||
|
|
||||||
|
For example, if you purchased the domain from Alibaba Cloud, the authorization information would be as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
accessKeyId: xxx
|
||||||
|
accessKeySecret: TOKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
If you purchased the domain from Tencent Cloud, the authorization information would be as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
secretId: xxx
|
||||||
|
secretKey: TOKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Authorization Information for the Deployment Service Provider
|
||||||
|
|
||||||
|
After Certimate applies for the certificate, it will automatically deploy the certificate to your specified target, such as Alibaba Cloud CDN. At this point, you need to fill in the authorization information for Alibaba Cloud. Certimate will use the authorization information and domain name you provided to locate the corresponding CDN service and deploy the certificate to that service.
|
||||||
|
|
||||||
|
The authorization information for the deployment service provider is the same as that for the DNS provider, with the distinction that the DNS provider's authorization information is used to prove that the domain belongs to you, while the deployment service provider's authorization information is used to provide authorization for the certificate deployment.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
Q: Do you provide SaaS services?
|
||||||
|
|
||||||
|
> A: No, we do not provide that. Currently, we only support self-hosted.
|
||||||
|
|
||||||
|
Q: Data Security?
|
||||||
|
|
||||||
|
> A: Since only self-hosted is supported, all data is stored on the user’s server. Additionally, the source code of Certimate is open-source, and the packaging process for binary files and Docker images is entirely done using GitHub Actions. This process is transparent and visible, allowing for independent auditing.
|
||||||
|
|
||||||
|
Q: Automatic Certificate Renewal?
|
||||||
|
|
||||||
|
> A: Certificates that have already been issued will be automatically renewed 10 days before expiration. The system checks once a day to see if any certificates are nearing expiration, and if so, it will automatically reapply for the certificate and deploy it to the target service.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Certimate is a free and open-source project, licensed under the [MIT License](LICENSE.md). You can use it for anything you want, even offering it as a paid service to users.
|
||||||
|
|
||||||
|
You can support the development of Certimate in the following ways:
|
||||||
|
|
||||||
|
* **Submit Code**: If you find a bug or have new feature requests, and you have relevant experience, [you can submit code to us](CONTRIBUTING_EN.md).
|
||||||
|
* **Submit an Issue**: For feature suggestions or bugs, you can [submit an issue](https://github.com/usual2970/certimate/issues) to us.
|
||||||
|
|
||||||
|
Support for more service providers, UI enhancements, bug fixes, and documentation improvements are all welcome. We encourage everyone to submit pull requests (PRs).
|
||||||
|
|
||||||
|
## Join the Community
|
||||||
|
|
||||||
|
* [Telegram-a new era of messaging](https://t.me/+ZXphsppxUg41YmVl)
|
||||||
|
* Wechat Group
|
||||||
|
|
||||||
|
<img src="https://i.imgur.com/zSHEoIm.png" width="400"/>
|
||||||
90
go.mod
90
go.mod
@@ -1,8 +1,8 @@
|
|||||||
module certimate
|
module certimate
|
||||||
|
|
||||||
go 1.22
|
go 1.22.0
|
||||||
|
|
||||||
toolchain go1.22.5
|
toolchain go1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alibabacloud-go/cas-20200407/v2 v2.3.0
|
github.com/alibabacloud-go/cas-20200407/v2 v2.3.0
|
||||||
@@ -10,7 +10,7 @@ require (
|
|||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9
|
||||||
github.com/alibabacloud-go/tea v1.2.2
|
github.com/alibabacloud-go/tea v1.2.2
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.6
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.6
|
||||||
github.com/go-acme/lego/v4 v4.17.4
|
github.com/go-acme/lego/v4 v4.19.2
|
||||||
github.com/gojek/heimdall/v7 v7.0.3
|
github.com/gojek/heimdall/v7 v7.0.3
|
||||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||||
github.com/nikoksr/notify v1.0.0
|
github.com/nikoksr/notify v1.0.0
|
||||||
@@ -18,10 +18,10 @@ require (
|
|||||||
github.com/pocketbase/dbx v1.10.1
|
github.com/pocketbase/dbx v1.10.1
|
||||||
github.com/pocketbase/pocketbase v0.22.18
|
github.com/pocketbase/pocketbase v0.22.18
|
||||||
github.com/qiniu/go-sdk/v7 v7.22.0
|
github.com/qiniu/go-sdk/v7 v7.22.0
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.992
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1017
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1017
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.992
|
golang.org/x/crypto v0.27.0
|
||||||
golang.org/x/crypto v0.26.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -31,8 +31,10 @@ require (
|
|||||||
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
||||||
github.com/blinkbean/dingtalk v1.1.3 // indirect
|
github.com/blinkbean/dingtalk v1.1.3 // indirect
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
||||||
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -45,39 +47,39 @@ require (
|
|||||||
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
|
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
|
||||||
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
|
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.712 // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15 // indirect
|
||||||
github.com/aliyun/credentials-go v1.3.1 // indirect
|
github.com/aliyun/credentials-go v1.3.1 // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
|
github.com/aws/aws-sdk-go-v2/config v1.27.33 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 // indirect
|
||||||
github.com/aws/smithy-go v1.20.3 // indirect
|
github.com/aws/smithy-go v1.20.4 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/clbanning/mxj/v2 v2.5.6 // indirect
|
github.com/clbanning/mxj/v2 v2.5.6 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.97.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.104.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/disintegration/imaging v1.6.2 // indirect
|
github.com/disintegration/imaging v1.6.2 // indirect
|
||||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/fatih/color v1.17.0 // indirect
|
github.com/fatih/color v1.17.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
||||||
github.com/ganigeorgiev/fexpr v0.4.1 // indirect
|
github.com/ganigeorgiev/fexpr v0.4.1 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect
|
github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect
|
||||||
@@ -86,8 +88,6 @@ require (
|
|||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
@@ -98,39 +98,39 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/miekg/dns v1.1.59 // indirect
|
github.com/miekg/dns v1.1.62 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/cobra v1.8.1 // indirect
|
github.com/spf13/cobra v1.8.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 // indirect
|
||||||
github.com/tjfoc/gmsm v1.3.2 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
gocloud.dev v0.37.0 // indirect
|
gocloud.dev v0.37.0 // indirect
|
||||||
golang.org/x/image v0.18.0 // indirect
|
golang.org/x/image v0.18.0 // indirect
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
golang.org/x/net v0.27.0 // indirect
|
golang.org/x/net v0.29.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.23.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.24.0 // indirect
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
golang.org/x/term v0.23.0 // indirect
|
golang.org/x/term v0.24.0 // indirect
|
||||||
golang.org/x/text v0.17.0 // indirect
|
golang.org/x/text v0.18.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.6.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.25.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
|
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
|
||||||
google.golang.org/api v0.189.0 // indirect
|
google.golang.org/api v0.197.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||||
google.golang.org/grpc v1.65.0 // indirect
|
google.golang.org/grpc v1.66.1 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
249
go.sum
249
go.sum
@@ -1,13 +1,13 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
|
||||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
|
||||||
cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
|
cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U=
|
||||||
cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
|
cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||||
cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU=
|
cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU=
|
||||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
cloud.google.com/go/compute/metadata v0.5.1 h1:NM6oZeZNlYjiwYje+sYFjEpP0Q0zCan1bmQW/KmIrGs=
|
||||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
cloud.google.com/go/compute/metadata v0.5.1/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
|
||||||
cloud.google.com/go/iam v1.1.11 h1:0mQ8UKSfdHLut6pH9FM3bI55KWR46ketn0PuXleDyxw=
|
cloud.google.com/go/iam v1.1.11 h1:0mQ8UKSfdHLut6pH9FM3bI55KWR46ketn0PuXleDyxw=
|
||||||
cloud.google.com/go/iam v1.1.11/go.mod h1:biXoiLWYIKntto2joP+62sd9uW5EpkZmKIvfNcTWlnQ=
|
cloud.google.com/go/iam v1.1.11/go.mod h1:biXoiLWYIKntto2joP+62sd9uW5EpkZmKIvfNcTWlnQ=
|
||||||
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
|
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
|
||||||
@@ -80,8 +80,8 @@ github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE
|
|||||||
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
|
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.712 h1:lM7JnA9dEdDFH9XOgRNQMDTQnOjlLkDTNA7c0aWTQ30=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15 h1:r2uwBUQhLhcPzaWz9tRJqc8MjYwHb+oF2+Q6467BF14=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.712/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||||
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
|
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
|
||||||
github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28=
|
github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28=
|
||||||
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
|
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
|
||||||
@@ -90,44 +90,44 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
github.com/aws/aws-sdk-go v1.51.11 h1:El5VypsMIz7sFwAAj/j06JX9UGs4KAbAIEaZ57bNY4s=
|
github.com/aws/aws-sdk-go v1.51.11 h1:El5VypsMIz7sFwAAj/j06JX9UGs4KAbAIEaZ57bNY4s=
|
||||||
github.com/aws/aws-sdk-go v1.51.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
github.com/aws/aws-sdk-go v1.51.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4/go.mod h1:/MQxMqci8tlqDH+pjmoLu1i0tbWCUP1hhyMRuFxpQCw=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
|
github.com/aws/aws-sdk-go-v2/config v1.27.33 h1:Nof9o/MsmH4oa0s2q9a0k7tMz5x/Yj5k06lDODWz3BU=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
|
github.com/aws/aws-sdk-go-v2/config v1.27.33/go.mod h1:kEqdYzRb8dd8Sy2pOdEbExTTF5v7ozEXX0McgPE7xks=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 h1:7Cxhp/BnT2RcGy4VisJ9miUPecY+lyE9I8JvcZofn9I=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.32/go.mod h1:P5/QMF3/DCHbXGEGkdbilXHsyTBX5D3HSwcrSc9p20I=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 h1:pfQ2sqNpMVK6xz2RbqLEL0GH87JOwSxPV2rzm8Zsb74=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13/go.mod h1:NG7RXPUlqfsCLLFfi0+IpKN4sCB9D9fw/qTaSB+xRoU=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 h1:u1KOU1S15ufyZqmH/rA3POkiRH6EcDANHj2xHRzq+zc=
|
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 h1:u1KOU1S15ufyZqmH/rA3POkiRH6EcDANHj2xHRzq+zc=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8/go.mod h1:WPv2FRnkIOoDv/8j2gSUsI4qDc7392w5anFB/I89GZ8=
|
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8/go.mod h1:WPv2FRnkIOoDv/8j2gSUsI4qDc7392w5anFB/I89GZ8=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 h1:Roo69qTpfu8OlJ2Tb7pAYVuF0CpuUMB0IYWwYP/4DZM=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17/go.mod h1:NcWPxQzGM1USQggaTVwz6VpqMZPX1CvDJLDh6jnOCa4=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19 h1:FLMkfEiRjhgeDTCjjLoc3URo/TBkgeQbocA78lfkzSI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19/go.mod h1:Vx+GucNSsdhaxs3aZIKfSUjKVGsxN25nX2SRcdhuw08=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 h1:rfprUlsdzgl7ZL2KlXiUAoJnI/VxfHCvDFr2QDFj6u4=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19/go.mod h1:SCWkEdRq8/7EK60NcvvQ6NXKuTcchAD4ROAsC37VEZE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 h1:u+EfGmksnJc/x5tq3A+OD7LrMbSSR/5TrKLvkdy/fhY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17/go.mod h1:VaMx6302JHax2vHJWgRo+5n9zvbacs3bLU/23DNQrTY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 h1:sZXIzO38GZOU+O0C+INqbH7C2yALwfMWpd64tONS/NE=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2 h1:Kp6PWAlXwP1UvIflkIP6MFZYBNDCa4mFCGtxrpICVOg=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2/go.mod h1:5FmD/Dqq57gP+XwaUnd5WFPipAuzrf0HmupX27Gvjvc=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 h1:pIaGg+08llrP7Q5aiz9ICWbY8cqhTkyy+0SHvfzQpTc=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7/go.mod h1:eEygMHnTKH/3kNp9Jr1n3PdejuSNcgwLe1dWgQtO0VQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 h1:/Cfdu0XV3mONYKaOt1Gr0k1KvQzkzPyiKUdlWJqy+J4=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7/go.mod h1:bCbAxKDqNvkHxRaIMnyVPXPo+OaPRwvmgzMxbz1VKSA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 h1:NKTa1eqZYw8tiHSRGpP0VtTdub/8KNk8sDkNPFaOKDE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7/go.mod h1:NXi1dIAGteSaRLqYgarlhP/Ij0cFT+qmCwiJqWh/U5o=
|
||||||
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
|
||||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||||
github.com/blinkbean/dingtalk v1.1.3 h1:MbidFZYom7DTFHD/YIs+eaI7kRy52kmWE/sy0xjo6E4=
|
github.com/blinkbean/dingtalk v1.1.3 h1:MbidFZYom7DTFHD/YIs+eaI7kRy52kmWE/sy0xjo6E4=
|
||||||
github.com/blinkbean/dingtalk v1.1.3/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto=
|
github.com/blinkbean/dingtalk v1.1.3/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto=
|
||||||
github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI=
|
github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI=
|
||||||
@@ -138,8 +138,8 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn
|
|||||||
github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g=
|
github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g=
|
||||||
github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cloudflare-go v0.97.0 h1:feZRGiRF1EbljnNIYdt8014FnOLtC3CCvgkLXu915ks=
|
github.com/cloudflare/cloudflare-go v0.104.0 h1:R/lB0dZupaZbOgibAH/BRrkFbZ6Acn/WsKg2iX2xXuY=
|
||||||
github.com/cloudflare/cloudflare-go v0.97.0/go.mod h1:JXRwuTfHpe5xFg8xytc2w0XC6LcrFsBVMS4WlVaiGg8=
|
github.com/cloudflare/cloudflare-go v0.104.0/go.mod h1:pfUQ4PIG4ISI0/Mmc21Bp86UnFU0ktmPf3iTgbSL+cM=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
@@ -147,8 +147,9 @@ github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
|||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
|
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
|
||||||
@@ -173,11 +174,11 @@ github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSe
|
|||||||
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
|
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
|
||||||
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
|
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
|
||||||
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
||||||
github.com/go-acme/lego/v4 v4.17.4 h1:h0nePd3ObP6o7kAkndtpTzCw8shOZuWckNYeUQwo36Q=
|
github.com/go-acme/lego/v4 v4.19.2 h1:Y8hrmMvWETdqzzkRly7m98xtPJJivWFsgWi8fcvZo+Y=
|
||||||
github.com/go-acme/lego/v4 v4.17.4/go.mod h1:dU94SvPNqimEeb7EVilGGSnS0nU1O5Exir0pQ4QFL4U=
|
github.com/go-acme/lego/v4 v4.19.2/go.mod h1:wtDe3dDkmV4/oI2nydpNXSJpvV10J9RCyZ6MbYxNtlQ=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
@@ -214,15 +215,18 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
|||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@@ -239,30 +243,26 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0 h1:e+8XbKB6IMn8A4OAyZccO4pYfB3s7bt6azNIPE7AnPg=
|
github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0 h1:e+8XbKB6IMn8A4OAyZccO4pYfB3s7bt6azNIPE7AnPg=
|
||||||
github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
github.com/google/pprof v0.0.0-20240625030939-27f56978b8b0/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||||
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||||
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
|
||||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
|
||||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 h1:X3E16S6AUZsQKhJIQ5kNnylnp0GtSy2YhIbxfvDavtU=
|
||||||
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI=
|
||||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
@@ -281,6 +281,7 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
|||||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@@ -309,8 +310,8 @@ github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxU
|
|||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -318,6 +319,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
@@ -333,8 +335,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
||||||
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
|
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
|
github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
|
||||||
github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||||
github.com/pocketbase/pocketbase v0.22.18 h1:yVckUhi5GDORqCb0BbtlvRB1CVxHY9HO9btEaeZHVJU=
|
github.com/pocketbase/pocketbase v0.22.18 h1:yVckUhi5GDORqCb0BbtlvRB1CVxHY9HO9btEaeZHVJU=
|
||||||
@@ -377,21 +380,24 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.898/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1017 h1:OymmfmyFkvHirY3WHsoRT3cdTEsqygLbMn8jM41erK4=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.992 h1:266lOve+E8vzhnrb/Mr05Ee+oxXD9C82JiusY/AZqXw=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1017/go.mod h1:gnLxGXlLmF+jDqWR1/RVoF/UUwxQxomQhkc0oN7KeuI=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.992/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.992/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 h1:LoYv5u+gUoFpU/AmIuTRG/2KiEkdm9gCC0dTvk8WITQ=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1002/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898/go.mod h1:c1j6YQ+vCbeA8kJ59Im4UnMd1GxovlpPBDhGZoewfn8=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1017 h1:SXrldOXwgomYuATVAuz5ofpTjB+99qVELgdy5R5kMgI=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1017/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 h1:QwE0dRkAAbdf+eACnkNULgDn9ZKUJpPWRyXdqJolP5E=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002/go.mod h1:WdC0FYbqYhJwQ3kbqri6hVP5HAEp+rzX9FToItTAzUg=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992 h1:A6O89OlCJQUpNxGqC/E5By04UNKBryIt5olQIGOx8mg=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992 h1:A6O89OlCJQUpNxGqC/E5By04UNKBryIt5olQIGOx8mg=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992/go.mod h1:BcvC7ZPdSlhRggVq4J1ToJlgv8bmODIAuSo0naFZOLo=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992/go.mod h1:BcvC7ZPdSlhRggVq4J1ToJlgv8bmODIAuSo0naFZOLo=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.992 h1:ttCM2rrkGipHMFTavrPExKCWcfNjT7AMQ5ERrPExdI4=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.992/go.mod h1:WtzarrflM+eoyD8vcRuIPd8fT5UXD4IhUry6iSAUnxc=
|
|
||||||
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
|
|
||||||
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||||
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
|
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
||||||
@@ -400,21 +406,27 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
|
|||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE=
|
||||||
|
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
gocloud.dev v0.37.0 h1:XF1rN6R0qZI/9DYjN16Uy0durAmSlf58DHOcb28GPro=
|
gocloud.dev v0.37.0 h1:XF1rN6R0qZI/9DYjN16Uy0durAmSlf58DHOcb28GPro=
|
||||||
@@ -425,13 +437,17 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@@ -453,8 +469,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.6.0-dev/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.6.0-dev/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -464,8 +480,10 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
@@ -474,11 +492,12 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|||||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||||
|
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -499,6 +518,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -510,8 +530,10 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -520,24 +542,28 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|||||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
|
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||||
|
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -551,8 +577,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -563,27 +589,28 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ
|
|||||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||||
google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
|
google.golang.org/api v0.197.0 h1:x6CwqQLsFiA5JKAiGyGBjc2bNtHtLddhJCE2IKuhhcQ=
|
||||||
google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
|
google.golang.org/api v0.197.0/go.mod h1:AuOuo20GoQ331nq7DquGHlU6d+2wN2fZ8O0ta60nRNw=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
|
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU=
|
||||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
|
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a h1:hqK4+jJZXCU4pW7jsAdGOVFIfLHQeV7LaizZKnZ84HI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
|
||||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package applicant
|
package applicant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"certimate/internal/utils/app"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
@@ -18,13 +20,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
configTypeTencent = "tencent"
|
configTypeAliyun = "aliyun"
|
||||||
configTypeAliyun = "aliyun"
|
configTypeTencent = "tencent"
|
||||||
configTypeCloudflare = "cloudflare"
|
configTypeHuaweicloud = "huaweicloud"
|
||||||
configTypeNamesilo = "namesilo"
|
configTypeCloudflare = "cloudflare"
|
||||||
configTypeGodaddy = "godaddy"
|
configTypeNamesilo = "namesilo"
|
||||||
|
configTypeGodaddy = "godaddy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultSSLProvider = "letsencrypt"
|
||||||
|
const (
|
||||||
|
sslProviderLetsencrypt = "letsencrypt"
|
||||||
|
sslProviderZeroSSL = "zerossl"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
zerosslUrl = "https://acme.zerossl.com/v2/DV90"
|
||||||
|
letsencryptUrl = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sslProviderUrls = map[string]string{
|
||||||
|
sslProviderLetsencrypt: letsencryptUrl,
|
||||||
|
sslProviderZeroSSL: zerosslUrl,
|
||||||
|
}
|
||||||
|
|
||||||
const defaultEmail = "536464346@qq.com"
|
const defaultEmail = "536464346@qq.com"
|
||||||
|
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
@@ -76,10 +95,12 @@ func Get(record *models.Record) (Applicant, error) {
|
|||||||
Nameservers: record.GetString("nameservers"),
|
Nameservers: record.GetString("nameservers"),
|
||||||
}
|
}
|
||||||
switch access.GetString("configType") {
|
switch access.GetString("configType") {
|
||||||
case configTypeTencent:
|
|
||||||
return NewTencent(option), nil
|
|
||||||
case configTypeAliyun:
|
case configTypeAliyun:
|
||||||
return NewAliyun(option), nil
|
return NewAliyun(option), nil
|
||||||
|
case configTypeTencent:
|
||||||
|
return NewTencent(option), nil
|
||||||
|
case configTypeHuaweicloud:
|
||||||
|
return NewHuaweiCloud(option), nil
|
||||||
case configTypeCloudflare:
|
case configTypeCloudflare:
|
||||||
return NewCloudflare(option), nil
|
return NewCloudflare(option), nil
|
||||||
case configTypeNamesilo:
|
case configTypeNamesilo:
|
||||||
@@ -92,7 +113,31 @@ func Get(record *models.Record) (Applicant, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SSLProviderConfig struct {
|
||||||
|
Config SSLProviderConfigContent `json:"config"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSLProviderConfigContent struct {
|
||||||
|
Zerossl struct {
|
||||||
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
|
EabKid string `json:"eabKid"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, error) {
|
func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, error) {
|
||||||
|
record, _ := app.GetApp().Dao().FindFirstRecordByFilter("settings", "name='ssl-provider'")
|
||||||
|
|
||||||
|
sslProvider := &SSLProviderConfig{
|
||||||
|
Config: SSLProviderConfigContent{},
|
||||||
|
Provider: defaultSSLProvider,
|
||||||
|
}
|
||||||
|
if record != nil {
|
||||||
|
if err := record.UnmarshalJSONField("content", sslProvider); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -106,7 +151,7 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
|
|||||||
config := lego.NewConfig(&myUser)
|
config := lego.NewConfig(&myUser)
|
||||||
|
|
||||||
// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
|
// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
|
||||||
config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
|
config.CADirURL = sslProviderUrls[sslProvider.Provider]
|
||||||
config.Certificate.KeyType = certcrypto.RSA2048
|
config.Certificate.KeyType = certcrypto.RSA2048
|
||||||
|
|
||||||
// A client facilitates communication with the CA server.
|
// A client facilitates communication with the CA server.
|
||||||
@@ -124,19 +169,13 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
|
|||||||
client.Challenge.SetDNS01Provider(provider, challengeOptions...)
|
client.Challenge.SetDNS01Provider(provider, challengeOptions...)
|
||||||
|
|
||||||
// New users will need to register
|
// New users will need to register
|
||||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
reg, err := getReg(client, sslProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to register: %w", err)
|
||||||
}
|
}
|
||||||
myUser.Registration = reg
|
myUser.Registration = reg
|
||||||
|
|
||||||
domains := []string{option.Domain}
|
domains := strings.Split(option.Domain, ";")
|
||||||
|
|
||||||
// 如果是通配置符域名,把根域名也加入
|
|
||||||
if strings.HasPrefix(option.Domain, "*.") && len(strings.Split(option.Domain, ".")) == 3 {
|
|
||||||
rootDomain := strings.TrimPrefix(option.Domain, "*.")
|
|
||||||
domains = append(domains, rootDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
request := certificate.ObtainRequest{
|
request := certificate.ObtainRequest{
|
||||||
Domains: domains,
|
Domains: domains,
|
||||||
@@ -155,6 +194,33 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
|
|||||||
IssuerCertificate: string(certificates.IssuerCertificate),
|
IssuerCertificate: string(certificates.IssuerCertificate),
|
||||||
Csr: string(certificates.CSR),
|
Csr: string(certificates.CSR),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReg(client *lego.Client, sslProvider *SSLProviderConfig) (*registration.Resource, error) {
|
||||||
|
var reg *registration.Resource
|
||||||
|
var err error
|
||||||
|
switch sslProvider.Provider {
|
||||||
|
case sslProviderZeroSSL:
|
||||||
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: sslProvider.Config.Zerossl.EabKid,
|
||||||
|
HmacEncoded: sslProvider.Config.Zerossl.EabHmacKey,
|
||||||
|
})
|
||||||
|
|
||||||
|
case sslProviderLetsencrypt:
|
||||||
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
|
|
||||||
|
default:
|
||||||
|
err = errors.New("unknown ssl provider")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseNameservers(ns string) []string {
|
func ParseNameservers(ns string) []string {
|
||||||
|
|||||||
35
internal/applicant/huaweicloud.go
Normal file
35
internal/applicant/huaweicloud.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package applicant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"certimate/internal/domain"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
huaweicloudProvider "github.com/go-acme/lego/v4/providers/dns/huaweicloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
type huaweicloud struct {
|
||||||
|
option *ApplyOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHuaweiCloud(option *ApplyOption) Applicant {
|
||||||
|
return &huaweicloud{
|
||||||
|
option: option,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *huaweicloud) Apply() (*Certificate, error) {
|
||||||
|
|
||||||
|
access := &domain.HuaweiCloudAccess{}
|
||||||
|
json.Unmarshal([]byte(t.option.Access), access)
|
||||||
|
|
||||||
|
os.Setenv("HUAWEICLOUD_REGION", access.Region) // 华为云的 SDK 要求必须传一个区域,实际上 DNS-01 流程里用不到,但不传会报错
|
||||||
|
os.Setenv("HUAWEICLOUD_ACCESS_KEY_ID", access.AccessKeyId)
|
||||||
|
os.Setenv("HUAWEICLOUD_SECRET_ACCESS_KEY", access.SecretAccessKey)
|
||||||
|
dnsProvider, err := huaweicloudProvider.NewDNSProvider()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply(t.option, dnsProvider)
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ const (
|
|||||||
targetWebhook = "webhook"
|
targetWebhook = "webhook"
|
||||||
targetTencentCdn = "tencent-cdn"
|
targetTencentCdn = "tencent-cdn"
|
||||||
targetQiniuCdn = "qiniu-cdn"
|
targetQiniuCdn = "qiniu-cdn"
|
||||||
|
targetLocal = "local"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerOption struct {
|
type DeployerOption struct {
|
||||||
@@ -127,7 +128,10 @@ func getWithAccess(record *models.Record, cert *applicant.Certificate, access *m
|
|||||||
case targetTencentCdn:
|
case targetTencentCdn:
|
||||||
return NewTencentCdn(option)
|
return NewTencentCdn(option)
|
||||||
case targetQiniuCdn:
|
case targetQiniuCdn:
|
||||||
|
|
||||||
return NewQiNiu(option)
|
return NewQiNiu(option)
|
||||||
|
case targetLocal:
|
||||||
|
return NewLocal(option), nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
106
internal/deployer/local.go
Normal file
106
internal/deployer/local.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package deployer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type localAccess struct {
|
||||||
|
Command string `json:"command"`
|
||||||
|
CertPath string `json:"certPath"`
|
||||||
|
KeyPath string `json:"keyPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type local struct {
|
||||||
|
option *DeployerOption
|
||||||
|
infos []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocal(option *DeployerOption) *local {
|
||||||
|
return &local{
|
||||||
|
option: option,
|
||||||
|
infos: make([]string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) GetID() string {
|
||||||
|
return fmt.Sprintf("%s-%s", l.option.AceessRecord.GetString("name"), l.option.AceessRecord.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) GetInfo() []string {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Deploy(ctx context.Context) error {
|
||||||
|
access := &localAccess{}
|
||||||
|
if err := json.Unmarshal([]byte(l.option.Access), access); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 复制文件
|
||||||
|
if err := copyFile(l.option.Certificate.Certificate, access.CertPath); err != nil {
|
||||||
|
return fmt.Errorf("复制证书失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := copyFile(l.option.Certificate.PrivateKey, access.KeyPath); err != nil {
|
||||||
|
return fmt.Errorf("复制私钥失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行命令
|
||||||
|
|
||||||
|
if err := execCmd(access.Command); err != nil {
|
||||||
|
return fmt.Errorf("执行命令失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func execCmd(command string) error {
|
||||||
|
// 执行命令
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cmd = exec.Command("cmd", "/C", command)
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("sh", "-c", command)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("执行命令失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(content string, path string) error {
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
|
||||||
|
// 如果目录不存在,创建目录
|
||||||
|
err := os.MkdirAll(dir, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建或打开文件
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建文件失败: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 写入内容到文件
|
||||||
|
_, err = file.Write([]byte(content))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("写入文件失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -19,14 +19,15 @@ type ssh struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type sshAccess struct {
|
type sshAccess struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Port string `json:"port"`
|
Port string `json:"port"`
|
||||||
Command string `json:"command"`
|
PreCommand string `json:"preCommand"`
|
||||||
CertPath string `json:"certPath"`
|
Command string `json:"command"`
|
||||||
KeyPath string `json:"keyPath"`
|
CertPath string `json:"certPath"`
|
||||||
|
KeyPath string `json:"keyPath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSSH(option *DeployerOption) (Deployer, error) {
|
func NewSSH(option *DeployerOption) (Deployer, error) {
|
||||||
@@ -56,6 +57,7 @@ func (s *ssh) Deploy(ctx context.Context) error {
|
|||||||
access.CertPath = strings.ReplaceAll(access.CertPath, key, v)
|
access.CertPath = strings.ReplaceAll(access.CertPath, key, v)
|
||||||
access.KeyPath = strings.ReplaceAll(access.KeyPath, key, v)
|
access.KeyPath = strings.ReplaceAll(access.KeyPath, key, v)
|
||||||
access.Command = strings.ReplaceAll(access.Command, key, v)
|
access.Command = strings.ReplaceAll(access.Command, key, v)
|
||||||
|
access.PreCommand = strings.ReplaceAll(access.PreCommand, key, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连接
|
// 连接
|
||||||
@@ -67,14 +69,13 @@ func (s *ssh) Deploy(ctx context.Context) error {
|
|||||||
|
|
||||||
s.infos = append(s.infos, toStr("ssh连接成功", nil))
|
s.infos = append(s.infos, toStr("ssh连接成功", nil))
|
||||||
|
|
||||||
// 上传
|
// 执行前置命令
|
||||||
session, err := client.NewSession()
|
if access.PreCommand != "" {
|
||||||
if err != nil {
|
err, stdout, stderr := s.sshExecCommand(client, access.PreCommand)
|
||||||
return fmt.Errorf("failed to create session: %w", err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to run pre-command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
s.infos = append(s.infos, toStr("ssh创建session成功", nil))
|
|
||||||
|
|
||||||
// 上传证书
|
// 上传证书
|
||||||
if err := s.upload(client, s.option.Certificate.Certificate, access.CertPath); err != nil {
|
if err := s.upload(client, s.option.Certificate.Certificate, access.CertPath); err != nil {
|
||||||
@@ -91,18 +92,28 @@ func (s *ssh) Deploy(ctx context.Context) error {
|
|||||||
s.infos = append(s.infos, toStr("ssh上传私钥成功", nil))
|
s.infos = append(s.infos, toStr("ssh上传私钥成功", nil))
|
||||||
|
|
||||||
// 执行命令
|
// 执行命令
|
||||||
|
err, stdout, stderr := s.sshExecCommand(client, access.Command)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.infos = append(s.infos, toStr("ssh执行命令成功", stdout))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ssh) sshExecCommand(client *sshPkg.Client, command string) (error, string, string) {
|
||||||
|
session, err := client.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create ssh session: %w", err), "", ""
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
var stdoutBuf bytes.Buffer
|
var stdoutBuf bytes.Buffer
|
||||||
session.Stdout = &stdoutBuf
|
session.Stdout = &stdoutBuf
|
||||||
var stderrBuf bytes.Buffer
|
var stderrBuf bytes.Buffer
|
||||||
session.Stderr = &stderrBuf
|
session.Stderr = &stderrBuf
|
||||||
|
err = session.Run(command)
|
||||||
if err := session.Run(access.Command); err != nil {
|
return err, stdoutBuf.String(), stderrBuf.String()
|
||||||
return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdoutBuf.String(), stderrBuf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
s.infos = append(s.infos, toStr("ssh执行命令成功", []string{stdoutBuf.String()}))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ssh) upload(client *sshPkg.Client, content, path string) error {
|
func (s *ssh) upload(client *sshPkg.Client, content, path string) error {
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import (
|
|||||||
"certimate/internal/utils/rand"
|
"certimate/internal/utils/rand"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||||
ssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
|
ssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
|
||||||
|
cdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tencentCdn struct {
|
type tencentCdn struct {
|
||||||
@@ -89,14 +92,29 @@ func (t *tencentCdn) deploy(certId string) error {
|
|||||||
// 实例化要请求产品的client对象,clientProfile是可选的
|
// 实例化要请求产品的client对象,clientProfile是可选的
|
||||||
client, _ := ssl.NewClient(t.credential, "", cpf)
|
client, _ := ssl.NewClient(t.credential, "", cpf)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 实例化一个请求对象,每个接口都会对应一个request对象
|
// 实例化一个请求对象,每个接口都会对应一个request对象
|
||||||
request := ssl.NewDeployCertificateInstanceRequest()
|
request := ssl.NewDeployCertificateInstanceRequest()
|
||||||
|
|
||||||
request.CertificateId = common.StringPtr(certId)
|
request.CertificateId = common.StringPtr(certId)
|
||||||
request.InstanceIdList = common.StringPtrs([]string{t.option.Domain})
|
|
||||||
request.ResourceType = common.StringPtr("cdn")
|
request.ResourceType = common.StringPtr("cdn")
|
||||||
request.Status = common.Int64Ptr(1)
|
request.Status = common.Int64Ptr(1)
|
||||||
|
|
||||||
|
// 如果是泛域名就从cdn列表下获取SSL证书中的可用域名
|
||||||
|
if(strings.Contains(t.option.Domain, "*")){
|
||||||
|
list, errGetList := t.getDomainList()
|
||||||
|
if errGetList != nil {
|
||||||
|
return fmt.Errorf("failed to get certificate domain list: %w", errGetList)
|
||||||
|
}
|
||||||
|
if list == nil || len(list) == 0 {
|
||||||
|
return fmt.Errorf("failed to get certificate domain list: empty list.")
|
||||||
|
}
|
||||||
|
request.InstanceIdList = common.StringPtrs(list)
|
||||||
|
}else{ // 否则直接使用传入的域名
|
||||||
|
request.InstanceIdList = common.StringPtrs([]string{t.option.Domain})
|
||||||
|
}
|
||||||
|
|
||||||
// 返回的resp是一个DeployCertificateInstanceResponse的实例,与请求对象对应
|
// 返回的resp是一个DeployCertificateInstanceResponse的实例,与请求对象对应
|
||||||
resp, err := client.DeployCertificateInstance(request)
|
resp, err := client.DeployCertificateInstance(request)
|
||||||
|
|
||||||
@@ -106,3 +124,27 @@ func (t *tencentCdn) deploy(certId string) error {
|
|||||||
t.infos = append(t.infos, toStr("部署证书", resp.Response))
|
t.infos = append(t.infos, toStr("部署证书", resp.Response))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tencentCdn) getDomainList() ([]string, error) {
|
||||||
|
cpf := profile.NewClientProfile()
|
||||||
|
cpf.HttpProfile.Endpoint = "cdn.tencentcloudapi.com"
|
||||||
|
client, _ := cdn.NewClient(t.credential, "", cpf)
|
||||||
|
|
||||||
|
request := cdn.NewDescribeCertDomainsRequest()
|
||||||
|
|
||||||
|
cert := base64.StdEncoding.EncodeToString([]byte(t.option.Certificate.Certificate))
|
||||||
|
request.Cert = &cert
|
||||||
|
|
||||||
|
|
||||||
|
response, err := client.DescribeCertDomains(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get domain list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
domains := make([]string, 0)
|
||||||
|
for _, domain := range response.Response.Domains {
|
||||||
|
domains = append(domains, *domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return domains, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ type TencentAccess struct {
|
|||||||
SecretKey string `json:"secretKey"`
|
SecretKey string `json:"secretKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HuaweiCloudAccess struct {
|
||||||
|
Region string `json:"region"`
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
SecretAccessKey string `json:"secretAccessKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type CloudflareAccess struct {
|
type CloudflareAccess struct {
|
||||||
DnsApiToken string `json:"dnsApiToken"`
|
DnsApiToken string `json:"dnsApiToken"`
|
||||||
}
|
}
|
||||||
@@ -27,3 +33,4 @@ type GodaddyAccess struct {
|
|||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
ApiSecret string `json:"apiSecret"`
|
ApiSecret string `json:"apiSecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ func Send(title, content string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n := notifyPackage.New()
|
||||||
// 添加推送渠道
|
// 添加推送渠道
|
||||||
notifyPackage.UseServices(notifiers...)
|
n.UseServices(notifiers...)
|
||||||
|
|
||||||
// 发送消息
|
// 发送消息
|
||||||
return notifyPackage.Send(context.Background(), title, content)
|
return n.Send(context.Background(), title, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNotifiers() ([]notifyPackage.Notifier, error) {
|
func getNotifiers() ([]notifyPackage.Notifier, error) {
|
||||||
|
|||||||
706
migrations/1727341442_collections_snapshot.go
Normal file
706
migrations/1727341442_collections_snapshot.go
Normal file
@@ -0,0 +1,706 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
jsonData := `[
|
||||||
|
{
|
||||||
|
"id": "z3p974ainxjqlvs",
|
||||||
|
"created": "2024-07-29 10:02:48.334Z",
|
||||||
|
"updated": "2024-09-26 08:20:28.305Z",
|
||||||
|
"name": "domains",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "iuaerpl2",
|
||||||
|
"name": "domain",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ukkhuw85",
|
||||||
|
"name": "email",
|
||||||
|
"type": "email",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"exceptDomains": null,
|
||||||
|
"onlyDomains": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "v98eebqq",
|
||||||
|
"name": "crontab",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "alc8e9ow",
|
||||||
|
"name": "access",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "4yzbv8urny5ja1e",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "topsc9bj",
|
||||||
|
"name": "certUrl",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "vixgq072",
|
||||||
|
"name": "certStableUrl",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "g3a3sza5",
|
||||||
|
"name": "privateKey",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "gr6iouny",
|
||||||
|
"name": "certificate",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "tk6vnrmn",
|
||||||
|
"name": "issuerCertificate",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "sjo6ibse",
|
||||||
|
"name": "csr",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "x03n1bkj",
|
||||||
|
"name": "expiredAt",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "srybpixz",
|
||||||
|
"name": "targetType",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"aliyun-oss",
|
||||||
|
"aliyun-cdn",
|
||||||
|
"aliyun-dcdn",
|
||||||
|
"ssh",
|
||||||
|
"webhook",
|
||||||
|
"tencent-cdn",
|
||||||
|
"qiniu-cdn",
|
||||||
|
"local"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "xy7yk0mb",
|
||||||
|
"name": "targetAccess",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "4yzbv8urny5ja1e",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "6jqeyggw",
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "hdsjcchf",
|
||||||
|
"name": "deployed",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "aiya3rev",
|
||||||
|
"name": "rightnow",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ixznmhzc",
|
||||||
|
"name": "lastDeployedAt",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ghtlkn5j",
|
||||||
|
"name": "lastDeployment",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "0a1o4e6sstp694f",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "zfnyj9he",
|
||||||
|
"name": "variables",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "1bspzuku",
|
||||||
|
"name": "group",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "teolp9pl72dxlxq",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "g65gfh7a",
|
||||||
|
"name": "nameservers",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX ` + "`" + `idx_4ABO6EQ` + "`" + ` ON ` + "`" + `domains` + "`" + ` (` + "`" + `domain` + "`" + `)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4yzbv8urny5ja1e",
|
||||||
|
"created": "2024-07-29 10:04:39.685Z",
|
||||||
|
"updated": "2024-09-26 08:36:59.632Z",
|
||||||
|
"name": "access",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "geeur58v",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "iql7jpwx",
|
||||||
|
"name": "config",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "hwy7m03o",
|
||||||
|
"name": "configType",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"aliyun",
|
||||||
|
"tencent",
|
||||||
|
"ssh",
|
||||||
|
"webhook",
|
||||||
|
"cloudflare",
|
||||||
|
"qiniu",
|
||||||
|
"namesilo",
|
||||||
|
"godaddy",
|
||||||
|
"local"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "lr33hiwg",
|
||||||
|
"name": "deleted",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "hsxcnlvd",
|
||||||
|
"name": "usage",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"apply",
|
||||||
|
"deploy",
|
||||||
|
"all"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "c8egzzwj",
|
||||||
|
"name": "group",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "teolp9pl72dxlxq",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX ` + "`" + `idx_wkoST0j` + "`" + ` ON ` + "`" + `access` + "`" + ` (` + "`" + `name` + "`" + `)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0a1o4e6sstp694f",
|
||||||
|
"created": "2024-07-30 06:30:27.801Z",
|
||||||
|
"updated": "2024-09-24 14:44:48.041Z",
|
||||||
|
"name": "deployments",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "farvlzk7",
|
||||||
|
"name": "domain",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "z3p974ainxjqlvs",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "jx5f69i3",
|
||||||
|
"name": "log",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "qbxdtg9q",
|
||||||
|
"name": "phase",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"check",
|
||||||
|
"apply",
|
||||||
|
"deploy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "rglrp1hz",
|
||||||
|
"name": "phaseSuccess",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "lt1g1blu",
|
||||||
|
"name": "deployedAt",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "wledpzgb",
|
||||||
|
"name": "wholeSuccess",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "_pb_users_auth_",
|
||||||
|
"created": "2024-09-12 13:09:54.234Z",
|
||||||
|
"updated": "2024-09-24 14:44:48.041Z",
|
||||||
|
"name": "users",
|
||||||
|
"type": "auth",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "users_name",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "users_avatar",
|
||||||
|
"name": "avatar",
|
||||||
|
"type": "file",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"mimeTypes": [
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/svg+xml",
|
||||||
|
"image/gif",
|
||||||
|
"image/webp"
|
||||||
|
],
|
||||||
|
"thumbs": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 5242880,
|
||||||
|
"protected": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "id = @request.auth.id",
|
||||||
|
"viewRule": "id = @request.auth.id",
|
||||||
|
"createRule": "",
|
||||||
|
"updateRule": "id = @request.auth.id",
|
||||||
|
"deleteRule": "id = @request.auth.id",
|
||||||
|
"options": {
|
||||||
|
"allowEmailAuth": true,
|
||||||
|
"allowOAuth2Auth": true,
|
||||||
|
"allowUsernameAuth": true,
|
||||||
|
"exceptEmailDomains": null,
|
||||||
|
"manageRule": null,
|
||||||
|
"minPasswordLength": 8,
|
||||||
|
"onlyEmailDomains": null,
|
||||||
|
"onlyVerified": false,
|
||||||
|
"requireEmail": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dy6ccjb60spfy6p",
|
||||||
|
"created": "2024-09-12 23:12:21.677Z",
|
||||||
|
"updated": "2024-09-24 14:44:48.041Z",
|
||||||
|
"name": "settings",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "1tcmdsdf",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "f9wyhypi",
|
||||||
|
"name": "content",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX ` + "`" + `idx_RO7X9Vw` + "`" + ` ON ` + "`" + `settings` + "`" + ` (` + "`" + `name` + "`" + `)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "teolp9pl72dxlxq",
|
||||||
|
"created": "2024-09-13 12:51:05.611Z",
|
||||||
|
"updated": "2024-09-24 14:44:48.041Z",
|
||||||
|
"name": "access_groups",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "7sajiv6i",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "xp8admif",
|
||||||
|
"name": "access",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "4yzbv8urny5ja1e",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": null,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX ` + "`" + `idx_RgRXp0R` + "`" + ` ON ` + "`" + `access_groups` + "`" + ` (` + "`" + `name` + "`" + `)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
]`
|
||||||
|
|
||||||
|
collections := []*models.Collection{}
|
||||||
|
if err := json.Unmarshal([]byte(jsonData), &collections); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return daos.New(db).ImportCollections(collections, true, nil)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
92
migrations/1728610007_updated_access.go
Normal file
92
migrations/1728610007_updated_access.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("4yzbv8urny5ja1e")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update
|
||||||
|
edit_configType := &schema.SchemaField{}
|
||||||
|
if err := json.Unmarshal([]byte(`{
|
||||||
|
"system": false,
|
||||||
|
"id": "hwy7m03o",
|
||||||
|
"name": "configType",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"aliyun",
|
||||||
|
"tencent",
|
||||||
|
"huaweicloud",
|
||||||
|
"qiniu",
|
||||||
|
"cloudflare",
|
||||||
|
"namesilo",
|
||||||
|
"godaddy",
|
||||||
|
"local",
|
||||||
|
"ssh",
|
||||||
|
"webhook"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`), edit_configType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
collection.Schema.AddField(edit_configType)
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("4yzbv8urny5ja1e")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update
|
||||||
|
edit_configType := &schema.SchemaField{}
|
||||||
|
if err := json.Unmarshal([]byte(`{
|
||||||
|
"system": false,
|
||||||
|
"id": "hwy7m03o",
|
||||||
|
"name": "configType",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"aliyun",
|
||||||
|
"tencent",
|
||||||
|
"huaweicloud",
|
||||||
|
"ssh",
|
||||||
|
"webhook",
|
||||||
|
"cloudflare",
|
||||||
|
"qiniu",
|
||||||
|
"namesilo",
|
||||||
|
"godaddy",
|
||||||
|
"local"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`), edit_configType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
collection.Schema.AddField(edit_configType)
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
308
ui/dist/assets/index-B-WSkv0U.js
vendored
308
ui/dist/assets/index-B-WSkv0U.js
vendored
File diff suppressed because one or more lines are too long
1
ui/dist/assets/index-BLt37AhW.css
vendored
1
ui/dist/assets/index-BLt37AhW.css
vendored
File diff suppressed because one or more lines are too long
1
ui/dist/assets/index-DOft-CKV.css
vendored
Normal file
1
ui/dist/assets/index-DOft-CKV.css
vendored
Normal file
File diff suppressed because one or more lines are too long
332
ui/dist/assets/index-DpHAV802.js
vendored
Normal file
332
ui/dist/assets/index-DpHAV802.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
ui/dist/imgs/providers/huaweicloud.svg
vendored
Normal file
1
ui/dist/imgs/providers/huaweicloud.svg
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1728551595312" class="icon" viewBox="0 0 1027 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4252" width="200" height="200"><path d="M378.88 143.36c20.48-6.826667 40.96-10.24 61.44-13.653333 23.893333 30.72 30.72 71.68 40.96 109.226666 6.826667 40.96 13.653333 81.92 13.653333 122.88 6.826667 27.306667 3.413333 58.026667 3.413334 85.333334s-3.413333 51.2 0 78.506666c0 37.546667-3.413333 71.68-3.413334 109.226667-6.826667 23.893333 0 47.786667-6.826666 71.68-10.24-3.413333-13.653333-13.653333-17.066667-20.48-40.96-61.44-78.506667-122.88-112.64-187.733333-34.133333-68.266667-68.266667-136.533333-71.68-215.04-6.826667-61.44 34.133333-119.466667 92.16-139.946667z m211.626667-10.24c6.826667-3.413333 10.24 0 17.066666 0 27.306667 6.826667 58.026667 10.24 81.92 27.306667s44.373333 40.96 51.2 68.266666c10.24 34.133333 6.826667 71.68 0 105.813334-10.24 44.373333-30.72 85.333333-51.2 126.293333-6.826667 13.653333-13.653333 23.893333-20.48 37.546667-10.24 23.893333-27.306667 47.786667-40.96 71.68-23.893333 40.96-47.786667 75.093333-71.68 116.053333-3.413333 3.413333-6.826667 13.653333-13.653333 6.826667-3.413333-40.96-6.826667-81.92-10.24-126.293334-6.826667-54.613333-3.413333-105.813333-3.413333-160.426666 3.413333-34.133333 3.413333-71.68 6.826666-105.813334 6.826667-40.96 13.653333-85.333333 27.306667-126.293333 13.653333-10.24 13.653333-30.72 27.306667-40.96zM160.426667 266.24c3.413333 0 6.826667 6.826667 10.24 10.24 98.986667 129.706667 187.733333 266.24 259.413333 413.013333 6.826667 10.24 13.653333 23.893333 13.653333 37.546667-13.653333-3.413333-23.893333-10.24-34.133333-17.066667-64.853333-34.133333-129.706667-71.68-194.56-109.226666-23.893333-17.066667-47.786667-34.133333-68.266667-51.2-40.96-27.306667-68.266667-78.506667-64.853333-129.706667 3.413333-61.44 37.546667-112.64 78.506667-153.6z m706.56 0h6.826666c17.066667 23.893333 40.96 47.786667 54.613334 75.093333 13.653333 23.893333 20.48 54.613333 23.893333 81.92 0 30.72-10.24 64.853333-34.133333 88.746667-13.653333 13.653333-23.893333 27.306667-40.96 37.546667-78.506667 61.44-163.84 109.226667-252.586667 153.6-13.653333 6.826667-23.893333 17.066667-40.96 17.066666 3.413333-17.066667 13.653333-34.133333 20.48-47.786666 58.026667-119.466667 129.706667-232.106667 208.213333-341.333334 17.066667-17.066667 37.546667-40.96 54.613334-64.853333z m-856.746667 273.066667c3.413333-3.413333 0-10.24 6.826667-13.653334 10.24 3.413333 20.48 10.24 27.306666 13.653334 122.88 68.266667 245.76 136.533333 365.226667 211.626666 3.413333 3.413333 6.826667 6.826667 6.826667 10.24H180.906667c-47.786667 0-92.16-20.48-126.293334-54.613333-27.306667-30.72-51.2-71.68-54.613333-112.64 6.826667-17.066667 3.413333-34.133333 10.24-54.613333z m983.04-3.413334c6.826667-3.413333 17.066667-10.24 23.893333-6.826666 0 17.066667 6.826667 37.546667 6.826667 54.613333-3.413333 23.893333-3.413333 44.373333-13.653333 64.853333-6.826667 17.066667-17.066667 37.546667-30.72 51.2-17.066667 13.653333-27.306667 30.72-47.786667 40.96-20.48 17.066667-51.2 20.48-75.093333 23.893334h-245.76c3.413333-3.413333 3.413333-6.826667 6.826666-10.24 122.88-78.506667 249.173333-150.186667 375.466667-218.453334zM184.32 798.72c44.373333-3.413333 88.746667 0 133.12-6.826667 30.72 0 64.853333-3.413333 95.573333 0-6.826667 13.653333-23.893333 20.48-34.133333 27.306667-34.133333 23.893333-68.266667 44.373333-105.813333 61.44s-81.92 10.24-112.64-13.653333c-23.893333-17.066667-44.373333-44.373333-58.026667-68.266667h81.92z m433.493333-6.826667c30.72-3.413333 61.44 0 95.573334 0 40.96 3.413333 85.333333 0 129.706666 6.826667 30.72 3.413333 61.44 0 88.746667 3.413333-10.24 20.48-27.306667 40.96-44.373333 58.026667-27.306667 27.306667-68.266667 40.96-105.813334 34.133333-34.133333-10.24-61.44-30.72-92.16-47.786666-17.066667-10.24-30.72-20.48-47.786666-30.72-10.24-10.24-17.066667-13.653333-23.893334-23.893334z" fill="#C71F1E" p-id="4253"></path></svg>
|
||||||
18
ui/dist/imgs/providers/letsencrypt.svg
vendored
Normal file
18
ui/dist/imgs/providers/letsencrypt.svg
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="96" height="96">
|
||||||
|
<path d="M0 0 C3.96616708 2.87966696 6.94216973 6.71174376 8.75390625 11.2734375 C8.94921875 14.2890625 8.94921875 14.2890625 8.87890625 17.5234375 C8.86085937 18.60109375 8.8428125 19.67875 8.82421875 20.7890625 C8.80101562 21.60890625 8.7778125 22.42875 8.75390625 23.2734375 C9.86765625 23.2321875 10.98140625 23.1909375 12.12890625 23.1484375 C15.75390625 23.2734375 15.75390625 23.2734375 17.75390625 25.2734375 C17.94178314 28.33535881 18.00936994 31.30529877 17.984375 34.3671875 C17.98391678 35.2696521 17.98345856 36.1721167 17.98298645 37.10192871 C17.97996265 39.01181349 17.97207256 40.92169577 17.9597168 42.83154297 C17.94148066 45.76148172 17.93920695 48.69110422 17.93945312 51.62109375 C17.93453722 53.47396108 17.92870858 55.32682625 17.921875 57.1796875 C17.92076218 58.05991577 17.91964935 58.94014404 17.91850281 59.8470459 C17.86996885 66.04131231 17.86996885 66.04131231 16.75390625 68.2734375 C-1.72609375 68.2734375 -20.20609375 68.2734375 -39.24609375 68.2734375 C-40.95558713 64.85445075 -40.40930624 60.94182137 -40.4140625 57.1796875 C-40.4173909 56.2772229 -40.4207193 55.3747583 -40.42414856 54.44494629 C-40.42921569 52.53508006 -40.43155695 50.62520497 -40.43139648 48.71533203 C-40.43358486 45.78533449 -40.45174793 42.85571222 -40.47070312 39.92578125 C-40.47363664 38.07291796 -40.47562172 36.22005287 -40.4765625 34.3671875 C-40.48374802 33.48695923 -40.49093353 32.60673096 -40.49833679 31.6998291 C-40.47491923 25.50226298 -40.47491923 25.50226298 -38.24609375 23.2734375 C-34.62109375 23.1484375 -34.62109375 23.1484375 -31.24609375 23.2734375 C-31.30410156 22.5 -31.36210937 21.7265625 -31.421875 20.9296875 C-31.73940921 13.59918343 -31.17057807 8.85901698 -26.24609375 3.2734375 C-18.78730361 -3.81241313 -9.01893001 -5.1380571 0 0 Z M-19.24609375 15.2734375 C-19.24609375 17.9134375 -19.24609375 20.5534375 -19.24609375 23.2734375 C-13.96609375 23.2734375 -8.68609375 23.2734375 -3.24609375 23.2734375 C-2.47540396 15.74874343 -2.47540396 15.74874343 -5.37109375 12.2734375 C-8.09392313 10.10082379 -8.09392313 10.10082379 -11.24609375 9.6484375 C-15.38792256 10.5113185 -16.80835846 11.83192885 -19.24609375 15.2734375 Z M-11.24609375 39.2734375 C-11.24609375 44.5534375 -11.24609375 49.8334375 -11.24609375 55.2734375 C-10.58609375 54.9434375 -9.92609375 54.6134375 -9.24609375 54.2734375 C-8.89430178 51.9428157 -8.56279196 49.60908683 -8.24609375 47.2734375 C-7.18359375 44.8359375 -7.18359375 44.8359375 -6.24609375 43.2734375 C-7.60933118 41.2334656 -7.60933118 41.2334656 -9.24609375 39.2734375 C-9.90609375 39.2734375 -10.56609375 39.2734375 -11.24609375 39.2734375 Z " fill="#10376E" transform="translate(59.24609375,27.7265625)"/>
|
||||||
|
<path d="M0 0 C1.12067871 0.01047363 2.24135742 0.02094727 3.39599609 0.03173828 C5.21357422 0.04140625 5.21357422 0.04140625 7.06787109 0.05126953 C8.33888672 0.06802734 9.60990234 0.08478516 10.91943359 0.10205078 C12.19818359 0.11107422 13.47693359 0.12009766 14.79443359 0.12939453 C17.96128526 0.15302775 21.12776783 0.18597449 24.29443359 0.22705078 C24.4024195 2.66573246 24.48169949 5.09964212 24.54443359 7.53955078 C24.59470703 8.57499023 24.59470703 8.57499023 24.64599609 9.63134766 C24.68505859 11.65283203 24.68505859 11.65283203 24.29443359 15.22705078 C22.27880859 17.20751953 22.27880859 17.20751953 20.29443359 18.22705078 C20.7362031 21.76120687 21.41676358 24.50596315 22.85693359 27.78955078 C24.29443359 32.22705078 24.29443359 32.22705078 24.29443359 45.22705078 C15.05443359 45.22705078 5.81443359 45.22705078 -3.70556641 45.22705078 C-5.41505978 41.80806403 -4.8687789 37.89543466 -4.87353516 34.13330078 C-4.87686356 33.23083618 -4.88019196 32.32837158 -4.88362122 31.39855957 C-4.88868835 29.48869334 -4.89102961 27.57881825 -4.89086914 25.66894531 C-4.89305752 22.73894777 -4.91122059 19.8093255 -4.93017578 16.87939453 C-4.9331093 15.02653124 -4.93509437 13.17366616 -4.93603516 11.32080078 C-4.94322067 10.44057251 -4.95040619 9.56034424 -4.95780945 8.65344238 C-4.92667455 0.41344589 -4.92667455 0.41344589 0 0 Z " fill="#0F3161" transform="translate(23.70556640625,50.77294921875)"/>
|
||||||
|
<path d="M0 0 C3.95863415 2.87419762 6.96698604 6.71040909 8.75390625 11.2734375 C8.94921875 14.5078125 8.94921875 14.5078125 8.87890625 18.0234375 C8.86085937 19.19390625 8.8428125 20.364375 8.82421875 21.5703125 C8.80101562 22.46234375 8.7778125 23.354375 8.75390625 24.2734375 C4.79390625 24.2734375 0.83390625 24.2734375 -3.24609375 24.2734375 C-3.32859375 22.8709375 -3.41109375 21.4684375 -3.49609375 20.0234375 C-3.85974947 16.86013089 -4.14926646 15.42047154 -5.93359375 12.7109375 C-8.99476118 10.80804964 -10.701589 10.68268671 -14.24609375 11.2734375 C-16.74330199 12.6018971 -16.74330199 12.6018971 -18.24609375 15.2734375 C-18.86751236 18.3074225 -19.06322936 21.16474289 -19.24609375 24.2734375 C-23.20609375 24.2734375 -27.16609375 24.2734375 -31.24609375 24.2734375 C-32.16565531 10.86827339 -32.16565531 10.86827339 -27.671875 4.85546875 C-20.21832025 -3.41491392 -9.84160472 -5.60673239 0 0 Z " fill="#F3A006" transform="translate(59.24609375,27.7265625)"/>
|
||||||
|
<path d="M0 0 C0 3.96 0 7.92 0 12 C-1.4540625 12.680625 -1.4540625 12.680625 -2.9375 13.375 C-6.07079582 14.65786221 -6.07079582 14.65786221 -7 17 C-7.22467831 18.53668865 -7.40796805 20.07968054 -7.5625 21.625 C-7.706875 23.06875 -7.85125 24.5125 -8 26 C-11.96 26 -15.92 26 -20 26 C-20.91789085 12.61919109 -20.91789085 12.61919109 -16.4375 6.5859375 C-11.06401953 0.65758677 -7.616909 -0.12285337 0 0 Z " fill="#D58C05" transform="translate(48,26)"/>
|
||||||
|
<path d="M0 0 C0.75 1.6875 0.75 1.6875 1 4 C-1.81446105 8.64997913 -6.25914236 12.37140566 -11 15 C-14 14.875 -14 14.875 -16 14 C-16.80078125 11.796875 -16.80078125 11.796875 -17 9 C-14.98046875 6.578125 -14.98046875 6.578125 -12.1875 4.25 C-10.81658203 3.08210938 -10.81658203 3.08210938 -9.41796875 1.890625 C-5.71909434 -1.00154821 -4.50480579 -1.06692769 0 0 Z " fill="#DA9004" transform="translate(82,13)"/>
|
||||||
|
<path d="M0 0 C5.0875582 0.44824301 7.6553513 1.85561989 11.4375 5.25 C12.32308594 6.01828125 13.20867188 6.7865625 14.12109375 7.578125 C16 10 16 10 15.78515625 12.796875 C15.52605469 13.52390625 15.26695313 14.2509375 15 15 C12.859375 15.76171875 12.859375 15.76171875 10 16 C7.140625 14.26953125 7.140625 14.26953125 4.25 11.8125 C3.28578125 11.01457031 2.3215625 10.21664063 1.328125 9.39453125 C-1.92423854 6.04939896 -1.92423854 6.04939896 -2 3 C-1.34 2.01 -0.68 1.02 0 0 Z " fill="#DA8F04" transform="translate(15,12)"/>
|
||||||
|
<path d="M0 0 C3 0.125 3 0.125 4 1.125 C4.07325168 3.98767567 4.09238205 6.825719 4.0625 9.6875 C4.05798828 10.49380859 4.05347656 11.30011719 4.04882812 12.13085938 C4.03700518 14.12893756 4.01906914 16.12697783 4 18.125 C1.6875 19.25 1.6875 19.25 -1 20.125 C-1.99 19.465 -2.98 18.805 -4 18.125 C-4.24817926 15.19116664 -4.32643881 12.48867315 -4.25 9.5625 C-4.24484375 8.76908203 -4.2396875 7.97566406 -4.234375 7.15820312 C-4.13892499 0.17245521 -4.13892499 0.17245521 0 0 Z " fill="#F5A105" transform="translate(48,-0.125)"/>
|
||||||
|
<path d="M0 0 C0.79792969 0.00386719 1.59585938 0.00773437 2.41796875 0.01171875 C3.21589844 0.00785156 4.01382813 0.00398438 4.8359375 0 C10.79136108 0.01011108 10.79136108 0.01011108 11.91796875 1.13671875 C11.95877658 3.13630239 11.96051231 5.13717129 11.91796875 7.13671875 C8.88367198 8.65386714 5.74195208 8.28035791 2.41796875 8.26171875 C1.72832031 8.26558594 1.03867188 8.26945312 0.328125 8.2734375 C-4.82869832 8.26338521 -4.82869832 8.26338521 -7.08203125 7.13671875 C-7.12457481 5.13717129 -7.12283908 3.13630239 -7.08203125 1.13671875 C-5.19650595 -0.74880655 -2.50434294 0.00425186 0 0 Z " fill="#DA9104" transform="translate(81.08203125,38.86328125)"/>
|
||||||
|
<path d="M0 0 C0.79792969 0.00386719 1.59585937 0.00773437 2.41796875 0.01171875 C3.21589844 0.00785156 4.01382812 0.00398438 4.8359375 0 C10.79136108 0.01011108 10.79136108 0.01011108 11.91796875 1.13671875 C12.16796875 3.57421875 12.16796875 3.57421875 11.91796875 6.13671875 C8.21555593 8.60499397 6.25885765 8.37430901 1.85546875 8.32421875 C-0.01818359 8.31455078 -0.01818359 8.31455078 -1.9296875 8.3046875 C-5.08203125 8.13671875 -5.08203125 8.13671875 -7.08203125 7.13671875 C-7.12457481 5.13717129 -7.12283908 3.13630239 -7.08203125 1.13671875 C-5.19650595 -0.74880655 -2.50434294 0.00425186 0 0 Z " fill="#D98F04" transform="translate(10.08203125,38.86328125)"/>
|
||||||
|
<path d="M0 0 C0.66 0.33 1.32 0.66 2 1 C0.62661079 5.37336228 -1.26467582 7.51333672 -4.8125 10.375 C-6.09060547 11.43847656 -6.09060547 11.43847656 -7.39453125 12.5234375 C-10 14 -10 14 -12.82421875 13.7265625 C-13.90123047 13.36691406 -13.90123047 13.36691406 -15 13 C-10.43369266 8.12927217 -5.25023461 4.10843602 0 0 Z " fill="#F4A005" transform="translate(81,14)"/>
|
||||||
|
<path d="M0 0 C0.99 0 1.98 0 3 0 C3 6.6 3 13.2 3 20 C1.68 19.34 0.36 18.68 -1 18 C-1.02684312 15.18743719 -1.04676188 12.37512759 -1.0625 9.5625 C-1.07087891 8.76005859 -1.07925781 7.95761719 -1.08789062 7.13085938 C-1.0965143 5.08704779 -1.0522815 3.04316098 -1 1 C-0.67 0.67 -0.34 0.34 0 0 Z " fill="#D88E04" transform="translate(45,0)"/>
|
||||||
|
<path d="M0 0 C5.23589631 0.4222497 7.61710401 1.95695114 11.375 5.5625 C12.24898437 6.38878906 13.12296875 7.21507812 14.0234375 8.06640625 C14.67570312 8.70449219 15.32796875 9.34257812 16 10 C15.67 11.32 15.34 12.64 15 14 C9.66666667 10 4.33333333 6 -1 2 C-0.67 1.34 -0.34 0.68 0 0 Z " fill="#F4A005" transform="translate(15,12)"/>
|
||||||
|
<path d="M0 0 C5.61 0 11.22 0 17 0 C17.33 0.99 17.66 1.98 18 3 C11.73 3 5.46 3 -1 3 C-0.67 2.01 -0.34 1.02 0 0 Z " fill="#F4A005" transform="translate(75,39)"/>
|
||||||
|
<path d="M0 0 C5.61 0 11.22 0 17 0 C17.33 0.99 17.66 1.98 18 3 C11.73 3 5.46 3 -1 3 C-0.67 2.01 -0.34 1.02 0 0 Z " fill="#F4A005" transform="translate(4,39)"/>
|
||||||
|
<path d="M0 0 C0 5.61 0 11.22 0 17 C-2 16 -2 16 -3.0625 12.8125 C-3.371875 11.554375 -3.68125 10.29625 -4 9 C-4.268125 7.9275 -4.53625 6.855 -4.8125 5.75 C-5 3 -5 3 -3.6875 1.125 C-2 0 -2 0 0 0 Z " fill="#D6D7D9" transform="translate(48,66)"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 9.8 KiB |
10
ui/dist/imgs/providers/local.svg
vendored
Normal file
10
ui/dist/imgs/providers/local.svg
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#29a34a" stroke="#29a34a">
|
||||||
|
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||||
|
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|
||||||
|
<g id="SVGRepo_iconCarrier">
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
1
ui/dist/imgs/providers/zerossl.svg
vendored
Normal file
1
ui/dist/imgs/providers/zerossl.svg
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h500v500c-165 0-330 0-500 0 0-165 0-330 0-500z" fill="#4d70d5"/><path d="m0 0h86v85h85v85h85v86c-28.05 0-56.1 0-85 0v85c-28.05 0-56.1 0-85 0 0-28.05 0-56.1 0-85-28.05 0-56.1 0-85 0v85c-28.38 0-56.76 0-86 0 0-28.38 0-56.76 0-86h85c0-28.05 0-56.1 0-85-27.72 0-55.44 0-84 0 0-28.05 0-56.1 0-85h84c0-28.05 0-56.1 0-85z" fill="#fefefe" transform="translate(175 76)"/><path d="m0 0h1v84h85v1c-28.05 0-56.1 0-85 0v85c-28.38 0-56.76 0-86 0 0-28.38 0-56.76 0-86h85c0-27.72 0-55.44 0-84zm-84 85v84h84c0-27.72 0-55.44 0-84-27.72 0-55.44 0-84 0z" fill="#bcc9ef" transform="translate(175 247)"/></svg>
|
||||||
|
After Width: | Height: | Size: 666 B |
4
ui/dist/index.html
vendored
4
ui/dist/index.html
vendored
@@ -5,8 +5,8 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Certimate - Your Trusted SSL Automation Partner</title>
|
<title>Certimate - Your Trusted SSL Automation Partner</title>
|
||||||
<script type="module" crossorigin src="/assets/index-B-WSkv0U.js"></script>
|
<script type="module" crossorigin src="/assets/index-DpHAV802.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BLt37AhW.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DOft-CKV.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-background">
|
<body class="bg-background">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
327
ui/package-lock.json
generated
327
ui/package-lock.json
generated
@@ -27,6 +27,9 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"i18next": "^23.15.1",
|
||||||
|
"i18next-browser-languagedetector": "^8.0.0",
|
||||||
|
"i18next-http-backend": "^2.6.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"lucide-react": "^0.417.0",
|
"lucide-react": "^0.417.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
@@ -34,6 +37,7 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.52.1",
|
"react-hook-form": "^7.52.1",
|
||||||
|
"react-i18next": "^15.0.2",
|
||||||
"react-router-dom": "^6.25.1",
|
"react-router-dom": "^6.25.1",
|
||||||
"tailwind-merge": "^2.4.0",
|
"tailwind-merge": "^2.4.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
@@ -53,7 +57,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.4.7",
|
"eslint-plugin-react-refresh": "^0.4.7",
|
||||||
"postcss": "^8.4.40",
|
"postcss": "^8.4.40",
|
||||||
"tailwindcss": "^3.4.7",
|
"tailwindcss": "^3.4.7",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^5.3.4"
|
"vite": "^5.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -382,6 +386,17 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.25.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.25.6.tgz",
|
||||||
|
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.24.7",
|
"version": "7.24.7",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.24.7.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.24.7.tgz",
|
||||||
@@ -2100,9 +2115,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
|
||||||
"integrity": "sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==",
|
"integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -2113,9 +2128,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
|
||||||
"integrity": "sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==",
|
"integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2126,9 +2141,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
|
||||||
"integrity": "sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==",
|
"integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2139,9 +2154,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
|
||||||
"integrity": "sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==",
|
"integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2152,9 +2167,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
|
||||||
"integrity": "sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==",
|
"integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -2165,9 +2180,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
|
||||||
"integrity": "sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==",
|
"integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -2178,9 +2193,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
|
||||||
"integrity": "sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==",
|
"integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2191,9 +2206,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
|
||||||
"integrity": "sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==",
|
"integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2204,9 +2219,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
|
||||||
"integrity": "sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==",
|
"integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -2217,9 +2232,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
|
||||||
"integrity": "sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==",
|
"integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -2230,9 +2245,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
|
||||||
"integrity": "sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==",
|
"integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -2243,9 +2258,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
|
||||||
"integrity": "sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==",
|
"integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2256,9 +2271,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
|
||||||
"integrity": "sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==",
|
"integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2269,9 +2284,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
|
||||||
"integrity": "sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==",
|
"integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2282,9 +2297,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
|
||||||
"integrity": "sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==",
|
"integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -2295,9 +2310,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
|
||||||
"integrity": "sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==",
|
"integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2350,7 +2365,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -2959,6 +2974,14 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-fetch": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.6.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
@@ -3697,6 +3720,52 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-parse-stringify": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||||
|
"dependencies": {
|
||||||
|
"void-elements": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next": {
|
||||||
|
"version": "23.15.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/i18next/-/i18next-23.15.1.tgz",
|
||||||
|
"integrity": "sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://locize.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://locize.com/i18next.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next-browser-languagedetector": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next-http-backend": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/i18next-http-backend/-/i18next-http-backend-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-rCilMAnlEQNeKOZY1+x8wLM5IpYOj10guGvEpeC59tNjj6MMreLIjIW8D1RclhD3ifLwn6d/Y9HEM1RUE6DSog==",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-fetch": "4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz",
|
||||||
@@ -4032,9 +4101,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"braces": "^3.0.3",
|
"braces": "^3.0.3",
|
||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
@@ -4112,6 +4181,25 @@
|
|||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.18",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz",
|
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz",
|
||||||
@@ -4290,9 +4378,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||||
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
|
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
@@ -4327,9 +4415,9 @@
|
|||||||
"integrity": "sha512-WJHyaqdAt95JgZ1OCRD099+DST4IIG0M/jMrCckWYDSN/6ocp61qsz7m6h0xI0J2N79ScBljceEC0fFAaQrrAw=="
|
"integrity": "sha512-WJHyaqdAt95JgZ1OCRD099+DST4IIG0M/jMrCckWYDSN/6ocp61qsz7m6h0xI0J2N79ScBljceEC0fFAaQrrAw=="
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.40",
|
"version": "8.4.47",
|
||||||
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.40.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||||
"integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
|
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -4346,8 +4434,8 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
"picocolors": "^1.0.1",
|
"picocolors": "^1.1.0",
|
||||||
"source-map-js": "^1.2.0"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
@@ -4553,6 +4641,27 @@
|
|||||||
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-i18next": {
|
||||||
|
"version": "15.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.0.2.tgz",
|
||||||
|
"integrity": "sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.25.0",
|
||||||
|
"html-parse-stringify": "^3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"i18next": ">= 23.2.3",
|
||||||
|
"react": ">= 16.8.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.14.2",
|
"version": "0.14.2",
|
||||||
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.2.tgz",
|
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.2.tgz",
|
||||||
@@ -4692,6 +4801,11 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.14.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.8",
|
"version": "1.22.8",
|
||||||
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
|
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
|
||||||
@@ -4743,9 +4857,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.19.0",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
|
||||||
"integrity": "sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==",
|
"integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.5"
|
"@types/estree": "1.0.5"
|
||||||
@@ -4758,22 +4872,22 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.19.0",
|
"@rollup/rollup-android-arm-eabi": "4.22.4",
|
||||||
"@rollup/rollup-android-arm64": "4.19.0",
|
"@rollup/rollup-android-arm64": "4.22.4",
|
||||||
"@rollup/rollup-darwin-arm64": "4.19.0",
|
"@rollup/rollup-darwin-arm64": "4.22.4",
|
||||||
"@rollup/rollup-darwin-x64": "4.19.0",
|
"@rollup/rollup-darwin-x64": "4.22.4",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.19.0",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.19.0",
|
"@rollup/rollup-linux-arm-musleabihf": "4.22.4",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.19.0",
|
"@rollup/rollup-linux-arm64-gnu": "4.22.4",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.19.0",
|
"@rollup/rollup-linux-arm64-musl": "4.22.4",
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.19.0",
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.19.0",
|
"@rollup/rollup-linux-riscv64-gnu": "4.22.4",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.19.0",
|
"@rollup/rollup-linux-s390x-gnu": "4.22.4",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.19.0",
|
"@rollup/rollup-linux-x64-gnu": "4.22.4",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.19.0",
|
"@rollup/rollup-linux-x64-musl": "4.22.4",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.19.0",
|
"@rollup/rollup-win32-arm64-msvc": "4.22.4",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.19.0",
|
"@rollup/rollup-win32-ia32-msvc": "4.22.4",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.19.0",
|
"@rollup/rollup-win32-x64-msvc": "4.22.4",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4869,9 +4983,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -5140,6 +5254,11 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||||
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
|
||||||
@@ -5187,9 +5306,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.5.4",
|
"version": "5.6.2",
|
||||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -5303,14 +5422,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.3.5",
|
"version": "5.4.8",
|
||||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
|
||||||
"integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==",
|
"integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.39",
|
"postcss": "^8.4.43",
|
||||||
"rollup": "^4.13.0"
|
"rollup": "^4.20.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
@@ -5329,6 +5448,7 @@
|
|||||||
"less": "*",
|
"less": "*",
|
||||||
"lightningcss": "^1.21.0",
|
"lightningcss": "^1.21.0",
|
||||||
"sass": "*",
|
"sass": "*",
|
||||||
|
"sass-embedded": "*",
|
||||||
"stylus": "*",
|
"stylus": "*",
|
||||||
"sugarss": "*",
|
"sugarss": "*",
|
||||||
"terser": "^5.4.0"
|
"terser": "^5.4.0"
|
||||||
@@ -5346,6 +5466,9 @@
|
|||||||
"sass": {
|
"sass": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"stylus": {
|
"stylus": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
@@ -5357,6 +5480,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/void-elements": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite --host",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
@@ -40,7 +40,11 @@
|
|||||||
"tailwind-merge": "^2.4.0",
|
"tailwind-merge": "^2.4.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vaul": "^0.9.1",
|
"vaul": "^0.9.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8",
|
||||||
|
"i18next": "^23.15.1",
|
||||||
|
"i18next-browser-languagedetector": "^8.0.0",
|
||||||
|
"i18next-http-backend": "^2.6.1",
|
||||||
|
"react-i18next": "^15.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
@@ -55,7 +59,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.4.7",
|
"eslint-plugin-react-refresh": "^0.4.7",
|
||||||
"postcss": "^8.4.40",
|
"postcss": "^8.4.40",
|
||||||
"tailwindcss": "^3.4.7",
|
"tailwindcss": "^3.4.7",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^5.3.4"
|
"vite": "^5.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
ui/public/imgs/providers/huaweicloud.svg
Normal file
1
ui/public/imgs/providers/huaweicloud.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1728551595312" class="icon" viewBox="0 0 1027 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4252" width="200" height="200"><path d="M378.88 143.36c20.48-6.826667 40.96-10.24 61.44-13.653333 23.893333 30.72 30.72 71.68 40.96 109.226666 6.826667 40.96 13.653333 81.92 13.653333 122.88 6.826667 27.306667 3.413333 58.026667 3.413334 85.333334s-3.413333 51.2 0 78.506666c0 37.546667-3.413333 71.68-3.413334 109.226667-6.826667 23.893333 0 47.786667-6.826666 71.68-10.24-3.413333-13.653333-13.653333-17.066667-20.48-40.96-61.44-78.506667-122.88-112.64-187.733333-34.133333-68.266667-68.266667-136.533333-71.68-215.04-6.826667-61.44 34.133333-119.466667 92.16-139.946667z m211.626667-10.24c6.826667-3.413333 10.24 0 17.066666 0 27.306667 6.826667 58.026667 10.24 81.92 27.306667s44.373333 40.96 51.2 68.266666c10.24 34.133333 6.826667 71.68 0 105.813334-10.24 44.373333-30.72 85.333333-51.2 126.293333-6.826667 13.653333-13.653333 23.893333-20.48 37.546667-10.24 23.893333-27.306667 47.786667-40.96 71.68-23.893333 40.96-47.786667 75.093333-71.68 116.053333-3.413333 3.413333-6.826667 13.653333-13.653333 6.826667-3.413333-40.96-6.826667-81.92-10.24-126.293334-6.826667-54.613333-3.413333-105.813333-3.413333-160.426666 3.413333-34.133333 3.413333-71.68 6.826666-105.813334 6.826667-40.96 13.653333-85.333333 27.306667-126.293333 13.653333-10.24 13.653333-30.72 27.306667-40.96zM160.426667 266.24c3.413333 0 6.826667 6.826667 10.24 10.24 98.986667 129.706667 187.733333 266.24 259.413333 413.013333 6.826667 10.24 13.653333 23.893333 13.653333 37.546667-13.653333-3.413333-23.893333-10.24-34.133333-17.066667-64.853333-34.133333-129.706667-71.68-194.56-109.226666-23.893333-17.066667-47.786667-34.133333-68.266667-51.2-40.96-27.306667-68.266667-78.506667-64.853333-129.706667 3.413333-61.44 37.546667-112.64 78.506667-153.6z m706.56 0h6.826666c17.066667 23.893333 40.96 47.786667 54.613334 75.093333 13.653333 23.893333 20.48 54.613333 23.893333 81.92 0 30.72-10.24 64.853333-34.133333 88.746667-13.653333 13.653333-23.893333 27.306667-40.96 37.546667-78.506667 61.44-163.84 109.226667-252.586667 153.6-13.653333 6.826667-23.893333 17.066667-40.96 17.066666 3.413333-17.066667 13.653333-34.133333 20.48-47.786666 58.026667-119.466667 129.706667-232.106667 208.213333-341.333334 17.066667-17.066667 37.546667-40.96 54.613334-64.853333z m-856.746667 273.066667c3.413333-3.413333 0-10.24 6.826667-13.653334 10.24 3.413333 20.48 10.24 27.306666 13.653334 122.88 68.266667 245.76 136.533333 365.226667 211.626666 3.413333 3.413333 6.826667 6.826667 6.826667 10.24H180.906667c-47.786667 0-92.16-20.48-126.293334-54.613333-27.306667-30.72-51.2-71.68-54.613333-112.64 6.826667-17.066667 3.413333-34.133333 10.24-54.613333z m983.04-3.413334c6.826667-3.413333 17.066667-10.24 23.893333-6.826666 0 17.066667 6.826667 37.546667 6.826667 54.613333-3.413333 23.893333-3.413333 44.373333-13.653333 64.853333-6.826667 17.066667-17.066667 37.546667-30.72 51.2-17.066667 13.653333-27.306667 30.72-47.786667 40.96-20.48 17.066667-51.2 20.48-75.093333 23.893334h-245.76c3.413333-3.413333 3.413333-6.826667 6.826666-10.24 122.88-78.506667 249.173333-150.186667 375.466667-218.453334zM184.32 798.72c44.373333-3.413333 88.746667 0 133.12-6.826667 30.72 0 64.853333-3.413333 95.573333 0-6.826667 13.653333-23.893333 20.48-34.133333 27.306667-34.133333 23.893333-68.266667 44.373333-105.813333 61.44s-81.92 10.24-112.64-13.653333c-23.893333-17.066667-44.373333-44.373333-58.026667-68.266667h81.92z m433.493333-6.826667c30.72-3.413333 61.44 0 95.573334 0 40.96 3.413333 85.333333 0 129.706666 6.826667 30.72 3.413333 61.44 0 88.746667 3.413333-10.24 20.48-27.306667 40.96-44.373333 58.026667-27.306667 27.306667-68.266667 40.96-105.813334 34.133333-34.133333-10.24-61.44-30.72-92.16-47.786666-17.066667-10.24-30.72-20.48-47.786666-30.72-10.24-10.24-17.066667-13.653333-23.893334-23.893334z" fill="#C71F1E" p-id="4253"></path></svg>
|
||||||
18
ui/public/imgs/providers/letsencrypt.svg
Normal file
18
ui/public/imgs/providers/letsencrypt.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="96" height="96">
|
||||||
|
<path d="M0 0 C3.96616708 2.87966696 6.94216973 6.71174376 8.75390625 11.2734375 C8.94921875 14.2890625 8.94921875 14.2890625 8.87890625 17.5234375 C8.86085937 18.60109375 8.8428125 19.67875 8.82421875 20.7890625 C8.80101562 21.60890625 8.7778125 22.42875 8.75390625 23.2734375 C9.86765625 23.2321875 10.98140625 23.1909375 12.12890625 23.1484375 C15.75390625 23.2734375 15.75390625 23.2734375 17.75390625 25.2734375 C17.94178314 28.33535881 18.00936994 31.30529877 17.984375 34.3671875 C17.98391678 35.2696521 17.98345856 36.1721167 17.98298645 37.10192871 C17.97996265 39.01181349 17.97207256 40.92169577 17.9597168 42.83154297 C17.94148066 45.76148172 17.93920695 48.69110422 17.93945312 51.62109375 C17.93453722 53.47396108 17.92870858 55.32682625 17.921875 57.1796875 C17.92076218 58.05991577 17.91964935 58.94014404 17.91850281 59.8470459 C17.86996885 66.04131231 17.86996885 66.04131231 16.75390625 68.2734375 C-1.72609375 68.2734375 -20.20609375 68.2734375 -39.24609375 68.2734375 C-40.95558713 64.85445075 -40.40930624 60.94182137 -40.4140625 57.1796875 C-40.4173909 56.2772229 -40.4207193 55.3747583 -40.42414856 54.44494629 C-40.42921569 52.53508006 -40.43155695 50.62520497 -40.43139648 48.71533203 C-40.43358486 45.78533449 -40.45174793 42.85571222 -40.47070312 39.92578125 C-40.47363664 38.07291796 -40.47562172 36.22005287 -40.4765625 34.3671875 C-40.48374802 33.48695923 -40.49093353 32.60673096 -40.49833679 31.6998291 C-40.47491923 25.50226298 -40.47491923 25.50226298 -38.24609375 23.2734375 C-34.62109375 23.1484375 -34.62109375 23.1484375 -31.24609375 23.2734375 C-31.30410156 22.5 -31.36210937 21.7265625 -31.421875 20.9296875 C-31.73940921 13.59918343 -31.17057807 8.85901698 -26.24609375 3.2734375 C-18.78730361 -3.81241313 -9.01893001 -5.1380571 0 0 Z M-19.24609375 15.2734375 C-19.24609375 17.9134375 -19.24609375 20.5534375 -19.24609375 23.2734375 C-13.96609375 23.2734375 -8.68609375 23.2734375 -3.24609375 23.2734375 C-2.47540396 15.74874343 -2.47540396 15.74874343 -5.37109375 12.2734375 C-8.09392313 10.10082379 -8.09392313 10.10082379 -11.24609375 9.6484375 C-15.38792256 10.5113185 -16.80835846 11.83192885 -19.24609375 15.2734375 Z M-11.24609375 39.2734375 C-11.24609375 44.5534375 -11.24609375 49.8334375 -11.24609375 55.2734375 C-10.58609375 54.9434375 -9.92609375 54.6134375 -9.24609375 54.2734375 C-8.89430178 51.9428157 -8.56279196 49.60908683 -8.24609375 47.2734375 C-7.18359375 44.8359375 -7.18359375 44.8359375 -6.24609375 43.2734375 C-7.60933118 41.2334656 -7.60933118 41.2334656 -9.24609375 39.2734375 C-9.90609375 39.2734375 -10.56609375 39.2734375 -11.24609375 39.2734375 Z " fill="#10376E" transform="translate(59.24609375,27.7265625)"/>
|
||||||
|
<path d="M0 0 C1.12067871 0.01047363 2.24135742 0.02094727 3.39599609 0.03173828 C5.21357422 0.04140625 5.21357422 0.04140625 7.06787109 0.05126953 C8.33888672 0.06802734 9.60990234 0.08478516 10.91943359 0.10205078 C12.19818359 0.11107422 13.47693359 0.12009766 14.79443359 0.12939453 C17.96128526 0.15302775 21.12776783 0.18597449 24.29443359 0.22705078 C24.4024195 2.66573246 24.48169949 5.09964212 24.54443359 7.53955078 C24.59470703 8.57499023 24.59470703 8.57499023 24.64599609 9.63134766 C24.68505859 11.65283203 24.68505859 11.65283203 24.29443359 15.22705078 C22.27880859 17.20751953 22.27880859 17.20751953 20.29443359 18.22705078 C20.7362031 21.76120687 21.41676358 24.50596315 22.85693359 27.78955078 C24.29443359 32.22705078 24.29443359 32.22705078 24.29443359 45.22705078 C15.05443359 45.22705078 5.81443359 45.22705078 -3.70556641 45.22705078 C-5.41505978 41.80806403 -4.8687789 37.89543466 -4.87353516 34.13330078 C-4.87686356 33.23083618 -4.88019196 32.32837158 -4.88362122 31.39855957 C-4.88868835 29.48869334 -4.89102961 27.57881825 -4.89086914 25.66894531 C-4.89305752 22.73894777 -4.91122059 19.8093255 -4.93017578 16.87939453 C-4.9331093 15.02653124 -4.93509437 13.17366616 -4.93603516 11.32080078 C-4.94322067 10.44057251 -4.95040619 9.56034424 -4.95780945 8.65344238 C-4.92667455 0.41344589 -4.92667455 0.41344589 0 0 Z " fill="#0F3161" transform="translate(23.70556640625,50.77294921875)"/>
|
||||||
|
<path d="M0 0 C3.95863415 2.87419762 6.96698604 6.71040909 8.75390625 11.2734375 C8.94921875 14.5078125 8.94921875 14.5078125 8.87890625 18.0234375 C8.86085937 19.19390625 8.8428125 20.364375 8.82421875 21.5703125 C8.80101562 22.46234375 8.7778125 23.354375 8.75390625 24.2734375 C4.79390625 24.2734375 0.83390625 24.2734375 -3.24609375 24.2734375 C-3.32859375 22.8709375 -3.41109375 21.4684375 -3.49609375 20.0234375 C-3.85974947 16.86013089 -4.14926646 15.42047154 -5.93359375 12.7109375 C-8.99476118 10.80804964 -10.701589 10.68268671 -14.24609375 11.2734375 C-16.74330199 12.6018971 -16.74330199 12.6018971 -18.24609375 15.2734375 C-18.86751236 18.3074225 -19.06322936 21.16474289 -19.24609375 24.2734375 C-23.20609375 24.2734375 -27.16609375 24.2734375 -31.24609375 24.2734375 C-32.16565531 10.86827339 -32.16565531 10.86827339 -27.671875 4.85546875 C-20.21832025 -3.41491392 -9.84160472 -5.60673239 0 0 Z " fill="#F3A006" transform="translate(59.24609375,27.7265625)"/>
|
||||||
|
<path d="M0 0 C0 3.96 0 7.92 0 12 C-1.4540625 12.680625 -1.4540625 12.680625 -2.9375 13.375 C-6.07079582 14.65786221 -6.07079582 14.65786221 -7 17 C-7.22467831 18.53668865 -7.40796805 20.07968054 -7.5625 21.625 C-7.706875 23.06875 -7.85125 24.5125 -8 26 C-11.96 26 -15.92 26 -20 26 C-20.91789085 12.61919109 -20.91789085 12.61919109 -16.4375 6.5859375 C-11.06401953 0.65758677 -7.616909 -0.12285337 0 0 Z " fill="#D58C05" transform="translate(48,26)"/>
|
||||||
|
<path d="M0 0 C0.75 1.6875 0.75 1.6875 1 4 C-1.81446105 8.64997913 -6.25914236 12.37140566 -11 15 C-14 14.875 -14 14.875 -16 14 C-16.80078125 11.796875 -16.80078125 11.796875 -17 9 C-14.98046875 6.578125 -14.98046875 6.578125 -12.1875 4.25 C-10.81658203 3.08210938 -10.81658203 3.08210938 -9.41796875 1.890625 C-5.71909434 -1.00154821 -4.50480579 -1.06692769 0 0 Z " fill="#DA9004" transform="translate(82,13)"/>
|
||||||
|
<path d="M0 0 C5.0875582 0.44824301 7.6553513 1.85561989 11.4375 5.25 C12.32308594 6.01828125 13.20867188 6.7865625 14.12109375 7.578125 C16 10 16 10 15.78515625 12.796875 C15.52605469 13.52390625 15.26695313 14.2509375 15 15 C12.859375 15.76171875 12.859375 15.76171875 10 16 C7.140625 14.26953125 7.140625 14.26953125 4.25 11.8125 C3.28578125 11.01457031 2.3215625 10.21664063 1.328125 9.39453125 C-1.92423854 6.04939896 -1.92423854 6.04939896 -2 3 C-1.34 2.01 -0.68 1.02 0 0 Z " fill="#DA8F04" transform="translate(15,12)"/>
|
||||||
|
<path d="M0 0 C3 0.125 3 0.125 4 1.125 C4.07325168 3.98767567 4.09238205 6.825719 4.0625 9.6875 C4.05798828 10.49380859 4.05347656 11.30011719 4.04882812 12.13085938 C4.03700518 14.12893756 4.01906914 16.12697783 4 18.125 C1.6875 19.25 1.6875 19.25 -1 20.125 C-1.99 19.465 -2.98 18.805 -4 18.125 C-4.24817926 15.19116664 -4.32643881 12.48867315 -4.25 9.5625 C-4.24484375 8.76908203 -4.2396875 7.97566406 -4.234375 7.15820312 C-4.13892499 0.17245521 -4.13892499 0.17245521 0 0 Z " fill="#F5A105" transform="translate(48,-0.125)"/>
|
||||||
|
<path d="M0 0 C0.79792969 0.00386719 1.59585938 0.00773437 2.41796875 0.01171875 C3.21589844 0.00785156 4.01382813 0.00398438 4.8359375 0 C10.79136108 0.01011108 10.79136108 0.01011108 11.91796875 1.13671875 C11.95877658 3.13630239 11.96051231 5.13717129 11.91796875 7.13671875 C8.88367198 8.65386714 5.74195208 8.28035791 2.41796875 8.26171875 C1.72832031 8.26558594 1.03867188 8.26945312 0.328125 8.2734375 C-4.82869832 8.26338521 -4.82869832 8.26338521 -7.08203125 7.13671875 C-7.12457481 5.13717129 -7.12283908 3.13630239 -7.08203125 1.13671875 C-5.19650595 -0.74880655 -2.50434294 0.00425186 0 0 Z " fill="#DA9104" transform="translate(81.08203125,38.86328125)"/>
|
||||||
|
<path d="M0 0 C0.79792969 0.00386719 1.59585937 0.00773437 2.41796875 0.01171875 C3.21589844 0.00785156 4.01382812 0.00398438 4.8359375 0 C10.79136108 0.01011108 10.79136108 0.01011108 11.91796875 1.13671875 C12.16796875 3.57421875 12.16796875 3.57421875 11.91796875 6.13671875 C8.21555593 8.60499397 6.25885765 8.37430901 1.85546875 8.32421875 C-0.01818359 8.31455078 -0.01818359 8.31455078 -1.9296875 8.3046875 C-5.08203125 8.13671875 -5.08203125 8.13671875 -7.08203125 7.13671875 C-7.12457481 5.13717129 -7.12283908 3.13630239 -7.08203125 1.13671875 C-5.19650595 -0.74880655 -2.50434294 0.00425186 0 0 Z " fill="#D98F04" transform="translate(10.08203125,38.86328125)"/>
|
||||||
|
<path d="M0 0 C0.66 0.33 1.32 0.66 2 1 C0.62661079 5.37336228 -1.26467582 7.51333672 -4.8125 10.375 C-6.09060547 11.43847656 -6.09060547 11.43847656 -7.39453125 12.5234375 C-10 14 -10 14 -12.82421875 13.7265625 C-13.90123047 13.36691406 -13.90123047 13.36691406 -15 13 C-10.43369266 8.12927217 -5.25023461 4.10843602 0 0 Z " fill="#F4A005" transform="translate(81,14)"/>
|
||||||
|
<path d="M0 0 C0.99 0 1.98 0 3 0 C3 6.6 3 13.2 3 20 C1.68 19.34 0.36 18.68 -1 18 C-1.02684312 15.18743719 -1.04676188 12.37512759 -1.0625 9.5625 C-1.07087891 8.76005859 -1.07925781 7.95761719 -1.08789062 7.13085938 C-1.0965143 5.08704779 -1.0522815 3.04316098 -1 1 C-0.67 0.67 -0.34 0.34 0 0 Z " fill="#D88E04" transform="translate(45,0)"/>
|
||||||
|
<path d="M0 0 C5.23589631 0.4222497 7.61710401 1.95695114 11.375 5.5625 C12.24898437 6.38878906 13.12296875 7.21507812 14.0234375 8.06640625 C14.67570312 8.70449219 15.32796875 9.34257812 16 10 C15.67 11.32 15.34 12.64 15 14 C9.66666667 10 4.33333333 6 -1 2 C-0.67 1.34 -0.34 0.68 0 0 Z " fill="#F4A005" transform="translate(15,12)"/>
|
||||||
|
<path d="M0 0 C5.61 0 11.22 0 17 0 C17.33 0.99 17.66 1.98 18 3 C11.73 3 5.46 3 -1 3 C-0.67 2.01 -0.34 1.02 0 0 Z " fill="#F4A005" transform="translate(75,39)"/>
|
||||||
|
<path d="M0 0 C5.61 0 11.22 0 17 0 C17.33 0.99 17.66 1.98 18 3 C11.73 3 5.46 3 -1 3 C-0.67 2.01 -0.34 1.02 0 0 Z " fill="#F4A005" transform="translate(4,39)"/>
|
||||||
|
<path d="M0 0 C0 5.61 0 11.22 0 17 C-2 16 -2 16 -3.0625 12.8125 C-3.371875 11.554375 -3.68125 10.29625 -4 9 C-4.268125 7.9275 -4.53625 6.855 -4.8125 5.75 C-5 3 -5 3 -3.6875 1.125 C-2 0 -2 0 0 0 Z " fill="#D6D7D9" transform="translate(48,66)"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 9.8 KiB |
10
ui/public/imgs/providers/local.svg
Normal file
10
ui/public/imgs/providers/local.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#29a34a" stroke="#29a34a">
|
||||||
|
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||||
|
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|
||||||
|
<g id="SVGRepo_iconCarrier">
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
1
ui/public/imgs/providers/zerossl.svg
Normal file
1
ui/public/imgs/providers/zerossl.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h500v500c-165 0-330 0-500 0 0-165 0-330 0-500z" fill="#4d70d5"/><path d="m0 0h86v85h85v85h85v86c-28.05 0-56.1 0-85 0v85c-28.05 0-56.1 0-85 0 0-28.05 0-56.1 0-85-28.05 0-56.1 0-85 0v85c-28.38 0-56.76 0-86 0 0-28.38 0-56.76 0-86h85c0-28.05 0-56.1 0-85-27.72 0-55.44 0-84 0 0-28.05 0-56.1 0-85h84c0-28.05 0-56.1 0-85z" fill="#fefefe" transform="translate(175 76)"/><path d="m0 0h1v84h85v1c-28.05 0-56.1 0-85 0v85c-28.38 0-56.76 0-86 0 0-28.38 0-56.76 0-86h85c0-27.72 0-55.44 0-84zm-84 85v84h84c0-27.72 0-55.44 0-84-27.72 0-55.44 0-84 0z" fill="#bcc9ef" transform="translate(175 247)"/></svg>
|
||||||
|
After Width: | Height: | Size: 666 B |
33
ui/src/components/LocaleToggle.tsx
Normal file
33
ui/src/components/LocaleToggle.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Languages } from "lucide-react";
|
||||||
|
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
|
export default function LocaleToggle() {
|
||||||
|
const { i18n } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon">
|
||||||
|
<Languages className="h-[1.2rem] w-[1.2rem] dark:text-white" />
|
||||||
|
<span className="sr-only">Toggle theme</span>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
{Object.keys(i18n.store.data).map(key => (
|
||||||
|
<DropdownMenuItem onClick={() => i18n.changeLanguage(key)}>
|
||||||
|
{i18n.store.data[key].name as string}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Moon, Sun } from "lucide-react";
|
import { Moon, Sun } from "lucide-react";
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -11,6 +12,8 @@ import { useTheme } from "./ThemeProvider";
|
|||||||
|
|
||||||
export function ThemeToggle() {
|
export function ThemeToggle() {
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -23,13 +26,13 @@ export function ThemeToggle() {
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => setTheme("light")}>
|
<DropdownMenuItem onClick={() => setTheme("light")}>
|
||||||
浅色
|
{t('theme.light')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
||||||
暗黑
|
{t('theme.dark')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setTheme("system")}>
|
<DropdownMenuItem onClick={() => setTheme("system")}>
|
||||||
系统
|
{t('theme.system')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
@@ -23,18 +24,21 @@ import { PbErrorData } from "@/domain/base";
|
|||||||
|
|
||||||
const AccessAliyunForm = ({
|
const AccessAliyunForm = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { addAccess, updateAccess } = useConfig();
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
accessKeyId: z.string().min(1).max(64),
|
accessKeyId: z.string().min(1, 'access.form.access.key.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
accessSecretId: z.string().min(1).max(64),
|
accessSecretId: z.string().min(1, 'access.form.access.key.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: AliyunConfig = {
|
let config: AliyunConfig = {
|
||||||
@@ -47,7 +51,7 @@ const AccessAliyunForm = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "aliyun",
|
configType: "aliyun",
|
||||||
accessKeyId: config.accessKeyId,
|
accessKeyId: config.accessKeyId,
|
||||||
accessSecretId: config.accessKeySecret,
|
accessSecretId: config.accessKeySecret,
|
||||||
@@ -67,6 +71,7 @@ const AccessAliyunForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -74,10 +79,11 @@ const AccessAliyunForm = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log(req);
|
||||||
addAccess(req);
|
addAccess(req);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = e as ClientResponseError;
|
const err = e as ClientResponseError;
|
||||||
@@ -111,9 +117,9 @@ const AccessAliyunForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -126,7 +132,7 @@ const AccessAliyunForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -141,7 +147,7 @@ const AccessAliyunForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -156,9 +162,9 @@ const AccessAliyunForm = ({
|
|||||||
name="accessKeyId"
|
name="accessKeyId"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>AccessKeyId</FormLabel>
|
<FormLabel>{t('access.form.access.key.id')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入AccessKeyId" {...field} />
|
<Input placeholder={t('access.form.access.key.id.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -171,9 +177,9 @@ const AccessAliyunForm = ({
|
|||||||
name="accessSecretId"
|
name="accessSecretId"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>AccessKeySecret</FormLabel>
|
<FormLabel>{t('access.form.access.key.secret')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入AccessKeySecret" {...field} />
|
<Input placeholder={t('access.form.access.key.secret.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -184,7 +190,7 @@ const AccessAliyunForm = ({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
@@ -22,17 +23,20 @@ import { PbErrorData } from "@/domain/base";
|
|||||||
|
|
||||||
const AccessCloudflareForm = ({
|
const AccessCloudflareForm = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { addAccess, updateAccess } = useConfig();
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
dnsApiToken: z.string().min(1).max(64),
|
dnsApiToken: z.string().min(1, 'access.form.cloud.dns.api.token.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: CloudflareConfig = {
|
let config: CloudflareConfig = {
|
||||||
@@ -44,7 +48,7 @@ const AccessCloudflareForm = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "cloudflare",
|
configType: "cloudflare",
|
||||||
dnsApiToken: config.dnsApiToken,
|
dnsApiToken: config.dnsApiToken,
|
||||||
},
|
},
|
||||||
@@ -63,6 +67,7 @@ const AccessCloudflareForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -70,7 +75,7 @@ const AccessCloudflareForm = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,9 +111,9 @@ const AccessCloudflareForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -121,7 +126,7 @@ const AccessCloudflareForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -136,7 +141,7 @@ const AccessCloudflareForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -151,9 +156,9 @@ const AccessCloudflareForm = ({
|
|||||||
name="dnsApiToken"
|
name="dnsApiToken"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>CLOUD_DNS_API_TOKEN</FormLabel>
|
<FormLabel>{t('access.form.cloud.dns.api.token')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入CLOUD_DNS_API_TOKEN" {...field} />
|
<Input placeholder={t('access.form.cloud.dns.api.token.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -162,7 +167,7 @@ const AccessCloudflareForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -8,17 +8,12 @@ import {
|
|||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import AccessTencentForm from "./AccessTencentForm";
|
|
||||||
|
|
||||||
import { Label } from "../ui/label";
|
|
||||||
|
|
||||||
import { Access, accessTypeMap } from "@/domain/access";
|
import { Access, accessTypeMap } from "@/domain/access";
|
||||||
import AccessAliyunForm from "./AccessAliyunForm";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
import AccessSSHForm from "./AccessSSHForm";
|
import { Label } from "../ui/label";
|
||||||
import WebhookForm from "./AccessWebhookFrom";
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -28,13 +23,19 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "../ui/select";
|
} from "../ui/select";
|
||||||
import AccessCloudflareForm from "./AccessCloudflareForm";
|
import AccessAliyunForm from "./AccessAliyunForm";
|
||||||
|
import AccessTencentForm from "./AccessTencentForm";
|
||||||
|
import AccessHuaweicloudForm from "./AccessHuaweicloudForm";
|
||||||
import AccessQiniuForm from "./AccessQiniuForm";
|
import AccessQiniuForm from "./AccessQiniuForm";
|
||||||
|
import AccessCloudflareForm from "./AccessCloudflareForm";
|
||||||
import AccessNamesiloForm from "./AccessNamesiloForm";
|
import AccessNamesiloForm from "./AccessNamesiloForm";
|
||||||
import AccessGodaddyFrom from "./AccessGodaddyForm";
|
import AccessGodaddyFrom from "./AccessGodaddyForm";
|
||||||
|
import AccessLocalForm from "./AccessLocalForm";
|
||||||
|
import AccessSSHForm from "./AccessSSHForm";
|
||||||
|
import AccessWebhookForm from "./AccessWebhookFrom";
|
||||||
|
|
||||||
type TargetConfigEditProps = {
|
type TargetConfigEditProps = {
|
||||||
op: "add" | "edit";
|
op: "add" | "edit" | "copy";
|
||||||
className?: string;
|
className?: string;
|
||||||
trigger: React.ReactNode;
|
trigger: React.ReactNode;
|
||||||
data?: Access;
|
data?: Access;
|
||||||
@@ -46,6 +47,7 @@ export function AccessEdit({
|
|||||||
className,
|
className,
|
||||||
}: TargetConfigEditProps) {
|
}: TargetConfigEditProps) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const typeKeys = Array.from(accessTypeMap.keys());
|
const typeKeys = Array.from(accessTypeMap.keys());
|
||||||
|
|
||||||
@@ -53,50 +55,33 @@ export function AccessEdit({
|
|||||||
|
|
||||||
let form = <> </>;
|
let form = <> </>;
|
||||||
switch (configType) {
|
switch (configType) {
|
||||||
case "tencent":
|
|
||||||
form = (
|
|
||||||
<AccessTencentForm
|
|
||||||
data={data}
|
|
||||||
onAfterReq={() => {
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "aliyun":
|
case "aliyun":
|
||||||
form = (
|
form = (
|
||||||
<AccessAliyunForm
|
<AccessAliyunForm
|
||||||
data={data}
|
data={data}
|
||||||
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "ssh":
|
case "tencent":
|
||||||
form = (
|
form = (
|
||||||
<AccessSSHForm
|
<AccessTencentForm
|
||||||
data={data}
|
data={data}
|
||||||
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "webhook":
|
case "huaweicloud":
|
||||||
form = (
|
form = (
|
||||||
<WebhookForm
|
<AccessHuaweicloudForm
|
||||||
data={data}
|
|
||||||
onAfterReq={() => {
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "cloudflare":
|
|
||||||
form = (
|
|
||||||
<AccessCloudflareForm
|
|
||||||
data={data}
|
data={data}
|
||||||
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
@@ -107,6 +92,18 @@ export function AccessEdit({
|
|||||||
form = (
|
form = (
|
||||||
<AccessQiniuForm
|
<AccessQiniuForm
|
||||||
data={data}
|
data={data}
|
||||||
|
op={op}
|
||||||
|
onAfterReq={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "cloudflare":
|
||||||
|
form = (
|
||||||
|
<AccessCloudflareForm
|
||||||
|
data={data}
|
||||||
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
@@ -117,6 +114,7 @@ export function AccessEdit({
|
|||||||
form = (
|
form = (
|
||||||
<AccessNamesiloForm
|
<AccessNamesiloForm
|
||||||
data={data}
|
data={data}
|
||||||
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
@@ -127,6 +125,40 @@ export function AccessEdit({
|
|||||||
form = (
|
form = (
|
||||||
<AccessGodaddyFrom
|
<AccessGodaddyFrom
|
||||||
data={data}
|
data={data}
|
||||||
|
op={op}
|
||||||
|
onAfterReq={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "local":
|
||||||
|
form = (
|
||||||
|
<AccessLocalForm
|
||||||
|
data={data}
|
||||||
|
op={op}
|
||||||
|
onAfterReq={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "ssh":
|
||||||
|
form = (
|
||||||
|
<AccessSSHForm
|
||||||
|
data={data}
|
||||||
|
op={op}
|
||||||
|
onAfterReq={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "webhook":
|
||||||
|
form = (
|
||||||
|
<AccessWebhookForm
|
||||||
|
data={data}
|
||||||
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
@@ -146,25 +178,30 @@ export function AccessEdit({
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{op == "add" ? "添加" : "编辑"}授权</DialogTitle>
|
<DialogTitle>
|
||||||
|
{op == "add"
|
||||||
|
? t("access.add")
|
||||||
|
: op == "edit"
|
||||||
|
? t("access.edit")
|
||||||
|
: t("access.copy")}
|
||||||
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<ScrollArea className="max-h-[80vh]">
|
<ScrollArea className="max-h-[80vh]">
|
||||||
<div className="container py-3">
|
<div className="container py-3">
|
||||||
<Label>服务商</Label>
|
<Label>{t("access.type")}</Label>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(val) => {
|
onValueChange={(val) => {
|
||||||
console.log(val);
|
|
||||||
setConfigType(val);
|
setConfigType(val);
|
||||||
}}
|
}}
|
||||||
defaultValue={configType}
|
defaultValue={configType}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="mt-3">
|
<SelectTrigger className="mt-3">
|
||||||
<SelectValue placeholder="请选择服务商" />
|
<SelectValue placeholder={t("access.type.not.empty")} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>服务商</SelectLabel>
|
<SelectLabel>{t("access.type")}</SelectLabel>
|
||||||
{typeKeys.map((key) => (
|
{typeKeys.map((key) => (
|
||||||
<SelectItem value={key} key={key}>
|
<SelectItem value={key} key={key}>
|
||||||
<div
|
<div
|
||||||
@@ -177,7 +214,7 @@ export function AccessEdit({
|
|||||||
src={accessTypeMap.get(key)?.[1]}
|
src={accessTypeMap.get(key)?.[1]}
|
||||||
className="h-6 w-6"
|
className="h-6 w-6"
|
||||||
/>
|
/>
|
||||||
<div>{accessTypeMap.get(key)?.[0]}</div>
|
<div>{t(accessTypeMap.get(key)?.[0] || "")}</div>
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
@@ -27,18 +28,21 @@ import { PbErrorData } from "@/domain/base";
|
|||||||
|
|
||||||
const AccessGodaddyFrom = ({
|
const AccessGodaddyFrom = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { addAccess, updateAccess } = useConfig();
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
apiKey: z.string().min(1).max(64),
|
apiKey: z.string().min(1, 'access.form.go.daddy.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
apiSecret: z.string().min(1).max(64),
|
apiSecret: z.string().min(1, 'access.form.go.daddy.api.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: GodaddyConfig = {
|
let config: GodaddyConfig = {
|
||||||
@@ -51,7 +55,7 @@ const AccessGodaddyFrom = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "godaddy",
|
configType: "godaddy",
|
||||||
apiKey: config.apiKey,
|
apiKey: config.apiKey,
|
||||||
apiSecret: config.apiSecret,
|
apiSecret: config.apiSecret,
|
||||||
@@ -72,6 +76,7 @@ const AccessGodaddyFrom = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -79,7 +84,7 @@ const AccessGodaddyFrom = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -115,9 +120,9 @@ const AccessGodaddyFrom = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -130,7 +135,7 @@ const AccessGodaddyFrom = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -145,7 +150,7 @@ const AccessGodaddyFrom = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -160,9 +165,9 @@ const AccessGodaddyFrom = ({
|
|||||||
name="apiKey"
|
name="apiKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>GODADDY_API_KEY</FormLabel>
|
<FormLabel>{t('access.form.go.daddy.api.key')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入GODADDY_API_KEY" {...field} />
|
<Input placeholder={t('access.form.go.daddy.api.key.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -175,9 +180,9 @@ const AccessGodaddyFrom = ({
|
|||||||
name="apiSecret"
|
name="apiSecret"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>GODADDY_API_SECRET</FormLabel>
|
<FormLabel>{t('access.form.go.daddy.api.secret')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入GODADDY_API_SECRET" {...field} />
|
<Input placeholder={t('access.form.go.daddy.api.secret.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -186,7 +191,7 @@ const AccessGodaddyFrom = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { update } from "@/repository/access_group";
|
|||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
import { PbErrorData } from "@/domain/base";
|
import { PbErrorData } from "@/domain/base";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type AccessGroupEditProps = {
|
type AccessGroupEditProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -35,9 +36,10 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
|||||||
const { reloadAccessGroups } = useConfig();
|
const { reloadAccessGroups } = useConfig();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.group.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
@@ -78,14 +80,13 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>添加分组</DialogTitle>
|
<DialogTitle>{t('access.group.add')}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="container py-3">
|
<div className="container py-3">
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
console.log(e);
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
form.handleSubmit(onSubmit)(e);
|
form.handleSubmit(onSubmit)(e);
|
||||||
}}
|
}}
|
||||||
@@ -96,9 +97,9 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>组名</FormLabel>
|
<FormLabel>{t('access.group.name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入组名" {...field} type="text" />
|
<Input placeholder={t('access.group.name.not.empty')} {...field} type="text" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -107,7 +108,7 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { Group } from "lucide-react";
|
|||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const AccessGroupList = () => {
|
const AccessGroupList = () => {
|
||||||
const {
|
const {
|
||||||
@@ -39,8 +40,7 @@ const AccessGroupList = () => {
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
const handleRemoveClick = async (id: string) => {
|
const handleRemoveClick = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -48,7 +48,7 @@ const AccessGroupList = () => {
|
|||||||
reloadAccessGroups();
|
reloadAccessGroups();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({
|
toast({
|
||||||
title: "删除失败",
|
title: t('delete.failed'),
|
||||||
description: getErrMessage(e),
|
description: getErrMessage(e),
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
@@ -69,10 +69,10 @@ const AccessGroupList = () => {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||||
请添加域名开始部署证书吧。
|
{t('access.group.domain.empty')}
|
||||||
</div>
|
</div>
|
||||||
<AccessGroupEdit
|
<AccessGroupEdit
|
||||||
trigger={<Button>新增授权组</Button>}
|
trigger={<Button>{t('access.group.add')}</Button>}
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,9 +86,7 @@ const AccessGroupList = () => {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{accessGroup.name}</CardTitle>
|
<CardTitle>{accessGroup.name}</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
共有
|
{t('access.group.total', { total: accessGroup.expand ? accessGroup.expand.access.length : 0 })}
|
||||||
{accessGroup.expand ? accessGroup.expand.access.length : 0}
|
|
||||||
个部署授权配置
|
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="min-h-[180px]">
|
<CardContent className="min-h-[180px]">
|
||||||
@@ -123,7 +121,7 @@ const AccessGroupList = () => {
|
|||||||
<Group size={40} />
|
<Group size={40} />
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2">
|
<div className="ml-2">
|
||||||
暂无部署授权配置,请添加后开始使用吧
|
{t('access.group.empty')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -151,7 +149,7 @@ const AccessGroupList = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
所有授权
|
{t('access.all')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -159,14 +157,14 @@ const AccessGroupList = () => {
|
|||||||
<Show
|
<Show
|
||||||
when={
|
when={
|
||||||
!accessGroup.expand ||
|
!accessGroup.expand ||
|
||||||
accessGroup.expand.access.length == 0
|
accessGroup.expand.access.length == 0
|
||||||
? true
|
? true
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Button size="sm" onClick={handleAddAccess}>
|
<Button size="sm" onClick={handleAddAccess}>
|
||||||
新增授权
|
{t('access.add')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -175,21 +173,21 @@ const AccessGroupList = () => {
|
|||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button variant={"destructive"} size={"sm"}>
|
<Button variant={"destructive"} size={"sm"}>
|
||||||
删除
|
{t('delete')}
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</AlertDialogTrigger>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle className="dark:text-gray-200">
|
<AlertDialogTitle className="dark:text-gray-200">
|
||||||
删除组
|
{t('access.group.delete')}
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
确定要删除部署授权组吗?
|
{t('access.group.delete.confirm')}
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel className="dark:text-gray-200">
|
<AlertDialogCancel className="dark:text-gray-200">
|
||||||
取消
|
{t('cancel')}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -198,7 +196,7 @@ const AccessGroupList = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
确认
|
{t('confirm')}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
|
|||||||
221
ui/src/components/certimate/AccessHuaweicloudForm.tsx
Normal file
221
ui/src/components/certimate/AccessHuaweicloudForm.tsx
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
|
import z from "zod";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import { Access, accessFormType, HuaweicloudConfig, getUsageByConfigType } from "@/domain/access";
|
||||||
|
import { save } from "@/repository/access";
|
||||||
|
import { useConfig } from "@/providers/config";
|
||||||
|
|
||||||
|
import { ClientResponseError } from "pocketbase";
|
||||||
|
import { PbErrorData } from "@/domain/base";
|
||||||
|
|
||||||
|
const AccessHuaweicloudForm = ({
|
||||||
|
data,
|
||||||
|
op,
|
||||||
|
onAfterReq,
|
||||||
|
}: {
|
||||||
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
|
onAfterReq: () => void;
|
||||||
|
}) => {
|
||||||
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const formSchema = z.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
|
configType: accessFormType,
|
||||||
|
region: z.string().min(1, 'access.form.region.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
|
accessKeyId: z.string().min(1, 'access.form.access.key.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
|
secretAccessKey: z.string().min(1, 'access.form.access.key.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
|
});
|
||||||
|
|
||||||
|
let config: HuaweicloudConfig = {
|
||||||
|
region: "cn-north-1",
|
||||||
|
accessKeyId: "",
|
||||||
|
secretAccessKey: "",
|
||||||
|
};
|
||||||
|
if (data) config = data.config as HuaweicloudConfig;
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
id: data?.id,
|
||||||
|
name: data?.name || '',
|
||||||
|
configType: "huaweicloud",
|
||||||
|
region: config.region,
|
||||||
|
accessKeyId: config.accessKeyId,
|
||||||
|
secretAccessKey: config.secretAccessKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
|
const req: Access = {
|
||||||
|
id: data.id as string,
|
||||||
|
name: data.name,
|
||||||
|
configType: data.configType,
|
||||||
|
usage: getUsageByConfigType(data.configType),
|
||||||
|
config: {
|
||||||
|
region: data.region,
|
||||||
|
accessKeyId: data.accessKeyId,
|
||||||
|
secretAccessKey: data.secretAccessKey,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
|
const rs = await save(req);
|
||||||
|
|
||||||
|
onAfterReq();
|
||||||
|
|
||||||
|
req.id = rs.id;
|
||||||
|
req.created = rs.created;
|
||||||
|
req.updated = rs.updated;
|
||||||
|
if (data.id && op == "edit") {
|
||||||
|
updateAccess(req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(req);
|
||||||
|
addAccess(req);
|
||||||
|
} catch (e) {
|
||||||
|
const err = e as ClientResponseError;
|
||||||
|
|
||||||
|
Object.entries(err.response.data as PbErrorData).forEach(
|
||||||
|
([key, value]) => {
|
||||||
|
form.setError(key as keyof z.infer<typeof formSchema>, {
|
||||||
|
type: "manual",
|
||||||
|
message: value.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="max-w-[35em] mx-auto mt-10">
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
form.handleSubmit(onSubmit)(e);
|
||||||
|
}}
|
||||||
|
className="space-y-8"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="id"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="hidden">
|
||||||
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="configType"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="hidden">
|
||||||
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="region"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('access.form.region')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t('access.form.region.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="accessKeyId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('access.form.access.key.id')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t('access.form.access.key.id.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="secretAccessKey"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('access.form.access.key.secret')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t('access.form.access.key.secret.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit">{t('save')}</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessHuaweicloudForm;
|
||||||
228
ui/src/components/certimate/AccessLocalForm.tsx
Normal file
228
ui/src/components/certimate/AccessLocalForm.tsx
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import {
|
||||||
|
Access,
|
||||||
|
accessFormType,
|
||||||
|
getUsageByConfigType,
|
||||||
|
LocalConfig,
|
||||||
|
SSHConfig,
|
||||||
|
} from "@/domain/access";
|
||||||
|
import { useConfig } from "@/providers/config";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "../ui/form";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { Textarea } from "../ui/textarea";
|
||||||
|
import { save } from "@/repository/access";
|
||||||
|
import { ClientResponseError } from "pocketbase";
|
||||||
|
import { PbErrorData } from "@/domain/base";
|
||||||
|
|
||||||
|
const AccessLocalForm = ({
|
||||||
|
data,
|
||||||
|
op,
|
||||||
|
onAfterReq,
|
||||||
|
}: {
|
||||||
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
|
onAfterReq: () => void;
|
||||||
|
}) => {
|
||||||
|
const { addAccess, updateAccess, reloadAccessGroups } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
|
configType: accessFormType,
|
||||||
|
|
||||||
|
command: z.string().min(1, 'access.form.ssh.command.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||||
|
certPath: z.string().min(0, 'access.form.ssh.cert.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||||
|
keyPath: z.string().min(0, 'access.form.ssh.key.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||||
|
});
|
||||||
|
|
||||||
|
let config: LocalConfig = {
|
||||||
|
command: "sudo service nginx restart",
|
||||||
|
certPath: "/etc/nginx/ssl/certificate.crt",
|
||||||
|
keyPath: "/etc/nginx/ssl/private.key",
|
||||||
|
};
|
||||||
|
if (data) config = data.config as SSHConfig;
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
id: data?.id,
|
||||||
|
name: data?.name || '',
|
||||||
|
configType: "local",
|
||||||
|
certPath: config.certPath,
|
||||||
|
keyPath: config.keyPath,
|
||||||
|
command: config.command,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
|
const req: Access = {
|
||||||
|
id: data.id as string,
|
||||||
|
name: data.name,
|
||||||
|
configType: data.configType,
|
||||||
|
usage: getUsageByConfigType(data.configType),
|
||||||
|
|
||||||
|
config: {
|
||||||
|
command: data.command,
|
||||||
|
certPath: data.certPath,
|
||||||
|
keyPath: data.keyPath,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
|
const rs = await save(req);
|
||||||
|
|
||||||
|
onAfterReq();
|
||||||
|
|
||||||
|
req.id = rs.id;
|
||||||
|
req.created = rs.created;
|
||||||
|
req.updated = rs.updated;
|
||||||
|
if (data.id && op == "edit") {
|
||||||
|
updateAccess(req);
|
||||||
|
} else {
|
||||||
|
addAccess(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadAccessGroups();
|
||||||
|
} catch (e) {
|
||||||
|
const err = e as ClientResponseError;
|
||||||
|
|
||||||
|
Object.entries(err.response.data as PbErrorData).forEach(
|
||||||
|
([key, value]) => {
|
||||||
|
form.setError(key as keyof z.infer<typeof formSchema>, {
|
||||||
|
type: "manual",
|
||||||
|
message: value.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="max-w-[35em] mx-auto mt-10">
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
form.handleSubmit(onSubmit)(e);
|
||||||
|
}}
|
||||||
|
className="space-y-3"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="id"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="hidden">
|
||||||
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="configType"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="hidden">
|
||||||
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="certPath"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('access.form.ssh.cert.path')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t('access.form.ssh.cert.path.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="keyPath"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('access.form.ssh.key.path')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t('access.form.ssh.key.path.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="command"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('access.form.ssh.command')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea placeholder={t('access.form.ssh.command.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit">{t('save')}</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessLocalForm;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
@@ -22,17 +23,20 @@ import { PbErrorData } from "@/domain/base";
|
|||||||
|
|
||||||
const AccessNamesiloForm = ({
|
const AccessNamesiloForm = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { addAccess, updateAccess } = useConfig();
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
apiKey: z.string().min(1).max(64),
|
apiKey: z.string().min(1, 'access.form.namesilo.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: NamesiloConfig = {
|
let config: NamesiloConfig = {
|
||||||
@@ -44,14 +48,13 @@ const AccessNamesiloForm = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "namesilo",
|
configType: "namesilo",
|
||||||
apiKey: config.apiKey,
|
apiKey: config.apiKey,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
console.log(data);
|
|
||||||
const req: Access = {
|
const req: Access = {
|
||||||
id: data.id as string,
|
id: data.id as string,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -63,6 +66,7 @@ const AccessNamesiloForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -70,7 +74,7 @@ const AccessNamesiloForm = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,9 +110,9 @@ const AccessNamesiloForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -121,7 +125,7 @@ const AccessNamesiloForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -136,7 +140,7 @@ const AccessNamesiloForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -151,9 +155,9 @@ const AccessNamesiloForm = ({
|
|||||||
name="apiKey"
|
name="apiKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>NAMESILO_API_KEY</FormLabel>
|
<FormLabel>{t('access.form.namesilo.api.key')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入NAMESILO_API_KEY" {...field} />
|
<Input placeholder={t('access.form.namesilo.api.key.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -162,7 +166,7 @@ const AccessNamesiloForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
@@ -23,18 +24,21 @@ import { PbErrorData } from "@/domain/base";
|
|||||||
|
|
||||||
const AccessQiniuForm = ({
|
const AccessQiniuForm = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { addAccess, updateAccess } = useConfig();
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
accessKey: z.string().min(1).max(64),
|
accessKey: z.string().min(1, 'access.form.access.key.not.empty').max(64),
|
||||||
secretKey: z.string().min(1).max(64),
|
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: QiniuConfig = {
|
let config: QiniuConfig = {
|
||||||
@@ -47,7 +51,7 @@ const AccessQiniuForm = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "qiniu",
|
configType: "qiniu",
|
||||||
accessKey: config.accessKey,
|
accessKey: config.accessKey,
|
||||||
secretKey: config.secretKey,
|
secretKey: config.secretKey,
|
||||||
@@ -67,6 +71,7 @@ const AccessQiniuForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -74,7 +79,7 @@ const AccessQiniuForm = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -111,9 +116,9 @@ const AccessQiniuForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -126,7 +131,7 @@ const AccessQiniuForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -141,7 +146,7 @@ const AccessQiniuForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -156,9 +161,9 @@ const AccessQiniuForm = ({
|
|||||||
name="accessKey"
|
name="accessKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>AccessKey</FormLabel>
|
<FormLabel>{t('access.form.access.key')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入AccessKey" {...field} />
|
<Input placeholder={t('access.form.access.key.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -171,9 +176,9 @@ const AccessQiniuForm = ({
|
|||||||
name="secretKey"
|
name="secretKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>SecretKey</FormLabel>
|
<FormLabel>{t('access.form.secret.key')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入SecretKey" {...field} />
|
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -184,7 +189,7 @@ const AccessQiniuForm = ({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { ClientResponseError } from "pocketbase";
|
|||||||
import { PbErrorData } from "@/domain/base";
|
import { PbErrorData } from "@/domain/base";
|
||||||
import { readFileContent } from "@/lib/file";
|
import { readFileContent } from "@/lib/file";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -38,9 +39,11 @@ import { updateById } from "@/repository/access_group";
|
|||||||
|
|
||||||
const AccessSSHForm = ({
|
const AccessSSHForm = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@@ -53,6 +56,7 @@ const AccessSSHForm = ({
|
|||||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
const [fileName, setFileName] = useState("");
|
const [fileName, setFileName] = useState("");
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const originGroup = data ? (data.group ? data.group : "") : "";
|
const originGroup = data ? (data.group ? data.group : "") : "";
|
||||||
|
|
||||||
@@ -62,25 +66,27 @@ const AccessSSHForm = ({
|
|||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
host: z.string().refine(
|
host: z.string().refine(
|
||||||
(str) => {
|
(str) => {
|
||||||
return ipReg.test(str) || domainReg.test(str);
|
return ipReg.test(str) || domainReg.test(str);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: "请输入正确的域名或IP",
|
message: "zod.rule.ssh.host",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
group: z.string().optional(),
|
group: z.string().optional(),
|
||||||
port: z.string().min(1).max(5),
|
port: z.string().min(1, 'access.form.ssh.port.not.empty').max(5, t('zod.rule.string.max', { max: 5 })),
|
||||||
username: z.string().min(1).max(64),
|
username: z.string().min(1, 'username.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
password: z.string().min(0).max(64),
|
password: z.string().min(0, 'password.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
key: z.string().min(0).max(20480),
|
key: z.string().min(0, 'access.form.ssh.key.not.empty').max(20480, t('zod.rule.string.max', { max: 20480 })),
|
||||||
keyFile: z.any().optional(),
|
keyFile: z.any().optional(),
|
||||||
command: z.string().min(1).max(2048),
|
|
||||||
certPath: z.string().min(0).max(2048),
|
preCommand: z.string().min(0).max(2048, t('zod.rule.string.max', { max: 2048 })).optional(),
|
||||||
keyPath: z.string().min(0).max(2048),
|
command: z.string().min(1, 'access.form.ssh.command.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||||
|
certPath: z.string().min(0, 'access.form.ssh.cert.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||||
|
keyPath: z.string().min(0, 'access.form.ssh.key.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: SSHConfig = {
|
let config: SSHConfig = {
|
||||||
@@ -90,6 +96,7 @@ const AccessSSHForm = ({
|
|||||||
password: "",
|
password: "",
|
||||||
key: "",
|
key: "",
|
||||||
keyFile: "",
|
keyFile: "",
|
||||||
|
preCommand: "",
|
||||||
command: "sudo service nginx restart",
|
command: "sudo service nginx restart",
|
||||||
certPath: "/etc/nginx/ssl/certificate.crt",
|
certPath: "/etc/nginx/ssl/certificate.crt",
|
||||||
keyPath: "/etc/nginx/ssl/private.key",
|
keyPath: "/etc/nginx/ssl/private.key",
|
||||||
@@ -100,7 +107,7 @@ const AccessSSHForm = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "ssh",
|
configType: "ssh",
|
||||||
group: data?.group,
|
group: data?.group,
|
||||||
host: config.host,
|
host: config.host,
|
||||||
@@ -112,11 +119,11 @@ const AccessSSHForm = ({
|
|||||||
certPath: config.certPath,
|
certPath: config.certPath,
|
||||||
keyPath: config.keyPath,
|
keyPath: config.keyPath,
|
||||||
command: config.command,
|
command: config.command,
|
||||||
|
preCommand: config.preCommand,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
console.log(data);
|
|
||||||
let group = data.group;
|
let group = data.group;
|
||||||
if (group == "emptyId") group = "";
|
if (group == "emptyId") group = "";
|
||||||
|
|
||||||
@@ -133,12 +140,14 @@ const AccessSSHForm = ({
|
|||||||
password: data.password,
|
password: data.password,
|
||||||
key: data.key,
|
key: data.key,
|
||||||
command: data.command,
|
command: data.command,
|
||||||
|
preCommand: data.preCommand,
|
||||||
certPath: data.certPath,
|
certPath: data.certPath,
|
||||||
keyPath: data.keyPath,
|
keyPath: data.keyPath,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -146,7 +155,7 @@ const AccessSSHForm = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
} else {
|
} else {
|
||||||
addAccess(req);
|
addAccess(req);
|
||||||
@@ -219,9 +228,9 @@ const AccessSSHForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -235,12 +244,12 @@ const AccessSSHForm = ({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className="w-full flex justify-between">
|
<FormLabel className="w-full flex justify-between">
|
||||||
<div>授权配置组(用于将一个域名证书部署到多个 ssh 主机)</div>
|
<div>{t('access.form.ssh.group.label')}</div>
|
||||||
<AccessGroupEdit
|
<AccessGroupEdit
|
||||||
trigger={
|
trigger={
|
||||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
<Plus size={14} />
|
<Plus size={14} />
|
||||||
新增
|
{t('add')}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -255,7 +264,7 @@ const AccessSSHForm = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="请选择分组" />
|
<SelectValue placeholder={t('access.group.not.empty')} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="emptyId">
|
<SelectItem value="emptyId">
|
||||||
@@ -295,7 +304,7 @@ const AccessSSHForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -310,7 +319,7 @@ const AccessSSHForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -325,9 +334,9 @@ const AccessSSHForm = ({
|
|||||||
name="host"
|
name="host"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="grow">
|
<FormItem className="grow">
|
||||||
<FormLabel>服务器HOST</FormLabel>
|
<FormLabel>{t('access.form.ssh.host')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入Host" {...field} />
|
<Input placeholder={t('access.form.ssh.host.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -340,10 +349,10 @@ const AccessSSHForm = ({
|
|||||||
name="port"
|
name="port"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>SSH端口</FormLabel>
|
<FormLabel>{t('access.form.ssh.port')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="请输入Port"
|
placeholder={t('access.form.ssh.port.not.empty')}
|
||||||
{...field}
|
{...field}
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
@@ -360,9 +369,9 @@ const AccessSSHForm = ({
|
|||||||
name="username"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>用户名</FormLabel>
|
<FormLabel>{t('username')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入用户名" {...field} />
|
<Input placeholder={t('username.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -375,10 +384,10 @@ const AccessSSHForm = ({
|
|||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>密码</FormLabel>
|
<FormLabel>{t('password')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="请输入密码"
|
placeholder={t('password.not.empty')}
|
||||||
{...field}
|
{...field}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
@@ -394,9 +403,9 @@ const AccessSSHForm = ({
|
|||||||
name="key"
|
name="key"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden>
|
<FormItem hidden>
|
||||||
<FormLabel>Key(使用证书登录)</FormLabel>
|
<FormLabel>{t('access.form.ssh.key')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入Key" {...field} />
|
<Input placeholder={t('access.form.ssh.key.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -409,7 +418,7 @@ const AccessSSHForm = ({
|
|||||||
name="keyFile"
|
name="keyFile"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Key(使用证书登录)</FormLabel>
|
<FormLabel>{t('access.form.ssh.key')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
@@ -419,10 +428,10 @@ const AccessSSHForm = ({
|
|||||||
className="w-48"
|
className="w-48"
|
||||||
onClick={handleSelectFileClick}
|
onClick={handleSelectFileClick}
|
||||||
>
|
>
|
||||||
{fileName ? fileName : "请选择文件"}
|
{fileName ? fileName : t('access.form.ssh.key.file.not.empty')}
|
||||||
</Button>
|
</Button>
|
||||||
<Input
|
<Input
|
||||||
placeholder="请输入Key"
|
placeholder={t('access.form.ssh.key.not.empty')}
|
||||||
{...field}
|
{...field}
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
@@ -443,9 +452,9 @@ const AccessSSHForm = ({
|
|||||||
name="certPath"
|
name="certPath"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>证书上传路径</FormLabel>
|
<FormLabel>{t('access.form.ssh.cert.path')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入证书上传路径" {...field} />
|
<Input placeholder={t('access.form.ssh.cert.path.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -458,9 +467,24 @@ const AccessSSHForm = ({
|
|||||||
name="keyPath"
|
name="keyPath"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>私钥上传路径</FormLabel>
|
<FormLabel>{t('access.form.ssh.key.path')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入私钥上传路径" {...field} />
|
<Input placeholder={t('access.form.ssh.key.path.not.empty')} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="preCommand"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('access.form.ssh.pre.command')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea placeholder={t('access.form.ssh.pre.command.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -473,9 +497,9 @@ const AccessSSHForm = ({
|
|||||||
name="command"
|
name="command"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Command</FormLabel>
|
<FormLabel>{t('access.form.ssh.command')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea placeholder="请输入要执行的命令" {...field} />
|
<Textarea placeholder={t('access.form.ssh.command.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -486,7 +510,7 @@ const AccessSSHForm = ({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
@@ -22,18 +23,21 @@ import { PbErrorData } from "@/domain/base";
|
|||||||
|
|
||||||
const AccessTencentForm = ({
|
const AccessTencentForm = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { addAccess, updateAccess } = useConfig();
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
secretId: z.string().min(1).max(64),
|
secretId: z.string().min(1, 'access.form.secret.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
secretKey: z.string().min(1).max(64),
|
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: TencentConfig = {
|
let config: TencentConfig = {
|
||||||
@@ -46,7 +50,7 @@ const AccessTencentForm = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "tencent",
|
configType: "tencent",
|
||||||
secretId: config.secretId,
|
secretId: config.secretId,
|
||||||
secretKey: config.secretKey,
|
secretKey: config.secretKey,
|
||||||
@@ -66,6 +70,7 @@ const AccessTencentForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -73,7 +78,7 @@ const AccessTencentForm = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -108,9 +113,9 @@ const AccessTencentForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -123,7 +128,7 @@ const AccessTencentForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -138,7 +143,7 @@ const AccessTencentForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -153,9 +158,9 @@ const AccessTencentForm = ({
|
|||||||
name="secretId"
|
name="secretId"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>SecretId</FormLabel>
|
<FormLabel>{t('access.form.secret.id')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入SecretId" {...field} />
|
<Input placeholder={t('access.form.secret.id.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -168,9 +173,9 @@ const AccessTencentForm = ({
|
|||||||
name="secretKey"
|
name="secretKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>SecretKey</FormLabel>
|
<FormLabel>{t('access.form.secret.key')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入SecretKey" {...field} />
|
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -179,7 +184,7 @@ const AccessTencentForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
@@ -22,17 +23,20 @@ import { PbErrorData } from "@/domain/base";
|
|||||||
|
|
||||||
const WebhookForm = ({
|
const WebhookForm = ({
|
||||||
data,
|
data,
|
||||||
|
op,
|
||||||
onAfterReq,
|
onAfterReq,
|
||||||
}: {
|
}: {
|
||||||
data?: Access;
|
data?: Access;
|
||||||
|
op: "add" | "edit" | "copy";
|
||||||
onAfterReq: () => void;
|
onAfterReq: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { addAccess, updateAccess } = useConfig();
|
const { addAccess, updateAccess } = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1).max(64),
|
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
url: z.string().url(),
|
url: z.string().url('zod.rule.url'),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: WebhookConfig = {
|
let config: WebhookConfig = {
|
||||||
@@ -44,14 +48,13 @@ const WebhookForm = ({
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name,
|
name: data?.name || '',
|
||||||
configType: "webhook",
|
configType: "webhook",
|
||||||
url: config.url,
|
url: config.url,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
console.log(data);
|
|
||||||
const req: Access = {
|
const req: Access = {
|
||||||
id: data.id as string,
|
id: data.id as string,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -63,6 +66,7 @@ const WebhookForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -70,7 +74,7 @@ const WebhookForm = ({
|
|||||||
req.id = rs.id;
|
req.id = rs.id;
|
||||||
req.created = rs.created;
|
req.created = rs.created;
|
||||||
req.updated = rs.updated;
|
req.updated = rs.updated;
|
||||||
if (data.id) {
|
if (data.id && op == "edit") {
|
||||||
updateAccess(req);
|
updateAccess(req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,9 +110,9 @@ const WebhookForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>名称</FormLabel>
|
<FormLabel>{t('name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入授权名称" {...field} />
|
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -121,7 +125,7 @@ const WebhookForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -136,7 +140,7 @@ const WebhookForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>配置类型</FormLabel>
|
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -151,9 +155,9 @@ const WebhookForm = ({
|
|||||||
name="url"
|
name="url"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Webhook Url</FormLabel>
|
<FormLabel>{t('access.form.webhook.url')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入Webhook Url" {...field} />
|
<Input placeholder={t('access.form.webhook.url.not.empty')} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -162,7 +166,7 @@ const WebhookForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
|
|
||||||
type DeployProgressProps = {
|
type DeployProgressProps = {
|
||||||
@@ -6,80 +11,63 @@ type DeployProgressProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
|
const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
|
||||||
let rs = <> </>;
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
let step = 0;
|
||||||
|
|
||||||
if (phase === "check") {
|
if (phase === "check") {
|
||||||
if (phaseSuccess) {
|
step = 1
|
||||||
rs = (
|
} else if (phase === "apply") {
|
||||||
<div className="flex items-center">
|
step = 2
|
||||||
<div className="text-xs text-nowrap text-green-600">检查 </div>
|
} else if (phase === "deploy") {
|
||||||
<Separator className="h-1 grow" />
|
step = 3
|
||||||
<div className="text-xs text-nowrap text-muted-foreground">获取</div>
|
|
||||||
<Separator className="h-1 grow" />
|
|
||||||
<div className="text-xs text-nowrap text-muted-foreground">部署</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
rs = (
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="text-xs text-nowrap text-red-600">检查 </div>
|
|
||||||
<Separator className="h-1 grow" />
|
|
||||||
<div className="text-xs text-nowrap text-muted-foreground">获取</div>
|
|
||||||
<Separator className="h-1 grow" />
|
|
||||||
<div className="text-xs text-nowrap text-muted-foreground">部署</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phase === "apply") {
|
return (
|
||||||
if (phaseSuccess) {
|
<div className="flex items-center">
|
||||||
rs = (
|
<div className={
|
||||||
<div className="flex items-center">
|
cn(
|
||||||
<div className="text-xs text-nowrap text-green-600">检查 </div>
|
"text-xs text-nowrap",
|
||||||
<Separator className="h-1 grow bg-green-600" />
|
step === 1 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
||||||
<div className="text-xs text-nowrap text-green-600">获取</div>
|
step > 1 ? "text-green-600" : "",
|
||||||
<Separator className="h-1 grow" />
|
)
|
||||||
<div className="text-xs text-nowrap text-muted-foreground">部署</div>
|
}>
|
||||||
</div>
|
{t('deploy.progress.check')}
|
||||||
);
|
</div>
|
||||||
} else {
|
<Separator className={
|
||||||
rs = (
|
cn(
|
||||||
<div className="flex items-center">
|
"h-1 grow max-w-[60px]",
|
||||||
<div className="text-xs text-nowrap text-green-600">检查 </div>
|
step > 1 ? "bg-green-600" : "",
|
||||||
<Separator className="h-1 grow bg-green-600" />
|
)
|
||||||
<div className="text-xs text-nowrap text-red-600">获取</div>
|
} />
|
||||||
<Separator className="h-1 grow" />
|
<div className={
|
||||||
<div className="text-xs text-nowrap text-muted-foreground">部署</div>
|
cn(
|
||||||
</div>
|
"text-xs text-nowrap",
|
||||||
);
|
step < 2 ? "text-muted-foreground" : "",
|
||||||
}
|
step === 2 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
||||||
}
|
step > 2 ? "text-green-600" : "",
|
||||||
|
)
|
||||||
if (phase === "deploy") {
|
}>
|
||||||
if (phaseSuccess) {
|
{t('deploy.progress.apply')}
|
||||||
rs = (
|
</div>
|
||||||
<div className="flex items-center">
|
<Separator className={
|
||||||
<div className="text-xs text-nowrap text-green-600">检查 </div>
|
cn(
|
||||||
<Separator className="h-1 grow bg-green-600" />
|
"h-1 grow max-w-[60px]",
|
||||||
<div className="text-xs text-nowrap text-green-600">获取</div>
|
step > 2 ? "bg-green-600" : "",
|
||||||
<Separator className="h-1 grow bg-green-600" />
|
)
|
||||||
<div className="text-xs text-nowrap text-green-600">部署</div>
|
} />
|
||||||
</div>
|
<div className={
|
||||||
);
|
cn(
|
||||||
} else {
|
"text-xs text-nowrap",
|
||||||
rs = (
|
step < 3 ? "text-muted-foreground" : "",
|
||||||
<div className="flex items-center">
|
step === 3 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
||||||
<div className="text-xs text-nowrap text-green-600">检查 </div>
|
step > 3 ? "text-green-600" : "",
|
||||||
<Separator className="h-1 grow bg-green-600" />
|
)
|
||||||
<div className="text-xs text-nowrap text-green-600">获取</div>
|
}>
|
||||||
<Separator className="h-1 grow bg-green-600" />
|
{t('deploy.progress.deploy')}
|
||||||
<div className="text-xs text-nowrap text-red-600">部署</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeployProgress;
|
export default DeployProgress;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -39,9 +40,10 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
} = useConfig();
|
} = useConfig();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
email: z.string().email(),
|
email: z.string().email("email.valid.message"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
@@ -54,7 +56,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
if ((emails.content as EmailsSetting).emails.includes(data.email)) {
|
if ((emails.content as EmailsSetting).emails.includes(data.email)) {
|
||||||
form.setError("email", {
|
form.setError("email", {
|
||||||
message: "邮箱已存在",
|
message: "email.already.exist",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -100,7 +102,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>添加邮箱</DialogTitle>
|
<DialogTitle>{t('email.add')}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="container py-3">
|
<div className="container py-3">
|
||||||
@@ -118,9 +120,9 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
name="email"
|
name="email"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>邮箱</FormLabel>
|
<FormLabel>{t('email')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="请输入邮箱" {...field} type="email" />
|
<Input placeholder={t('email.not.empty.message')} {...field} type="email" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -129,7 +131,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
245
ui/src/components/certimate/StringList.tsx
Normal file
245
ui/src/components/certimate/StringList.tsx
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
import Show from "../Show";
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { FormControl, FormLabel } from "../ui/form";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "../ui/dialog";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Edit, Plus, Trash2 } from "lucide-react";
|
||||||
|
|
||||||
|
type StringListProps = {
|
||||||
|
className?: string;
|
||||||
|
value: string;
|
||||||
|
valueType?: "domain" | "ip";
|
||||||
|
onValueChange: (value: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const titles: Record<string, string> = {
|
||||||
|
domain: "domain",
|
||||||
|
ip: "IP",
|
||||||
|
};
|
||||||
|
|
||||||
|
const StringList = ({
|
||||||
|
value,
|
||||||
|
className,
|
||||||
|
onValueChange,
|
||||||
|
valueType = "domain",
|
||||||
|
}: StringListProps) => {
|
||||||
|
const [list, setList] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useMemo(() => {
|
||||||
|
if (value) {
|
||||||
|
setList(value.split(";"));
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const changeList = () => {
|
||||||
|
onValueChange(list.join(";"));
|
||||||
|
};
|
||||||
|
changeList();
|
||||||
|
}, [list]);
|
||||||
|
|
||||||
|
const addVal = (val: string) => {
|
||||||
|
if (list.includes(val)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setList([...list, val]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const editVal = (index: number, val: string) => {
|
||||||
|
const newList = [...list];
|
||||||
|
newList[index] = val;
|
||||||
|
setList(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRemoveClick = (index: number) => {
|
||||||
|
const newList = [...list];
|
||||||
|
newList.splice(index, 1);
|
||||||
|
setList(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={cn(className)}>
|
||||||
|
<FormLabel className="flex justify-between items-center">
|
||||||
|
<div>{t(titles[valueType])}</div>
|
||||||
|
|
||||||
|
<Show when={list.length > 0}>
|
||||||
|
<StringEdit
|
||||||
|
op="add"
|
||||||
|
onValueChange={(val: string) => {
|
||||||
|
addVal(val);
|
||||||
|
}}
|
||||||
|
valueType={valueType}
|
||||||
|
value={""}
|
||||||
|
trigger={
|
||||||
|
<div className="flex items-center text-primary">
|
||||||
|
<Plus size={16} className="cursor-pointer " />
|
||||||
|
|
||||||
|
<div className="text-sm ">{t("add")}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Show
|
||||||
|
when={list.length > 0}
|
||||||
|
fallback={
|
||||||
|
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
|
||||||
|
<div className="text-muted-foreground">暂未添加域名</div>
|
||||||
|
|
||||||
|
<StringEdit
|
||||||
|
value={""}
|
||||||
|
trigger={t("add")}
|
||||||
|
onValueChange={addVal}
|
||||||
|
valueType={valueType}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="border rounded-md p-3 text-sm mt-2 text-gray-700 space-y-2 dark:text-white dark:border-stone-700 dark:bg-stone-950">
|
||||||
|
{list.map((item, index) => (
|
||||||
|
<div key={index} className="flex justify-between items-center">
|
||||||
|
<div>{item}</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<StringEdit
|
||||||
|
op="edit"
|
||||||
|
valueType={valueType}
|
||||||
|
trigger={
|
||||||
|
<Edit
|
||||||
|
size={16}
|
||||||
|
className="cursor-pointer text-gray-600 dark:text-white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
value={item}
|
||||||
|
onValueChange={(val: string) => {
|
||||||
|
editVal(index, val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Trash2
|
||||||
|
size={16}
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
onRemoveClick(index);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StringList;
|
||||||
|
|
||||||
|
type ValueType = "domain" | "ip";
|
||||||
|
|
||||||
|
type StringEditProps = {
|
||||||
|
value: string;
|
||||||
|
trigger: React.ReactNode;
|
||||||
|
onValueChange: (value: string) => void;
|
||||||
|
valueType: ValueType;
|
||||||
|
op?: "add" | "edit";
|
||||||
|
};
|
||||||
|
|
||||||
|
const StringEdit = ({
|
||||||
|
trigger,
|
||||||
|
value,
|
||||||
|
onValueChange,
|
||||||
|
op = "add",
|
||||||
|
valueType,
|
||||||
|
}: StringEditProps) => {
|
||||||
|
const [currentValue, setCurrentValue] = useState<string>("");
|
||||||
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentValue(value);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const domainSchema = z
|
||||||
|
.string()
|
||||||
|
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||||
|
message: t("domain.not.empty.verify.message"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ipSchema = z.string().ip({ message: t("ip.not.empty.verify.message") });
|
||||||
|
|
||||||
|
const schedules: Record<ValueType, z.ZodString> = {
|
||||||
|
domain: domainSchema,
|
||||||
|
ip: ipSchema,
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSaveClick = useCallback(() => {
|
||||||
|
const schema = schedules[valueType];
|
||||||
|
|
||||||
|
const resp = schema.safeParse(currentValue);
|
||||||
|
if (!resp.success) {
|
||||||
|
setError(JSON.parse(resp.error.message)[0].message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentValue("");
|
||||||
|
setOpen(false);
|
||||||
|
setError("");
|
||||||
|
|
||||||
|
onValueChange(currentValue);
|
||||||
|
}, [currentValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
setOpen(open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTrigger className="text-primary">{trigger}</DialogTrigger>
|
||||||
|
<DialogContent className="dark:text-white">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="dark:text-white">
|
||||||
|
{t(titles[valueType])}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<Input
|
||||||
|
value={currentValue}
|
||||||
|
className="dark:text-white"
|
||||||
|
onChange={(e) => {
|
||||||
|
setCurrentValue(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Show when={error.length > 0}>
|
||||||
|
<div className="text-red-500 text-sm">{error}</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onSaveClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{op === "add" ? t("add") : t("confirm")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import { BookOpen } from "lucide-react";
|
import { BookOpen } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
import { version } from "@/domain/version";
|
import { version } from "@/domain/version";
|
||||||
|
|
||||||
const Version = () => {
|
const Version = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed right-0 bottom-0 w-full flex justify-between p-5">
|
<div className="fixed right-0 bottom-0 w-full flex justify-between p-5">
|
||||||
<div className=""></div>
|
<div className=""></div>
|
||||||
@@ -14,7 +17,7 @@ const Version = () => {
|
|||||||
className="flex items-center"
|
className="flex items-center"
|
||||||
>
|
>
|
||||||
<BookOpen size={16} />
|
<BookOpen size={16} />
|
||||||
<div className="ml-1">文档</div>
|
<div className="ml-1">{t('document')}</div>
|
||||||
</a>
|
</a>
|
||||||
<Separator orientation="vertical" className="mx-2" />
|
<Separator orientation="vertical" className="mx-2" />
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { update } from "@/repository/settings";
|
import { update } from "@/repository/settings";
|
||||||
import { getErrMessage } from "@/lib/error";
|
import { getErrMessage } from "@/lib/error";
|
||||||
import { useToast } from "../ui/use-toast";
|
import { useToast } from "../ui/use-toast";
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
type DingTalkSetting = {
|
type DingTalkSetting = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -17,6 +18,7 @@ type DingTalkSetting = {
|
|||||||
|
|
||||||
const DingTalk = () => {
|
const DingTalk = () => {
|
||||||
const { config, setChannels } = useNotify();
|
const { config, setChannels } = useNotify();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [dingtalk, setDingtalk] = useState<DingTalkSetting>({
|
const [dingtalk, setDingtalk] = useState<DingTalkSetting>({
|
||||||
id: config.id ?? "",
|
id: config.id ?? "",
|
||||||
@@ -70,15 +72,15 @@ const DingTalk = () => {
|
|||||||
|
|
||||||
setChannels(resp);
|
setChannels(resp);
|
||||||
toast({
|
toast({
|
||||||
title: "保存成功",
|
title: t('save.succeed'),
|
||||||
description: "配置保存成功",
|
description: t('setting.notify.config.save.succeed'),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = getErrMessage(e);
|
const msg = getErrMessage(e);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "保存失败",
|
title: t('save.failed'),
|
||||||
description: "配置保存失败:" + msg,
|
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -100,7 +102,7 @@ const DingTalk = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
placeholder="加签的签名"
|
placeholder={t('access.form.ding.access.token.placeholder')}
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
value={dingtalk.data.secret}
|
value={dingtalk.data.secret}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -127,7 +129,7 @@ const DingTalk = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="airplane-mode">是否启用</Label>
|
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
@@ -136,7 +138,7 @@ const DingTalk = () => {
|
|||||||
handleSaveClick();
|
handleSaveClick();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
保存
|
{t('save')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from "@/domain/settings";
|
} from "@/domain/settings";
|
||||||
import { getSetting, update } from "@/repository/settings";
|
import { getSetting, update } from "@/repository/settings";
|
||||||
import { useToast } from "../ui/use-toast";
|
import { useToast } from "../ui/use-toast";
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const NotifyTemplate = () => {
|
const NotifyTemplate = () => {
|
||||||
const [id, setId] = useState("");
|
const [id, setId] = useState("");
|
||||||
@@ -17,6 +18,7 @@ const NotifyTemplate = () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const featchData = async () => {
|
const featchData = async () => {
|
||||||
@@ -66,8 +68,8 @@ const NotifyTemplate = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "保存成功",
|
title: t('save.succeed'),
|
||||||
description: "通知模板保存成功",
|
description: t('setting.notify.template.save.succeed'),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,7 +83,7 @@ const NotifyTemplate = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="text-muted-foreground text-sm mt-1">
|
<div className="text-muted-foreground text-sm mt-1">
|
||||||
可选的变量, COUNT:即将过期张数
|
{t('setting.notify.template.variables.tips.title')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Textarea
|
<Textarea
|
||||||
@@ -92,10 +94,10 @@ const NotifyTemplate = () => {
|
|||||||
}}
|
}}
|
||||||
></Textarea>
|
></Textarea>
|
||||||
<div className="text-muted-foreground text-sm mt-1">
|
<div className="text-muted-foreground text-sm mt-1">
|
||||||
可选的变量, COUNT:即将过期张数,DOMAINS:域名列表
|
{t('setting.notify.template.variables.tips.content')}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
<Button onClick={handleSaveClick}>保存</Button>
|
<Button onClick={handleSaveClick}>{t('save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { update } from "@/repository/settings";
|
import { update } from "@/repository/settings";
|
||||||
import { getErrMessage } from "@/lib/error";
|
import { getErrMessage } from "@/lib/error";
|
||||||
import { useToast } from "../ui/use-toast";
|
import { useToast } from "../ui/use-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type TelegramSetting = {
|
type TelegramSetting = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -17,6 +18,7 @@ type TelegramSetting = {
|
|||||||
|
|
||||||
const Telegram = () => {
|
const Telegram = () => {
|
||||||
const { config, setChannels } = useNotify();
|
const { config, setChannels } = useNotify();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [telegram, setTelegram] = useState<TelegramSetting>({
|
const [telegram, setTelegram] = useState<TelegramSetting>({
|
||||||
id: config.id ?? "",
|
id: config.id ?? "",
|
||||||
@@ -70,15 +72,15 @@ const Telegram = () => {
|
|||||||
|
|
||||||
setChannels(resp);
|
setChannels(resp);
|
||||||
toast({
|
toast({
|
||||||
title: "保存成功",
|
title: t('save.succeed'),
|
||||||
description: "配置保存成功",
|
description: t('setting.notify.config.save.succeed'),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = getErrMessage(e);
|
const msg = getErrMessage(e);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "保存失败",
|
title: t('save.failed'),
|
||||||
description: "配置保存失败:" + msg,
|
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -128,7 +130,7 @@ const Telegram = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="airplane-mode">是否启用</Label>
|
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
@@ -137,7 +139,7 @@ const Telegram = () => {
|
|||||||
handleSaveClick();
|
handleSaveClick();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
保存
|
{t('save')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { update } from "@/repository/settings";
|
|||||||
import { getErrMessage } from "@/lib/error";
|
import { getErrMessage } from "@/lib/error";
|
||||||
import { useToast } from "../ui/use-toast";
|
import { useToast } from "../ui/use-toast";
|
||||||
import { isValidURL } from "@/lib/url";
|
import { isValidURL } from "@/lib/url";
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
type WebhookSetting = {
|
type WebhookSetting = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -18,6 +19,7 @@ type WebhookSetting = {
|
|||||||
|
|
||||||
const Webhook = () => {
|
const Webhook = () => {
|
||||||
const { config, setChannels } = useNotify();
|
const { config, setChannels } = useNotify();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [webhook, setWebhook] = useState<WebhookSetting>({
|
const [webhook, setWebhook] = useState<WebhookSetting>({
|
||||||
id: config.id ?? "",
|
id: config.id ?? "",
|
||||||
@@ -59,8 +61,8 @@ const Webhook = () => {
|
|||||||
webhook.data.url = webhook.data.url.trim();
|
webhook.data.url = webhook.data.url.trim();
|
||||||
if (!isValidURL(webhook.data.url)) {
|
if (!isValidURL(webhook.data.url)) {
|
||||||
toast({
|
toast({
|
||||||
title: "保存失败",
|
title: t('save.failed'),
|
||||||
description: "Url格式不正确",
|
description: t('setting.notify.config.save.failed.url.not.valid'),
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -79,15 +81,15 @@ const Webhook = () => {
|
|||||||
|
|
||||||
setChannels(resp);
|
setChannels(resp);
|
||||||
toast({
|
toast({
|
||||||
title: "保存成功",
|
title: t('save.succeed'),
|
||||||
description: "配置保存成功",
|
description: t('setting.notify.config.save.succeed'),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = getErrMessage(e);
|
const msg = getErrMessage(e);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "保存失败",
|
title: t('save.failed'),
|
||||||
description: "配置保存失败:" + msg,
|
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -123,7 +125,7 @@ const Webhook = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="airplane-mode">是否启用</Label>
|
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
@@ -132,7 +134,7 @@ const Webhook = () => {
|
|||||||
handleSaveClick();
|
handleSaveClick();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
保存
|
{t('save')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
const Form = FormProvider
|
const Form = FormProvider
|
||||||
|
|
||||||
@@ -145,7 +146,9 @@ const FormMessage = React.forwardRef<
|
|||||||
React.HTMLAttributes<HTMLParagraphElement>
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
>(({ className, children, ...props }, ref) => {
|
>(({ className, children, ...props }, ref) => {
|
||||||
const { error, formMessageId } = useFormField()
|
const { error, formMessageId } = useFormField()
|
||||||
const body = error ? String(error?.message) : children
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const body = error ? t(String(error?.message)) : children
|
||||||
|
|
||||||
if (!body) {
|
if (!body) {
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
|
|||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { ButtonProps, buttonVariants } from "@/components/ui/button";
|
import { ButtonProps, buttonVariants } from "@/components/ui/button";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
|
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
|
||||||
<nav
|
<nav
|
||||||
@@ -62,33 +63,41 @@ PaginationLink.displayName = "PaginationLink";
|
|||||||
const PaginationPrevious = ({
|
const PaginationPrevious = ({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => (
|
}: React.ComponentProps<typeof PaginationLink>) => {
|
||||||
<PaginationLink
|
const { t } = useTranslation()
|
||||||
aria-label="Go to previous page"
|
|
||||||
size="default"
|
return (
|
||||||
className={cn("gap-1 pl-2.5", className)}
|
<PaginationLink
|
||||||
{...props}
|
aria-label="Go to previous page"
|
||||||
>
|
size="default"
|
||||||
<ChevronLeft className="h-4 w-4" />
|
className={cn("gap-1 pl-2.5", className)}
|
||||||
<span>上一页</span>
|
{...props}
|
||||||
</PaginationLink>
|
>
|
||||||
);
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
<span>{t('pagination.prev')}</span>
|
||||||
|
</PaginationLink>
|
||||||
|
)
|
||||||
|
};
|
||||||
PaginationPrevious.displayName = "PaginationPrevious";
|
PaginationPrevious.displayName = "PaginationPrevious";
|
||||||
|
|
||||||
const PaginationNext = ({
|
const PaginationNext = ({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => (
|
}: React.ComponentProps<typeof PaginationLink>) => {
|
||||||
<PaginationLink
|
const { t } = useTranslation()
|
||||||
aria-label="Go to next page"
|
|
||||||
size="default"
|
return (
|
||||||
className={cn("gap-1 pr-2.5", className)}
|
<PaginationLink
|
||||||
{...props}
|
aria-label="Go to next page"
|
||||||
>
|
size="default"
|
||||||
<span>下一页</span>
|
className={cn("gap-1 pr-2.5", className)}
|
||||||
<ChevronRight className="h-4 w-4" />
|
{...props}
|
||||||
</PaginationLink>
|
>
|
||||||
);
|
<span>{t('pagination.next')}</span>
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</PaginationLink>
|
||||||
|
)
|
||||||
|
};
|
||||||
PaginationNext.displayName = "PaginationNext";
|
PaginationNext.displayName = "PaginationNext";
|
||||||
|
|
||||||
const PaginationEllipsis = ({
|
const PaginationEllipsis = ({
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const accessTypeMap: Map<string, [string, string]> = new Map([
|
export const accessTypeMap: Map<string, [string, string]> = new Map([
|
||||||
["tencent", ["腾讯云", "/imgs/providers/tencent.svg"]],
|
["aliyun", ["aliyun", "/imgs/providers/aliyun.svg"]],
|
||||||
["aliyun", ["阿里云", "/imgs/providers/aliyun.svg"]],
|
["tencent", ["tencent", "/imgs/providers/tencent.svg"]],
|
||||||
["cloudflare", ["Cloudflare", "/imgs/providers/cloudflare.svg"]],
|
["huaweicloud", ["huaweicloud", "/imgs/providers/huaweicloud.svg"]],
|
||||||
["namesilo", ["Namesilo", "/imgs/providers/namesilo.svg"]],
|
["qiniu", ["qiniu", "/imgs/providers/qiniu.svg"]],
|
||||||
["godaddy", ["GoDaddy", "/imgs/providers/godaddy.svg"]],
|
["cloudflare", ["cloudflare", "/imgs/providers/cloudflare.svg"]],
|
||||||
["qiniu", ["七牛云", "/imgs/providers/qiniu.svg"]],
|
["namesilo", ["namesilo", "/imgs/providers/namesilo.svg"]],
|
||||||
["ssh", ["SSH部署", "/imgs/providers/ssh.svg"]],
|
["godaddy", ["go.daddy", "/imgs/providers/godaddy.svg"]],
|
||||||
["webhook", ["Webhook", "/imgs/providers/webhook.svg"]],
|
["local", ["local.deployment", "/imgs/providers/local.svg"]],
|
||||||
|
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
|
||||||
|
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const getProviderInfo = (t: string) => {
|
export const getProviderInfo = (t: string) => {
|
||||||
@@ -19,14 +21,16 @@ export const accessFormType = z.union(
|
|||||||
[
|
[
|
||||||
z.literal("aliyun"),
|
z.literal("aliyun"),
|
||||||
z.literal("tencent"),
|
z.literal("tencent"),
|
||||||
z.literal("ssh"),
|
z.literal("huaweicloud"),
|
||||||
z.literal("webhook"),
|
|
||||||
z.literal("cloudflare"),
|
|
||||||
z.literal("qiniu"),
|
z.literal("qiniu"),
|
||||||
|
z.literal("cloudflare"),
|
||||||
z.literal("namesilo"),
|
z.literal("namesilo"),
|
||||||
z.literal("godaddy"),
|
z.literal("godaddy"),
|
||||||
|
z.literal("local"),
|
||||||
|
z.literal("ssh"),
|
||||||
|
z.literal("webhook"),
|
||||||
],
|
],
|
||||||
{ message: "请选择云服务商" }
|
{ message: "access.not.empty" }
|
||||||
);
|
);
|
||||||
|
|
||||||
type AccessUsage = "apply" | "deploy" | "all";
|
type AccessUsage = "apply" | "deploy" | "all";
|
||||||
@@ -38,54 +42,65 @@ export type Access = {
|
|||||||
usage: AccessUsage;
|
usage: AccessUsage;
|
||||||
group?: string;
|
group?: string;
|
||||||
config:
|
config:
|
||||||
| TencentConfig
|
|
||||||
| AliyunConfig
|
| AliyunConfig
|
||||||
| SSHConfig
|
| TencentConfig
|
||||||
| WebhookConfig
|
| HuaweicloudConfig
|
||||||
| CloudflareConfig
|
|
||||||
| QiniuConfig
|
| QiniuConfig
|
||||||
|
| CloudflareConfig
|
||||||
| NamesiloConfig
|
| NamesiloConfig
|
||||||
| GodaddyConfig;
|
| GodaddyConfig
|
||||||
|
| LocalConfig
|
||||||
|
| SSHConfig
|
||||||
|
| WebhookConfig;
|
||||||
deleted?: string;
|
deleted?: string;
|
||||||
created?: string;
|
created?: string;
|
||||||
updated?: string;
|
updated?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type QiniuConfig = {
|
|
||||||
accessKey: string;
|
|
||||||
secretKey: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type WebhookConfig = {
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CloudflareConfig = {
|
|
||||||
dnsApiToken: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TencentConfig = {
|
|
||||||
secretId: string;
|
|
||||||
secretKey: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AliyunConfig = {
|
export type AliyunConfig = {
|
||||||
accessKeyId: string;
|
accessKeyId: string;
|
||||||
accessKeySecret: string;
|
accessKeySecret: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TencentConfig = {
|
||||||
|
secretId: string;
|
||||||
|
secretKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HuaweicloudConfig = {
|
||||||
|
region: string;
|
||||||
|
accessKeyId: string;
|
||||||
|
secretAccessKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type QiniuConfig = {
|
||||||
|
accessKey: string;
|
||||||
|
secretKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CloudflareConfig = {
|
||||||
|
dnsApiToken: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type NamesiloConfig = {
|
export type NamesiloConfig = {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GodaddyConfig = {
|
export type GodaddyConfig = {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
apiSecret: string;
|
apiSecret: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LocalConfig = {
|
||||||
|
command: string;
|
||||||
|
certPath: string;
|
||||||
|
keyPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type SSHConfig = {
|
export type SSHConfig = {
|
||||||
host: string;
|
host: string;
|
||||||
port: string;
|
port: string;
|
||||||
|
preCommand?: string;
|
||||||
command: string;
|
command: string;
|
||||||
username: string;
|
username: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
@@ -95,20 +110,28 @@ export type SSHConfig = {
|
|||||||
keyPath: string;
|
keyPath: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WebhookConfig = {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const getUsageByConfigType = (configType: string): AccessUsage => {
|
export const getUsageByConfigType = (configType: string): AccessUsage => {
|
||||||
switch (configType) {
|
switch (configType) {
|
||||||
case "aliyun":
|
case "aliyun":
|
||||||
case "tencent":
|
case "tencent":
|
||||||
|
case "huaweicloud":
|
||||||
return "all";
|
return "all";
|
||||||
|
|
||||||
|
case "qiniu":
|
||||||
|
case "local":
|
||||||
case "ssh":
|
case "ssh":
|
||||||
case "webhook":
|
case "webhook":
|
||||||
case "qiniu":
|
|
||||||
return "deploy";
|
return "deploy";
|
||||||
|
|
||||||
case "cloudflare":
|
case "cloudflare":
|
||||||
case "namesilo":
|
case "namesilo":
|
||||||
case "godaddy":
|
case "godaddy":
|
||||||
return "apply";
|
return "apply";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "all";
|
return "all";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,22 @@ export type Domain = {
|
|||||||
expand?: {
|
expand?: {
|
||||||
lastDeployment?: Deployment;
|
lastDeployment?: Deployment;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
applyConfig?: ApplyConfig;
|
||||||
|
deployConfig?: DeployConfig[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeployConfig = {
|
||||||
|
access: string;
|
||||||
|
type: string;
|
||||||
|
config?: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApplyConfig = {
|
||||||
|
access: string;
|
||||||
|
email: string;
|
||||||
|
timeout?: number;
|
||||||
|
nameservers?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Statistic = {
|
export type Statistic = {
|
||||||
@@ -40,13 +56,14 @@ export const getLastDeployment = (domain: Domain): Deployment | undefined => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const targetTypeMap: Map<string, [string, string]> = new Map([
|
export const targetTypeMap: Map<string, [string, string]> = new Map([
|
||||||
["aliyun-cdn", ["阿里云-CDN", "/imgs/providers/aliyun.svg"]],
|
["aliyun-cdn", ["aliyun.cdn", "/imgs/providers/aliyun.svg"]],
|
||||||
["aliyun-oss", ["阿里云-OSS", "/imgs/providers/aliyun.svg"]],
|
["aliyun-oss", ["aliyun.oss", "/imgs/providers/aliyun.svg"]],
|
||||||
["aliyun-dcdn", ["阿里云-DCDN", "/imgs/providers/aliyun.svg"]],
|
["aliyun-dcdn", ["aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
|
||||||
["tencent-cdn", ["腾讯云-CDN", "/imgs/providers/tencent.svg"]],
|
["tencent-cdn", ["tencent.cdn", "/imgs/providers/tencent.svg"]],
|
||||||
["ssh", ["SSH部署", "/imgs/providers/ssh.svg"]],
|
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
|
||||||
["qiniu-cdn", ["七牛云-CDN", "/imgs/providers/qiniu.svg"]],
|
["qiniu-cdn", ["qiniu.cdn", "/imgs/providers/qiniu.svg"]],
|
||||||
["webhook", ["Webhook", "/imgs/providers/webhook.svg"]],
|
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
|
||||||
|
["local", ["local.deployment", "/imgs/providers/local.svg"]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const targetTypeKeys = Array.from(targetTypeMap.keys());
|
export const targetTypeKeys = Array.from(targetTypeMap.keys());
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
export type Setting = {
|
export type Setting = {
|
||||||
id?: string;
|
id?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
content?: EmailsSetting | NotifyTemplates | NotifyChannels;
|
content?:
|
||||||
|
| EmailsSetting
|
||||||
|
| NotifyTemplates
|
||||||
|
| NotifyChannels
|
||||||
|
| SSLProviderSetting;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EmailsSetting = {
|
export type EmailsSetting = {
|
||||||
@@ -49,3 +53,14 @@ export const defaultNotifyTemplate: NotifyTemplate = {
|
|||||||
title: "您有{COUNT}张证书即将过期",
|
title: "您有{COUNT}张证书即将过期",
|
||||||
content: "有{COUNT}张证书即将过期,域名分别为{DOMAINS},请保持关注!",
|
content: "有{COUNT}张证书即将过期,域名分别为{DOMAINS},请保持关注!",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SSLProvider = "letsencrypt" | "zerossl";
|
||||||
|
|
||||||
|
export type SSLProviderSetting = {
|
||||||
|
provider: SSLProvider;
|
||||||
|
config: {
|
||||||
|
[key: string]: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export const version = "Certimate v0.1.11";
|
export const version = "Certimate v0.1.19";
|
||||||
|
|||||||
22
ui/src/i18n/index.ts
Normal file
22
ui/src/i18n/index.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import i18n from 'i18next';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
|
|
||||||
|
import resources from './locales'
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(LanguageDetector)
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
resources,
|
||||||
|
fallbackLng: 'zh',
|
||||||
|
debug: true,
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false,
|
||||||
|
},
|
||||||
|
backend: {
|
||||||
|
loadPath: '/locales/{{lng}}.json',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
219
ui/src/i18n/locales/en.json
Normal file
219
ui/src/i18n/locales/en.json
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
{
|
||||||
|
"ca": "Certificate Authority",
|
||||||
|
"username": "Username",
|
||||||
|
"username.not.empty": "Please enter username",
|
||||||
|
"password": "Password",
|
||||||
|
"password.not.empty": "Please enter password",
|
||||||
|
"email": "Email",
|
||||||
|
"logout": "Logout",
|
||||||
|
"setting": "Settings",
|
||||||
|
"account": "Account",
|
||||||
|
"template": "Template",
|
||||||
|
"save": "Save",
|
||||||
|
"no.data": "No data available",
|
||||||
|
"status": "Status",
|
||||||
|
"operation": "Operation",
|
||||||
|
"enable": "Enable",
|
||||||
|
"disable": "Disable",
|
||||||
|
"deploy": "Deploy",
|
||||||
|
"download": "Download",
|
||||||
|
"delete": "Delete",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"edit": "Edit",
|
||||||
|
"copy": "Copy",
|
||||||
|
"succeed": "Successful",
|
||||||
|
"add": "Add",
|
||||||
|
"document": "Document",
|
||||||
|
"variables": "Variables",
|
||||||
|
"dns": "Domain Name Server",
|
||||||
|
"name": "Name",
|
||||||
|
"create.time": "CreateTime",
|
||||||
|
"update.time": "UpdateTime",
|
||||||
|
"created.in": "Created in",
|
||||||
|
"updated.in": "Updated in",
|
||||||
|
"basic.setting": "Basic Settings",
|
||||||
|
"advanced.setting": "Advanced Settings",
|
||||||
|
"operation.succeed": "Operation Successful",
|
||||||
|
"save.succeed": "Save Successful",
|
||||||
|
"save.failed": "Save Failed",
|
||||||
|
"update.succeed": "Update Successful",
|
||||||
|
"update.failed": "Update Failed",
|
||||||
|
"delete.failed": "Delete Failed",
|
||||||
|
"ding.talk": "Ding Talk",
|
||||||
|
"telegram": "Telegram",
|
||||||
|
"webhook": "Webhook",
|
||||||
|
"local.deployment": "Local Deployment",
|
||||||
|
"tencent": "Tencent",
|
||||||
|
"tencent.cdn": "Tencent-CDN",
|
||||||
|
"aliyun": "Alibaba Cloud",
|
||||||
|
"aliyun.cdn": "Alibaba Cloud-CDN",
|
||||||
|
"aliyun.oss": "Alibaba Cloud-OSS",
|
||||||
|
"aliyun.dcdn": "Alibaba Cloud-DCDN",
|
||||||
|
"huaweicloud": "Huawei Cloud",
|
||||||
|
"qiniu": "Qiniu",
|
||||||
|
"qiniu.cdn": "Qiniu-CDN",
|
||||||
|
"cloudflare": "Cloudflare",
|
||||||
|
"namesilo": "Namesilo",
|
||||||
|
"go.daddy": "GoDaddy",
|
||||||
|
"ssh": "SSH Deployment",
|
||||||
|
"zod.rule.string.max": "Please enter no more than {{max}} characters",
|
||||||
|
"zod.rule.url": "Please enter a valid URL",
|
||||||
|
"zod.rule.ssh.host": "Please enter the correct domain name or IP",
|
||||||
|
"login.submit": "Log In",
|
||||||
|
"login.username.no.empty.message": "Please enter a valid email address",
|
||||||
|
"login.password.length.message": "Password should be at least 10 characters",
|
||||||
|
"menu.auth.management": "Authorization Management",
|
||||||
|
"theme.light": "Light",
|
||||||
|
"theme.dark": "Dark",
|
||||||
|
"theme.system": "System",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"dashboard.all": "All",
|
||||||
|
"dashboard.near.expired": "About to Expire",
|
||||||
|
"dashboard.enabled": "Enabled",
|
||||||
|
"dashboard.not.enabled": "Not Enabled",
|
||||||
|
"dashboard.unit": "Unit",
|
||||||
|
"deployment.log.name": "Deployment History",
|
||||||
|
"deployment.log.empty": "You have not created any deployments yet, please add a domain to start deployment!",
|
||||||
|
"deployment.log.status": "Status",
|
||||||
|
"deployment.log.stage": "Stage",
|
||||||
|
"deployment.log.last.execution.time": "Last Execution Time",
|
||||||
|
"deployment.log.detail.button.text": "Log",
|
||||||
|
"deployment.log.detail": "Deployment Details",
|
||||||
|
"pagination.next": "Next",
|
||||||
|
"pagination.prev": "Previous",
|
||||||
|
"domain": "Domain",
|
||||||
|
"domain.add": "Add Domain",
|
||||||
|
"domain.edit":"Edit Domain",
|
||||||
|
"domain.delete": "Delete Domain",
|
||||||
|
"domain.not.empty.verify.message": "Please enter domain",
|
||||||
|
"domain.management.name": "Domain List",
|
||||||
|
"domain.management.start.deploy.succeed.tips": "Deployment initiated, please check the deployment log later.",
|
||||||
|
"domain.management.execution.failed": "Execution Failed",
|
||||||
|
"domain.management.execution.failed.tips": "Execution failed, please check the details in <1>Deployment History</1>.",
|
||||||
|
"domain.management.empty": "Please add a domain to start deploying the certificate.",
|
||||||
|
"domain.management.expiry.date": "Validity Period",
|
||||||
|
"domain.management.expiry.date1": "Valid for {{date}} days",
|
||||||
|
"domain.management.expiry.date2": "Expiry on {{date}}",
|
||||||
|
"domain.management.last.execution.time": "Last Execution Time",
|
||||||
|
"domain.management.last.execution.status": "Last Execution Status",
|
||||||
|
"domain.management.last.execution.stage": "Last Execution Stage",
|
||||||
|
"domain.management.enable": "Enable",
|
||||||
|
"domain.management.start.deploying": "Deploy Now",
|
||||||
|
"domain.management.forced.deployment": "Force Deployment",
|
||||||
|
"domain.management.delete.confirm": "Are you sure you want to delete this domain?",
|
||||||
|
"domain.management.edit.title": "Edit Domain",
|
||||||
|
"domain.management.edit.dns.access.label": "DNS Provider Authorization Configuration",
|
||||||
|
"domain.management.edit.dns.access.not.empty.message": "Please select DNS provider authorization configuration",
|
||||||
|
"domain.management.edit.access.label": "Provider Authorization Configuration",
|
||||||
|
"domain.management.edit.access.not.empty.message": "Please select authorization configuration",
|
||||||
|
"domain.management.edit.target.type": "Deployment Service Type",
|
||||||
|
"domain.management.edit.target.type.not.empty.message": "Please select deployment service type",
|
||||||
|
"domain.management.edit.succeed.tips": "Successful domain editing",
|
||||||
|
"domain.management.edit.target.access": "Deployment Service Provider Authorization Configuration",
|
||||||
|
"domain.management.edit.target.access.content.label": "Provider Authorization Configuration",
|
||||||
|
"domain.management.edit.target.access.not.empty.message": "Please select authorization configuration",
|
||||||
|
"domain.management.edit.target.access.verify.msg": "At least one of the deployment authorization and deployment authorization group must be selected",
|
||||||
|
"domain.management.edit.group.label": "Deployment Configuration Group (used to deploy a domain certificate to multiple ssh hosts)",
|
||||||
|
"domain.management.edit.group.not.empty.message": "Please select group",
|
||||||
|
"domain.management.edit.email.not.empty.message": "Please select email",
|
||||||
|
"domain.management.edit.email.description": "(A email is required to apply for a certificate)",
|
||||||
|
"domain.management.edit.variables.placeholder": "It can be used in SSH deployment, like:\nkey=val;\nkey2=val2;",
|
||||||
|
"domain.management.edit.dns.placeholder": "Custom domain name server, separates multiple entries with semicolon, like:\n8.8.8.8;\n8.8.4.4;",
|
||||||
|
"domain.management.add.succeed.tips": "Domain added successfully",
|
||||||
|
"email.add": "Add Email",
|
||||||
|
"email.list": "Email List",
|
||||||
|
"email.valid.message": "Please enter a valid email address",
|
||||||
|
"email.already.exist": "Email already exists",
|
||||||
|
"email.not.empty.message": "Please enter email",
|
||||||
|
"setting.notify.menu": "Notification Push",
|
||||||
|
"setting.submit": "Confirm Changes",
|
||||||
|
"setting.account.email.valid.message": "Please enter a valid email address",
|
||||||
|
"setting.account.email.placeholder": "Please enter email",
|
||||||
|
"setting.account.email.change.succeed": "Account email altered successfully",
|
||||||
|
"setting.account.email.change.failed": "Account email alteration failed",
|
||||||
|
"setting.account.log.back.in": "Please login again",
|
||||||
|
"setting.password.length.message": "Password should be at least 10 characters",
|
||||||
|
"setting.password.not.match": "Passwords do not match",
|
||||||
|
"setting.password.change.succeed": "Password changed successfully",
|
||||||
|
"setting.password.change.failed": "Password change failed",
|
||||||
|
"setting.password.current.password": "Current Password",
|
||||||
|
"setting.password.new.password": "New Password",
|
||||||
|
"setting.password.confirm.password": "Confirm Password",
|
||||||
|
"setting.notify.template.save.succeed": "Notification template saved successfully",
|
||||||
|
"setting.notify.template.variables.tips.title": "Optional variables, COUNT: number of expiring soon",
|
||||||
|
"setting.notify.template.variables.tips.content": "Optional variables, COUNT: number of expiring soon, DOMAINS: Domain list",
|
||||||
|
"setting.notify.config.enable": "Enable",
|
||||||
|
"setting.notify.config.save.succeed": "Configuration saved successfully",
|
||||||
|
"setting.notify.config.save.failed": "Configuration save failed",
|
||||||
|
"setting.notify.config.save.failed.url.not.valid": "Invalid Url format",
|
||||||
|
"setting.ca.not.empty": "Please select a Certificate Authority",
|
||||||
|
"setting.ca.eab_kid.not.empty": "Please enter EAB_KID",
|
||||||
|
"setting.ca.eab_hmac_key.not.empty": "Please enter EAB_HMAC_KEY.",
|
||||||
|
"setting.ca.eab_kid_hmac_key.not.empty": "Please enter EAB_KID and EAB_HMAC_KEY",
|
||||||
|
"deploy.progress.check": "Check",
|
||||||
|
"deploy.progress.apply": "Apply",
|
||||||
|
"deploy.progress.deploy": "Deploy",
|
||||||
|
"access.management": "Authorization Management",
|
||||||
|
"access.add": "Add Authorization",
|
||||||
|
"access.edit": "Edit Authorization",
|
||||||
|
"access.copy": "Copy Authorization",
|
||||||
|
"access.delete.confirm": "Are you sure you want to delete the deployment authorization?",
|
||||||
|
"access.all": "All Authorizations",
|
||||||
|
"access.list": "Authorization List",
|
||||||
|
"access.type": "Provider",
|
||||||
|
"access.type.not.empty": "Please select a provider",
|
||||||
|
"access.not.empty": "Please select a cloud provider",
|
||||||
|
"access.empty": "Please add authorization to start deploying certificate.",
|
||||||
|
"access.group.management": "Authorization Group Management",
|
||||||
|
"access.group.add": "Add Authorization Group",
|
||||||
|
"access.group.not.empty": "Please select a group",
|
||||||
|
"access.group.name": "Group Name",
|
||||||
|
"access.group.name.not.empty": "Please enter group name",
|
||||||
|
"access.group.delete": "Delete Group",
|
||||||
|
"access.group.delete.confirm": "Are you sure you want to delete the deployment authorization group?",
|
||||||
|
"access.group.domain.empty": "Please add a domain to start deploying the certificate.",
|
||||||
|
"access.group.empty": "No deployment authorization configuration yet, please add after starting use.",
|
||||||
|
"access.group.total": "Totally {{total}} deployment authorization configuration",
|
||||||
|
"access.form.name.not.empty": "Please enter authorization name",
|
||||||
|
"access.form.config.field": "Configuration Type",
|
||||||
|
"access.form.access.key.id": "AccessKeyId",
|
||||||
|
"access.form.access.key.id.not.empty": "Please enter AccessKeyId",
|
||||||
|
"access.form.access.key.secret": "AccessKeySecret",
|
||||||
|
"access.form.access.key.secret.not.empty": "Please enter AccessKeySecret",
|
||||||
|
"access.form.cloud.dns.api.token": "CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.form.cloud.dns.api.token.not.empty": "Please enter CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.form.go.daddy.api.key": "GO_DADDY_API_KEY",
|
||||||
|
"access.form.go.daddy.api.key.not.empty": "Please enter GO_DADDY_API_KEY",
|
||||||
|
"access.form.go.daddy.api.secret": "GO_DADDY_API_SECRET",
|
||||||
|
"access.form.go.daddy.api.secret.not.empty": "Please enter GO_DADDY_API_SECRET",
|
||||||
|
"access.form.namesilo.api.key": "NAMESILO_API_KEY",
|
||||||
|
"access.form.namesilo.api.key.not.empty": "Please enter NAMESILO_API_KEY",
|
||||||
|
"access.form.secret.id": "SecretId",
|
||||||
|
"access.form.secret.id.not.empty": "Please enter SecretId",
|
||||||
|
"access.form.secret.key": "SecretKey",
|
||||||
|
"access.form.secret.key.not.empty": "Please enter SecretKey",
|
||||||
|
"access.form.access.key": "AccessKey",
|
||||||
|
"access.form.access.key.not.empty": "Please enter AccessKey",
|
||||||
|
"access.form.region": "Region",
|
||||||
|
"access.form.region.not.empty": "Please enter Region",
|
||||||
|
"access.form.webhook.url": "Webhook URL",
|
||||||
|
"access.form.webhook.url.not.empty": "Please enter Webhook URL",
|
||||||
|
"access.form.ssh.group.label": "Authorization Configuration Group (used to deploy a single domain certificate to multiple SSH hosts)",
|
||||||
|
"access.form.ssh.host": "Server Host",
|
||||||
|
"access.form.ssh.host.not.empty": "Please enter Host",
|
||||||
|
"access.form.ssh.port": "SSH Port",
|
||||||
|
"access.form.ssh.port.not.empty": "Please enter Port",
|
||||||
|
"access.form.ssh.key": "Key (Log in using certificate)",
|
||||||
|
"access.form.ssh.key.not.empty": "Please enter Key",
|
||||||
|
"access.form.ssh.key.file.not.empty": "Please select file",
|
||||||
|
"access.form.ssh.cert.path": "Certificate Save Path",
|
||||||
|
"access.form.ssh.cert.path.not.empty": "Please enter certificate save path",
|
||||||
|
"access.form.ssh.key.path": "Private Key Save Path",
|
||||||
|
"access.form.ssh.key.path.not.empty": "Please enter private key save path",
|
||||||
|
"access.form.ssh.pre.command": "Pre-deployment Command",
|
||||||
|
"access.form.ssh.pre.command.not.empty": "Command to be executed before deploying the certificate",
|
||||||
|
"access.form.ssh.command": "Command",
|
||||||
|
"access.form.ssh.command.not.empty": "Please enter command",
|
||||||
|
"access.form.ding.access.token.placeholder": "Signature for signed addition"
|
||||||
|
}
|
||||||
17
ui/src/i18n/locales/index.ts
Normal file
17
ui/src/i18n/locales/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Resource } from 'i18next'
|
||||||
|
|
||||||
|
import zh from './zh.json'
|
||||||
|
import en from './en.json'
|
||||||
|
|
||||||
|
const resources: Resource = {
|
||||||
|
zh: {
|
||||||
|
name: '简体中文',
|
||||||
|
translation: zh
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
name: 'English',
|
||||||
|
translation: en
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default resources;
|
||||||
219
ui/src/i18n/locales/zh.json
Normal file
219
ui/src/i18n/locales/zh.json
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
{
|
||||||
|
"ca": "证书颁发机构",
|
||||||
|
"username": "用户名",
|
||||||
|
"username.not.empty": "请输入用户名",
|
||||||
|
"password": "密码",
|
||||||
|
"password.not.empty": "请输入密码",
|
||||||
|
"email": "邮箱",
|
||||||
|
"logout": "退出登录",
|
||||||
|
"setting": "设置",
|
||||||
|
"account": "账户",
|
||||||
|
"template": "模版",
|
||||||
|
"save": "保存",
|
||||||
|
"no.data": "暂无数据",
|
||||||
|
"status": "状态",
|
||||||
|
"operation": "操作",
|
||||||
|
"enable": "启用",
|
||||||
|
"disable": "禁用",
|
||||||
|
"deploy": "部署",
|
||||||
|
"download": "下载",
|
||||||
|
"delete": "删除",
|
||||||
|
"cancel": "取消",
|
||||||
|
"confirm": "确认",
|
||||||
|
"edit": "编辑",
|
||||||
|
"copy": "复制",
|
||||||
|
"succeed": "成功",
|
||||||
|
"add": "新增",
|
||||||
|
"document": "文档",
|
||||||
|
"variables": "变量",
|
||||||
|
"dns": "域名服务器",
|
||||||
|
"name": "名称",
|
||||||
|
"create.time": "创建时间",
|
||||||
|
"update.time": "更新时间",
|
||||||
|
"created.in": "创建于",
|
||||||
|
"updated.in": "更新于",
|
||||||
|
"basic.setting": "基础设置",
|
||||||
|
"advanced.setting": "高级设置",
|
||||||
|
"operation.succeed": "操作成功",
|
||||||
|
"save.succeed": "保存成功",
|
||||||
|
"save.failed": "保存失败",
|
||||||
|
"update.succeed": "修改成功",
|
||||||
|
"update.failed": "修改失败",
|
||||||
|
"delete.failed": "删除失败",
|
||||||
|
"ding.talk": "钉钉",
|
||||||
|
"telegram": "Telegram",
|
||||||
|
"webhook": "Webhook",
|
||||||
|
"local.deployment": "本地部署",
|
||||||
|
"tencent": "腾讯云",
|
||||||
|
"tencent.cdn": "腾讯云-CDN",
|
||||||
|
"aliyun": "阿里云",
|
||||||
|
"aliyun.cdn": "阿里云-CDN",
|
||||||
|
"aliyun.oss": "阿里云-OSS",
|
||||||
|
"aliyun.dcdn": "阿里云-DCDN",
|
||||||
|
"huaweicloud": "华为云",
|
||||||
|
"qiniu": "七牛云",
|
||||||
|
"qiniu.cdn": "七牛云-CDN",
|
||||||
|
"cloudflare": "Cloudflare",
|
||||||
|
"namesilo": "Namesilo",
|
||||||
|
"go.daddy": "GoDaddy",
|
||||||
|
"ssh": "SSH 部署",
|
||||||
|
"zod.rule.string.max": "请输入不超过 {{max}} 个字符",
|
||||||
|
"zod.rule.url": "请输入有效的 url 地址",
|
||||||
|
"zod.rule.ssh.host": "请输入正确的域名或IP",
|
||||||
|
"login.submit": "登录",
|
||||||
|
"login.username.no.empty.message": "请输入正确的邮箱地址",
|
||||||
|
"login.password.length.message": "密码至少10个字符",
|
||||||
|
"menu.auth.management": "授权管理",
|
||||||
|
"theme.light": "浅色",
|
||||||
|
"theme.dark": "暗黑",
|
||||||
|
"theme.system": "系统",
|
||||||
|
"dashboard": "控制面板",
|
||||||
|
"dashboard.all": "所有",
|
||||||
|
"dashboard.near.expired": "即将过期",
|
||||||
|
"dashboard.enabled": "启用中",
|
||||||
|
"dashboard.not.enabled": "未启用",
|
||||||
|
"dashboard.unit": "个",
|
||||||
|
"deployment.log.name": "部署历史",
|
||||||
|
"deployment.log.empty": "你暂未创建任何部署,请先添加域名进行部署吧!",
|
||||||
|
"deployment.log.status": "状态",
|
||||||
|
"deployment.log.stage": "阶段",
|
||||||
|
"deployment.log.last.execution.time": "最近执行时间",
|
||||||
|
"deployment.log.detail.button.text": "日志",
|
||||||
|
"deployment.log.detail": "部署详情",
|
||||||
|
"pagination.next": "下一页",
|
||||||
|
"pagination.prev": "上一页",
|
||||||
|
"domain": "域名",
|
||||||
|
"domain.add": "新增域名",
|
||||||
|
"domain.edit": "编辑域名",
|
||||||
|
"domain.delete": "删除域名",
|
||||||
|
"domain.not.empty.verify.message": "请输入域名",
|
||||||
|
"domain.management.name": "域名列表",
|
||||||
|
"domain.management.start.deploy.succeed.tips": "已发起部署,请稍后查看部署日志。",
|
||||||
|
"domain.management.execution.failed": "执行失败",
|
||||||
|
"domain.management.execution.failed.tips": "执行失败,请在 <1>部署历史</1> 查看详情。",
|
||||||
|
"domain.management.empty": "请添加域名开始部署证书吧。",
|
||||||
|
"domain.management.expiry.date": "有效期限",
|
||||||
|
"domain.management.expiry.date1": "有效期 {{date}} 天",
|
||||||
|
"domain.management.expiry.date2": "{{date}} 到期",
|
||||||
|
"domain.management.last.execution.time": "最近执行时间",
|
||||||
|
"domain.management.last.execution.status": "最近执行状态",
|
||||||
|
"domain.management.last.execution.stage": "最近执行阶段",
|
||||||
|
"domain.management.enable": "是否启用",
|
||||||
|
"domain.management.start.deploying": "立即部署",
|
||||||
|
"domain.management.forced.deployment": "强行部署",
|
||||||
|
"domain.management.delete.confirm": "确定要删除域名吗?",
|
||||||
|
"domain.management.edit.title": "编辑域名",
|
||||||
|
"domain.management.edit.dns.access.label": "DNS 服务商授权配置",
|
||||||
|
"domain.management.edit.dns.access.not.empty.message": "请选择DNS服务商授权配置",
|
||||||
|
"domain.management.edit.access.label": "服务商授权配置",
|
||||||
|
"domain.management.edit.access.not.empty.message": "请选择授权配置",
|
||||||
|
"domain.management.edit.target.type": "部署服务类型",
|
||||||
|
"domain.management.edit.target.type.not.empty.message": "请选择部署服务类型",
|
||||||
|
"domain.management.edit.succeed.tips": "域名编辑成功",
|
||||||
|
"domain.management.edit.target.access": "部署服务商授权配置",
|
||||||
|
"domain.management.edit.target.access.content.label": "服务商授权配置",
|
||||||
|
"domain.management.edit.target.access.not.empty.message": "请选择授权配置",
|
||||||
|
"domain.management.edit.target.access.verify.msg": "部署授权和部署授权组至少选一个",
|
||||||
|
"domain.management.edit.group.label": "部署配置组(用于将一个域名证书部署到多个 ssh 主机)",
|
||||||
|
"domain.management.edit.group.not.empty.message": "请选择分组",
|
||||||
|
"domain.management.edit.email.not.empty.message": "请选择邮箱",
|
||||||
|
"domain.management.edit.email.description": "(申请证书需要提供邮箱)",
|
||||||
|
"domain.management.edit.variables.placeholder": "可在SSH部署中使用,形如:\nkey=val;\nkey2=val2;",
|
||||||
|
"domain.management.edit.dns.placeholder": "自定义域名服务器,多个用分号隔开,如:\n8.8.8.8;\n8.8.4.4;",
|
||||||
|
"domain.management.add.succeed.tips": "域名添加成功",
|
||||||
|
"email.add": "添加邮箱",
|
||||||
|
"email.list": "邮箱列表",
|
||||||
|
"email.valid.message": "请输入正确的邮箱地址",
|
||||||
|
"email.already.exist": "邮箱已存在",
|
||||||
|
"email.not.empty.message": "请输入邮箱",
|
||||||
|
"setting.notify.menu": "消息推送",
|
||||||
|
"setting.submit": "确认修改",
|
||||||
|
"setting.account.email.valid.message": "请输入正确的邮箱地址",
|
||||||
|
"setting.account.email.placeholder": "请输入邮箱",
|
||||||
|
"setting.account.email.change.succeed": "修改账户邮箱成功",
|
||||||
|
"setting.account.email.change.failed": "修改账户邮箱失败",
|
||||||
|
"setting.account.log.back.in": "请重新登录",
|
||||||
|
"setting.password.length.message": "密码至少10个字符",
|
||||||
|
"setting.password.not.match": "两次密码不一致",
|
||||||
|
"setting.password.change.succeed": "修改密码成功",
|
||||||
|
"setting.password.change.failed": "修改密码失败",
|
||||||
|
"setting.password.current.password": "当前密码",
|
||||||
|
"setting.password.new.password": "新密码",
|
||||||
|
"setting.password.confirm.password": "确认密码",
|
||||||
|
"setting.notify.template.save.succeed": "通知模板保存成功",
|
||||||
|
"setting.notify.template.variables.tips.title": "可选的变量, COUNT:即将过期张数",
|
||||||
|
"setting.notify.template.variables.tips.content": "可选的变量, COUNT:即将过期张数,DOMAINS:域名列表",
|
||||||
|
"setting.notify.config.enable": "是否启用",
|
||||||
|
"setting.notify.config.save.succeed": "配置保存成功",
|
||||||
|
"setting.notify.config.save.failed": "配置保存失败",
|
||||||
|
"setting.notify.config.save.failed.url.not.valid": "Url格式不正确",
|
||||||
|
"setting.ca.not.empty": "请选择证书分发机构",
|
||||||
|
"setting.ca.eab_kid.not.empty": "请输入EAB_KID",
|
||||||
|
"setting.ca.eab_hmac_key.not.empty": "请输入EAB_HMAC_KEY",
|
||||||
|
"setting.ca.eab_kid_hmac_key.not.empty": "请输入EAB_KID和EAB_HMAC_KEY",
|
||||||
|
"deploy.progress.check": "检查",
|
||||||
|
"deploy.progress.apply": "获取",
|
||||||
|
"deploy.progress.deploy": "部署",
|
||||||
|
"access.management": "授权管理",
|
||||||
|
"access.add": "添加授权",
|
||||||
|
"access.edit": "编辑授权",
|
||||||
|
"access.copy": "复制授权",
|
||||||
|
"access.delete.confirm": "确定要删除授权吗?",
|
||||||
|
"access.all": "所有授权",
|
||||||
|
"access.list": "授权列表",
|
||||||
|
"access.type": "服务商",
|
||||||
|
"access.type.not.empty": "请选择服务商",
|
||||||
|
"access.not.empty": "请选择云服务商",
|
||||||
|
"access.empty": "请添加授权开始部署证书吧。",
|
||||||
|
"access.group.management": "授权组管理",
|
||||||
|
"access.group.add": "添加授权组",
|
||||||
|
"access.group.not.empty": "请选择分组",
|
||||||
|
"access.group.name": "组名",
|
||||||
|
"access.group.name.not.empty": "请输入组名",
|
||||||
|
"access.group.delete": "删除组",
|
||||||
|
"access.group.delete.confirm": "确定要删除部署授权组吗?",
|
||||||
|
"access.group.domain.empty": "请添加域名开始部署证书吧。",
|
||||||
|
"access.group.empty": "暂无部署授权配置,请添加后开始使用吧",
|
||||||
|
"access.group.total": "共有 {{total}} 个部署授权配置",
|
||||||
|
"access.form.name.not.empty": "请输入授权名称",
|
||||||
|
"access.form.config.field": "配置类型",
|
||||||
|
"access.form.access.key.id": "AccessKeyId",
|
||||||
|
"access.form.access.key.id.not.empty": "请输入 AccessKeyId",
|
||||||
|
"access.form.access.key.secret": "AccessKeySecret",
|
||||||
|
"access.form.access.key.secret.not.empty": "请输入 AccessKeySecret",
|
||||||
|
"access.form.cloud.dns.api.token": "CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.form.cloud.dns.api.token.not.empty": "请输入 CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.form.go.daddy.api.key": "GO_DADDY_API_KEY",
|
||||||
|
"access.form.go.daddy.api.key.not.empty": "请输入 GO_DADDY_API_KEY",
|
||||||
|
"access.form.go.daddy.api.secret": "GO_DADDY_API_SECRET",
|
||||||
|
"access.form.go.daddy.api.secret.not.empty": "请输入 GO_DADDY_API_SECRET",
|
||||||
|
"access.form.namesilo.api.key": "NAMESILO_API_KEY",
|
||||||
|
"access.form.namesilo.api.key.not.empty": "请输入 NAMESILO_API_KEY",
|
||||||
|
"access.form.secret.id": "SecretId",
|
||||||
|
"access.form.secret.id.not.empty": "请输入 SecretId",
|
||||||
|
"access.form.secret.key": "SecretKey",
|
||||||
|
"access.form.secret.key.not.empty": "请输入 SecretKey",
|
||||||
|
"access.form.access.key": "AccessKey",
|
||||||
|
"access.form.access.key.not.empty": "请输入 AccessKey",
|
||||||
|
"access.form.region": "Region",
|
||||||
|
"access.form.region.not.empty": "请输入区域",
|
||||||
|
"access.form.webhook.url": "Webhook URL",
|
||||||
|
"access.form.webhook.url.not.empty": "请输入 Webhook URL",
|
||||||
|
"access.form.ssh.group.label": "授权配置组(用于将一个域名证书部署到多个 ssh 主机)",
|
||||||
|
"access.form.ssh.host": "服务器 Host",
|
||||||
|
"access.form.ssh.host.not.empty": "请输入 Host",
|
||||||
|
"access.form.ssh.port": "SSH 端口",
|
||||||
|
"access.form.ssh.port.not.empty": "请输入 Port",
|
||||||
|
"access.form.ssh.key": "Key(使用证书登录)",
|
||||||
|
"access.form.ssh.key.not.empty": "请输入 Key",
|
||||||
|
"access.form.ssh.key.file.not.empty": "请选择文件",
|
||||||
|
"access.form.ssh.cert.path": "证书保存路径",
|
||||||
|
"access.form.ssh.cert.path.not.empty": "请输入证书保存路径",
|
||||||
|
"access.form.ssh.key.path": "私钥保存路径",
|
||||||
|
"access.form.ssh.key.path.not.empty": "请输入私钥保存路径",
|
||||||
|
"access.form.ssh.pre.command": "前置 Command",
|
||||||
|
"access.form.ssh.pre.command.not.empty": "在部署证书前执行的前置命令",
|
||||||
|
"access.form.ssh.command": "Command",
|
||||||
|
"access.form.ssh.command.not.empty": "请输入要执行的命令",
|
||||||
|
"access.form.ding.access.token.placeholder": "加签的签名"
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import "./global.css";
|
|||||||
import { RouterProvider } from "react-router-dom";
|
import { RouterProvider } from "react-router-dom";
|
||||||
import { router } from "./router.tsx";
|
import { router } from "./router.tsx";
|
||||||
import { ThemeProvider } from "./components/ThemeProvider.tsx";
|
import { ThemeProvider } from "./components/ThemeProvider.tsx";
|
||||||
|
import "@/i18n";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
useNavigate,
|
useNavigate,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import { CircleUser, Earth, History, Home, Menu, Server } from "lucide-react";
|
import { CircleUser, Earth, History, Home, Menu, Server } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
@@ -22,12 +23,14 @@ import { cn } from "@/lib/utils";
|
|||||||
import { ConfigProvider } from "@/providers/config";
|
import { ConfigProvider } from "@/providers/config";
|
||||||
import { getPb } from "@/repository/api";
|
import { getPb } from "@/repository/api";
|
||||||
import { ThemeToggle } from "@/components/ThemeToggle";
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||||
|
import LocaleToggle from "@/components/LocaleToggle";
|
||||||
|
|
||||||
import Version from "@/components/certimate/Version";
|
import Version from "@/components/certimate/Version";
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
if (!getPb().authStore.isValid || !getPb().authStore.isAdmin) {
|
if (!getPb().authStore.isValid || !getPb().authStore.isAdmin) {
|
||||||
return <Navigate to="/login" />;
|
return <Navigate to="/login" />;
|
||||||
@@ -70,7 +73,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Home className="h-4 w-4" />
|
<Home className="h-4 w-4" />
|
||||||
控制面板
|
{t('dashboard')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/domains"
|
to="/domains"
|
||||||
@@ -80,7 +83,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Earth className="h-4 w-4" />
|
<Earth className="h-4 w-4" />
|
||||||
域名列表
|
{t('domain.management.name')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/access"
|
to="/access"
|
||||||
@@ -90,7 +93,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Server className="h-4 w-4" />
|
<Server className="h-4 w-4" />
|
||||||
授权管理
|
{t('menu.auth.management')}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
@@ -101,7 +104,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<History className="h-4 w-4" />
|
<History className="h-4 w-4" />
|
||||||
部署历史
|
{t('deployment.log.name')}
|
||||||
</Link>
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,7 +141,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Home className="h-5 w-5" />
|
<Home className="h-5 w-5" />
|
||||||
控制面板
|
{t('dashboard')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/domains"
|
to="/domains"
|
||||||
@@ -148,7 +151,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Earth className="h-5 w-5" />
|
<Earth className="h-5 w-5" />
|
||||||
域名列表
|
{t('domain.management.name')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/access"
|
to="/access"
|
||||||
@@ -158,7 +161,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Server className="h-5 w-5" />
|
<Server className="h-5 w-5" />
|
||||||
授权管理
|
{t('menu.auth.management')}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
@@ -169,13 +172,14 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<History className="h-5 w-5" />
|
<History className="h-5 w-5" />
|
||||||
部署历史
|
{t('deployment.log.name')}
|
||||||
</Link>
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
<div className="w-full flex-1"></div>
|
<div className="w-full flex-1"></div>
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
|
<LocaleToggle />
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -188,15 +192,15 @@ export default function Dashboard() {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuLabel>账户</DropdownMenuLabel>
|
<DropdownMenuLabel>{t('account')}</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
<DropdownMenuItem onClick={handleSettingClick}>
|
<DropdownMenuItem onClick={handleSettingClick}>
|
||||||
偏好设置
|
{t('setting')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<DropdownMenuItem onClick={handleLogoutClick}>
|
<DropdownMenuItem onClick={handleLogoutClick}>
|
||||||
退出
|
{t('logout')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
import { KeyRound, Megaphone, UserRound } from "lucide-react";
|
import { KeyRound, Megaphone, ShieldCheck, UserRound } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const SettingLayout = () => {
|
const SettingLayout = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [tabValue, setTabValue] = useState("account");
|
const [tabValue, setTabValue] = useState("account");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const pathname = location.pathname;
|
const pathname = location.pathname;
|
||||||
@@ -20,7 +22,7 @@ const SettingLayout = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<div className="text-muted-foreground border-b dark:border-stone-500 py-5">
|
<div className="text-muted-foreground border-b dark:border-stone-500 py-5">
|
||||||
偏好设置
|
{t("setting")}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full mt-5 p-0 md:p-3 flex justify-center">
|
<div className="w-full mt-5 p-0 md:p-3 flex justify-center">
|
||||||
<Tabs defaultValue="account" className="w-full" value={tabValue}>
|
<Tabs defaultValue="account" className="w-full" value={tabValue}>
|
||||||
@@ -33,7 +35,7 @@ const SettingLayout = () => {
|
|||||||
className="px-5"
|
className="px-5"
|
||||||
>
|
>
|
||||||
<UserRound size={14} />
|
<UserRound size={14} />
|
||||||
<div className="ml-1">账户</div>
|
<div className="ml-1">{t("account")}</div>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="password"
|
value="password"
|
||||||
@@ -43,7 +45,7 @@ const SettingLayout = () => {
|
|||||||
className="px-5"
|
className="px-5"
|
||||||
>
|
>
|
||||||
<KeyRound size={14} />
|
<KeyRound size={14} />
|
||||||
<div className="ml-1">密码</div>
|
<div className="ml-1">{t("password")}</div>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
@@ -54,7 +56,17 @@ const SettingLayout = () => {
|
|||||||
className="px-5"
|
className="px-5"
|
||||||
>
|
>
|
||||||
<Megaphone size={14} />
|
<Megaphone size={14} />
|
||||||
<div className="ml-1">消息推送</div>
|
<div className="ml-1">{t("setting.notify.menu")}</div>
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="ssl-provider"
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/setting/ssl-provider");
|
||||||
|
}}
|
||||||
|
className="px-5"
|
||||||
|
>
|
||||||
|
<ShieldCheck size={14} />
|
||||||
|
<div className="ml-1">{t("ca")}</div>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value={tabValue}>
|
<TabsContent value={tabValue}>
|
||||||
|
|||||||
@@ -9,8 +9,18 @@ import { Access as AccessType, accessTypeMap } from "@/domain/access";
|
|||||||
import { convertZulu2Beijing } from "@/lib/time";
|
import { convertZulu2Beijing } from "@/lib/time";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
import { remove } from "@/repository/access";
|
import { remove } from "@/repository/access";
|
||||||
|
import { t } from "i18next";
|
||||||
import { Key } from "lucide-react";
|
import { Key } from "lucide-react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent, AlertDialogDescription, AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger
|
||||||
|
} from "@/components/ui/alert-dialog.tsx";
|
||||||
|
|
||||||
const Access = () => {
|
const Access = () => {
|
||||||
const { config, deleteAccess } = useConfig();
|
const { config, deleteAccess } = useConfig();
|
||||||
@@ -46,11 +56,11 @@ const Access = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="text-muted-foreground">授权管理</div>
|
<div className="text-muted-foreground">{t("access.management")}</div>
|
||||||
{tab != "access_group" ? (
|
{tab != "access_group" ? (
|
||||||
<AccessEdit trigger={<Button>添加授权</Button>} op="add" />
|
<AccessEdit trigger={<Button>{t("access.add")}</Button>} op="add" />
|
||||||
) : (
|
) : (
|
||||||
<AccessGroupEdit trigger={<Button>添加授权组</Button>} />
|
<AccessGroupEdit trigger={<Button>{t("access.group.add")}</Button>} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -66,7 +76,7 @@ const Access = () => {
|
|||||||
handleTabItemClick("access");
|
handleTabItemClick("access");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
授权管理
|
{t("access.management")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="access_group"
|
value="access_group"
|
||||||
@@ -74,7 +84,7 @@ const Access = () => {
|
|||||||
handleTabItemClick("access_group");
|
handleTabItemClick("access_group");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
授权组管理
|
{t("access.group.management")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="access">
|
<TabsContent value="access">
|
||||||
@@ -85,10 +95,10 @@ const Access = () => {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||||
请添加授权开始部署证书吧。
|
{t("access.empty")}
|
||||||
</div>
|
</div>
|
||||||
<AccessEdit
|
<AccessEdit
|
||||||
trigger={<Button>添加授权</Button>}
|
trigger={<Button>{t("access.add")}</Button>}
|
||||||
op="add"
|
op="add"
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
/>
|
/>
|
||||||
@@ -96,15 +106,15 @@ const Access = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
||||||
<div className="w-48">名称</div>
|
<div className="w-48">{t("name")}</div>
|
||||||
<div className="w-48">服务商</div>
|
<div className="w-48">{t("access.type")}</div>
|
||||||
|
|
||||||
<div className="w-52">创建时间</div>
|
<div className="w-60">{t("create.time")}</div>
|
||||||
<div className="w-52">更新时间</div>
|
<div className="w-60">{t("update.time")}</div>
|
||||||
<div className="grow">操作</div>
|
<div className="grow">{t("operation")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||||
授权列表
|
{t("access.list")}
|
||||||
</div>
|
</div>
|
||||||
{accesses
|
{accesses
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
@@ -124,37 +134,69 @@ const Access = () => {
|
|||||||
src={accessTypeMap.get(access.configType)?.[1]}
|
src={accessTypeMap.get(access.configType)?.[1]}
|
||||||
className="w-6"
|
className="w-6"
|
||||||
/>
|
/>
|
||||||
<div>{accessTypeMap.get(access.configType)?.[0]}</div>
|
<div>
|
||||||
|
{t(accessTypeMap.get(access.configType)?.[0] || "")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="sm:w-52 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-60 w-full pt-1 sm:pt-0 flex items-center">
|
||||||
创建于{" "}
|
{t("created.in")}{" "}
|
||||||
{access.created && convertZulu2Beijing(access.created)}
|
{access.created && convertZulu2Beijing(access.created)}
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:w-52 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-60 w-full pt-1 sm:pt-0 flex items-center">
|
||||||
更新于{" "}
|
{t("updated.in")}{" "}
|
||||||
{access.updated && convertZulu2Beijing(access.updated)}
|
{access.updated && convertZulu2Beijing(access.updated)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center grow justify-start pt-1 sm:pt-0">
|
<div className="flex items-center grow justify-start pt-1 sm:pt-0">
|
||||||
<AccessEdit
|
<AccessEdit
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant={"link"} className="p-0">
|
<Button variant={"link"} className="p-0">
|
||||||
编辑
|
{t("edit")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
op="edit"
|
op="edit"
|
||||||
data={access}
|
data={access}
|
||||||
/>
|
/>
|
||||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||||
<Button
|
<AccessEdit
|
||||||
variant={"link"}
|
trigger={
|
||||||
className="p-0"
|
<Button variant={"link"} className="p-0">
|
||||||
onClick={() => {
|
{t("copy")}
|
||||||
handleDelete(access);
|
</Button>
|
||||||
}}
|
}
|
||||||
>
|
op="copy"
|
||||||
删除
|
data={access}
|
||||||
</Button>
|
/>
|
||||||
|
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||||
|
<AlertDialog>
|
||||||
|
<AlertDialogTrigger asChild>
|
||||||
|
<Button variant={"link"} size={"sm"}>
|
||||||
|
{t('delete')}
|
||||||
|
</Button>
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle className="dark:text-gray-200">
|
||||||
|
{t('access.group.delete')}
|
||||||
|
</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
{t('access.delete.confirm')}
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel className="dark:text-gray-200">
|
||||||
|
{t('cancel')}
|
||||||
|
</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={() => {
|
||||||
|
handleDelete(access);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('confirm')}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const [statistic, setStatistic] = useState<Statistic>();
|
const [statistic, setStatistic] = useState<Statistic>();
|
||||||
const [deployments, setDeployments] = useState<Deployment[]>();
|
const [deployments, setDeployments] = useState<Deployment[]>();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchStatistic = async () => {
|
const fetchStatistic = async () => {
|
||||||
@@ -55,7 +57,7 @@ const Dashboard = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="text-muted-foreground">控制面板</div>
|
<div className="text-muted-foreground">{t("dashboard")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex mt-10 gap-5 flex-col flex-wrap md:flex-row">
|
<div className="flex mt-10 gap-5 flex-col flex-wrap md:flex-row">
|
||||||
<div className="w-full md:w-[250px] 3xl:w-[300px] flex items-center rounded-md p-3 shadow-lg border">
|
<div className="w-full md:w-[250px] 3xl:w-[300px] flex items-center rounded-md p-3 shadow-lg border">
|
||||||
@@ -63,7 +65,9 @@ const Dashboard = () => {
|
|||||||
<SquareSigma size={48} strokeWidth={1} className="text-blue-400" />
|
<SquareSigma size={48} strokeWidth={1} className="text-blue-400" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">所有</div>
|
<div className="text-muted-foreground font-semibold">
|
||||||
|
{t("dashboard.all")}
|
||||||
|
</div>
|
||||||
<div className="flex items-baseline">
|
<div className="flex items-baseline">
|
||||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||||
{statistic?.total ? (
|
{statistic?.total ? (
|
||||||
@@ -74,7 +78,9 @@ const Dashboard = () => {
|
|||||||
0
|
0
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">个</div>
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
|
{t("dashboard.unit")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,7 +90,9 @@ const Dashboard = () => {
|
|||||||
<CalendarX2 size={48} strokeWidth={1} className="text-red-400" />
|
<CalendarX2 size={48} strokeWidth={1} className="text-red-400" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">即将过期</div>
|
<div className="text-muted-foreground font-semibold">
|
||||||
|
{t("dashboard.near.expired")}
|
||||||
|
</div>
|
||||||
<div className="flex items-baseline">
|
<div className="flex items-baseline">
|
||||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||||
{statistic?.expired ? (
|
{statistic?.expired ? (
|
||||||
@@ -95,7 +103,9 @@ const Dashboard = () => {
|
|||||||
0
|
0
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">个</div>
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
|
{t("dashboard.unit")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -109,7 +119,9 @@ const Dashboard = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">启用中</div>
|
<div className="text-muted-foreground font-semibold">
|
||||||
|
{t("dashboard.enabled")}
|
||||||
|
</div>
|
||||||
<div className="flex items-baseline">
|
<div className="flex items-baseline">
|
||||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||||
{statistic?.enabled ? (
|
{statistic?.enabled ? (
|
||||||
@@ -120,7 +132,9 @@ const Dashboard = () => {
|
|||||||
0
|
0
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">个</div>
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
|
{t("dashboard.unit")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +144,9 @@ const Dashboard = () => {
|
|||||||
<Ban size={48} strokeWidth={1} className="text-gray-400" />
|
<Ban size={48} strokeWidth={1} className="text-gray-400" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">未启用</div>
|
<div className="text-muted-foreground font-semibold">
|
||||||
|
{t("dashboard.not.enabled")}
|
||||||
|
</div>
|
||||||
<div className="flex items-baseline">
|
<div className="flex items-baseline">
|
||||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||||
{statistic?.disabled ? (
|
{statistic?.disabled ? (
|
||||||
@@ -144,28 +160,29 @@ const Dashboard = () => {
|
|||||||
0
|
0
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">个</div>
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
|
{t("dashboard.unit")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground mt-5 text-sm">部署历史</div>
|
<div className="text-muted-foreground mt-5 text-sm">
|
||||||
|
{t("deployment.log.name")}
|
||||||
|
</div>
|
||||||
|
|
||||||
{deployments?.length == 0 ? (
|
{deployments?.length == 0 ? (
|
||||||
<>
|
<>
|
||||||
<Alert className="max-w-[40em] mt-10">
|
<Alert className="max-w-[40em] mt-10">
|
||||||
<AlertTitle>暂无数据</AlertTitle>
|
<AlertTitle>{t("no.data")}</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<div className="flex items-center mt-5">
|
<div className="flex items-center mt-5">
|
||||||
<div>
|
<div>
|
||||||
<Smile className="text-yellow-400" size={36} />
|
<Smile className="text-yellow-400" size={36} />
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2">
|
<div className="ml-2"> {t("deployment.log.empty")}</div>
|
||||||
{" "}
|
|
||||||
你暂未创建任何部署,请先添加域名进行部署吧!
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex justify-end">
|
<div className="mt-2 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
@@ -173,7 +190,7 @@ const Dashboard = () => {
|
|||||||
navigate("/edit");
|
navigate("/edit");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
添加域名
|
{t("domain.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
@@ -182,16 +199,18 @@ const Dashboard = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
||||||
<div className="w-48">域名</div>
|
<div className="w-48">{t("domain")}</div>
|
||||||
|
|
||||||
<div className="w-24">状态</div>
|
<div className="w-24">{t("deployment.log.status")}</div>
|
||||||
<div className="w-56">阶段</div>
|
<div className="w-56">{t("deployment.log.stage")}</div>
|
||||||
<div className="w-56 sm:ml-2 text-center">最近执行时间</div>
|
<div className="w-56 sm:ml-2 text-center">
|
||||||
|
{t("deployment.log.last.execution.time")}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grow">操作</div>
|
<div className="grow">{t("operation")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||||
部署历史
|
{t("deployment.log.name")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{deployments?.map((deployment) => (
|
{deployments?.map((deployment) => (
|
||||||
@@ -200,7 +219,14 @@ const Dashboard = () => {
|
|||||||
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
||||||
>
|
>
|
||||||
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">
|
||||||
{deployment.expand.domain?.domain}
|
{deployment.expand.domain?.domain
|
||||||
|
.split(";")
|
||||||
|
.map((domain: string) => (
|
||||||
|
<>
|
||||||
|
{domain}
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:w-24 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-24 w-full pt-1 sm:pt-0 flex items-center">
|
||||||
<DeployState deployment={deployment} />
|
<DeployState deployment={deployment} />
|
||||||
@@ -218,14 +244,14 @@ const Dashboard = () => {
|
|||||||
<Sheet>
|
<Sheet>
|
||||||
<SheetTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<Button variant={"link"} className="p-0">
|
<Button variant={"link"} className="p-0">
|
||||||
日志
|
{t("deployment.log.detail.button.text")}
|
||||||
</Button>
|
</Button>
|
||||||
</SheetTrigger>
|
</SheetTrigger>
|
||||||
<SheetContent className="sm:max-w-5xl">
|
<SheetContent className="sm:max-w-5xl">
|
||||||
<SheetHeader>
|
<SheetHeader>
|
||||||
<SheetTitle>
|
<SheetTitle>
|
||||||
{deployment.expand.domain?.domain}-{deployment.id}
|
{deployment.expand.domain?.domain}-{deployment.id}
|
||||||
部署详情
|
{t("deployment.log.detail")}
|
||||||
</SheetTitle>
|
</SheetTitle>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">
|
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@@ -38,6 +37,8 @@ import EmailsEdit from "@/components/certimate/EmailsEdit";
|
|||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { EmailsSetting } from "@/domain/settings";
|
import { EmailsSetting } from "@/domain/settings";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import StringList from "@/components/certimate/StringList";
|
||||||
|
|
||||||
const Edit = () => {
|
const Edit = () => {
|
||||||
const {
|
const {
|
||||||
@@ -47,6 +48,7 @@ const Edit = () => {
|
|||||||
const [domain, setDomain] = useState<Domain>();
|
const [domain, setDomain] = useState<Domain>();
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [tab, setTab] = useState<"base" | "advance">("base");
|
const [tab, setTab] = useState<"base" | "advance">("base");
|
||||||
|
|
||||||
@@ -68,16 +70,16 @@ const Edit = () => {
|
|||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
domain: z.string().min(1, {
|
||||||
message: "请输入正确的域名",
|
message: "domain.not.empty.verify.message",
|
||||||
}),
|
}),
|
||||||
email: z.string().email().optional(),
|
email: z.string().email("email.valid.message").optional(),
|
||||||
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
|
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
|
||||||
message: "请选择DNS服务商授权配置",
|
message: "domain.management.edit.dns.access.not.empty.message",
|
||||||
}),
|
}),
|
||||||
targetAccess: z.string().optional(),
|
targetAccess: z.string().optional(),
|
||||||
targetType: z.string().regex(/^[a-zA-Z0-9-]+$/, {
|
targetType: z.string().regex(/^[a-zA-Z0-9-]+$/, {
|
||||||
message: "请选择部署服务类型",
|
message: "domain.management.edit.target.type.not.empty.message",
|
||||||
}),
|
}),
|
||||||
variables: z.string().optional(),
|
variables: z.string().optional(),
|
||||||
group: z.string().optional(),
|
group: z.string().optional(),
|
||||||
@@ -138,11 +140,11 @@ const Edit = () => {
|
|||||||
if (group == "" && targetAccess == "") {
|
if (group == "" && targetAccess == "") {
|
||||||
form.setError("group", {
|
form.setError("group", {
|
||||||
type: "manual",
|
type: "manual",
|
||||||
message: "部署授权和部署授权组至少选一个",
|
message: "domain.management.edit.target.access.verify.msg",
|
||||||
});
|
});
|
||||||
form.setError("targetAccess", {
|
form.setError("targetAccess", {
|
||||||
type: "manual",
|
type: "manual",
|
||||||
message: "部署授权和部署授权组至少选一个",
|
message: "domain.management.edit.target.access.verify.msg",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -162,13 +164,13 @@ const Edit = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await save(req);
|
await save(req);
|
||||||
let description = "域名编辑成功";
|
let description = t("domain.management.edit.succeed.tips");
|
||||||
if (req.id == "") {
|
if (req.id == "") {
|
||||||
description = "域名添加成功";
|
description = t("domain.management.add.succeed.tips");
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "成功",
|
title: t("succeed"),
|
||||||
description,
|
description,
|
||||||
});
|
});
|
||||||
navigate("/domains");
|
navigate("/domains");
|
||||||
@@ -193,7 +195,7 @@ const Edit = () => {
|
|||||||
<div className="">
|
<div className="">
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<div className=" h-5 text-muted-foreground">
|
<div className=" h-5 text-muted-foreground">
|
||||||
{domain?.id ? "编辑" : "新增"}域名
|
{domain?.id ? t("domain.edit") : t("domain.add")}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 flex w-full justify-center md:space-x-10 flex-col md:flex-row">
|
<div className="mt-5 flex w-full justify-center md:space-x-10 flex-col md:flex-row">
|
||||||
<div className="w-full md:w-[200px] text-muted-foreground space-x-3 md:space-y-3 flex-row md:flex-col flex">
|
<div className="w-full md:w-[200px] text-muted-foreground space-x-3 md:space-y-3 flex-row md:flex-col flex">
|
||||||
@@ -206,7 +208,7 @@ const Edit = () => {
|
|||||||
setTab("base");
|
setTab("base");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
基础设置
|
{t("basic.setting")}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -217,7 +219,7 @@ const Edit = () => {
|
|||||||
setTab("advance");
|
setTab("advance");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
高级设置
|
{t("advanced.setting")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -232,10 +234,15 @@ const Edit = () => {
|
|||||||
name="domain"
|
name="domain"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "base"}>
|
<FormItem hidden={tab != "base"}>
|
||||||
<FormLabel>域名</FormLabel>
|
<>
|
||||||
<FormControl>
|
<StringList
|
||||||
<Input placeholder="请输入域名" {...field} />
|
value={field.value}
|
||||||
</FormControl>
|
valueType="domain"
|
||||||
|
onValueChange={(domain: string) => {
|
||||||
|
form.setValue("domain", domain);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@@ -247,12 +254,15 @@ const Edit = () => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "base"}>
|
<FormItem hidden={tab != "base"}>
|
||||||
<FormLabel className="flex w-full justify-between">
|
<FormLabel className="flex w-full justify-between">
|
||||||
<div>Email(申请证书需要提供邮箱)</div>
|
<div>
|
||||||
|
{t("email") +
|
||||||
|
t("domain.management.edit.email.description")}
|
||||||
|
</div>
|
||||||
<EmailsEdit
|
<EmailsEdit
|
||||||
trigger={
|
trigger={
|
||||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
<Plus size={14} />
|
<Plus size={14} />
|
||||||
新增
|
{t("add")}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -266,11 +276,15 @@ const Edit = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="请选择邮箱" />
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"domain.management.edit.email.not.empty.message"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>邮箱列表</SelectLabel>
|
<SelectLabel>{t("email.list")}</SelectLabel>
|
||||||
{(emails.content as EmailsSetting).emails.map(
|
{(emails.content as EmailsSetting).emails.map(
|
||||||
(item) => (
|
(item) => (
|
||||||
<SelectItem key={item} value={item}>
|
<SelectItem key={item} value={item}>
|
||||||
@@ -293,12 +307,14 @@ const Edit = () => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "base"}>
|
<FormItem hidden={tab != "base"}>
|
||||||
<FormLabel className="flex w-full justify-between">
|
<FormLabel className="flex w-full justify-between">
|
||||||
<div>DNS 服务商授权配置</div>
|
<div>
|
||||||
|
{t("domain.management.edit.dns.access.label")}
|
||||||
|
</div>
|
||||||
<AccessEdit
|
<AccessEdit
|
||||||
trigger={
|
trigger={
|
||||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
<Plus size={14} />
|
<Plus size={14} />
|
||||||
新增
|
{t("add")}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
op="add"
|
op="add"
|
||||||
@@ -313,11 +329,17 @@ const Edit = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="请选择授权配置" />
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"domain.management.edit.access.not.empty.message"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>服务商授权配置</SelectLabel>
|
<SelectLabel>
|
||||||
|
{t("domain.management.edit.access.label")}
|
||||||
|
</SelectLabel>
|
||||||
{accesses
|
{accesses
|
||||||
.filter((item) => item.usage != "deploy")
|
.filter((item) => item.usage != "deploy")
|
||||||
.map((item) => (
|
.map((item) => (
|
||||||
@@ -349,7 +371,9 @@ const Edit = () => {
|
|||||||
name="targetType"
|
name="targetType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "base"}>
|
<FormItem hidden={tab != "base"}>
|
||||||
<FormLabel>部署服务类型</FormLabel>
|
<FormLabel>
|
||||||
|
{t("domain.management.edit.target.type")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
{...field}
|
{...field}
|
||||||
@@ -359,11 +383,17 @@ const Edit = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="请选择部署服务类型" />
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"domain.management.edit.target.type.not.empty.message"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>部署服务类型</SelectLabel>
|
<SelectLabel>
|
||||||
|
{t("domain.management.edit.target.type")}
|
||||||
|
</SelectLabel>
|
||||||
{targetTypeKeys.map((key) => (
|
{targetTypeKeys.map((key) => (
|
||||||
<SelectItem key={key} value={key}>
|
<SelectItem key={key} value={key}>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
@@ -371,7 +401,9 @@ const Edit = () => {
|
|||||||
className="w-6"
|
className="w-6"
|
||||||
src={targetTypeMap.get(key)?.[1]}
|
src={targetTypeMap.get(key)?.[1]}
|
||||||
/>
|
/>
|
||||||
<div>{targetTypeMap.get(key)?.[0]}</div>
|
<div>
|
||||||
|
{t(targetTypeMap.get(key)?.[0] || "")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -390,12 +422,12 @@ const Edit = () => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "base"}>
|
<FormItem hidden={tab != "base"}>
|
||||||
<FormLabel className="w-full flex justify-between">
|
<FormLabel className="w-full flex justify-between">
|
||||||
<div>部署服务商授权配置</div>
|
<div>{t("domain.management.edit.target.access")}</div>
|
||||||
<AccessEdit
|
<AccessEdit
|
||||||
trigger={
|
trigger={
|
||||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
<Plus size={14} />
|
<Plus size={14} />
|
||||||
新增
|
{t("add")}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
op="add"
|
op="add"
|
||||||
@@ -409,12 +441,19 @@ const Edit = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="请选择授权配置" />
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"domain.management.edit.target.access.not.empty.message"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>
|
<SelectLabel>
|
||||||
服务商授权配置{form.getValues().targetAccess}
|
{t(
|
||||||
|
"domain.management.edit.target.access.content.label"
|
||||||
|
)}{" "}
|
||||||
|
{form.getValues().targetAccess}
|
||||||
</SelectLabel>
|
</SelectLabel>
|
||||||
<SelectItem value="emptyId">
|
<SelectItem value="emptyId">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
@@ -450,9 +489,7 @@ const Edit = () => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "advance" || targetType != "ssh"}>
|
<FormItem hidden={tab != "advance" || targetType != "ssh"}>
|
||||||
<FormLabel className="w-full flex justify-between">
|
<FormLabel className="w-full flex justify-between">
|
||||||
<div>
|
<div>{t("domain.management.edit.group.label")}</div>
|
||||||
部署配置组(用于将一个域名证书部署到多个 ssh 主机)
|
|
||||||
</div>
|
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
@@ -464,7 +501,11 @@ const Edit = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="请选择分组" />
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"domain.management.edit.group.not.empty.message"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="emptyId">
|
<SelectItem value="emptyId">
|
||||||
@@ -509,10 +550,12 @@ const Edit = () => {
|
|||||||
name="variables"
|
name="variables"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "advance"}>
|
<FormItem hidden={tab != "advance"}>
|
||||||
<FormLabel>变量</FormLabel>
|
<FormLabel>{t("variables")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder={`可在SSH部署中使用,形如:\nkey=val;\nkey2=val2;`}
|
placeholder={t(
|
||||||
|
"domain.management.edit.variables.placeholder"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
className="placeholder:whitespace-pre-wrap"
|
className="placeholder:whitespace-pre-wrap"
|
||||||
/>
|
/>
|
||||||
@@ -528,10 +571,12 @@ const Edit = () => {
|
|||||||
name="nameservers"
|
name="nameservers"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden={tab != "advance"}>
|
<FormItem hidden={tab != "advance"}>
|
||||||
<FormLabel>域名服务器</FormLabel>
|
<FormLabel>{t("dns")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder={`自定义域名服务器,多个用分号隔开,如:\n8.8.8.8;\n8.8.4.4;`}
|
placeholder={t(
|
||||||
|
"domain.management.edit.dns.placeholder"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
className="placeholder:whitespace-pre-wrap"
|
className="placeholder:whitespace-pre-wrap"
|
||||||
/>
|
/>
|
||||||
@@ -543,7 +588,7 @@ const Edit = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">{t("save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -35,11 +35,13 @@ import { TooltipContent, TooltipProvider } from "@radix-ui/react-tooltip";
|
|||||||
import { Earth } from "lucide-react";
|
import { Earth } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const query = new URLSearchParams(location.search);
|
const query = new URLSearchParams(location.search);
|
||||||
@@ -127,23 +129,24 @@ const Home = () => {
|
|||||||
await save(domain);
|
await save(domain);
|
||||||
|
|
||||||
toast.toast({
|
toast.toast({
|
||||||
title: "操作成功",
|
title: t("operation.succeed"),
|
||||||
description: "已发起部署,请稍后查看部署日志。",
|
description: t("domain.management.start.deploy.succeed.tips"),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.toast({
|
toast.toast({
|
||||||
title: "执行失败",
|
title: t("domain.management.execution.failed"),
|
||||||
description: (
|
description: (
|
||||||
<>
|
// 这里的 text 只是占位作用,实际文案在 src/i18n/locales/[lang].json
|
||||||
执行失败,请查看
|
<Trans i18nKey="domain.management.execution.failed.tips">
|
||||||
|
text1
|
||||||
<Link
|
<Link
|
||||||
to={`/history?domain=${domain.id}`}
|
to={`/history?domain=${domain.id}`}
|
||||||
className="underline text-blue-500"
|
className="underline text-blue-500"
|
||||||
>
|
>
|
||||||
部署日志
|
text2
|
||||||
</Link>
|
</Link>
|
||||||
查看详情。
|
text3
|
||||||
</>
|
</Trans>
|
||||||
),
|
),
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
@@ -175,8 +178,10 @@ const Home = () => {
|
|||||||
<div className="">
|
<div className="">
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="text-muted-foreground">域名列表</div>
|
<div className="text-muted-foreground">
|
||||||
<Button onClick={handleCreateClick}>新增域名</Button>
|
{t("domain.management.name")}
|
||||||
|
</div>
|
||||||
|
<Button onClick={handleCreateClick}>{t("domain.add")}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!domains.length ? (
|
{!domains.length ? (
|
||||||
@@ -187,26 +192,32 @@ const Home = () => {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||||
请添加域名开始部署证书吧。
|
{t("domain.management.empty")}
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={handleCreateClick} className="mt-3">
|
<Button onClick={handleCreateClick} className="mt-3">
|
||||||
添加域名
|
{t("domain.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
||||||
<div className="w-36">域名</div>
|
<div className="w-36">{t("domain")}</div>
|
||||||
<div className="w-40">有效期限</div>
|
<div className="w-40">{t("domain.management.expiry.date")}</div>
|
||||||
<div className="w-32">最近执行状态</div>
|
<div className="w-32">
|
||||||
<div className="w-64">最近执行阶段</div>
|
{t("domain.management.last.execution.status")}
|
||||||
<div className="w-40 sm:ml-2">最近执行时间</div>
|
</div>
|
||||||
<div className="w-24">是否启用</div>
|
<div className="w-64">
|
||||||
<div className="grow">操作</div>
|
{t("domain.management.last.execution.stage")}
|
||||||
|
</div>
|
||||||
|
<div className="w-40 sm:ml-2">
|
||||||
|
{t("domain.management.last.execution.time")}
|
||||||
|
</div>
|
||||||
|
<div className="w-24">{t("domain.management.enable")}</div>
|
||||||
|
<div className="grow">{t("operation")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||||
域名
|
{t("domain")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{domains.map((domain) => (
|
{domains.map((domain) => (
|
||||||
@@ -214,15 +225,26 @@ const Home = () => {
|
|||||||
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
||||||
key={domain.id}
|
key={domain.id}
|
||||||
>
|
>
|
||||||
<div className="sm:w-36 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-36 w-full pt-1 sm:pt-0 flex items-center truncate">
|
||||||
{domain.domain}
|
{domain.domain.split(";").map((item) => (
|
||||||
|
<>
|
||||||
|
{item}
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:w-40 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-40 w-full pt-1 sm:pt-0 flex items-center">
|
||||||
<div>
|
<div>
|
||||||
{domain.expiredAt ? (
|
{domain.expiredAt ? (
|
||||||
<>
|
<>
|
||||||
<div>有效期90天</div>
|
<div>
|
||||||
<div>{getDate(domain.expiredAt)}到期</div>
|
{t("domain.management.expiry.date1", { date: 90 })}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{t("domain.management.expiry.date2", {
|
||||||
|
date: getDate(domain.expiredAt),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
"---"
|
"---"
|
||||||
@@ -266,7 +288,7 @@ const Home = () => {
|
|||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<div className="border rounded-sm px-3 bg-background text-muted-foreground text-xs">
|
<div className="border rounded-sm px-3 bg-background text-muted-foreground text-xs">
|
||||||
{domain.enabled ? "禁用" : "启用"}
|
{domain.enabled ? t("disable") : t("enable")}
|
||||||
</div>
|
</div>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -278,7 +300,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleHistoryClick(domain.id)}
|
onClick={() => handleHistoryClick(domain.id)}
|
||||||
>
|
>
|
||||||
部署历史
|
{t("deployment.log.name")}
|
||||||
</Button>
|
</Button>
|
||||||
<Show when={domain.enabled ? true : false}>
|
<Show when={domain.enabled ? true : false}>
|
||||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||||
@@ -287,7 +309,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleRightNowClick(domain)}
|
onClick={() => handleRightNowClick(domain)}
|
||||||
>
|
>
|
||||||
立即部署
|
{t("domain.management.start.deploying")}
|
||||||
</Button>
|
</Button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
@@ -304,7 +326,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleForceClick(domain)}
|
onClick={() => handleForceClick(domain)}
|
||||||
>
|
>
|
||||||
强行部署
|
{t("domain.management.forced.deployment")}
|
||||||
</Button>
|
</Button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
@@ -315,7 +337,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleDownloadClick(domain)}
|
onClick={() => handleDownloadClick(domain)}
|
||||||
>
|
>
|
||||||
下载
|
{t("download")}
|
||||||
</Button>
|
</Button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
@@ -325,24 +347,26 @@ const Home = () => {
|
|||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button variant={"link"} className="p-0">
|
<Button variant={"link"} className="p-0">
|
||||||
删除
|
{t("delete")}
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</AlertDialogTrigger>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>删除域名</AlertDialogTitle>
|
<AlertDialogTitle>
|
||||||
|
{t("domain.delete")}
|
||||||
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
确定要删除域名吗?
|
{t("domain.management.delete.confirm")}
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleDeleteClick(domain.id);
|
handleDeleteClick(domain.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
确认
|
{t("confirm")}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
@@ -354,7 +378,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleEditClick(domain.id)}
|
onClick={() => handleEditClick(domain.id)}
|
||||||
>
|
>
|
||||||
编辑
|
{t("edit")}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ import { list } from "@/repository/deployment";
|
|||||||
import { Smile } from "lucide-react";
|
import { Smile } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const History = () => {
|
const History = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [deployments, setDeployments] = useState<Deployment[]>();
|
const [deployments, setDeployments] = useState<Deployment[]>();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
const { t } = useTranslation();
|
||||||
const domain = searchParams.get("domain");
|
const domain = searchParams.get("domain");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -38,20 +40,17 @@ const History = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea className="h-[80vh] overflow-hidden">
|
<ScrollArea className="h-[80vh] overflow-hidden">
|
||||||
<div className="text-muted-foreground">部署历史</div>
|
<div className="text-muted-foreground">{t("deployment.log.name")}</div>
|
||||||
{!deployments?.length ? (
|
{!deployments?.length ? (
|
||||||
<>
|
<>
|
||||||
<Alert className="max-w-[40em] mx-auto mt-20">
|
<Alert className="max-w-[40em] mx-auto mt-20">
|
||||||
<AlertTitle>暂无数据</AlertTitle>
|
<AlertTitle>{t("no.data")}</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<div className="flex items-center mt-5">
|
<div className="flex items-center mt-5">
|
||||||
<div>
|
<div>
|
||||||
<Smile className="text-yellow-400" size={36} />
|
<Smile className="text-yellow-400" size={36} />
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2">
|
<div className="ml-2"> {t("deployment.log.empty")}</div>
|
||||||
{" "}
|
|
||||||
你暂未创建任何部署,请先添加域名进行部署吧!
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex justify-end">
|
<div className="mt-2 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
@@ -59,7 +58,7 @@ const History = () => {
|
|||||||
navigate("/");
|
navigate("/");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
添加域名
|
{t("domain.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
@@ -68,16 +67,18 @@ const History = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
||||||
<div className="w-48">域名</div>
|
<div className="w-48">{t("domain")}</div>
|
||||||
|
|
||||||
<div className="w-24">状态</div>
|
<div className="w-24">{t("deployment.log.status")}</div>
|
||||||
<div className="w-56">阶段</div>
|
<div className="w-56">{t("deployment.log.stage")}</div>
|
||||||
<div className="w-56 sm:ml-2 text-center">最近执行时间</div>
|
<div className="w-56 sm:ml-2 text-center">
|
||||||
|
{t("deployment.log.last.execution.time")}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grow">操作</div>
|
<div className="grow">{t("operation")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||||
部署历史
|
{t("deployment.log.name")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{deployments?.map((deployment) => (
|
{deployments?.map((deployment) => (
|
||||||
@@ -86,7 +87,14 @@ const History = () => {
|
|||||||
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
||||||
>
|
>
|
||||||
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">
|
||||||
{deployment.expand.domain?.domain}
|
{deployment.expand.domain?.domain
|
||||||
|
.split(";")
|
||||||
|
.map((domain: string) => (
|
||||||
|
<>
|
||||||
|
{domain}
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:w-24 w-full pt-1 sm:pt-0 flex items-center">
|
<div className="sm:w-24 w-full pt-1 sm:pt-0 flex items-center">
|
||||||
<DeployState deployment={deployment} />
|
<DeployState deployment={deployment} />
|
||||||
@@ -104,14 +112,14 @@ const History = () => {
|
|||||||
<Sheet>
|
<Sheet>
|
||||||
<SheetTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<Button variant={"link"} className="p-0">
|
<Button variant={"link"} className="p-0">
|
||||||
日志
|
{t("deployment.log.detail.button.text")}
|
||||||
</Button>
|
</Button>
|
||||||
</SheetTrigger>
|
</SheetTrigger>
|
||||||
<SheetContent className="sm:max-w-5xl">
|
<SheetContent className="sm:max-w-5xl">
|
||||||
<SheetHeader>
|
<SheetHeader>
|
||||||
<SheetTitle>
|
<SheetTitle>
|
||||||
{deployment.expand.domain?.domain}-{deployment.id}
|
{deployment.expand.domain?.domain}-{deployment.id}
|
||||||
部署详情
|
{t("deployment.log.detail")}
|
||||||
</SheetTitle>
|
</SheetTitle>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">
|
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
@@ -11,20 +16,19 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { getErrMessage } from "@/lib/error";
|
import { getErrMessage } from "@/lib/error";
|
||||||
import { getPb } from "@/repository/api";
|
import { getPb } from "@/repository/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
username: z.string().email({
|
username: z.string().email({
|
||||||
message: "请输入正确的邮箱地址",
|
message: "login.username.no.empty.message",
|
||||||
}),
|
}),
|
||||||
password: z.string().min(10, {
|
password: z.string().min(10, {
|
||||||
message: "密码至少10个字符",
|
message: "login.password.length.message",
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@@ -61,7 +65,7 @@ const Login = () => {
|
|||||||
name="username"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>用户名</FormLabel>
|
<FormLabel>{t('username')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="email" {...field} />
|
<Input placeholder="email" {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -76,9 +80,9 @@ const Login = () => {
|
|||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>密码</FormLabel>
|
<FormLabel>{t('password')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="shadcn" {...field} type="password" />
|
<Input placeholder="password" {...field} type="password" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -86,7 +90,7 @@ const Login = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">登录</Button>
|
<Button type="submit">{t('login.submit')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -15,16 +15,18 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
email: z.string().email("请输入正确的邮箱"),
|
email: z.string().email("setting.account.email.valid.message"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const Account = () => {
|
const Account = () => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [changed, setChanged] = useState(false);
|
const [changed, setChanged] = useState(false);
|
||||||
|
|
||||||
@@ -43,8 +45,8 @@ const Account = () => {
|
|||||||
|
|
||||||
getPb().authStore.clear();
|
getPb().authStore.clear();
|
||||||
toast({
|
toast({
|
||||||
title: "修改账户邮箱功",
|
title: t("setting.account.email.change.succeed"),
|
||||||
description: "请重新登录",
|
description: t("setting.account.log.back.in"),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
@@ -52,7 +54,7 @@ const Account = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = getErrMessage(e);
|
const message = getErrMessage(e);
|
||||||
toast({
|
toast({
|
||||||
title: "修改账户邮箱失败",
|
title: t("setting.account.email.change.failed"),
|
||||||
description: message,
|
description: message,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
@@ -72,10 +74,10 @@ const Account = () => {
|
|||||||
name="email"
|
name="email"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>邮箱</FormLabel>
|
<FormLabel>{t('email')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="请输入邮箱"
|
placeholder={t('setting.email.placeholder')}
|
||||||
{...field}
|
{...field}
|
||||||
type="email"
|
type="email"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -92,10 +94,10 @@ const Account = () => {
|
|||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
{changed ? (
|
{changed ? (
|
||||||
<Button type="submit">确认修改</Button>
|
<Button type="submit">{t('setting.submit')}</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button type="submit" disabled variant={"secondary"}>
|
<Button type="submit" disabled variant={"secondary"}>
|
||||||
确认修改
|
{t('setting.submit')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,15 +9,18 @@ import {
|
|||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "@/components/ui/accordion";
|
} from "@/components/ui/accordion";
|
||||||
import { NotifyProvider } from "@/providers/notify";
|
import { NotifyProvider } from "@/providers/notify";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const Notify = () => {
|
const Notify = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NotifyProvider>
|
<NotifyProvider>
|
||||||
<div className="border rounded-sm p-5 shadow-lg">
|
<div className="border rounded-sm p-5 shadow-lg">
|
||||||
<Accordion type={"multiple"} className="dark:text-stone-200">
|
<Accordion type={"multiple"} className="dark:text-stone-200">
|
||||||
<AccordionItem value="item-1" className="dark:border-stone-200">
|
<AccordionItem value="item-1" className="dark:border-stone-200">
|
||||||
<AccordionTrigger>模板</AccordionTrigger>
|
<AccordionTrigger>{t('template')}</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<NotifyTemplate />
|
<NotifyTemplate />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
@@ -27,21 +30,21 @@ const Notify = () => {
|
|||||||
<div className="border rounded-md p-5 mt-7 shadow-lg">
|
<div className="border rounded-md p-5 mt-7 shadow-lg">
|
||||||
<Accordion type={"single"} className="dark:text-stone-200">
|
<Accordion type={"single"} className="dark:text-stone-200">
|
||||||
<AccordionItem value="item-2" className="dark:border-stone-200">
|
<AccordionItem value="item-2" className="dark:border-stone-200">
|
||||||
<AccordionTrigger>钉钉</AccordionTrigger>
|
<AccordionTrigger>{t('ding.talk')}</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<DingTalk />
|
<DingTalk />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|
||||||
<AccordionItem value="item-4" className="dark:border-stone-200">
|
<AccordionItem value="item-4" className="dark:border-stone-200">
|
||||||
<AccordionTrigger>Telegram</AccordionTrigger>
|
<AccordionTrigger>{t('telegram')}</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<Telegram />
|
<Telegram />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|
||||||
<AccordionItem value="item-5" className="dark:border-stone-200">
|
<AccordionItem value="item-5" className="dark:border-stone-200">
|
||||||
<AccordionTrigger>Webhook</AccordionTrigger>
|
<AccordionTrigger>{t('webhook')}</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<Webhook />
|
<Webhook />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
|
|||||||
@@ -14,29 +14,31 @@ import { getPb } from "@/repository/api";
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const formSchema = z
|
const formSchema = z
|
||||||
.object({
|
.object({
|
||||||
oldPassword: z.string().min(10, {
|
oldPassword: z.string().min(10, {
|
||||||
message: "密码至少10个字符",
|
message: "setting.password.length.message",
|
||||||
}),
|
}),
|
||||||
newPassword: z.string().min(10, {
|
newPassword: z.string().min(10, {
|
||||||
message: "密码至少10个字符",
|
message: "setting.password.length.message",
|
||||||
}),
|
}),
|
||||||
confirmPassword: z.string().min(10, {
|
confirmPassword: z.string().min(10, {
|
||||||
message: "密码至少10个字符",
|
message: "setting.password.length.message",
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.refine((data) => data.newPassword === data.confirmPassword, {
|
.refine((data) => data.newPassword === data.confirmPassword, {
|
||||||
message: "两次密码不一致",
|
message: "setting.password.not.match",
|
||||||
path: ["confirmPassword"],
|
path: ["confirmPassword"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const Password = () => {
|
const Password = () => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
@@ -66,8 +68,8 @@ const Password = () => {
|
|||||||
|
|
||||||
getPb().authStore.clear();
|
getPb().authStore.clear();
|
||||||
toast({
|
toast({
|
||||||
title: "修改密码成功",
|
title: t('setting.password.change.succeed'),
|
||||||
description: "请重新登录",
|
description: t("setting.account.log.back.in"),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
@@ -75,7 +77,7 @@ const Password = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = getErrMessage(e);
|
const message = getErrMessage(e);
|
||||||
toast({
|
toast({
|
||||||
title: "修改密码失败",
|
title: t('setting.password.change.failed'),
|
||||||
description: message,
|
description: message,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
@@ -95,9 +97,9 @@ const Password = () => {
|
|||||||
name="oldPassword"
|
name="oldPassword"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>当前密码</FormLabel>
|
<FormLabel>{t('setting.password.current.password')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="当前密码" {...field} type="password" />
|
<Input placeholder={t('setting.password.current.password')} {...field} type="password" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -110,7 +112,7 @@ const Password = () => {
|
|||||||
name="newPassword"
|
name="newPassword"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>新密码</FormLabel>
|
<FormLabel>{t('setting.password.new.password')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="newPassword"
|
placeholder="newPassword"
|
||||||
@@ -129,7 +131,7 @@ const Password = () => {
|
|||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>确认密码</FormLabel>
|
<FormLabel>{t('setting.password.confirm.password')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="confirmPassword"
|
placeholder="confirmPassword"
|
||||||
@@ -143,7 +145,7 @@ const Password = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">确认修改</Button>
|
<Button type="submit">{t('setting.submit')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
250
ui/src/pages/setting/SSLProvider.tsx
Normal file
250
ui/src/pages/setting/SSLProvider.tsx
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SSLProvider as SSLProviderType,
|
||||||
|
SSLProviderSetting,
|
||||||
|
Setting,
|
||||||
|
} from "@/domain/settings";
|
||||||
|
import { getErrMessage } from "@/lib/error";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
import { getSetting, update } from "@/repository/settings";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const SSLProvider = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
provider: z.enum(["letsencrypt", "zerossl"], {
|
||||||
|
message: t("setting.ca.not.empty"),
|
||||||
|
}),
|
||||||
|
eabKid: z.string().optional(),
|
||||||
|
eabHmacKey: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
provider: "letsencrypt",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [provider, setProvider] = useState("letsencrypt");
|
||||||
|
|
||||||
|
const [config, setConfig] = useState<Setting>();
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async () => {
|
||||||
|
const setting = await getSetting("ssl-provider");
|
||||||
|
|
||||||
|
if (setting) {
|
||||||
|
setConfig(setting);
|
||||||
|
const content = setting.content as SSLProviderSetting;
|
||||||
|
|
||||||
|
form.setValue("provider", content.provider);
|
||||||
|
form.setValue("eabKid", content.config[content.provider].eabKid);
|
||||||
|
form.setValue(
|
||||||
|
"eabHmacKey",
|
||||||
|
content.config[content.provider].eabHmacKey
|
||||||
|
);
|
||||||
|
setProvider(content.provider);
|
||||||
|
} else {
|
||||||
|
form.setValue("provider", "letsencrypt");
|
||||||
|
setProvider("letsencrypt");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getOptionCls = (val: string) => {
|
||||||
|
if (provider === val) {
|
||||||
|
return "border-primary";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||||
|
if (values.provider === "zerossl") {
|
||||||
|
if (!values.eabKid) {
|
||||||
|
form.setError("eabKid", {
|
||||||
|
message: t("setting.ca.eab_kid_hmac_key.not.empty"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!values.eabHmacKey) {
|
||||||
|
form.setError("eabHmacKey", {
|
||||||
|
message: t("setting.ca.eab_kid_hmac_key.not.empty"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!values.eabKid || !values.eabHmacKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setting: Setting = {
|
||||||
|
id: config?.id,
|
||||||
|
name: "ssl-provider",
|
||||||
|
content: {
|
||||||
|
provider: values.provider,
|
||||||
|
config: {
|
||||||
|
letsencrypt: {},
|
||||||
|
zerossl: {
|
||||||
|
eabKid: values.eabKid ?? "",
|
||||||
|
eabHmacKey: values.eabHmacKey ?? "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await update(setting);
|
||||||
|
toast({
|
||||||
|
title: t("update.succeed"),
|
||||||
|
description: t("update.succeed"),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
const message = getErrMessage(e);
|
||||||
|
toast({
|
||||||
|
title: t("update.failed"),
|
||||||
|
description: message,
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="w-full md:max-w-[35em]">
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-8 dark:text-stone-200"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="provider"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t("ca")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
{...field}
|
||||||
|
className="flex"
|
||||||
|
onValueChange={(val) => {
|
||||||
|
setProvider(val);
|
||||||
|
form.setValue("provider", val as SSLProviderType);
|
||||||
|
}}
|
||||||
|
value={provider}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<RadioGroupItem value="letsencrypt" id="letsencrypt" />
|
||||||
|
<Label htmlFor="letsencrypt">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex items-center space-x-2 border p-2 rounded cursor-pointer",
|
||||||
|
getOptionCls("letsencrypt")
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={"/imgs/providers/letsencrypt.svg"}
|
||||||
|
className="h-6"
|
||||||
|
/>
|
||||||
|
<div>{"Let's Encrypt"}</div>
|
||||||
|
</div>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<RadioGroupItem value="zerossl" id="zerossl" />
|
||||||
|
<Label htmlFor="zerossl">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex items-center space-x-2 border p-2 rounded cursor-pointer",
|
||||||
|
getOptionCls("zerossl")
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={"/imgs/providers/zerossl.svg"}
|
||||||
|
className="h-6"
|
||||||
|
/>
|
||||||
|
<div>{"ZeroSSL"}</div>
|
||||||
|
</div>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="eabKid"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem hidden={provider !== "zerossl"}>
|
||||||
|
<FormLabel>EAB_KID</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("setting.ca.eab_kid.not.empty")}
|
||||||
|
{...field}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="eabHmacKey"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem hidden={provider !== "zerossl"}>
|
||||||
|
<FormLabel>EAB_HMAC_KEY</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("setting.ca.eab_hmac_key.not.empty")}
|
||||||
|
{...field}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit">{t("setting.submit")}</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SSLProvider;
|
||||||
@@ -12,6 +12,7 @@ import SettingLayout from "./pages/SettingLayout";
|
|||||||
import Dashboard from "./pages/dashboard/Dashboard";
|
import Dashboard from "./pages/dashboard/Dashboard";
|
||||||
import Account from "./pages/setting/Account";
|
import Account from "./pages/setting/Account";
|
||||||
import Notify from "./pages/setting/Notify";
|
import Notify from "./pages/setting/Notify";
|
||||||
|
import SSLProvider from "./pages/setting/SSLProvider";
|
||||||
|
|
||||||
export const router = createHashRouter([
|
export const router = createHashRouter([
|
||||||
{
|
{
|
||||||
@@ -54,6 +55,10 @@ export const router = createHashRouter([
|
|||||||
path: "/setting/notify",
|
path: "/setting/notify",
|
||||||
element: <Notify />,
|
element: <Notify />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/setting/ssl-provider",
|
||||||
|
element: <SSLProvider />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user