Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
704d2eed32 | ||
|
|
3348301493 | ||
|
|
afeae4269c | ||
|
|
be15f2b6a6 | ||
|
|
0c1d3341f4 | ||
|
|
1a9cad355c | ||
|
|
2c97fa929a | ||
|
|
e397793153 | ||
|
|
9bd279a8a0 | ||
|
|
dd7897feff | ||
|
|
d851a86a9b | ||
|
|
62133a91a6 | ||
|
|
5b30fc8aba | ||
|
|
2ed94bf509 | ||
|
|
1928a47961 | ||
|
|
19f5348802 | ||
|
|
f148240bcf | ||
|
|
f914931bc9 | ||
|
|
8c1033634d | ||
|
|
781b79f529 | ||
|
|
7d74e1d41e | ||
|
|
ad91703492 | ||
|
|
a007c81e9a | ||
|
|
39bffe3389 | ||
|
|
3b06c7b0a6 | ||
|
|
3f2767b28b | ||
|
|
312c6e685a | ||
|
|
d2b6ab75b7 | ||
|
|
9f1b00f04c | ||
|
|
dc16294b3d | ||
|
|
77dfcef168 | ||
|
|
30ef5841d6 |
@@ -15,6 +15,7 @@ Certimate 就是为了解决上述问题而产生的,它具有以下特点:
|
|||||||
|
|
||||||
相关文章:
|
相关文章:
|
||||||
|
|
||||||
|
* [⚠️⚠️⚠️V0.2.0-第一个不向后兼容的版本](https://docs.certimate.me/blog/v0.2.0)
|
||||||
* [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)
|
||||||
|
|
||||||
@@ -32,6 +33,11 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
|
|||||||
./certimate serve
|
./certimate serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
或运行以下命令自动给 Certimate 自身添加证书
|
||||||
|
```bash
|
||||||
|
./certimate serve 你的域名
|
||||||
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> MacOS 在执行二进制文件时会提示:无法打开“certimate”,因为Apple无法检查其是否包含恶意软件。可在系统设置> 隐私与安全性> 安全性 中点击 "仍然允许",然后再次尝试执行二进制文件。
|
> MacOS 在执行二进制文件时会提示:无法打开“certimate”,因为Apple无法检查其是否包含恶意软件。可在系统设置> 隐私与安全性> 安全性 中点击 "仍然允许",然后再次尝试执行二进制文件。
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ You can download the precompiled binary files directly from the [Releases page](
|
|||||||
./certimate serve
|
./certimate serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or run the following command to automatically add a certificate to Certimate itself.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./certimate serve yourDomain
|
||||||
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!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.
|
> 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.
|
||||||
|
|
||||||
|
|||||||
91
go.mod
91
go.mod
@@ -1,16 +1,16 @@
|
|||||||
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/cdn-20180510/v5 v5.0.0
|
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0
|
||||||
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/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||||
|
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,9 +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
|
||||||
golang.org/x/crypto v0.26.0
|
golang.org/x/crypto v0.27.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -30,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 (
|
||||||
@@ -44,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
|
||||||
@@ -85,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
|
||||||
@@ -97,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
|
||||||
|
|||||||
252
go.sum
252
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=
|
||||||
@@ -31,12 +31,9 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
|
|||||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
|
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||||
github.com/alibabacloud-go/cas-20200407/v2 v2.3.0 h1:nOrp0n2nFZiYN0wIG7S26YVVaMMzOBkX9GJqUvYnGeE=
|
|
||||||
github.com/alibabacloud-go/cas-20200407/v2 v2.3.0/go.mod h1:yzkgdLANANu/v56k0ptslGl++JJL4Op1V09HTavfoCo=
|
|
||||||
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0 h1:yTKngw4rBR3hdpoo/uCyBffYXfPfjNjlaDL8nTYUIds=
|
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0 h1:yTKngw4rBR3hdpoo/uCyBffYXfPfjNjlaDL8nTYUIds=
|
||||||
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0/go.mod h1:HxQrwVKBx3/6bIwmdDcpqBpSQt2tpi/j4LfEhl+QFPk=
|
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0/go.mod h1:HxQrwVKBx3/6bIwmdDcpqBpSQt2tpi/j4LfEhl+QFPk=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
|
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9 h1:fxMCrZatZfXq5nLcgkmWBXmU3FLC1OR+m/SqVtMqflk=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9 h1:fxMCrZatZfXq5nLcgkmWBXmU3FLC1OR+m/SqVtMqflk=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
|
||||||
@@ -80,8 +77,10 @@ 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/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
|
||||||
|
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||||
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 +89,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 +137,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 +146,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 +173,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 +214,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 +242,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 +280,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 +309,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 +318,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 +334,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,19 +379,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/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=
|
||||||
@@ -398,21 +405,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=
|
||||||
@@ -423,13 +436,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=
|
||||||
@@ -451,8 +468,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=
|
||||||
@@ -462,8 +479,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=
|
||||||
@@ -472,11 +491,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=
|
||||||
@@ -497,6 +517,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=
|
||||||
@@ -508,8 +529,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=
|
||||||
@@ -518,24 +541,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=
|
||||||
@@ -549,8 +576,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=
|
||||||
@@ -561,27 +588,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=
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package applicant
|
|||||||
import (
|
import (
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||||
@@ -25,6 +26,7 @@ func (a *aliyun) Apply() (*Certificate, error) {
|
|||||||
|
|
||||||
os.Setenv("ALICLOUD_ACCESS_KEY", access.AccessKeyId)
|
os.Setenv("ALICLOUD_ACCESS_KEY", access.AccessKeyId)
|
||||||
os.Setenv("ALICLOUD_SECRET_KEY", access.AccessKeySecret)
|
os.Setenv("ALICLOUD_SECRET_KEY", access.AccessKeySecret)
|
||||||
|
os.Setenv("ALICLOUD_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", a.option.Timeout))
|
||||||
dnsProvider, err := alidns.NewDNSProvider()
|
dnsProvider, err := alidns.NewDNSProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package applicant
|
package applicant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"certimate/internal/domain"
|
||||||
"certimate/internal/utils/app"
|
"certimate/internal/utils/app"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
@@ -20,11 +21,12 @@ 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 defaultSSLProvider = "letsencrypt"
|
||||||
@@ -45,6 +47,8 @@ var sslProviderUrls = map[string]string{
|
|||||||
|
|
||||||
const defaultEmail = "536464346@qq.com"
|
const defaultEmail = "536464346@qq.com"
|
||||||
|
|
||||||
|
const defaultTimeout = 60
|
||||||
|
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
CertUrl string `json:"certUrl"`
|
CertUrl string `json:"certUrl"`
|
||||||
CertStableUrl string `json:"certStableUrl"`
|
CertStableUrl string `json:"certStableUrl"`
|
||||||
@@ -59,6 +63,7 @@ type ApplyOption struct {
|
|||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
Access string `json:"access"`
|
Access string `json:"access"`
|
||||||
Nameservers string `json:"nameservers"`
|
Nameservers string `json:"nameservers"`
|
||||||
|
Timeout int64 `json:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyUser struct {
|
type MyUser struct {
|
||||||
@@ -82,8 +87,22 @@ type Applicant interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Get(record *models.Record) (Applicant, error) {
|
func Get(record *models.Record) (Applicant, error) {
|
||||||
access := record.ExpandedOne("access")
|
|
||||||
email := record.GetString("email")
|
if record.GetString("applyConfig") == "" {
|
||||||
|
return nil, errors.New("apply config is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
applyConfig := &domain.ApplyConfig{}
|
||||||
|
|
||||||
|
record.UnmarshalJSONField("applyConfig", applyConfig)
|
||||||
|
|
||||||
|
access, err := app.GetApp().Dao().FindRecordById("access", applyConfig.Access)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("access record not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
email := applyConfig.Email
|
||||||
if email == "" {
|
if email == "" {
|
||||||
email = defaultEmail
|
email = defaultEmail
|
||||||
}
|
}
|
||||||
@@ -91,13 +110,16 @@ func Get(record *models.Record) (Applicant, error) {
|
|||||||
Email: email,
|
Email: email,
|
||||||
Domain: record.GetString("domain"),
|
Domain: record.GetString("domain"),
|
||||||
Access: access.GetString("config"),
|
Access: access.GetString("config"),
|
||||||
Nameservers: record.GetString("nameservers"),
|
Nameservers: applyConfig.Nameservers,
|
||||||
|
Timeout: applyConfig.Timeout,
|
||||||
}
|
}
|
||||||
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:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package applicant
|
|||||||
import (
|
import (
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
cf "github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
cf "github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||||
@@ -23,6 +24,7 @@ func (c *cloudflare) Apply() (*Certificate, error) {
|
|||||||
json.Unmarshal([]byte(c.option.Access), access)
|
json.Unmarshal([]byte(c.option.Access), access)
|
||||||
|
|
||||||
os.Setenv("CLOUDFLARE_DNS_API_TOKEN", access.DnsApiToken)
|
os.Setenv("CLOUDFLARE_DNS_API_TOKEN", access.DnsApiToken)
|
||||||
|
os.Setenv("CLOUDFLARE_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", c.option.Timeout))
|
||||||
|
|
||||||
provider, err := cf.NewDNSProvider()
|
provider, err := cf.NewDNSProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package applicant
|
|||||||
import (
|
import (
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
godaddyProvider "github.com/go-acme/lego/v4/providers/dns/godaddy"
|
godaddyProvider "github.com/go-acme/lego/v4/providers/dns/godaddy"
|
||||||
@@ -25,6 +26,7 @@ func (a *godaddy) Apply() (*Certificate, error) {
|
|||||||
|
|
||||||
os.Setenv("GODADDY_API_KEY", access.ApiKey)
|
os.Setenv("GODADDY_API_KEY", access.ApiKey)
|
||||||
os.Setenv("GODADDY_API_SECRET", access.ApiSecret)
|
os.Setenv("GODADDY_API_SECRET", access.ApiSecret)
|
||||||
|
os.Setenv("GODADDY_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", a.option.Timeout))
|
||||||
|
|
||||||
dnsProvider, err := godaddyProvider.NewDNSProvider()
|
dnsProvider, err := godaddyProvider.NewDNSProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
38
internal/applicant/huaweicloud.go
Normal file
38
internal/applicant/huaweicloud.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package applicant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"certimate/internal/domain"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"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)
|
||||||
|
os.Setenv("HUAWEICLOUD_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", t.option.Timeout))
|
||||||
|
|
||||||
|
dnsProvider, err := huaweicloudProvider.NewDNSProvider()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply(t.option, dnsProvider)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package applicant
|
|||||||
import (
|
import (
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
namesiloProvider "github.com/go-acme/lego/v4/providers/dns/namesilo"
|
namesiloProvider "github.com/go-acme/lego/v4/providers/dns/namesilo"
|
||||||
@@ -24,6 +25,7 @@ func (a *namesilo) Apply() (*Certificate, error) {
|
|||||||
json.Unmarshal([]byte(a.option.Access), access)
|
json.Unmarshal([]byte(a.option.Access), access)
|
||||||
|
|
||||||
os.Setenv("NAMESILO_API_KEY", access.ApiKey)
|
os.Setenv("NAMESILO_API_KEY", access.ApiKey)
|
||||||
|
os.Setenv("NAMESILO_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", a.option.Timeout))
|
||||||
|
|
||||||
dnsProvider, err := namesiloProvider.NewDNSProvider()
|
dnsProvider, err := namesiloProvider.NewDNSProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package applicant
|
|||||||
import (
|
import (
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
|
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
|
||||||
@@ -25,6 +26,8 @@ func (t *tencent) Apply() (*Certificate, error) {
|
|||||||
|
|
||||||
os.Setenv("TENCENTCLOUD_SECRET_ID", access.SecretId)
|
os.Setenv("TENCENTCLOUD_SECRET_ID", access.SecretId)
|
||||||
os.Setenv("TENCENTCLOUD_SECRET_KEY", access.SecretKey)
|
os.Setenv("TENCENTCLOUD_SECRET_KEY", access.SecretKey)
|
||||||
|
os.Setenv("TENCENTCLOUD_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", t.option.Timeout))
|
||||||
|
|
||||||
dnsProvider, err := tencentcloud.NewDNSProvider()
|
dnsProvider, err := tencentcloud.NewDNSProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,23 +1,16 @@
|
|||||||
package deployer
|
package deployer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"certimate/internal/applicant"
|
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
"certimate/internal/utils/rand"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
cas20200407 "github.com/alibabacloud-go/cas-20200407/v2/client"
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
|
||||||
util "github.com/alibabacloud-go/tea-utils/v2/service"
|
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type aliyun struct {
|
type aliyun struct {
|
||||||
client *cas20200407.Client
|
client *oss.Client
|
||||||
option *DeployerOption
|
option *DeployerOption
|
||||||
infos []string
|
infos []string
|
||||||
}
|
}
|
||||||
@@ -47,160 +40,31 @@ func (a *aliyun) GetInfo() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliyun) Deploy(ctx context.Context) error {
|
func (a *aliyun) Deploy(ctx context.Context) error {
|
||||||
|
err := a.client.PutBucketCnameWithCertificate(getDeployString(a.option.DeployConfig, "bucket"), oss.PutBucketCname{
|
||||||
// 查询有没有对应的资源
|
Cname: getDeployString(a.option.DeployConfig, "domain"),
|
||||||
resource, err := a.resource()
|
CertificateConfiguration: &oss.CertificateConfiguration{
|
||||||
if err != nil {
|
Certificate: a.option.Certificate.Certificate,
|
||||||
return err
|
PrivateKey: a.option.Certificate.PrivateKey,
|
||||||
}
|
Force: true,
|
||||||
|
},
|
||||||
a.infos = append(a.infos, toStr("查询对应的资源", resource))
|
|
||||||
|
|
||||||
// 查询有没有对应的联系人
|
|
||||||
contacts, err := a.contacts()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a.infos = append(a.infos, toStr("查询联系人", contacts))
|
|
||||||
|
|
||||||
// 上传证书
|
|
||||||
certId, err := a.uploadCert(&a.option.Certificate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a.infos = append(a.infos, toStr("上传证书", certId))
|
|
||||||
|
|
||||||
// 部署证书
|
|
||||||
jobId, err := a.deploy(resource, certId, contacts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a.infos = append(a.infos, toStr("创建部署证书任务", jobId))
|
|
||||||
|
|
||||||
// 等待部署成功
|
|
||||||
err = a.updateDeployStatus(*jobId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 部署成功后删除旧的证书
|
|
||||||
a.deleteCert(resource)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aliyun) updateDeployStatus(jobId int64) error {
|
|
||||||
// 查询部署状态
|
|
||||||
req := &cas20200407.UpdateDeploymentJobStatusRequest{
|
|
||||||
JobId: tea.Int64(jobId),
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := a.client.UpdateDeploymentJobStatus(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.infos = append(a.infos, toStr("查询对应的资源", resp))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aliyun) deleteCert(resource *cas20200407.ListCloudResourcesResponseBodyData) error {
|
|
||||||
// 查询有没有对应的资源
|
|
||||||
if resource.CertId == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除证书
|
|
||||||
_, err := a.client.DeleteUserCertificate(&cas20200407.DeleteUserCertificateRequest{
|
|
||||||
CertId: resource.CertId,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("deploy aliyun oss error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliyun) contacts() ([]*cas20200407.ListContactResponseBodyContactList, error) {
|
func (a *aliyun) createClient(accessKeyId, accessKeySecret string) (*oss.Client, error) {
|
||||||
listContactRequest := &cas20200407.ListContactRequest{}
|
client, err := oss.New(
|
||||||
runtime := &util.RuntimeOptions{}
|
getDeployString(a.option.DeployConfig, "endpoint"),
|
||||||
|
accessKeyId,
|
||||||
resp, err := a.client.ListContactWithOptions(listContactRequest, runtime)
|
accessKeySecret,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("create aliyun client error: %w", err)
|
||||||
}
|
}
|
||||||
if resp.Body.TotalCount == nil {
|
return client, nil
|
||||||
return nil, errors.New("no contact found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Body.ContactList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aliyun) deploy(resource *cas20200407.ListCloudResourcesResponseBodyData, certId int64, contacts []*cas20200407.ListContactResponseBodyContactList) (*int64, error) {
|
|
||||||
contactIds := make([]string, 0, len(contacts))
|
|
||||||
for _, contact := range contacts {
|
|
||||||
contactIds = append(contactIds, fmt.Sprintf("%d", *contact.ContactId))
|
|
||||||
}
|
|
||||||
// 部署证书
|
|
||||||
createCloudResourceRequest := &cas20200407.CreateDeploymentJobRequest{
|
|
||||||
CertIds: tea.String(fmt.Sprintf("%d", certId)),
|
|
||||||
Name: tea.String(a.option.Domain + rand.RandStr(6)),
|
|
||||||
JobType: tea.String("user"),
|
|
||||||
ResourceIds: tea.String(fmt.Sprintf("%d", *resource.Id)),
|
|
||||||
ContactIds: tea.String(strings.Join(contactIds, ",")),
|
|
||||||
}
|
|
||||||
runtime := &util.RuntimeOptions{}
|
|
||||||
|
|
||||||
resp, err := a.client.CreateDeploymentJobWithOptions(createCloudResourceRequest, runtime)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.Body.JobId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aliyun) uploadCert(cert *applicant.Certificate) (int64, error) {
|
|
||||||
uploadUserCertificateRequest := &cas20200407.UploadUserCertificateRequest{
|
|
||||||
Cert: &cert.Certificate,
|
|
||||||
Key: &cert.PrivateKey,
|
|
||||||
Name: tea.String(a.option.Domain + rand.RandStr(6)),
|
|
||||||
}
|
|
||||||
runtime := &util.RuntimeOptions{}
|
|
||||||
|
|
||||||
resp, err := a.client.UploadUserCertificateWithOptions(uploadUserCertificateRequest, runtime)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return *resp.Body.CertId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aliyun) createClient(accessKeyId, accessKeySecret string) (_result *cas20200407.Client, _err error) {
|
|
||||||
config := &openapi.Config{
|
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
|
||||||
}
|
|
||||||
config.Endpoint = tea.String("cas.aliyuncs.com")
|
|
||||||
_result = &cas20200407.Client{}
|
|
||||||
_result, _err = cas20200407.NewClient(config)
|
|
||||||
return _result, _err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aliyun) resource() (*cas20200407.ListCloudResourcesResponseBodyData, error) {
|
|
||||||
|
|
||||||
listCloudResourcesRequest := &cas20200407.ListCloudResourcesRequest{
|
|
||||||
CloudProduct: tea.String(a.option.Product),
|
|
||||||
Keyword: tea.String(a.option.Domain),
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := a.client.ListCloudResources(listCloudResourcesRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if *resp.Body.Total == 0 {
|
|
||||||
return nil, errors.New("no resource found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Body.Data[0], nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package deployer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
|
"certimate/internal/utils/rand"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -46,9 +47,9 @@ func (a *AliyunCdn) GetInfo() []string {
|
|||||||
|
|
||||||
func (a *AliyunCdn) Deploy(ctx context.Context) error {
|
func (a *AliyunCdn) Deploy(ctx context.Context) error {
|
||||||
|
|
||||||
certName := fmt.Sprintf("%s-%s", a.option.Domain, a.option.DomainId)
|
certName := fmt.Sprintf("%s-%s-%s", a.option.Domain, a.option.DomainId, rand.RandStr(6))
|
||||||
setCdnDomainSSLCertificateRequest := &cdn20180510.SetCdnDomainSSLCertificateRequest{
|
setCdnDomainSSLCertificateRequest := &cdn20180510.SetCdnDomainSSLCertificateRequest{
|
||||||
DomainName: tea.String(a.option.Domain),
|
DomainName: tea.String(getDeployString(a.option.DeployConfig, "domain")),
|
||||||
CertName: tea.String(certName),
|
CertName: tea.String(certName),
|
||||||
CertType: tea.String("upload"),
|
CertType: tea.String("upload"),
|
||||||
SSLProtocol: tea.String("on"),
|
SSLProtocol: tea.String("on"),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package deployer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
|
"certimate/internal/utils/rand"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -51,9 +52,9 @@ func (a *AliyunEsa) GetInfo() []string {
|
|||||||
|
|
||||||
func (a *AliyunEsa) Deploy(ctx context.Context) error {
|
func (a *AliyunEsa) Deploy(ctx context.Context) error {
|
||||||
|
|
||||||
certName := fmt.Sprintf("%s-%s", a.option.Domain, a.option.DomainId)
|
certName := fmt.Sprintf("%s-%s-%s", a.option.Domain, a.option.DomainId, rand.RandStr(6))
|
||||||
setDcdnDomainSSLCertificateRequest := &dcdn20180115.SetDcdnDomainSSLCertificateRequest{
|
setDcdnDomainSSLCertificateRequest := &dcdn20180115.SetDcdnDomainSSLCertificateRequest{
|
||||||
DomainName: tea.String(a.option.Domain),
|
DomainName: tea.String(getDeployString(a.option.DeployConfig, "domain")),
|
||||||
CertName: tea.String(certName),
|
CertName: tea.String(certName),
|
||||||
CertType: tea.String("upload"),
|
CertType: tea.String("upload"),
|
||||||
SSLProtocol: tea.String("on"),
|
SSLProtocol: tea.String("on"),
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package deployer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"certimate/internal/applicant"
|
"certimate/internal/applicant"
|
||||||
|
"certimate/internal/domain"
|
||||||
"certimate/internal/utils/app"
|
"certimate/internal/utils/app"
|
||||||
"certimate/internal/utils/variables"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -30,6 +30,7 @@ type DeployerOption struct {
|
|||||||
Product string `json:"product"`
|
Product string `json:"product"`
|
||||||
Access string `json:"access"`
|
Access string `json:"access"`
|
||||||
AceessRecord *models.Record `json:"-"`
|
AceessRecord *models.Record `json:"-"`
|
||||||
|
DeployConfig domain.DeployConfig `json:"deployConfig"`
|
||||||
Certificate applicant.Certificate `json:"certificate"`
|
Certificate applicant.Certificate `json:"certificate"`
|
||||||
Variables map[string]string `json:"variables"`
|
Variables map[string]string `json:"variables"`
|
||||||
}
|
}
|
||||||
@@ -42,52 +43,29 @@ type Deployer interface {
|
|||||||
|
|
||||||
func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error) {
|
func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error) {
|
||||||
rs := make([]Deployer, 0)
|
rs := make([]Deployer, 0)
|
||||||
|
if record.GetString("deployConfig") == "" {
|
||||||
if record.GetString("targetAccess") != "" {
|
return rs, nil
|
||||||
singleDeployer, err := Get(record, cert)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rs = append(rs, singleDeployer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if record.GetString("group") != "" {
|
deployConfigs := make([]domain.DeployConfig, 0)
|
||||||
group := record.ExpandedOne("group")
|
|
||||||
|
|
||||||
if errs := app.GetApp().Dao().ExpandRecord(group, []string{"access"}, nil); len(errs) > 0 {
|
|
||||||
|
|
||||||
errList := make([]error, 0)
|
|
||||||
for name, err := range errs {
|
|
||||||
errList = append(errList, fmt.Errorf("展开记录失败,%s: %w", name, err))
|
|
||||||
}
|
|
||||||
err := errors.Join(errList...)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
records := group.ExpandedAll("access")
|
|
||||||
|
|
||||||
deployers, err := getByGroup(record, cert, records...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rs = append(rs, deployers...)
|
|
||||||
|
|
||||||
|
err := record.UnmarshalJSONField("deployConfig", &deployConfigs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析部署配置失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rs, nil
|
if len(deployConfigs) == 0 {
|
||||||
|
return rs, nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
for _, deployConfig := range deployConfigs {
|
||||||
|
|
||||||
func getByGroup(record *models.Record, cert *applicant.Certificate, accesses ...*models.Record) ([]Deployer, error) {
|
deployer, err := getWithDeployConfig(record, cert, deployConfig)
|
||||||
|
|
||||||
rs := make([]Deployer, 0)
|
|
||||||
|
|
||||||
for _, access := range accesses {
|
|
||||||
deployer, err := getWithAccess(record, cert, access)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rs = append(rs, deployer)
|
rs = append(rs, deployer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +73,21 @@ func getByGroup(record *models.Record, cert *applicant.Certificate, accesses ...
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWithAccess(record *models.Record, cert *applicant.Certificate, access *models.Record) (Deployer, error) {
|
func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, deployConfig domain.DeployConfig) (Deployer, error) {
|
||||||
|
|
||||||
|
access, err := app.GetApp().Dao().FindRecordById("access", deployConfig.Access)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("access record not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
option := &DeployerOption{
|
option := &DeployerOption{
|
||||||
DomainId: record.Id,
|
DomainId: record.Id,
|
||||||
Domain: record.GetString("domain"),
|
Domain: record.GetString("domain"),
|
||||||
Product: getProduct(record),
|
Product: getProduct(deployConfig.Type),
|
||||||
Access: access.GetString("config"),
|
Access: access.GetString("config"),
|
||||||
AceessRecord: access,
|
AceessRecord: access,
|
||||||
Variables: variables.Parse2Map(record.GetString("variables")),
|
DeployConfig: deployConfig,
|
||||||
}
|
}
|
||||||
if cert != nil {
|
if cert != nil {
|
||||||
option.Certificate = *cert
|
option.Certificate = *cert
|
||||||
@@ -114,7 +98,7 @@ func getWithAccess(record *models.Record, cert *applicant.Certificate, access *m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch record.GetString("targetType") {
|
switch deployConfig.Type {
|
||||||
case targetAliyunOss:
|
case targetAliyunOss:
|
||||||
return NewAliyun(option)
|
return NewAliyun(option)
|
||||||
case targetAliyunCdn:
|
case targetAliyunCdn:
|
||||||
@@ -136,16 +120,8 @@ func getWithAccess(record *models.Record, cert *applicant.Certificate, access *m
|
|||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func Get(record *models.Record, cert *applicant.Certificate) (Deployer, error) {
|
func getProduct(t string) string {
|
||||||
|
rs := strings.Split(t, "-")
|
||||||
access := record.ExpandedOne("targetAccess")
|
|
||||||
|
|
||||||
return getWithAccess(record, cert, access)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getProduct(record *models.Record) string {
|
|
||||||
targetType := record.GetString("targetType")
|
|
||||||
rs := strings.Split(targetType, "-")
|
|
||||||
if len(rs) < 2 {
|
if len(rs) < 2 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -159,3 +135,39 @@ func toStr(tag string, data any) string {
|
|||||||
byts, _ := json.Marshal(data)
|
byts, _ := json.Marshal(data)
|
||||||
return tag + ":" + string(byts)
|
return tag + ":" + string(byts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDeployString(conf domain.DeployConfig, key string) string {
|
||||||
|
if _, ok := conf.Config[key]; !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok := conf.Config[key].(string)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDeployVariables(conf domain.DeployConfig) map[string]string {
|
||||||
|
rs := make(map[string]string)
|
||||||
|
data, ok := conf.Config["variables"]
|
||||||
|
if !ok {
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, _ := json.Marshal(data)
|
||||||
|
|
||||||
|
kvData := make([]domain.KV, 0)
|
||||||
|
|
||||||
|
if err := json.Unmarshal(bts, &kvData); err != nil {
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kv := range kvData {
|
||||||
|
rs[kv.Key] = kv.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type localAccess struct {
|
type localAccess struct {
|
||||||
Command string `json:"command"`
|
|
||||||
CertPath string `json:"certPath"`
|
|
||||||
KeyPath string `json:"keyPath"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type local struct {
|
type local struct {
|
||||||
@@ -41,18 +38,27 @@ func (l *local) Deploy(ctx context.Context) error {
|
|||||||
if err := json.Unmarshal([]byte(l.option.Access), access); err != nil {
|
if err := json.Unmarshal([]byte(l.option.Access), access); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preCommand := getDeployString(l.option.DeployConfig, "preCommand")
|
||||||
|
|
||||||
|
if preCommand != "" {
|
||||||
|
if err := execCmd(preCommand); err != nil {
|
||||||
|
return fmt.Errorf("执行前置命令失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 复制文件
|
// 复制文件
|
||||||
if err := copyFile(l.option.Certificate.Certificate, access.CertPath); err != nil {
|
if err := copyFile(l.option.Certificate.Certificate, getDeployString(l.option.DeployConfig, "certPath")); err != nil {
|
||||||
return fmt.Errorf("复制证书失败: %w", err)
|
return fmt.Errorf("复制证书失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := copyFile(l.option.Certificate.PrivateKey, access.KeyPath); err != nil {
|
if err := copyFile(l.option.Certificate.PrivateKey, getDeployString(l.option.DeployConfig, "keyPath")); err != nil {
|
||||||
return fmt.Errorf("复制私钥失败: %w", err)
|
return fmt.Errorf("复制私钥失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行命令
|
// 执行命令
|
||||||
|
|
||||||
if err := execCmd(access.Command); err != nil {
|
if err := execCmd(getDeployString(l.option.DeployConfig, "command")); err != nil {
|
||||||
return fmt.Errorf("执行命令失败: %w", err)
|
return fmt.Errorf("执行命令失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func (q *qiuniu) Deploy(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *qiuniu) enableHttps(certId string) error {
|
func (q *qiuniu) enableHttps(certId string) error {
|
||||||
path := fmt.Sprintf("/domain/%s/sslize", q.option.Domain)
|
path := fmt.Sprintf("/domain/%s/sslize", getDeployString(q.option.DeployConfig, "domain"))
|
||||||
|
|
||||||
body := &modifyDomainCertReq{
|
body := &modifyDomainCertReq{
|
||||||
CertID: certId,
|
CertID: certId,
|
||||||
@@ -104,7 +104,7 @@ type domainInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *qiuniu) getDomainInfo() (*domainInfo, error) {
|
func (q *qiuniu) getDomainInfo() (*domainInfo, error) {
|
||||||
path := fmt.Sprintf("/domain/%s", q.option.Domain)
|
path := fmt.Sprintf("/domain/%s", getDeployString(q.option.DeployConfig, "domain"))
|
||||||
|
|
||||||
res, err := q.req(qiniuGateway+path, http.MethodGet, nil)
|
res, err := q.req(qiniuGateway+path, http.MethodGet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -135,8 +135,8 @@ func (q *qiuniu) uploadCert() (string, error) {
|
|||||||
path := "/sslcert"
|
path := "/sslcert"
|
||||||
|
|
||||||
body := &uploadCertReq{
|
body := &uploadCertReq{
|
||||||
Name: q.option.Domain,
|
Name: getDeployString(q.option.DeployConfig, "domain"),
|
||||||
CommonName: q.option.Domain,
|
CommonName: getDeployString(q.option.DeployConfig, "domain"),
|
||||||
Pri: q.option.Certificate.PrivateKey,
|
Pri: q.option.Certificate.PrivateKey,
|
||||||
Ca: q.option.Certificate.Certificate,
|
Ca: q.option.Certificate.Certificate,
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ type modifyDomainCertReq struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *qiuniu) modifyDomainCert(certId string) error {
|
func (q *qiuniu) modifyDomainCert(certId string) error {
|
||||||
path := fmt.Sprintf("/domain/%s/httpsconf", q.option.Domain)
|
path := fmt.Sprintf("/domain/%s/httpsconf", getDeployString(q.option.DeployConfig, "domain"))
|
||||||
|
|
||||||
body := &modifyDomainCertReq{
|
body := &modifyDomainCertReq{
|
||||||
CertID: certId,
|
CertID: certId,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
xpath "path"
|
xpath "path"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
sshPkg "golang.org/x/crypto/ssh"
|
sshPkg "golang.org/x/crypto/ssh"
|
||||||
@@ -19,15 +18,11 @@ 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"`
|
||||||
PreCommand string `json:"preCommand"`
|
|
||||||
Command string `json:"command"`
|
|
||||||
CertPath string `json:"certPath"`
|
|
||||||
KeyPath string `json:"keyPath"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSSH(option *DeployerOption) (Deployer, error) {
|
func NewSSH(option *DeployerOption) (Deployer, error) {
|
||||||
@@ -50,16 +45,6 @@ func (s *ssh) Deploy(ctx context.Context) error {
|
|||||||
if err := json.Unmarshal([]byte(s.option.Access), access); err != nil {
|
if err := json.Unmarshal([]byte(s.option.Access), access); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将证书路径和命令中的变量替换为实际值
|
|
||||||
for k, v := range s.option.Variables {
|
|
||||||
key := fmt.Sprintf("${%s}", k)
|
|
||||||
access.CertPath = strings.ReplaceAll(access.CertPath, key, v)
|
|
||||||
access.KeyPath = strings.ReplaceAll(access.KeyPath, key, v)
|
|
||||||
access.Command = strings.ReplaceAll(access.Command, key, v)
|
|
||||||
access.PreCommand = strings.ReplaceAll(access.PreCommand, key, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 连接
|
// 连接
|
||||||
client, err := s.getClient(access)
|
client, err := s.getClient(access)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,29 +55,30 @@ func (s *ssh) Deploy(ctx context.Context) error {
|
|||||||
s.infos = append(s.infos, toStr("ssh连接成功", nil))
|
s.infos = append(s.infos, toStr("ssh连接成功", nil))
|
||||||
|
|
||||||
// 执行前置命令
|
// 执行前置命令
|
||||||
if access.PreCommand != "" {
|
preCommand := getDeployString(s.option.DeployConfig, "preCommand")
|
||||||
err, stdout, stderr := s.sshExecCommand(client, access.PreCommand)
|
if preCommand != "" {
|
||||||
|
err, stdout, stderr := s.sshExecCommand(client, preCommand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to run pre-command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
|
return fmt.Errorf("failed to run pre-command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传证书
|
// 上传证书
|
||||||
if err := s.upload(client, s.option.Certificate.Certificate, access.CertPath); err != nil {
|
if err := s.upload(client, s.option.Certificate.Certificate, getDeployString(s.option.DeployConfig, "certPath")); err != nil {
|
||||||
return fmt.Errorf("failed to upload certificate: %w", err)
|
return fmt.Errorf("failed to upload certificate: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.infos = append(s.infos, toStr("ssh上传证书成功", nil))
|
s.infos = append(s.infos, toStr("ssh上传证书成功", nil))
|
||||||
|
|
||||||
// 上传私钥
|
// 上传私钥
|
||||||
if err := s.upload(client, s.option.Certificate.PrivateKey, access.KeyPath); err != nil {
|
if err := s.upload(client, s.option.Certificate.PrivateKey, getDeployString(s.option.DeployConfig, "keyPath")); err != nil {
|
||||||
return fmt.Errorf("failed to upload private key: %w", err)
|
return fmt.Errorf("failed to upload private key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.infos = append(s.infos, toStr("ssh上传私钥成功", nil))
|
s.infos = append(s.infos, toStr("ssh上传私钥成功", nil))
|
||||||
|
|
||||||
// 执行命令
|
// 执行命令
|
||||||
err, stdout, stderr := s.sshExecCommand(client, access.Command)
|
err, stdout, stderr := s.sshExecCommand(client, getDeployString(s.option.DeployConfig, "command"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
|
return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ import (
|
|||||||
"certimate/internal/domain"
|
"certimate/internal/domain"
|
||||||
"certimate/internal/utils/rand"
|
"certimate/internal/utils/rand"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
cdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
|
||||||
"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"
|
||||||
@@ -73,7 +76,7 @@ func (t *tencentCdn) uploadCert() (string, error) {
|
|||||||
request.CertificatePublicKey = common.StringPtr(t.option.Certificate.Certificate)
|
request.CertificatePublicKey = common.StringPtr(t.option.Certificate.Certificate)
|
||||||
request.CertificatePrivateKey = common.StringPtr(t.option.Certificate.PrivateKey)
|
request.CertificatePrivateKey = common.StringPtr(t.option.Certificate.PrivateKey)
|
||||||
request.Alias = common.StringPtr(t.option.Domain + "_" + rand.RandStr(6))
|
request.Alias = common.StringPtr(t.option.Domain + "_" + rand.RandStr(6))
|
||||||
request.Repeatable = common.BoolPtr(true)
|
request.Repeatable = common.BoolPtr(false)
|
||||||
|
|
||||||
response, err := client.UploadCertificate(request)
|
response, err := client.UploadCertificate(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,10 +96,24 @@ func (t *tencentCdn) deploy(certId string) error {
|
|||||||
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证书中的可用域名
|
||||||
|
domain := getDeployString(t.option.DeployConfig, "domain")
|
||||||
|
if strings.Contains(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{domain})
|
||||||
|
}
|
||||||
|
|
||||||
// 返回的resp是一个DeployCertificateInstanceResponse的实例,与请求对象对应
|
// 返回的resp是一个DeployCertificateInstanceResponse的实例,与请求对象对应
|
||||||
resp, err := client.DeployCertificateInstance(request)
|
resp, err := client.DeployCertificateInstance(request)
|
||||||
|
|
||||||
@@ -106,3 +123,26 @@ 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ type webhookAccess struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type hookData struct {
|
type hookData struct {
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
Certificate string `json:"certificate"`
|
Certificate string `json:"certificate"`
|
||||||
PrivateKey string `json:"privateKey"`
|
PrivateKey string `json:"privateKey"`
|
||||||
|
Variables map[string]string `json:"variables"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type webhook struct {
|
type webhook struct {
|
||||||
@@ -50,6 +51,7 @@ func (w *webhook) Deploy(ctx context.Context) error {
|
|||||||
Domain: w.option.Domain,
|
Domain: w.option.Domain,
|
||||||
Certificate: w.option.Certificate.Certificate,
|
Certificate: w.option.Certificate.Certificate,
|
||||||
PrivateKey: w.option.Certificate.PrivateKey,
|
PrivateKey: w.option.Certificate.PrivateKey,
|
||||||
|
Variables: getDeployVariables(w.option.DeployConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := json.Marshal(data)
|
body, _ := json.Marshal(data)
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
20
internal/domain/domains.go
Normal file
20
internal/domain/domains.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
type ApplyConfig struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Access string `json:"access"`
|
||||||
|
Timeout int64 `json:"timeout"`
|
||||||
|
Nameservers string `json:"nameservers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployConfig struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Access string `json:"access"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Config map[string]any `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KV struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"certimate/internal/deployer"
|
"certimate/internal/deployer"
|
||||||
"certimate/internal/utils/app"
|
"certimate/internal/utils/app"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -41,18 +40,6 @@ func deploy(ctx context.Context, record *models.Record) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
history.record(checkPhase, "获取记录成功", nil)
|
history.record(checkPhase, "获取记录成功", nil)
|
||||||
if errs := app.GetApp().Dao().ExpandRecord(currRecord, []string{"access", "targetAccess", "group"}, nil); len(errs) > 0 {
|
|
||||||
|
|
||||||
errList := make([]error, 0)
|
|
||||||
for name, err := range errs {
|
|
||||||
errList = append(errList, fmt.Errorf("展开记录失败,%s: %w", name, err))
|
|
||||||
}
|
|
||||||
err = errors.Join(errList...)
|
|
||||||
app.GetApp().Logger().Error("展开记录失败", "err", err)
|
|
||||||
history.record(checkPhase, "获取授权信息失败", &RecordInfo{Err: err})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
history.record(checkPhase, "获取授权信息成功", nil)
|
|
||||||
|
|
||||||
cert := currRecord.GetString("certificate")
|
cert := currRecord.GetString("certificate")
|
||||||
expiredAt := currRecord.GetDateTime("expiredAt").Time()
|
expiredAt := currRecord.GetDateTime("expiredAt").Time()
|
||||||
@@ -106,6 +93,13 @@ func deploy(ctx context.Context, record *models.Record) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 没有部署配置,也算成功
|
||||||
|
if len(deployers) == 0 {
|
||||||
|
history.record(deployPhase, "没有部署配置", &RecordInfo{Info: []string{"没有部署配置"}})
|
||||||
|
history.setWholeSuccess(true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, deployer := range deployers {
|
for _, deployer := range deployers {
|
||||||
if err = deployer.Deploy(ctx); err != nil {
|
if err = deployer.Deploy(ctx); err != 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
731
migrations/1728778480_collections_snapshot.go
Normal file
731
migrations/1728778480_collections_snapshot.go
Normal file
@@ -0,0 +1,731 @@
|
|||||||
|
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-10-08 06:50:56.637Z",
|
||||||
|
"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": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "wwrzc3jo",
|
||||||
|
"name": "applyConfig",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "474iwy8r",
|
||||||
|
"name": "deployConfig",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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-10-11 13:55:13.777Z",
|
||||||
|
"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",
|
||||||
|
"huaweicloud",
|
||||||
|
"qiniu",
|
||||||
|
"cloudflare",
|
||||||
|
"namesilo",
|
||||||
|
"godaddy",
|
||||||
|
"local",
|
||||||
|
"ssh",
|
||||||
|
"webhook"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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-26 12:29:38.334Z",
|
||||||
|
"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-26 12:29:38.334Z",
|
||||||
|
"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-26 12:29:38.334Z",
|
||||||
|
"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-26 12:29:38.334Z",
|
||||||
|
"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
|
||||||
|
})
|
||||||
|
}
|
||||||
332
ui/dist/assets/index-47M77bCI.js
vendored
332
ui/dist/assets/index-47M77bCI.js
vendored
File diff suppressed because one or more lines are too long
1
ui/dist/assets/index-CV_7sKTK.css
vendored
Normal file
1
ui/dist/assets/index-CV_7sKTK.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
ui/dist/assets/index-ClQTEWmX.css
vendored
1
ui/dist/assets/index-ClQTEWmX.css
vendored
File diff suppressed because one or more lines are too long
324
ui/dist/assets/index-DipHpsma.js
vendored
Normal file
324
ui/dist/assets/index-DipHpsma.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>
|
||||||
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-47M77bCI.js"></script>
|
<script type="module" crossorigin src="/assets/index-DipHpsma.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-ClQTEWmX.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-CV_7sKTK.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-background">
|
<body class="bg-background">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
38
ui/package-lock.json
generated
38
ui/package-lock.json
generated
@@ -30,9 +30,11 @@
|
|||||||
"i18next": "^23.15.1",
|
"i18next": "^23.15.1",
|
||||||
"i18next-browser-languagedetector": "^8.0.0",
|
"i18next-browser-languagedetector": "^8.0.0",
|
||||||
"i18next-http-backend": "^2.6.1",
|
"i18next-http-backend": "^2.6.1",
|
||||||
|
"immer": "^10.1.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",
|
||||||
|
"nanoid": "^5.0.7",
|
||||||
"pocketbase": "^0.21.4",
|
"pocketbase": "^0.21.4",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -3780,6 +3782,15 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/immer": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/immer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -4159,9 +4170,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "5.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
|
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.0.7.tgz",
|
||||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
"integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -4169,10 +4180,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^18 || >=20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/natural-compare": {
|
"node_modules/natural-compare": {
|
||||||
@@ -4561,6 +4572,23 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss/node_modules/nanoid": {
|
||||||
|
"version": "3.3.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
|
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
|
|||||||
@@ -29,22 +29,24 @@
|
|||||||
"@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",
|
||||||
|
"immer": "^10.1.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",
|
||||||
|
"nanoid": "^5.0.7",
|
||||||
"pocketbase": "^0.21.4",
|
"pocketbase": "^0.21.4",
|
||||||
"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",
|
||||||
"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",
|
||||||
|
|||||||
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>
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
export default function LocaleToggle() {
|
export default function LocaleToggle() {
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -22,7 +22,7 @@ export default function LocaleToggle() {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
{Object.keys(i18n.store.data).map(key => (
|
{Object.keys(i18n.store.data).map((key) => (
|
||||||
<DropdownMenuItem onClick={() => i18n.changeLanguage(key)}>
|
<DropdownMenuItem onClick={() => i18n.changeLanguage(key)}>
|
||||||
{i18n.store.data[key].name as string}
|
{i18n.store.data[key].name as string}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Moon, Sun } from "lucide-react";
|
import { Moon, Sun } from "lucide-react";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -14,7 +14,6 @@ export function ThemeToggle() {
|
|||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@@ -26,13 +25,13 @@ export function ThemeToggle() {
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => setTheme("light")}>
|
<DropdownMenuItem onClick={() => setTheme("light")}>
|
||||||
{t('theme.light')}
|
{t("common.theme.light")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
||||||
{t('theme.dark')}
|
{t("common.theme.dark")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setTheme("system")}>
|
<DropdownMenuItem onClick={() => setTheme("system")}>
|
||||||
{t('theme.system')}
|
{t("common.theme.system")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import { Access, accessFormType, AliyunConfig, getUsageByConfigType } from "@/domain/access";
|
import {
|
||||||
|
Access,
|
||||||
|
accessFormType,
|
||||||
|
AliyunConfig,
|
||||||
|
getUsageByConfigType,
|
||||||
|
} from "@/domain/access";
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
|
|
||||||
@@ -35,10 +40,19 @@ const AccessAliyunForm = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
accessKeyId: z.string().min(1, 'access.form.access.key.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
accessKeyId: z
|
||||||
accessSecretId: z.string().min(1, 'access.form.access.key.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
.string()
|
||||||
|
.min(1, "access.authorization.form.access_key_id.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
accessSecretId: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.access_key_secret.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: AliyunConfig = {
|
let config: AliyunConfig = {
|
||||||
@@ -51,7 +65,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,
|
||||||
@@ -71,7 +85,7 @@ const AccessAliyunForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -117,9 +131,12 @@ const AccessAliyunForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -132,7 +149,7 @@ const AccessAliyunForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -147,7 +164,7 @@ const AccessAliyunForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -162,9 +179,12 @@ const AccessAliyunForm = ({
|
|||||||
name="accessKeyId"
|
name="accessKeyId"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.access.key.id')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.access_key_id.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.access.key.id.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.access_key_id.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -177,9 +197,12 @@ const AccessAliyunForm = ({
|
|||||||
name="accessSecretId"
|
name="accessSecretId"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.access.key.secret')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.access_key_secret.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.access.key.secret.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.access_key_secret.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -190,7 +213,7 @@ const AccessAliyunForm = ({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import { Access, accessFormType, CloudflareConfig, getUsageByConfigType } from "@/domain/access";
|
import {
|
||||||
|
Access,
|
||||||
|
accessFormType,
|
||||||
|
CloudflareConfig,
|
||||||
|
getUsageByConfigType,
|
||||||
|
} from "@/domain/access";
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
@@ -34,9 +39,15 @@ const AccessCloudflareForm = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
dnsApiToken: z.string().min(1, 'access.form.cloud.dns.api.token.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
dnsApiToken: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.cloud_dns_api_token.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: CloudflareConfig = {
|
let config: CloudflareConfig = {
|
||||||
@@ -48,7 +59,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,
|
||||||
},
|
},
|
||||||
@@ -67,7 +78,7 @@ const AccessCloudflareForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -111,9 +122,12 @@ const AccessCloudflareForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -126,7 +140,7 @@ const AccessCloudflareForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -141,7 +155,7 @@ const AccessCloudflareForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -156,9 +170,14 @@ const AccessCloudflareForm = ({
|
|||||||
name="dnsApiToken"
|
name="dnsApiToken"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.cloud.dns.api.token')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.cloud_dns_api_token.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.cloud.dns.api.token.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.cloud_dns_api_token.placeholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -167,7 +186,7 @@ const AccessCloudflareForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -10,16 +10,10 @@ import { ScrollArea } from "@/components/ui/scroll-area";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
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,
|
||||||
@@ -29,11 +23,16 @@ 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 AccessLocalForm from "./AccessLocalForm";
|
||||||
|
import AccessSSHForm from "./AccessSSHForm";
|
||||||
|
import AccessWebhookForm from "./AccessWebhookFrom";
|
||||||
|
|
||||||
type TargetConfigEditProps = {
|
type TargetConfigEditProps = {
|
||||||
op: "add" | "edit" | "copy";
|
op: "add" | "edit" | "copy";
|
||||||
@@ -56,17 +55,6 @@ export function AccessEdit({
|
|||||||
|
|
||||||
let form = <> </>;
|
let form = <> </>;
|
||||||
switch (configType) {
|
switch (configType) {
|
||||||
case "tencent":
|
|
||||||
form = (
|
|
||||||
<AccessTencentForm
|
|
||||||
data={data}
|
|
||||||
op={op}
|
|
||||||
onAfterReq={() => {
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "aliyun":
|
case "aliyun":
|
||||||
form = (
|
form = (
|
||||||
<AccessAliyunForm
|
<AccessAliyunForm
|
||||||
@@ -78,9 +66,9 @@ export function AccessEdit({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "ssh":
|
case "tencent":
|
||||||
form = (
|
form = (
|
||||||
<AccessSSHForm
|
<AccessTencentForm
|
||||||
data={data}
|
data={data}
|
||||||
op={op}
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
@@ -89,20 +77,9 @@ export function AccessEdit({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "webhook":
|
case "huaweicloud":
|
||||||
form = (
|
form = (
|
||||||
<WebhookForm
|
<AccessHuaweicloudForm
|
||||||
data={data}
|
|
||||||
op={op}
|
|
||||||
onAfterReq={() => {
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "cloudflare":
|
|
||||||
form = (
|
|
||||||
<AccessCloudflareForm
|
|
||||||
data={data}
|
data={data}
|
||||||
op={op}
|
op={op}
|
||||||
onAfterReq={() => {
|
onAfterReq={() => {
|
||||||
@@ -122,6 +99,17 @@ export function AccessEdit({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "cloudflare":
|
||||||
|
form = (
|
||||||
|
<AccessCloudflareForm
|
||||||
|
data={data}
|
||||||
|
op={op}
|
||||||
|
onAfterReq={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
case "namesilo":
|
case "namesilo":
|
||||||
form = (
|
form = (
|
||||||
<AccessNamesiloForm
|
<AccessNamesiloForm
|
||||||
@@ -155,6 +143,28 @@ export function AccessEdit({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "ssh":
|
||||||
|
form = (
|
||||||
|
<AccessSSHForm
|
||||||
|
data={data}
|
||||||
|
op={op}
|
||||||
|
onAfterReq={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "webhook":
|
||||||
|
form = (
|
||||||
|
<AccessWebhookForm
|
||||||
|
data={data}
|
||||||
|
op={op}
|
||||||
|
onAfterReq={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOptionCls = (val: string) => {
|
const getOptionCls = (val: string) => {
|
||||||
@@ -168,11 +178,17 @@ 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" ? t('access.add') : op == "edit" ? t('access.edit') : t('access.copy')}</DialogTitle>
|
<DialogTitle>
|
||||||
|
{op == "add"
|
||||||
|
? t("access.authorization.add")
|
||||||
|
: op == "edit"
|
||||||
|
? t("access.authorization.edit")
|
||||||
|
: t("access.authorization.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>{t('access.type')}</Label>
|
<Label>{t("access.authorization.form.type.label")}</Label>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(val) => {
|
onValueChange={(val) => {
|
||||||
@@ -181,11 +197,11 @@ export function AccessEdit({
|
|||||||
defaultValue={configType}
|
defaultValue={configType}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="mt-3">
|
<SelectTrigger className="mt-3">
|
||||||
<SelectValue placeholder={t('access.type.not.empty')} />
|
<SelectValue placeholder={t("access.authorization.form.type.placeholder")} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>{t('access.type')}</SelectLabel>
|
<SelectLabel>{t("access.authorization.form.type.list")}</SelectLabel>
|
||||||
{typeKeys.map((key) => (
|
{typeKeys.map((key) => (
|
||||||
<SelectItem value={key} key={key}>
|
<SelectItem value={key} key={key}>
|
||||||
<div
|
<div
|
||||||
@@ -198,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>{t(accessTypeMap.get(key)?.[0] || '')}</div>
|
<div>{t(accessTypeMap.get(key)?.[0] || "")}</div>
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -39,10 +39,19 @@ const AccessGodaddyFrom = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
apiKey: z.string().min(1, 'access.form.go.daddy.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
apiKey: z
|
||||||
apiSecret: z.string().min(1, 'access.form.go.daddy.api.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
.string()
|
||||||
|
.min(1, "access.authorization.form.godaddy_api_key.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
apiSecret: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.godaddy_api_secret.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: GodaddyConfig = {
|
let config: GodaddyConfig = {
|
||||||
@@ -55,7 +64,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,
|
||||||
@@ -76,7 +85,7 @@ const AccessGodaddyFrom = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -120,9 +129,12 @@ const AccessGodaddyFrom = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -135,7 +147,7 @@ const AccessGodaddyFrom = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -150,7 +162,7 @@ const AccessGodaddyFrom = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -165,9 +177,12 @@ const AccessGodaddyFrom = ({
|
|||||||
name="apiKey"
|
name="apiKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.go.daddy.api.key')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.godaddy_api_key.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.go.daddy.api.key.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.godaddy_api_key.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -180,9 +195,14 @@ const AccessGodaddyFrom = ({
|
|||||||
name="apiSecret"
|
name="apiSecret"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.go.daddy.api.secret')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.godaddy_api_secret.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.go.daddy.api.secret.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.godaddy_api_secret.placeholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -191,7 +211,7 @@ const AccessGodaddyFrom = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
name: z.string().min(1, 'access.group.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.group.form.name.errmsg.empty")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
@@ -80,7 +83,7 @@ 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>{t('access.group.add')}</DialogTitle>
|
<DialogTitle>{t("access.group.add")}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="container py-3">
|
<div className="container py-3">
|
||||||
@@ -97,9 +100,13 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.group.name')}</FormLabel>
|
<FormLabel>{t("access.group.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.group.name.not.empty')} {...field} type="text" />
|
<Input
|
||||||
|
placeholder={t("access.group.form.name.errmsg.empty")}
|
||||||
|
{...field}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -108,7 +115,7 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const AccessGroupList = () => {
|
|||||||
reloadAccessGroups();
|
reloadAccessGroups();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast({
|
toast({
|
||||||
title: t('delete.failed'),
|
title: t("common.delete.failed.message"),
|
||||||
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')}
|
{t("access.group.domains.nodata")}
|
||||||
</div>
|
</div>
|
||||||
<AccessGroupEdit
|
<AccessGroupEdit
|
||||||
trigger={<Button>{t('access.group.add')}</Button>}
|
trigger={<Button>{t("access.group.add")}</Button>}
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,7 +86,11 @@ 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 })}
|
{t("access.group.total", {
|
||||||
|
total: accessGroup.expand
|
||||||
|
? accessGroup.expand.access.length
|
||||||
|
: 0,
|
||||||
|
})}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="min-h-[180px]">
|
<CardContent className="min-h-[180px]">
|
||||||
@@ -120,9 +124,7 @@ const AccessGroupList = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Group size={40} />
|
<Group size={40} />
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2">
|
<div className="ml-2">{t("access.group.nodata")}</div>
|
||||||
{t('access.group.empty')}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -149,7 +151,7 @@ const AccessGroupList = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('access.all')}
|
{t("access.group.domains")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -157,14 +159,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')}
|
{t("access.authorization.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -173,21 +175,21 @@ const AccessGroupList = () => {
|
|||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button variant={"destructive"} size={"sm"}>
|
<Button variant={"destructive"} size={"sm"}>
|
||||||
{t('delete')}
|
{t("common.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')}
|
{t("access.group.delete")}
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
{t('access.group.delete.confirm')}
|
{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')}
|
{t("common.cancel")}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -196,7 +198,7 @@ const AccessGroupList = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('confirm')}
|
{t("common.confirm")}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
|
|||||||
250
ui/src/components/certimate/AccessHuaweicloudForm.tsx
Normal file
250
ui/src/components/certimate/AccessHuaweicloudForm.tsx
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
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.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
configType: accessFormType,
|
||||||
|
region: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.region.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
accessKeyId: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.access_key_id.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
secretAccessKey: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.access_key_secret.placeholder")
|
||||||
|
.max(64, t("common.errmsg.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("access.authorization.form.name.label")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="id"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="hidden">
|
||||||
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="configType"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="hidden">
|
||||||
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="region"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t("access.authorization.form.region.label")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.region.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="accessKeyId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t("access.authorization.form.access_key_id.label")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.access_key_id.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="secretAccessKey"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t("access.authorization.form.access_key_secret.label")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.access_key_secret.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessHuaweicloudForm;
|
||||||
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import { Access, accessFormType, getUsageByConfigType } from "@/domain/access";
|
||||||
Access,
|
|
||||||
accessFormType,
|
|
||||||
getUsageByConfigType,
|
|
||||||
LocalConfig,
|
|
||||||
SSHConfig,
|
|
||||||
} from "@/domain/access";
|
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@@ -20,7 +14,7 @@ import {
|
|||||||
} from "../ui/form";
|
} from "../ui/form";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Textarea } from "../ui/textarea";
|
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
import { PbErrorData } from "@/domain/base";
|
import { PbErrorData } from "@/domain/base";
|
||||||
@@ -39,30 +33,19 @@ const AccessLocalForm = ({
|
|||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
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>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
name: data?.name || '',
|
name: data?.name || "",
|
||||||
configType: "local",
|
configType: "local",
|
||||||
certPath: config.certPath,
|
|
||||||
keyPath: config.keyPath,
|
|
||||||
command: config.command,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,15 +56,11 @@ const AccessLocalForm = ({
|
|||||||
configType: data.configType,
|
configType: data.configType,
|
||||||
usage: getUsageByConfigType(data.configType),
|
usage: getUsageByConfigType(data.configType),
|
||||||
|
|
||||||
config: {
|
config: {},
|
||||||
command: data.command,
|
|
||||||
certPath: data.certPath,
|
|
||||||
keyPath: data.keyPath,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -128,9 +107,12 @@ const AccessLocalForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -143,7 +125,7 @@ const AccessLocalForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -158,7 +140,7 @@ const AccessLocalForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -168,55 +150,10 @@ const AccessLocalForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<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 />
|
<FormMessage />
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import { Access, accessFormType, getUsageByConfigType, NamesiloConfig } from "@/domain/access";
|
import {
|
||||||
|
Access,
|
||||||
|
accessFormType,
|
||||||
|
getUsageByConfigType,
|
||||||
|
NamesiloConfig,
|
||||||
|
} from "@/domain/access";
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
@@ -34,9 +39,15 @@ const AccessNamesiloForm = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
apiKey: z.string().min(1, 'access.form.namesilo.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
apiKey: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.namesilo_api_key.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: NamesiloConfig = {
|
let config: NamesiloConfig = {
|
||||||
@@ -48,7 +59,7 @@ 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,
|
||||||
},
|
},
|
||||||
@@ -66,7 +77,7 @@ const AccessNamesiloForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -110,9 +121,12 @@ const AccessNamesiloForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -125,7 +139,7 @@ const AccessNamesiloForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -140,7 +154,7 @@ const AccessNamesiloForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -155,9 +169,12 @@ const AccessNamesiloForm = ({
|
|||||||
name="apiKey"
|
name="apiKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.namesilo.api.key')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.namesilo_api_key.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.namesilo.api.key.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.namesilo_api_key.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -166,7 +183,7 @@ const AccessNamesiloForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import { Access, accessFormType, getUsageByConfigType, QiniuConfig } from "@/domain/access";
|
import {
|
||||||
|
Access,
|
||||||
|
accessFormType,
|
||||||
|
getUsageByConfigType,
|
||||||
|
QiniuConfig,
|
||||||
|
} from "@/domain/access";
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
|
|
||||||
@@ -35,10 +40,13 @@ const AccessQiniuForm = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
accessKey: z.string().min(1, 'access.form.access.key.not.empty').max(64),
|
accessKey: z.string().min(1, "access.authorization.form.access_key.placeholder").max(64),
|
||||||
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64),
|
secretKey: z.string().min(1, "access.authorization.form.secret_key.placeholder").max(64),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: QiniuConfig = {
|
let config: QiniuConfig = {
|
||||||
@@ -51,7 +59,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,
|
||||||
@@ -71,7 +79,7 @@ const AccessQiniuForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -116,9 +124,12 @@ const AccessQiniuForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -131,7 +142,7 @@ const AccessQiniuForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -146,7 +157,7 @@ const AccessQiniuForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -161,9 +172,12 @@ const AccessQiniuForm = ({
|
|||||||
name="accessKey"
|
name="accessKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.access.key')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.access_key.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.access.key.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.access_key.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -176,9 +190,12 @@ const AccessQiniuForm = ({
|
|||||||
name="secretKey"
|
name="secretKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.secret.key')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.secret_key.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.secret_key.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -189,7 +206,7 @@ const AccessQiniuForm = ({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
} from "../ui/form";
|
} from "../ui/form";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Textarea } from "../ui/textarea";
|
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
import { PbErrorData } from "@/domain/base";
|
import { PbErrorData } from "@/domain/base";
|
||||||
@@ -66,27 +65,37 @@ const AccessSSHForm = ({
|
|||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.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: "zod.rule.ssh.host",
|
message: "common.errmsg.host_invalid",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
group: z.string().optional(),
|
group: z.string().optional(),
|
||||||
port: z.string().min(1, 'access.form.ssh.port.not.empty').max(5, t('zod.rule.string.max', { max: 5 })),
|
port: z
|
||||||
username: z.string().min(1, 'username.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
.string()
|
||||||
password: z.string().min(0, 'password.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
.min(1, "access.authorization.form.ssh_port.placeholder")
|
||||||
key: z.string().min(0, 'access.form.ssh.key.not.empty').max(20480, t('zod.rule.string.max', { max: 20480 })),
|
.max(5, t("common.errmsg.string_max", { max: 5 })),
|
||||||
|
username: z
|
||||||
|
.string()
|
||||||
|
.min(1, "username.not.empty")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(0, "password.not.empty")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
key: z
|
||||||
|
.string()
|
||||||
|
.min(0, "access.authorization.form.ssh_key.placeholder")
|
||||||
|
.max(20480, t("common.errmsg.string_max", { max: 20480 })),
|
||||||
keyFile: z.any().optional(),
|
keyFile: z.any().optional(),
|
||||||
|
|
||||||
preCommand: z.string().min(0).max(2048, t('zod.rule.string.max', { max: 2048 })).optional(),
|
|
||||||
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 = {
|
||||||
@@ -96,10 +105,6 @@ const AccessSSHForm = ({
|
|||||||
password: "",
|
password: "",
|
||||||
key: "",
|
key: "",
|
||||||
keyFile: "",
|
keyFile: "",
|
||||||
preCommand: "",
|
|
||||||
command: "sudo service nginx restart",
|
|
||||||
certPath: "/etc/nginx/ssl/certificate.crt",
|
|
||||||
keyPath: "/etc/nginx/ssl/private.key",
|
|
||||||
};
|
};
|
||||||
if (data) config = data.config as SSHConfig;
|
if (data) config = data.config as SSHConfig;
|
||||||
|
|
||||||
@@ -107,7 +112,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,
|
||||||
@@ -116,10 +121,6 @@ const AccessSSHForm = ({
|
|||||||
password: config.password,
|
password: config.password,
|
||||||
key: config.key,
|
key: config.key,
|
||||||
keyFile: config.keyFile,
|
keyFile: config.keyFile,
|
||||||
certPath: config.certPath,
|
|
||||||
keyPath: config.keyPath,
|
|
||||||
command: config.command,
|
|
||||||
preCommand: config.preCommand,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -139,15 +140,11 @@ const AccessSSHForm = ({
|
|||||||
username: data.username,
|
username: data.username,
|
||||||
password: data.password,
|
password: data.password,
|
||||||
key: data.key,
|
key: data.key,
|
||||||
command: data.command,
|
|
||||||
preCommand: data.preCommand,
|
|
||||||
certPath: data.certPath,
|
|
||||||
keyPath: data.keyPath,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -228,9 +225,16 @@ const AccessSSHForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.name.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.name.placeholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -244,12 +248,12 @@ const AccessSSHForm = ({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className="w-full flex justify-between">
|
<FormLabel className="w-full flex justify-between">
|
||||||
<div>{t('access.form.ssh.group.label')}</div>
|
<div>{t("access.authorization.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')}
|
{t("common.add")}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -264,7 +268,11 @@ const AccessSSHForm = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder={t('access.group.not.empty')} />
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.access_group.placeholder"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="emptyId">
|
<SelectItem value="emptyId">
|
||||||
@@ -304,7 +312,9 @@ const AccessSSHForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.config.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -319,7 +329,9 @@ const AccessSSHForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.config.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -334,9 +346,16 @@ const AccessSSHForm = ({
|
|||||||
name="host"
|
name="host"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="grow">
|
<FormItem className="grow">
|
||||||
<FormLabel>{t('access.form.ssh.host')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.ssh_host.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.ssh.host.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.ssh_host.placeholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -349,10 +368,14 @@ const AccessSSHForm = ({
|
|||||||
name="port"
|
name="port"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.ssh.port')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.ssh_port.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('access.form.ssh.port.not.empty')}
|
placeholder={t(
|
||||||
|
"access.authorization.form.ssh_port.placeholder"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
@@ -369,9 +392,16 @@ const AccessSSHForm = ({
|
|||||||
name="username"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('username')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.username.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('username.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.username.placeholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -384,10 +414,14 @@ const AccessSSHForm = ({
|
|||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('password')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.password.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('password.not.empty')}
|
placeholder={t(
|
||||||
|
"access.authorization.form.password.placeholder"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
@@ -403,9 +437,16 @@ const AccessSSHForm = ({
|
|||||||
name="key"
|
name="key"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem hidden>
|
<FormItem hidden>
|
||||||
<FormLabel>{t('access.form.ssh.key')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.ssh_key.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.ssh.key.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.ssh_key.placeholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -418,7 +459,9 @@ const AccessSSHForm = ({
|
|||||||
name="keyFile"
|
name="keyFile"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.ssh.key')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("access.authorization.form.ssh_key.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
@@ -428,10 +471,16 @@ const AccessSSHForm = ({
|
|||||||
className="w-48"
|
className="w-48"
|
||||||
onClick={handleSelectFileClick}
|
onClick={handleSelectFileClick}
|
||||||
>
|
>
|
||||||
{fileName ? fileName : t('access.form.ssh.key.file.not.empty')}
|
{fileName
|
||||||
|
? fileName
|
||||||
|
: t(
|
||||||
|
"access.authorization.form.ssh_key_file.placeholder"
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('access.form.ssh.key.not.empty')}
|
placeholder={t(
|
||||||
|
"access.authorization.form.ssh_key.placeholder"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
@@ -447,70 +496,10 @@ const AccessSSHForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<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="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>
|
|
||||||
|
|
||||||
<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 />
|
<FormMessage />
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import { Access, accessFormType, getUsageByConfigType, TencentConfig } from "@/domain/access";
|
import {
|
||||||
|
Access,
|
||||||
|
accessFormType,
|
||||||
|
getUsageByConfigType,
|
||||||
|
TencentConfig,
|
||||||
|
} from "@/domain/access";
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
@@ -34,10 +39,19 @@ const AccessTencentForm = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
secretId: z.string().min(1, 'access.form.secret.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
secretId: z
|
||||||
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
.string()
|
||||||
|
.min(1, "access.authorization.form.secret_id.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
|
secretKey: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.secret_key.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: TencentConfig = {
|
let config: TencentConfig = {
|
||||||
@@ -50,7 +64,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,
|
||||||
@@ -70,7 +84,7 @@ const AccessTencentForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -113,9 +127,12 @@ const AccessTencentForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -128,7 +145,7 @@ const AccessTencentForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -143,7 +160,7 @@ const AccessTencentForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -158,9 +175,12 @@ const AccessTencentForm = ({
|
|||||||
name="secretId"
|
name="secretId"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.secret.id')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.secret_id.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.secret.id.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.secret_id.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -173,9 +193,12 @@ const AccessTencentForm = ({
|
|||||||
name="secretKey"
|
name="secretKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.secret.key')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.secret_key.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.secret_key.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -184,7 +207,7 @@ const AccessTencentForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import { Access, accessFormType, getUsageByConfigType, WebhookConfig } from "@/domain/access";
|
import {
|
||||||
|
Access,
|
||||||
|
accessFormType,
|
||||||
|
getUsageByConfigType,
|
||||||
|
WebhookConfig,
|
||||||
|
} from "@/domain/access";
|
||||||
import { save } from "@/repository/access";
|
import { save } from "@/repository/access";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
@@ -34,9 +39,12 @@ const WebhookForm = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, "access.authorization.form.name.placeholder")
|
||||||
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
configType: accessFormType,
|
configType: accessFormType,
|
||||||
url: z.string().url('zod.rule.url'),
|
url: z.string().url("common.errmsg.url_invalid"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let config: WebhookConfig = {
|
let config: WebhookConfig = {
|
||||||
@@ -48,7 +56,7 @@ 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,
|
||||||
},
|
},
|
||||||
@@ -66,7 +74,7 @@ const WebhookForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.id = op == "copy" ? "" : req.id;
|
req.id = op == "copy" ? "" : req.id;
|
||||||
const rs = await save(req);
|
const rs = await save(req);
|
||||||
|
|
||||||
onAfterReq();
|
onAfterReq();
|
||||||
@@ -110,9 +118,12 @@ const WebhookForm = ({
|
|||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('name')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.name.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -125,7 +136,7 @@ const WebhookForm = ({
|
|||||||
name="id"
|
name="id"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -140,7 +151,7 @@ const WebhookForm = ({
|
|||||||
name="configType"
|
name="configType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="hidden">
|
<FormItem className="hidden">
|
||||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -155,9 +166,12 @@ const WebhookForm = ({
|
|||||||
name="url"
|
name="url"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('access.form.webhook.url')}</FormLabel>
|
<FormLabel>{t("access.authorization.form.webhook_url.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('access.form.webhook.url.not.empty')} {...field} />
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.webhook_url.placeholder")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -166,7 +180,7 @@ const WebhookForm = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
850
ui/src/components/certimate/DeployList.tsx
Normal file
850
ui/src/components/certimate/DeployList.tsx
Normal file
@@ -0,0 +1,850 @@
|
|||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { EditIcon, Plus, Trash2 } from "lucide-react";
|
||||||
|
import {
|
||||||
|
DeployConfig,
|
||||||
|
KVType,
|
||||||
|
targetTypeKeys,
|
||||||
|
targetTypeMap,
|
||||||
|
} from "@/domain/domain";
|
||||||
|
import Show from "../Show";
|
||||||
|
import { Alert, AlertDescription } from "../ui/alert";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "../ui/dialog";
|
||||||
|
|
||||||
|
import { Label } from "../ui/label";
|
||||||
|
import { useConfig } from "@/providers/config";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "../ui/select";
|
||||||
|
import { accessTypeMap } from "@/domain/access";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { AccessEdit } from "./AccessEdit";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Textarea } from "../ui/textarea";
|
||||||
|
import KVList from "./KVList";
|
||||||
|
import { produce } from "immer";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
type DeployEditContextProps = {
|
||||||
|
deploy: DeployConfig;
|
||||||
|
error: Record<string, string>;
|
||||||
|
setDeploy: (deploy: DeployConfig) => void;
|
||||||
|
setError: (error: Record<string, string>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeployEditContext = createContext<DeployEditContextProps>(
|
||||||
|
{} as DeployEditContextProps
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useDeployEditContext = () => {
|
||||||
|
return useContext(DeployEditContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
type DeployListProps = {
|
||||||
|
deploys: DeployConfig[];
|
||||||
|
onChange: (deploys: DeployConfig[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeployList = ({ deploys, onChange }: DeployListProps) => {
|
||||||
|
const [list, setList] = useState<DeployConfig[]>([]);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setList(deploys);
|
||||||
|
}, [deploys]);
|
||||||
|
|
||||||
|
const handleAdd = (deploy: DeployConfig) => {
|
||||||
|
deploy.id = nanoid();
|
||||||
|
|
||||||
|
const newList = [...list, deploy];
|
||||||
|
|
||||||
|
setList(newList);
|
||||||
|
|
||||||
|
onChange(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (id: string) => {
|
||||||
|
const newList = list.filter((item) => item.id !== id);
|
||||||
|
|
||||||
|
setList(newList);
|
||||||
|
|
||||||
|
onChange(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = (deploy: DeployConfig) => {
|
||||||
|
const newList = list.map((item) => {
|
||||||
|
if (item.id === deploy.id) {
|
||||||
|
return { ...deploy };
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
setList(newList);
|
||||||
|
|
||||||
|
onChange(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Show
|
||||||
|
when={list.length > 0}
|
||||||
|
fallback={
|
||||||
|
<Alert className="w-full border dark:border-stone-400">
|
||||||
|
<AlertDescription className="flex flex-col items-center">
|
||||||
|
<div>{t("domain.deployment.nodata")}</div>
|
||||||
|
<div className="flex justify-end mt-2">
|
||||||
|
<DeployEditDialog
|
||||||
|
onSave={(config: DeployConfig) => {
|
||||||
|
handleAdd(config);
|
||||||
|
}}
|
||||||
|
trigger={<Button size={"sm"}>{t("common.add")}</Button>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex justify-end py-2 border-b dark:border-stone-400">
|
||||||
|
<DeployEditDialog
|
||||||
|
trigger={<Button size={"sm"}>{t("common.add")}</Button>}
|
||||||
|
onSave={(config: DeployConfig) => {
|
||||||
|
handleAdd(config);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full md:w-[35em] rounded mt-5 border dark:border-stone-400 dark:text-stone-200">
|
||||||
|
<div className="">
|
||||||
|
{list.map((item) => (
|
||||||
|
<DeployItem
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
onDelete={() => {
|
||||||
|
handleDelete(item.id ?? "");
|
||||||
|
}}
|
||||||
|
onSave={(deploy: DeployConfig) => {
|
||||||
|
handleSave(deploy);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type DeployItemProps = {
|
||||||
|
item: DeployConfig;
|
||||||
|
onDelete: () => void;
|
||||||
|
onSave: (deploy: DeployConfig) => void;
|
||||||
|
};
|
||||||
|
const DeployItem = ({ item, onDelete, onSave }: DeployItemProps) => {
|
||||||
|
const {
|
||||||
|
config: { accesses },
|
||||||
|
} = useConfig();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const access = accesses.find((access) => access.id === item.access);
|
||||||
|
const getImg = () => {
|
||||||
|
if (!access) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessType = accessTypeMap.get(access.configType);
|
||||||
|
|
||||||
|
if (accessType) {
|
||||||
|
return accessType[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTypeName = () => {
|
||||||
|
if (!access) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessType = targetTypeMap.get(item.type);
|
||||||
|
|
||||||
|
if (accessType) {
|
||||||
|
return t(accessType[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-between text-sm p-3 items-center text-stone-700 dark:text-stone-200">
|
||||||
|
<div className="flex space-x-2 items-center">
|
||||||
|
<div>
|
||||||
|
<img src={getImg()} className="w-9"></img>
|
||||||
|
</div>
|
||||||
|
<div className="text-stone-600 flex-col flex space-y-0 dark:text-stone-200">
|
||||||
|
<div>{getTypeName()}</div>
|
||||||
|
<div>{access?.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<DeployEditDialog
|
||||||
|
trigger={<EditIcon size={16} className="cursor-pointer" />}
|
||||||
|
deployConfig={item}
|
||||||
|
onSave={(deploy: DeployConfig) => {
|
||||||
|
onSave(deploy);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Trash2
|
||||||
|
size={16}
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
onDelete();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type DeployEditDialogProps = {
|
||||||
|
trigger: React.ReactNode;
|
||||||
|
deployConfig?: DeployConfig;
|
||||||
|
onSave: (deploy: DeployConfig) => void;
|
||||||
|
};
|
||||||
|
const DeployEditDialog = ({
|
||||||
|
trigger,
|
||||||
|
deployConfig,
|
||||||
|
onSave,
|
||||||
|
}: DeployEditDialogProps) => {
|
||||||
|
const {
|
||||||
|
config: { accesses },
|
||||||
|
} = useConfig();
|
||||||
|
|
||||||
|
const [deployType, setDeployType] = useState<TargetType>();
|
||||||
|
|
||||||
|
const [locDeployConfig, setLocDeployConfig] = useState<DeployConfig>({
|
||||||
|
access: "",
|
||||||
|
type: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [error, setError] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (deployConfig) {
|
||||||
|
setLocDeployConfig({ ...deployConfig });
|
||||||
|
} else {
|
||||||
|
setLocDeployConfig({
|
||||||
|
access: "",
|
||||||
|
type: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [deployConfig]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const temp = locDeployConfig.type.split("-");
|
||||||
|
console.log(temp);
|
||||||
|
let t;
|
||||||
|
if (temp && temp.length > 1) {
|
||||||
|
t = temp[1];
|
||||||
|
} else {
|
||||||
|
t = locDeployConfig.type;
|
||||||
|
}
|
||||||
|
setDeployType(t as TargetType);
|
||||||
|
setError({});
|
||||||
|
}, [locDeployConfig.type]);
|
||||||
|
|
||||||
|
const setDeploy = useCallback(
|
||||||
|
(deploy: DeployConfig) => {
|
||||||
|
if (deploy.type !== locDeployConfig.type) {
|
||||||
|
setLocDeployConfig({ ...deploy, access: "", config: {} });
|
||||||
|
} else {
|
||||||
|
setLocDeployConfig({ ...deploy });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[locDeployConfig.type]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const targetAccesses = accesses.filter((item) => {
|
||||||
|
if (item.usage == "apply") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locDeployConfig.type == "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const types = locDeployConfig.type.split("-");
|
||||||
|
return item.configType === types[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSaveClick = () => {
|
||||||
|
// 验证数据
|
||||||
|
// 保存数据
|
||||||
|
// 清理数据
|
||||||
|
// 关闭弹框
|
||||||
|
const newError = { ...error };
|
||||||
|
if (locDeployConfig.type === "") {
|
||||||
|
newError.type = t("domain.deployment.form.access.placeholder");
|
||||||
|
} else {
|
||||||
|
newError.type = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locDeployConfig.access === "") {
|
||||||
|
newError.access = t("domain.deployment.form.access.placeholder");
|
||||||
|
} else {
|
||||||
|
newError.access = "";
|
||||||
|
}
|
||||||
|
setError(newError);
|
||||||
|
|
||||||
|
for (const key in newError) {
|
||||||
|
if (newError[key] !== "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSave(locDeployConfig);
|
||||||
|
|
||||||
|
setLocDeployConfig({
|
||||||
|
access: "",
|
||||||
|
type: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
setError({});
|
||||||
|
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DeployEditContext.Provider
|
||||||
|
value={{
|
||||||
|
deploy: locDeployConfig,
|
||||||
|
setDeploy: setDeploy,
|
||||||
|
error: error,
|
||||||
|
setError: setError,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
|
<DialogTrigger>{trigger}</DialogTrigger>
|
||||||
|
<DialogContent className="dark:text-stone-200">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{t("history.page.title")}</DialogTitle>
|
||||||
|
<DialogDescription></DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
{/* 部署方式 */}
|
||||||
|
<div>
|
||||||
|
<Label>{t("domain.deployment.form.type.label")}</Label>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
value={locDeployConfig.type}
|
||||||
|
onValueChange={(val: string) => {
|
||||||
|
setDeploy({ ...locDeployConfig, type: val });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="mt-2">
|
||||||
|
<SelectValue
|
||||||
|
placeholder={t("domain.deployment.form.type.placeholder")}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>
|
||||||
|
{t("domain.deployment.form.type.list")}
|
||||||
|
</SelectLabel>
|
||||||
|
{targetTypeKeys.map((item) => (
|
||||||
|
<SelectItem key={item} value={item}>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<img
|
||||||
|
className="w-6"
|
||||||
|
src={targetTypeMap.get(item)?.[1]}
|
||||||
|
/>
|
||||||
|
<div>{t(targetTypeMap.get(item)?.[0] ?? "")}</div>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<div className="text-red-500 text-sm mt-1">{error.type}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 授权配置 */}
|
||||||
|
<div>
|
||||||
|
<Label className="flex justify-between">
|
||||||
|
<div>{t("domain.deployment.form.access.label")}</div>
|
||||||
|
<AccessEdit
|
||||||
|
trigger={
|
||||||
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
|
<Plus size={14} />
|
||||||
|
{t("common.add")}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
op="add"
|
||||||
|
/>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
value={locDeployConfig.access}
|
||||||
|
onValueChange={(val: string) => {
|
||||||
|
setDeploy({ ...locDeployConfig, access: val });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="mt-2">
|
||||||
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"domain.deployment.form.access.placeholder"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>
|
||||||
|
{t("domain.deployment.form.access.list")}
|
||||||
|
</SelectLabel>
|
||||||
|
{targetAccesses.map((item) => (
|
||||||
|
<SelectItem key={item.id} value={item.id}>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<img
|
||||||
|
className="w-6"
|
||||||
|
src={accessTypeMap.get(item.configType)?.[1]}
|
||||||
|
/>
|
||||||
|
<div>{item.name}</div>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<div className="text-red-500 text-sm mt-1">{error.access}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 其他参数 */}
|
||||||
|
<DeployEdit type={deployType!} />
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleSaveClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common.save")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</DeployEditContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type TargetType = "ssh" | "cdn" | "webhook" | "local" | "oss" | "dcdn";
|
||||||
|
|
||||||
|
type DeployEditProps = {
|
||||||
|
type: TargetType;
|
||||||
|
};
|
||||||
|
const DeployEdit = ({ type }: DeployEditProps) => {
|
||||||
|
const getDeploy = () => {
|
||||||
|
switch (type) {
|
||||||
|
case "ssh":
|
||||||
|
return <DeploySSH />;
|
||||||
|
case "local":
|
||||||
|
return <DeploySSH />;
|
||||||
|
case "cdn":
|
||||||
|
return <DeployCDN />;
|
||||||
|
case "dcdn":
|
||||||
|
return <DeployCDN />;
|
||||||
|
case "oss":
|
||||||
|
return <DeployOSS />;
|
||||||
|
case "webhook":
|
||||||
|
return <DeployWebhook />;
|
||||||
|
default:
|
||||||
|
return <DeployCDN />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return getDeploy();
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeploySSH = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { setError } = useDeployEditContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setError({});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const { deploy: data, setDeploy } = useDeployEditContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data.id) {
|
||||||
|
setDeploy({
|
||||||
|
...data,
|
||||||
|
config: {
|
||||||
|
certPath: "/etc/nginx/ssl/nginx.crt",
|
||||||
|
keyPath: "/etc/nginx/ssl/nginx.key",
|
||||||
|
preCommand: "",
|
||||||
|
command: "sudo service nginx reload",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<div>
|
||||||
|
<Label>{t("access.authorization.form.ssh_cert_path.label")}</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={t("access.authorization.form.ssh_cert_path.label")}
|
||||||
|
className="w-full mt-1"
|
||||||
|
value={data?.config?.certPath}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.certPath = e.target.value;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>{t("access.authorization.form.ssh_key_path.label")}</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.ssh_key_path.placeholder"
|
||||||
|
)}
|
||||||
|
className="w-full mt-1"
|
||||||
|
value={data?.config?.keyPath}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.keyPath = e.target.value;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Label>{t("access.authorization.form.ssh_pre_command.label")}</Label>
|
||||||
|
<Textarea
|
||||||
|
className="mt-1"
|
||||||
|
value={data?.config?.preCommand}
|
||||||
|
placeholder={t(
|
||||||
|
"access.authorization.form.ssh_pre_command.placeholder"
|
||||||
|
)}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.preCommand = e.target.value;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
></Textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Label>{t("access.authorization.form.ssh_command.label")}</Label>
|
||||||
|
<Textarea
|
||||||
|
className="mt-1"
|
||||||
|
value={data?.config?.command}
|
||||||
|
placeholder={t("access.authorization.form.ssh_command.placeholder")}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.command = e.target.value;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
></Textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeployCDN = () => {
|
||||||
|
const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setError({});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const resp = domainSchema.safeParse(data.config?.domain);
|
||||||
|
if (!resp.success) {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: JSON.parse(resp.error.message)[0].message,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const domainSchema = z
|
||||||
|
.string()
|
||||||
|
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||||
|
message: t("common.errmsg.domain_invalid"),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<div>
|
||||||
|
<Label>{t("domain.deployment.form.cdn_domain.label")}</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"domain.deployment.form.cdn_domain.placeholder"
|
||||||
|
)}
|
||||||
|
className="w-full mt-1"
|
||||||
|
value={data?.config?.domain}
|
||||||
|
onChange={(e) => {
|
||||||
|
const temp = e.target.value;
|
||||||
|
|
||||||
|
const resp = domainSchema.safeParse(temp);
|
||||||
|
if (!resp.success) {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: JSON.parse(resp.error.message)[0].message,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.domain = temp;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="text-red-600 text-sm mt-1">{error?.domain}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeployOSS = () => {
|
||||||
|
const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setError({});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const resp = domainSchema.safeParse(data.config?.domain);
|
||||||
|
if (!resp.success) {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: JSON.parse(resp.error.message)[0].message,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const bucketResp = bucketSchema.safeParse(data.config?.domain);
|
||||||
|
if (!bucketResp.success) {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
bucket: JSON.parse(bucketResp.error.message)[0].message,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
bucket: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data.id) {
|
||||||
|
setDeploy({
|
||||||
|
...data,
|
||||||
|
config: {
|
||||||
|
endpoint: "oss-cn-hangzhou.aliyuncs.com",
|
||||||
|
bucket: "",
|
||||||
|
domain: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const domainSchema = z
|
||||||
|
.string()
|
||||||
|
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||||
|
message: t("common.errmsg.domain_invalid"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const bucketSchema = z.string().min(1, {
|
||||||
|
message: t("domain.deployment.form.oss_bucket.placeholder"),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<div>
|
||||||
|
<Label>{t("domain.deployment.form.oss_endpoint.label")}</Label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
className="w-full mt-1"
|
||||||
|
value={data?.config?.endpoint}
|
||||||
|
onChange={(e) => {
|
||||||
|
const temp = e.target.value;
|
||||||
|
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.endpoint = temp;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="text-red-600 text-sm mt-1">{error?.endpoint}</div>
|
||||||
|
|
||||||
|
<Label>{t("domain.deployment.form.oss_bucket")}</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"domain.deployment.form.oss_bucket.placeholder"
|
||||||
|
)}
|
||||||
|
className="w-full mt-1"
|
||||||
|
value={data?.config?.bucket}
|
||||||
|
onChange={(e) => {
|
||||||
|
const temp = e.target.value;
|
||||||
|
|
||||||
|
const resp = bucketSchema.safeParse(temp);
|
||||||
|
if (!resp.success) {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
bucket: JSON.parse(resp.error.message)[0].message,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
bucket: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.bucket = temp;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="text-red-600 text-sm mt-1">{error?.bucket}</div>
|
||||||
|
|
||||||
|
<Label>{t("domain.deployment.form.cdn_domain.label")}</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={t("domain.deployment.form.cdn_domain.label")}
|
||||||
|
className="w-full mt-1"
|
||||||
|
value={data?.config?.domain}
|
||||||
|
onChange={(e) => {
|
||||||
|
const temp = e.target.value;
|
||||||
|
|
||||||
|
const resp = domainSchema.safeParse(temp);
|
||||||
|
if (!resp.success) {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: JSON.parse(resp.error.message)[0].message,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError({
|
||||||
|
...error,
|
||||||
|
domain: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.domain = temp;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="text-red-600 text-sm mt-1">{error?.domain}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeployWebhook = () => {
|
||||||
|
const { deploy: data, setDeploy } = useDeployEditContext();
|
||||||
|
|
||||||
|
const { setError } = useDeployEditContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setError({});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<KVList
|
||||||
|
variables={data?.config?.variables}
|
||||||
|
onValueChange={(variables: KVType[]) => {
|
||||||
|
const newData = produce(data, (draft) => {
|
||||||
|
if (!draft.config) {
|
||||||
|
draft.config = {};
|
||||||
|
}
|
||||||
|
draft.config.variables = variables;
|
||||||
|
});
|
||||||
|
setDeploy(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeployList;
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
|
|
||||||
@@ -16,58 +15,52 @@ const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
|
|||||||
let step = 0;
|
let step = 0;
|
||||||
|
|
||||||
if (phase === "check") {
|
if (phase === "check") {
|
||||||
step = 1
|
step = 1;
|
||||||
} else if (phase === "apply") {
|
} else if (phase === "apply") {
|
||||||
step = 2
|
step = 2;
|
||||||
} else if (phase === "deploy") {
|
} else if (phase === "deploy") {
|
||||||
step = 3
|
step = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className={
|
<div
|
||||||
cn(
|
className={cn(
|
||||||
"text-xs text-nowrap",
|
"text-xs text-nowrap",
|
||||||
step === 1 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
step === 1 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||||
step > 1 ? "text-green-600" : "",
|
step > 1 ? "text-green-600" : ""
|
||||||
)
|
)}
|
||||||
}>
|
>
|
||||||
{t('deploy.progress.check')}
|
{t("history.props.stage.progress.check")}
|
||||||
</div>
|
</div>
|
||||||
<Separator className={
|
<Separator
|
||||||
cn(
|
className={cn("h-1 grow max-w-[60px]", step > 1 ? "bg-green-600" : "")}
|
||||||
"h-1 grow max-w-[60px]",
|
/>
|
||||||
step > 1 ? "bg-green-600" : "",
|
<div
|
||||||
)
|
className={cn(
|
||||||
} />
|
|
||||||
<div className={
|
|
||||||
cn(
|
|
||||||
"text-xs text-nowrap",
|
"text-xs text-nowrap",
|
||||||
step < 2 ? "text-muted-foreground" : "",
|
step < 2 ? "text-muted-foreground" : "",
|
||||||
step === 2 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
step === 2 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||||
step > 2 ? "text-green-600" : "",
|
step > 2 ? "text-green-600" : ""
|
||||||
)
|
)}
|
||||||
}>
|
>
|
||||||
{t('deploy.progress.apply')}
|
{t("history.props.stage.progress.apply")}
|
||||||
</div>
|
</div>
|
||||||
<Separator className={
|
<Separator
|
||||||
cn(
|
className={cn("h-1 grow max-w-[60px]", step > 2 ? "bg-green-600" : "")}
|
||||||
"h-1 grow max-w-[60px]",
|
/>
|
||||||
step > 2 ? "bg-green-600" : "",
|
<div
|
||||||
)
|
className={cn(
|
||||||
} />
|
|
||||||
<div className={
|
|
||||||
cn(
|
|
||||||
"text-xs text-nowrap",
|
"text-xs text-nowrap",
|
||||||
step < 3 ? "text-muted-foreground" : "",
|
step < 3 ? "text-muted-foreground" : "",
|
||||||
step === 3 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
step === 3 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||||
step > 3 ? "text-green-600" : "",
|
step > 3 ? "text-green-600" : ""
|
||||||
)
|
)}
|
||||||
}>
|
>
|
||||||
{t('deploy.progress.deploy')}
|
{t("history.props.stage.progress.deploy")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeployProgress;
|
export default DeployProgress;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
email: z.string().email("email.valid.message"),
|
email: z.string().email("common.errmsg.email_invalid"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
@@ -56,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: "email.already.exist",
|
message: "common.errmsg.email_duplicate",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -102,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>{t('email.add')}</DialogTitle>
|
<DialogTitle>{t("domain.application.form.email.add")}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="container py-3">
|
<div className="container py-3">
|
||||||
@@ -120,9 +120,13 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
name="email"
|
name="email"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('email')}</FormLabel>
|
<FormLabel>{t("domain.application.form.email.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('email.not.empty.message')} {...field} type="email" />
|
<Input
|
||||||
|
placeholder={t("common.errmsg.email_empty")}
|
||||||
|
{...field}
|
||||||
|
type="email"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -131,7 +135,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('save')}</Button>
|
<Button type="submit">{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
252
ui/src/components/certimate/KVList.tsx
Normal file
252
ui/src/components/certimate/KVList.tsx
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
import { KVType } from "@/domain/domain";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Label } from "../ui/label";
|
||||||
|
import { Edit, Plus, Trash2 } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Show from "../Show";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "../ui/dialog";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
|
import { produce } from "immer";
|
||||||
|
|
||||||
|
type KVListProps = {
|
||||||
|
variables?: KVType[];
|
||||||
|
onValueChange?: (variables: KVType[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const KVList = ({ variables, onValueChange }: KVListProps) => {
|
||||||
|
const [locVariables, setLocVariables] = useState<KVType[]>([]);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (variables) {
|
||||||
|
setLocVariables(variables);
|
||||||
|
}
|
||||||
|
}, [variables]);
|
||||||
|
|
||||||
|
const handleAddClick = (variable: KVType) => {
|
||||||
|
// 查看是否存在key,存在则更新,不存在则添加
|
||||||
|
const index = locVariables.findIndex((item) => {
|
||||||
|
return item.key === variable.key;
|
||||||
|
});
|
||||||
|
|
||||||
|
const newList = produce(locVariables, (draft) => {
|
||||||
|
if (index === -1) {
|
||||||
|
draft.push(variable);
|
||||||
|
} else {
|
||||||
|
draft[index] = variable;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setLocVariables(newList);
|
||||||
|
|
||||||
|
onValueChange?.(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteClick = (index: number) => {
|
||||||
|
const newList = [...locVariables];
|
||||||
|
newList.splice(index, 1);
|
||||||
|
setLocVariables(newList);
|
||||||
|
|
||||||
|
onValueChange?.(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditClick = (index: number, variable: KVType) => {
|
||||||
|
const newList = [...locVariables];
|
||||||
|
newList[index] = variable;
|
||||||
|
setLocVariables(newList);
|
||||||
|
|
||||||
|
onValueChange?.(newList);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-between dark:text-stone-200">
|
||||||
|
<Label>{t("domain.deployment.form.variables.label")}</Label>
|
||||||
|
<Show when={!!locVariables?.length}>
|
||||||
|
<KVEdit
|
||||||
|
variable={{
|
||||||
|
key: "",
|
||||||
|
value: "",
|
||||||
|
}}
|
||||||
|
trigger={
|
||||||
|
<div className="flex items-center text-primary">
|
||||||
|
<Plus size={16} className="cursor-pointer " />
|
||||||
|
|
||||||
|
<div className="text-sm ">{t("common.add")}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
onSave={(variable) => {
|
||||||
|
handleAddClick(variable);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show
|
||||||
|
when={!!locVariables?.length}
|
||||||
|
fallback={
|
||||||
|
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
|
||||||
|
<div className="text-muted-foreground">
|
||||||
|
{t("domain.deployment.form.variables.empty")}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<KVEdit
|
||||||
|
trigger={
|
||||||
|
<div className="flex items-center text-primary">
|
||||||
|
<Plus size={16} className="cursor-pointer " />
|
||||||
|
|
||||||
|
<div className="text-sm ">{t("common.add")}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
variable={{
|
||||||
|
key: "",
|
||||||
|
value: "",
|
||||||
|
}}
|
||||||
|
onSave={(variable) => {
|
||||||
|
handleAddClick(variable);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="border p-3 rounded-md text-stone-700 text-sm dark:text-stone-200">
|
||||||
|
{locVariables?.map((item, index) => (
|
||||||
|
<div key={index} className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
{item.key}={item.value}
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<KVEdit
|
||||||
|
trigger={<Edit size={16} className="cursor-pointer" />}
|
||||||
|
variable={item}
|
||||||
|
onSave={(variable) => {
|
||||||
|
handleEditClick(index, variable);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Trash2
|
||||||
|
size={16}
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
handleDeleteClick(index);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type KVEditProps = {
|
||||||
|
variable?: KVType;
|
||||||
|
trigger: React.ReactNode;
|
||||||
|
onSave: (variable: KVType) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const KVEdit = ({ variable, trigger, onSave }: KVEditProps) => {
|
||||||
|
const [locVariable, setLocVariable] = useState<KVType>({
|
||||||
|
key: "",
|
||||||
|
value: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (variable) setLocVariable(variable!);
|
||||||
|
}, [variable]);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [err, setErr] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
|
const handleSaveClick = () => {
|
||||||
|
if (!locVariable.key) {
|
||||||
|
setErr({
|
||||||
|
key: t("domain.deployment.form.variables.key.required"),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!locVariable.value) {
|
||||||
|
setErr({
|
||||||
|
value: t("domain.deployment.form.variables.value.required"),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSave?.(locVariable);
|
||||||
|
|
||||||
|
setOpen(false);
|
||||||
|
|
||||||
|
setErr({});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onOpenChange={() => {
|
||||||
|
setOpen(!open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTrigger>{trigger}</DialogTrigger>
|
||||||
|
<DialogContent className="dark:text-stone-200">
|
||||||
|
<DialogHeader className="flex flex-col">
|
||||||
|
<DialogTitle>{t("domain.deployment.form.variables.label")}</DialogTitle>
|
||||||
|
|
||||||
|
<div className="pt-5 flex flex-col items-start">
|
||||||
|
<Label>{t("domain.deployment.form.variables.key")}</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={t("domain.deployment.form.variables.key.placeholder")}
|
||||||
|
value={locVariable?.key}
|
||||||
|
onChange={(e) => {
|
||||||
|
setLocVariable({ ...locVariable, key: e.target.value });
|
||||||
|
}}
|
||||||
|
className="w-full mt-1"
|
||||||
|
/>
|
||||||
|
<div className="text-red-500 text-sm mt-1">{err?.key}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pt-2 flex flex-col items-start">
|
||||||
|
<Label>{t("domain.deployment.form.variables.value")}</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={t("domain.deployment.form.variables.value.placeholder")}
|
||||||
|
value={locVariable?.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
setLocVariable({ ...locVariable, value: e.target.value });
|
||||||
|
}}
|
||||||
|
className="w-full mt-1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="text-red-500 text-sm mt-1">{err?.value}</div>
|
||||||
|
</div>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
handleSaveClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common.save")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KVList;
|
||||||
@@ -20,13 +20,14 @@ import { Edit, Plus, Trash2 } from "lucide-react";
|
|||||||
type StringListProps = {
|
type StringListProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
value: string;
|
value: string;
|
||||||
valueType?: "domain" | "ip";
|
valueType?: ValueType;
|
||||||
onValueChange: (value: string) => void;
|
onValueChange: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const titles: Record<string, string> = {
|
const titles: Record<string, string> = {
|
||||||
domain: "domain",
|
domain: "common.text.domain",
|
||||||
ip: "IP",
|
ip: "common.text.ip",
|
||||||
|
dns: "common.text.dns",
|
||||||
};
|
};
|
||||||
|
|
||||||
const StringList = ({
|
const StringList = ({
|
||||||
@@ -89,7 +90,7 @@ const StringList = ({
|
|||||||
<div className="flex items-center text-primary">
|
<div className="flex items-center text-primary">
|
||||||
<Plus size={16} className="cursor-pointer " />
|
<Plus size={16} className="cursor-pointer " />
|
||||||
|
|
||||||
<div className="text-sm ">{t("add")}</div>
|
<div className="text-sm ">{t("common.add")}</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -100,18 +101,20 @@ const StringList = ({
|
|||||||
when={list.length > 0}
|
when={list.length > 0}
|
||||||
fallback={
|
fallback={
|
||||||
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
|
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
|
||||||
<div className="text-muted-foreground">暂未添加域名</div>
|
<div className="text-muted-foreground">
|
||||||
|
{t('common.text.' + valueType + '.empty')}
|
||||||
|
</div>
|
||||||
|
|
||||||
<StringEdit
|
<StringEdit
|
||||||
value={""}
|
value={""}
|
||||||
trigger={t("add")}
|
trigger={t("common.add")}
|
||||||
onValueChange={addVal}
|
onValueChange={addVal}
|
||||||
valueType={valueType}
|
valueType={valueType}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="border rounded-md p-3 text-sm mt-2 text-gray-700 space-y-2">
|
<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) => (
|
{list.map((item, index) => (
|
||||||
<div key={index} className="flex justify-between items-center">
|
<div key={index} className="flex justify-between items-center">
|
||||||
<div>{item}</div>
|
<div>{item}</div>
|
||||||
@@ -122,7 +125,7 @@ const StringList = ({
|
|||||||
trigger={
|
trigger={
|
||||||
<Edit
|
<Edit
|
||||||
size={16}
|
size={16}
|
||||||
className="cursor-pointer text-gray-600"
|
className="cursor-pointer text-gray-600 dark:text-white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
value={item}
|
value={item}
|
||||||
@@ -150,7 +153,7 @@ const StringList = ({
|
|||||||
|
|
||||||
export default StringList;
|
export default StringList;
|
||||||
|
|
||||||
type ValueType = "domain" | "ip";
|
type ValueType = "domain" | "dns" | "host";
|
||||||
|
|
||||||
type StringEditProps = {
|
type StringEditProps = {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -179,14 +182,15 @@ const StringEdit = ({
|
|||||||
const domainSchema = z
|
const domainSchema = z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||||
message: t("domain.not.empty.verify.message"),
|
message: t("common.errmsg.domain_invalid"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ipSchema = z.string().ip({ message: t("ip.not.empty.verify.message") });
|
const ipSchema = z.string().ip({ message: t("common.errmsg.ip_invalid") });
|
||||||
|
|
||||||
const schedules: Record<ValueType, z.ZodString> = {
|
const schedules: Record<ValueType, z.ZodString> = {
|
||||||
domain: domainSchema,
|
domain: domainSchema,
|
||||||
ip: ipSchema,
|
dns: ipSchema,
|
||||||
|
host: ipSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSaveClick = useCallback(() => {
|
const onSaveClick = useCallback(() => {
|
||||||
@@ -213,12 +217,15 @@ const StringEdit = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTrigger className="text-primary">{trigger}</DialogTrigger>
|
<DialogTrigger className="text-primary">{trigger}</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent className="dark:text-white">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{t(titles[valueType])}</DialogTitle>
|
<DialogTitle className="dark:text-white">
|
||||||
|
{t(titles[valueType])}
|
||||||
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<Input
|
<Input
|
||||||
value={currentValue}
|
value={currentValue}
|
||||||
|
className="dark:text-white"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setCurrentValue(e.target.value);
|
setCurrentValue(e.target.value);
|
||||||
}}
|
}}
|
||||||
@@ -233,7 +240,7 @@ const StringEdit = ({
|
|||||||
onSaveClick();
|
onSaveClick();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{op === "add" ? t("add") : t("confirm")}
|
{op === "add" ? t("common.add") : t("common.confirm")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Separator } from "../ui/separator";
|
|||||||
import { version } from "@/domain/version";
|
import { version } from "@/domain/version";
|
||||||
|
|
||||||
const Version = () => {
|
const Version = () => {
|
||||||
const { t } = useTranslation()
|
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">
|
||||||
@@ -17,7 +17,7 @@ const Version = () => {
|
|||||||
className="flex items-center"
|
className="flex items-center"
|
||||||
>
|
>
|
||||||
<BookOpen size={16} />
|
<BookOpen size={16} />
|
||||||
<div className="ml-1">{t('document')}</div>
|
<div className="ml-1">{t("common.menu.document")}</div>
|
||||||
</a>
|
</a>
|
||||||
<Separator orientation="vertical" className="mx-2" />
|
<Separator orientation="vertical" className="mx-2" />
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -8,7 +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'
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type DingTalkSetting = {
|
type DingTalkSetting = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -72,15 +72,17 @@ const DingTalk = () => {
|
|||||||
|
|
||||||
setChannels(resp);
|
setChannels(resp);
|
||||||
toast({
|
toast({
|
||||||
title: t('save.succeed'),
|
title: t("common.save.succeeded.message"),
|
||||||
description: t('setting.notify.config.save.succeed'),
|
description: t("settings.notification.config.saved.message"),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = getErrMessage(e);
|
const msg = getErrMessage(e);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: t('save.failed'),
|
title: t("common.save.failed.message"),
|
||||||
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
description: `${t(
|
||||||
|
"settings.notification.config.failed.message"
|
||||||
|
)}: ${msg}`,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -102,7 +104,7 @@ const DingTalk = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('access.form.ding.access.token.placeholder')}
|
placeholder={t("settings.notification.dingtalk.secret.placeholder")}
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
value={dingtalk.data.secret}
|
value={dingtalk.data.secret}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -129,7 +131,9 @@ const DingTalk = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
|
<Label htmlFor="airplane-mode">
|
||||||
|
{t("settings.notification.config.enable")}
|
||||||
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
@@ -138,7 +142,7 @@ const DingTalk = () => {
|
|||||||
handleSaveClick();
|
handleSaveClick();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('save')}
|
{t("common.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +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'
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const NotifyTemplate = () => {
|
const NotifyTemplate = () => {
|
||||||
const [id, setId] = useState("");
|
const [id, setId] = useState("");
|
||||||
@@ -68,8 +68,8 @@ const NotifyTemplate = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: t('save.succeed'),
|
title: t("common.save.succeeded.message"),
|
||||||
description: t('setting.notify.template.save.succeed'),
|
description: t("settings.notification.template.saved.message"),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ const NotifyTemplate = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="text-muted-foreground text-sm mt-1">
|
<div className="text-muted-foreground text-sm mt-1">
|
||||||
{t('setting.notify.template.variables.tips.title')}
|
{t("settings.notification.template.variables.tips.title")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Textarea
|
<Textarea
|
||||||
@@ -94,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">
|
||||||
{t('setting.notify.template.variables.tips.content')}
|
{t("settings.notification.template.variables.tips.content")}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
<Button onClick={handleSaveClick}>{t('save')}</Button>
|
<Button onClick={handleSaveClick}>{t("common.save")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const Telegram = () => {
|
|||||||
const data = getDetailTelegram();
|
const data = getDetailTelegram();
|
||||||
setTelegram({
|
setTelegram({
|
||||||
id: config.id ?? "",
|
id: config.id ?? "",
|
||||||
name: "telegram",
|
name: "common.provider.telegram",
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
}, [config]);
|
}, [config]);
|
||||||
@@ -72,15 +72,17 @@ const Telegram = () => {
|
|||||||
|
|
||||||
setChannels(resp);
|
setChannels(resp);
|
||||||
toast({
|
toast({
|
||||||
title: t('save.succeed'),
|
title: t("common.save.succeeded.message"),
|
||||||
description: t('setting.notify.config.save.succeed'),
|
description: t("settings.notification.config.saved.message"),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = getErrMessage(e);
|
const msg = getErrMessage(e);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: t('save.failed'),
|
title: t("common.save.failed.message"),
|
||||||
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
description: `${t(
|
||||||
|
"settings.notification.config.failed.message"
|
||||||
|
)}: ${msg}`,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -130,7 +132,9 @@ const Telegram = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
|
<Label htmlFor="airplane-mode">
|
||||||
|
{t("settings.notification.config.enable")}
|
||||||
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
@@ -139,7 +143,7 @@ const Telegram = () => {
|
|||||||
handleSaveClick();
|
handleSaveClick();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('save')}
|
{t("common.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +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'
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type WebhookSetting = {
|
type WebhookSetting = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -61,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: t('save.failed'),
|
title: t("common.save.failed.message"),
|
||||||
description: t('setting.notify.config.save.failed.url.not.valid'),
|
description: t("settings.notification.url.errmsg.invalid"),
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -81,15 +81,17 @@ const Webhook = () => {
|
|||||||
|
|
||||||
setChannels(resp);
|
setChannels(resp);
|
||||||
toast({
|
toast({
|
||||||
title: t('save.succeed'),
|
title: t("common.save.succeeded.message"),
|
||||||
description: t('setting.notify.config.save.succeed'),
|
description: t("settings.notification.config.saved.message"),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = getErrMessage(e);
|
const msg = getErrMessage(e);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: t('save.failed'),
|
title: t("common.save.failed.message"),
|
||||||
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
description: `${t(
|
||||||
|
"settings.notification.config.failed.message"
|
||||||
|
)}: ${msg}`,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -125,7 +127,9 @@ const Webhook = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
|
<Label htmlFor="airplane-mode">
|
||||||
|
{t("settings.notification.config.enable")}
|
||||||
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
@@ -134,7 +138,7 @@ const Webhook = () => {
|
|||||||
handleSaveClick();
|
handleSaveClick();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('save')}
|
{t("common.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||||
import { ChevronDown } from "lucide-react"
|
import { ChevronDown } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const Accordion = AccordionPrimitive.Root
|
const Accordion = AccordionPrimitive.Root;
|
||||||
|
|
||||||
const AccordionItem = React.forwardRef<
|
const AccordionItem = React.forwardRef<
|
||||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||||
@@ -15,8 +15,8 @@ const AccordionItem = React.forwardRef<
|
|||||||
className={cn("border-b", className)}
|
className={cn("border-b", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
AccordionItem.displayName = "AccordionItem"
|
AccordionItem.displayName = "AccordionItem";
|
||||||
|
|
||||||
const AccordionTrigger = React.forwardRef<
|
const AccordionTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||||
@@ -35,8 +35,8 @@ const AccordionTrigger = React.forwardRef<
|
|||||||
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||||
</AccordionPrimitive.Trigger>
|
</AccordionPrimitive.Trigger>
|
||||||
</AccordionPrimitive.Header>
|
</AccordionPrimitive.Header>
|
||||||
))
|
));
|
||||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
const AccordionContent = React.forwardRef<
|
const AccordionContent = React.forwardRef<
|
||||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||||
@@ -49,8 +49,8 @@ const AccordionContent = React.forwardRef<
|
|||||||
>
|
>
|
||||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||||
</AccordionPrimitive.Content>
|
</AccordionPrimitive.Content>
|
||||||
))
|
));
|
||||||
|
|
||||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const alertVariants = cva(
|
const alertVariants = cva(
|
||||||
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
||||||
@@ -17,7 +17,7 @@ const alertVariants = cva(
|
|||||||
variant: "default",
|
variant: "default",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
|
|
||||||
const Alert = React.forwardRef<
|
const Alert = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
@@ -29,8 +29,8 @@ const Alert = React.forwardRef<
|
|||||||
className={cn(alertVariants({ variant }), className)}
|
className={cn(alertVariants({ variant }), className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
Alert.displayName = "Alert"
|
Alert.displayName = "Alert";
|
||||||
|
|
||||||
const AlertTitle = React.forwardRef<
|
const AlertTitle = React.forwardRef<
|
||||||
HTMLParagraphElement,
|
HTMLParagraphElement,
|
||||||
@@ -41,8 +41,8 @@ const AlertTitle = React.forwardRef<
|
|||||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
AlertTitle.displayName = "AlertTitle"
|
AlertTitle.displayName = "AlertTitle";
|
||||||
|
|
||||||
const AlertDescription = React.forwardRef<
|
const AlertDescription = React.forwardRef<
|
||||||
HTMLParagraphElement,
|
HTMLParagraphElement,
|
||||||
@@ -53,7 +53,7 @@ const AlertDescription = React.forwardRef<
|
|||||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
AlertDescription.displayName = "AlertDescription"
|
AlertDescription.displayName = "AlertDescription";
|
||||||
|
|
||||||
export { Alert, AlertTitle, AlertDescription }
|
export { Alert, AlertTitle, AlertDescription };
|
||||||
|
|||||||
115
ui/src/components/ui/breadcrumb.tsx
Normal file
115
ui/src/components/ui/breadcrumb.tsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Breadcrumb = React.forwardRef<
|
||||||
|
HTMLElement,
|
||||||
|
React.ComponentPropsWithoutRef<"nav"> & {
|
||||||
|
separator?: React.ReactNode
|
||||||
|
}
|
||||||
|
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
|
||||||
|
Breadcrumb.displayName = "Breadcrumb"
|
||||||
|
|
||||||
|
const BreadcrumbList = React.forwardRef<
|
||||||
|
HTMLOListElement,
|
||||||
|
React.ComponentPropsWithoutRef<"ol">
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ol
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
BreadcrumbList.displayName = "BreadcrumbList"
|
||||||
|
|
||||||
|
const BreadcrumbItem = React.forwardRef<
|
||||||
|
HTMLLIElement,
|
||||||
|
React.ComponentPropsWithoutRef<"li">
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<li
|
||||||
|
ref={ref}
|
||||||
|
className={cn("inline-flex items-center gap-1.5", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
BreadcrumbItem.displayName = "BreadcrumbItem"
|
||||||
|
|
||||||
|
const BreadcrumbLink = React.forwardRef<
|
||||||
|
HTMLAnchorElement,
|
||||||
|
React.ComponentPropsWithoutRef<"a"> & {
|
||||||
|
asChild?: boolean
|
||||||
|
}
|
||||||
|
>(({ asChild, className, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "a"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
ref={ref}
|
||||||
|
className={cn("transition-colors hover:text-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
BreadcrumbLink.displayName = "BreadcrumbLink"
|
||||||
|
|
||||||
|
const BreadcrumbPage = React.forwardRef<
|
||||||
|
HTMLSpanElement,
|
||||||
|
React.ComponentPropsWithoutRef<"span">
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<span
|
||||||
|
ref={ref}
|
||||||
|
role="link"
|
||||||
|
aria-disabled="true"
|
||||||
|
aria-current="page"
|
||||||
|
className={cn("font-normal text-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
BreadcrumbPage.displayName = "BreadcrumbPage"
|
||||||
|
|
||||||
|
const BreadcrumbSeparator = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"li">) => (
|
||||||
|
<li
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cn("[&>svg]:size-3.5", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children ?? <ChevronRight />}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
|
||||||
|
|
||||||
|
const BreadcrumbEllipsis = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"span">) => (
|
||||||
|
<span
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
|
<span className="sr-only">More</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
|
||||||
|
|
||||||
|
export {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
BreadcrumbEllipsis,
|
||||||
|
}
|
||||||
@@ -64,7 +64,7 @@ const PaginationPrevious = ({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => {
|
}: React.ComponentProps<typeof PaginationLink>) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaginationLink
|
<PaginationLink
|
||||||
@@ -74,9 +74,9 @@ const PaginationPrevious = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChevronLeft className="h-4 w-4" />
|
<ChevronLeft className="h-4 w-4" />
|
||||||
<span>{t('pagination.prev')}</span>
|
<span>{t("common.pagination.prev")}</span>
|
||||||
</PaginationLink>
|
</PaginationLink>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
PaginationPrevious.displayName = "PaginationPrevious";
|
PaginationPrevious.displayName = "PaginationPrevious";
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ const PaginationNext = ({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof PaginationLink>) => {
|
}: React.ComponentProps<typeof PaginationLink>) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaginationLink
|
<PaginationLink
|
||||||
@@ -93,26 +93,30 @@ const PaginationNext = ({
|
|||||||
className={cn("gap-1 pr-2.5", className)}
|
className={cn("gap-1 pr-2.5", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<span>{t('pagination.next')}</span>
|
<span>{t("common.pagination.next")}</span>
|
||||||
<ChevronRight className="h-4 w-4" />
|
<ChevronRight className="h-4 w-4" />
|
||||||
</PaginationLink>
|
</PaginationLink>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
PaginationNext.displayName = "PaginationNext";
|
PaginationNext.displayName = "PaginationNext";
|
||||||
|
|
||||||
const PaginationEllipsis = ({
|
const PaginationEllipsis = ({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"span">) => (
|
}: React.ComponentProps<"span">) => {
|
||||||
<span
|
const { t } = useTranslation();
|
||||||
aria-hidden
|
|
||||||
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
return (
|
||||||
{...props}
|
<span
|
||||||
>
|
aria-hidden
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
||||||
<span className="sr-only">More pages</span>
|
{...props}
|
||||||
</span>
|
>
|
||||||
);
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
|
<span className="sr-only">{t("common.pagination.more")}</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
PaginationEllipsis.displayName = "PaginationEllipsis";
|
PaginationEllipsis.displayName = "PaginationEllipsis";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -1,76 +1,73 @@
|
|||||||
// Inspired by react-hot-toast library
|
// Inspired by react-hot-toast library
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
|
|
||||||
import type {
|
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
|
||||||
ToastActionElement,
|
|
||||||
ToastProps,
|
|
||||||
} from "@/components/ui/toast"
|
|
||||||
|
|
||||||
const TOAST_LIMIT = 1
|
const TOAST_LIMIT = 1;
|
||||||
const TOAST_REMOVE_DELAY = 1000000
|
const TOAST_REMOVE_DELAY = 1000000;
|
||||||
|
|
||||||
type ToasterToast = ToastProps & {
|
type ToasterToast = ToastProps & {
|
||||||
id: string
|
id: string;
|
||||||
title?: React.ReactNode
|
title?: React.ReactNode;
|
||||||
description?: React.ReactNode
|
description?: React.ReactNode;
|
||||||
action?: ToastActionElement
|
action?: ToastActionElement;
|
||||||
}
|
};
|
||||||
|
|
||||||
const actionTypes = {
|
const actionTypes = {
|
||||||
ADD_TOAST: "ADD_TOAST",
|
ADD_TOAST: "ADD_TOAST",
|
||||||
UPDATE_TOAST: "UPDATE_TOAST",
|
UPDATE_TOAST: "UPDATE_TOAST",
|
||||||
DISMISS_TOAST: "DISMISS_TOAST",
|
DISMISS_TOAST: "DISMISS_TOAST",
|
||||||
REMOVE_TOAST: "REMOVE_TOAST",
|
REMOVE_TOAST: "REMOVE_TOAST",
|
||||||
} as const
|
} as const;
|
||||||
|
|
||||||
let count = 0
|
let count = 0;
|
||||||
|
|
||||||
function genId() {
|
function genId() {
|
||||||
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
||||||
return count.toString()
|
return count.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionType = typeof actionTypes
|
type ActionType = typeof actionTypes;
|
||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
| {
|
| {
|
||||||
type: ActionType["ADD_TOAST"]
|
type: ActionType["ADD_TOAST"];
|
||||||
toast: ToasterToast
|
toast: ToasterToast;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["UPDATE_TOAST"]
|
type: ActionType["UPDATE_TOAST"];
|
||||||
toast: Partial<ToasterToast>
|
toast: Partial<ToasterToast>;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["DISMISS_TOAST"]
|
type: ActionType["DISMISS_TOAST"];
|
||||||
toastId?: ToasterToast["id"]
|
toastId?: ToasterToast["id"];
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["REMOVE_TOAST"]
|
type: ActionType["REMOVE_TOAST"];
|
||||||
toastId?: ToasterToast["id"]
|
toastId?: ToasterToast["id"];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
toasts: ToasterToast[]
|
toasts: ToasterToast[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
|
|
||||||
const addToRemoveQueue = (toastId: string) => {
|
const addToRemoveQueue = (toastId: string) => {
|
||||||
if (toastTimeouts.has(toastId)) {
|
if (toastTimeouts.has(toastId)) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
toastTimeouts.delete(toastId)
|
toastTimeouts.delete(toastId);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "REMOVE_TOAST",
|
type: "REMOVE_TOAST",
|
||||||
toastId: toastId,
|
toastId: toastId,
|
||||||
})
|
});
|
||||||
}, TOAST_REMOVE_DELAY)
|
}, TOAST_REMOVE_DELAY);
|
||||||
|
|
||||||
toastTimeouts.set(toastId, timeout)
|
toastTimeouts.set(toastId, timeout);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const reducer = (state: State, action: Action): State => {
|
export const reducer = (state: State, action: Action): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@@ -78,7 +75,7 @@ export const reducer = (state: State, action: Action): State => {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||||
}
|
};
|
||||||
|
|
||||||
case "UPDATE_TOAST":
|
case "UPDATE_TOAST":
|
||||||
return {
|
return {
|
||||||
@@ -86,19 +83,19 @@ export const reducer = (state: State, action: Action): State => {
|
|||||||
toasts: state.toasts.map((t) =>
|
toasts: state.toasts.map((t) =>
|
||||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||||
),
|
),
|
||||||
}
|
};
|
||||||
|
|
||||||
case "DISMISS_TOAST": {
|
case "DISMISS_TOAST": {
|
||||||
const { toastId } = action
|
const { toastId } = action;
|
||||||
|
|
||||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||||
// but I'll keep it here for simplicity
|
// but I'll keep it here for simplicity
|
||||||
if (toastId) {
|
if (toastId) {
|
||||||
addToRemoveQueue(toastId)
|
addToRemoveQueue(toastId);
|
||||||
} else {
|
} else {
|
||||||
state.toasts.forEach((toast) => {
|
state.toasts.forEach((toast) => {
|
||||||
addToRemoveQueue(toast.id)
|
addToRemoveQueue(toast.id);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -111,44 +108,44 @@ export const reducer = (state: State, action: Action): State => {
|
|||||||
}
|
}
|
||||||
: t
|
: t
|
||||||
),
|
),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
case "REMOVE_TOAST":
|
case "REMOVE_TOAST":
|
||||||
if (action.toastId === undefined) {
|
if (action.toastId === undefined) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: [],
|
toasts: [],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const listeners: Array<(state: State) => void> = []
|
const listeners: Array<(state: State) => void> = [];
|
||||||
|
|
||||||
let memoryState: State = { toasts: [] }
|
let memoryState: State = { toasts: [] };
|
||||||
|
|
||||||
function dispatch(action: Action) {
|
function dispatch(action: Action) {
|
||||||
memoryState = reducer(memoryState, action)
|
memoryState = reducer(memoryState, action);
|
||||||
listeners.forEach((listener) => {
|
listeners.forEach((listener) => {
|
||||||
listener(memoryState)
|
listener(memoryState);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
type Toast = Omit<ToasterToast, "id">
|
type Toast = Omit<ToasterToast, "id">;
|
||||||
|
|
||||||
function toast({ ...props }: Toast) {
|
function toast({ ...props }: Toast) {
|
||||||
const id = genId()
|
const id = genId();
|
||||||
|
|
||||||
const update = (props: ToasterToast) =>
|
const update = (props: ToasterToast) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "UPDATE_TOAST",
|
type: "UPDATE_TOAST",
|
||||||
toast: { ...props, id },
|
toast: { ...props, id },
|
||||||
})
|
});
|
||||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "ADD_TOAST",
|
type: "ADD_TOAST",
|
||||||
@@ -157,36 +154,36 @@ function toast({ ...props }: Toast) {
|
|||||||
id,
|
id,
|
||||||
open: true,
|
open: true,
|
||||||
onOpenChange: (open) => {
|
onOpenChange: (open) => {
|
||||||
if (!open) dismiss()
|
if (!open) dismiss();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
dismiss,
|
dismiss,
|
||||||
update,
|
update,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function useToast() {
|
function useToast() {
|
||||||
const [state, setState] = React.useState<State>(memoryState)
|
const [state, setState] = React.useState<State>(memoryState);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
listeners.push(setState)
|
listeners.push(setState);
|
||||||
return () => {
|
return () => {
|
||||||
const index = listeners.indexOf(setState)
|
const index = listeners.indexOf(setState);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
listeners.splice(index, 1)
|
listeners.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}, [state])
|
}, [state]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toast,
|
toast,
|
||||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useToast, toast }
|
export { useToast, toast };
|
||||||
|
|||||||
@@ -1,15 +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", ["tencent", "/imgs/providers/tencent.svg"]],
|
["aliyun", ["common.provider.aliyun", "/imgs/providers/aliyun.svg"]],
|
||||||
["aliyun", ["aliyun", "/imgs/providers/aliyun.svg"]],
|
["tencent", ["common.provider.tencent", "/imgs/providers/tencent.svg"]],
|
||||||
["cloudflare", ["cloudflare", "/imgs/providers/cloudflare.svg"]],
|
["huaweicloud", ["common.provider.huaweicloud", "/imgs/providers/huaweicloud.svg"]],
|
||||||
["namesilo", ["namesilo", "/imgs/providers/namesilo.svg"]],
|
["qiniu", ["common.provider.qiniu", "/imgs/providers/qiniu.svg"]],
|
||||||
["godaddy", ["go.daddy", "/imgs/providers/godaddy.svg"]],
|
["cloudflare", ["common.provider.cloudflare", "/imgs/providers/cloudflare.svg"]],
|
||||||
["qiniu", ["qiniu", "/imgs/providers/qiniu.svg"]],
|
["namesilo", ["common.provider.namesilo", "/imgs/providers/namesilo.svg"]],
|
||||||
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
|
["godaddy", ["common.provider.godaddy", "/imgs/providers/godaddy.svg"]],
|
||||||
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
|
["local", ["common.provider.local", "/imgs/providers/local.svg"]],
|
||||||
["local", ["local.deployment", "/imgs/providers/local.svg"]],
|
["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]],
|
||||||
|
["webhook", ["common.provider.webhook", "/imgs/providers/webhook.svg"]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const getProviderInfo = (t: string) => {
|
export const getProviderInfo = (t: string) => {
|
||||||
@@ -20,15 +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("local"),
|
||||||
|
z.literal("ssh"),
|
||||||
|
z.literal("webhook"),
|
||||||
],
|
],
|
||||||
{ message: "access.not.empty" }
|
{ message: "access.authorization.form.type.placeholder" }
|
||||||
);
|
);
|
||||||
|
|
||||||
type AccessUsage = "apply" | "deploy" | "all";
|
type AccessUsage = "apply" | "deploy" | "all";
|
||||||
@@ -40,86 +42,88 @@ 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;
|
| 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 = Record<string, string>;
|
||||||
|
|
||||||
export type SSHConfig = {
|
export type SSHConfig = {
|
||||||
host: string;
|
host: string;
|
||||||
port: string;
|
port: string;
|
||||||
preCommand?: string;
|
|
||||||
command: string;
|
|
||||||
username: string;
|
username: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
keyFile?: string;
|
keyFile?: string;
|
||||||
certPath: string;
|
|
||||||
keyPath: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LocalConfig = {
|
export type WebhookConfig = {
|
||||||
command: string;
|
url: string;
|
||||||
certPath: string;
|
|
||||||
keyPath: 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 "ssh":
|
|
||||||
case "webhook":
|
|
||||||
case "qiniu":
|
case "qiniu":
|
||||||
case "local":
|
case "local":
|
||||||
|
case "ssh":
|
||||||
|
case "webhook":
|
||||||
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Deployment, Pahse } from "./deployment";
|
import { Deployment, Pahse } from "./deployment";
|
||||||
|
|
||||||
export type Domain = {
|
export type Domain = {
|
||||||
id: string;
|
id?: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
crontab: string;
|
crontab: string;
|
||||||
access: string;
|
access: string;
|
||||||
targetAccess?: string;
|
targetAccess?: string;
|
||||||
targetType: string;
|
targetType?: string;
|
||||||
expiredAt?: string;
|
expiredAt?: string;
|
||||||
phase?: Pahse;
|
phase?: Pahse;
|
||||||
phaseSuccess?: boolean;
|
phaseSuccess?: boolean;
|
||||||
@@ -31,10 +31,20 @@ export type Domain = {
|
|||||||
deployConfig?: DeployConfig[];
|
deployConfig?: DeployConfig[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type KVType = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type DeployConfig = {
|
export type DeployConfig = {
|
||||||
|
id?: string;
|
||||||
access: string;
|
access: string;
|
||||||
type: string;
|
type: string;
|
||||||
config?: Record<string, string>;
|
config?: {
|
||||||
|
[key: string]: string;
|
||||||
|
} & {
|
||||||
|
variables?: KVType[];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApplyConfig = {
|
export type ApplyConfig = {
|
||||||
@@ -56,14 +66,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", ["aliyun.cdn", "/imgs/providers/aliyun.svg"]],
|
["aliyun-oss", ["common.provider.aliyun.oss", "/imgs/providers/aliyun.svg"]],
|
||||||
["aliyun-oss", ["aliyun.oss", "/imgs/providers/aliyun.svg"]],
|
["aliyun-cdn", ["common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"]],
|
||||||
["aliyun-dcdn", ["aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
|
["aliyun-dcdn", ["common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
|
||||||
["tencent-cdn", ["tencent.cdn", "/imgs/providers/tencent.svg"]],
|
["tencent-cdn", ["common.provider.tencent.cdn", "/imgs/providers/tencent.svg"]],
|
||||||
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
|
["qiniu-cdn", ["common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"]],
|
||||||
["qiniu-cdn", ["qiniu.cdn", "/imgs/providers/qiniu.svg"]],
|
["local", ["common.provider.local", "/imgs/providers/local.svg"]],
|
||||||
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
|
["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]],
|
||||||
["local", ["local.deployment", "/imgs/providers/local.svg"]],
|
["webhook", ["common.provider.webhook", "/imgs/providers/webhook.svg"]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const targetTypeKeys = Array.from(targetTypeMap.keys());
|
export const targetTypeKeys = Array.from(targetTypeMap.keys());
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ export type NotifyChannelWebhook = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const defaultNotifyTemplate: NotifyTemplate = {
|
export const defaultNotifyTemplate: NotifyTemplate = {
|
||||||
title: "您有{COUNT}张证书即将过期",
|
title: "您有 {COUNT} 张证书即将过期",
|
||||||
content: "有{COUNT}张证书即将过期,域名分别为{DOMAINS},请保持关注!",
|
content: "有 {COUNT} 张证书即将过期,域名分别为 {DOMAINS},请保持关注!",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SSLProvider = "letsencrypt" | "zerossl";
|
export type SSLProvider = "letsencrypt" | "zerossl";
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export const version = "Certimate v0.1.16";
|
export const version = "Certimate v0.2.1";
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import i18n from 'i18next';
|
import i18n from "i18next";
|
||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from "react-i18next";
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
|
|
||||||
import resources from './locales'
|
import resources from "./locales";
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
resources,
|
resources,
|
||||||
fallbackLng: 'zh',
|
fallbackLng: "zh",
|
||||||
debug: true,
|
debug: true,
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
backend: {
|
backend: {
|
||||||
loadPath: '/locales/{{lng}}.json',
|
loadPath: "/locales/{{lng}}.json",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|||||||
@@ -1,216 +0,0 @@
|
|||||||
{
|
|
||||||
"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",
|
|
||||||
"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.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/en/index.ts
Normal file
17
ui/src/i18n/locales/en/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import nlsCommon from "./nls.common.json";
|
||||||
|
import nlsLogin from "./nls.login.json";
|
||||||
|
import nlsDashboard from "./nls.dashboard.json";
|
||||||
|
import nlsSettings from "./nls.settings.json";
|
||||||
|
import nlsDomain from "./nls.domain.json";
|
||||||
|
import nlsAccess from "./nls.access.json";
|
||||||
|
import nlsHistory from "./nls.history.json";
|
||||||
|
|
||||||
|
export default Object.freeze({
|
||||||
|
...nlsCommon,
|
||||||
|
...nlsLogin,
|
||||||
|
...nlsDashboard,
|
||||||
|
...nlsSettings,
|
||||||
|
...nlsDomain,
|
||||||
|
...nlsAccess,
|
||||||
|
...nlsHistory,
|
||||||
|
});
|
||||||
77
ui/src/i18n/locales/en/nls.access.json
Normal file
77
ui/src/i18n/locales/en/nls.access.json
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"access.page.title": "Authorization Management",
|
||||||
|
|
||||||
|
"access.authorization.tab": "Authorization",
|
||||||
|
"access.authorization.nodata": "Please add authorization to start deploying certificate.",
|
||||||
|
|
||||||
|
"access.authorization.add": "Add Authorization",
|
||||||
|
"access.authorization.edit": "Edit Authorization",
|
||||||
|
"access.authorization.copy": "Copy Authorization",
|
||||||
|
"access.authorization.delete": "Delete Authorization",
|
||||||
|
"access.authorization.delete.confirm": "Are you sure you want to delete the deployment authorization?",
|
||||||
|
|
||||||
|
"access.authorization.form.type.label": "Provider",
|
||||||
|
"access.authorization.form.type.placeholder": "Please select a provider",
|
||||||
|
"access.authorization.form.type.list": "Authorization List",
|
||||||
|
"access.authorization.form.name.label": "Name",
|
||||||
|
"access.authorization.form.name.placeholder": "Please enter authorization name",
|
||||||
|
"access.authorization.form.config.label": "Configuration Type",
|
||||||
|
"access.authorization.form.region.label": "Region",
|
||||||
|
"access.authorization.form.region.placeholder": "Please enter Region",
|
||||||
|
"access.authorization.form.access_key_id.label": "AccessKeyId",
|
||||||
|
"access.authorization.form.access_key_id.placeholder": "Please enter AccessKeyId",
|
||||||
|
"access.authorization.form.access_key_secret.label": "AccessKeySecret",
|
||||||
|
"access.authorization.form.access_key_secret.placeholder": "Please enter AccessKeySecret",
|
||||||
|
"access.authorization.form.access_key.label": "AccessKey",
|
||||||
|
"access.authorization.form.access_key.placeholder": "Please enter AccessKey",
|
||||||
|
"access.authorization.form.secret_id.label": "SecretId",
|
||||||
|
"access.authorization.form.secret_id.placeholder": "Please enter SecretId",
|
||||||
|
"access.authorization.form.secret_key.label": "SecretKey",
|
||||||
|
"access.authorization.form.secret_key.placeholder": "Please enter SecretKey",
|
||||||
|
"access.authorization.form.cloud_dns_api_token.label": "CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.authorization.form.cloud_dns_api_token.placeholder": "Please enter CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.authorization.form.godaddy_api_key.label": "GO_DADDY_API_KEY",
|
||||||
|
"access.authorization.form.godaddy_api_key.placeholder": "Please enter GO_DADDY_API_KEY",
|
||||||
|
"access.authorization.form.godaddy_api_secret.label": "GO_DADDY_API_SECRET",
|
||||||
|
"access.authorization.form.godaddy_api_secret.placeholder": "Please enter GO_DADDY_API_SECRET",
|
||||||
|
"access.authorization.form.namesilo_api_key.label": "NAMESILO_API_KEY",
|
||||||
|
"access.authorization.form.namesilo_api_key.placeholder": "Please enter NAMESILO_API_KEY",
|
||||||
|
"access.authorization.form.username.label": "Username",
|
||||||
|
"access.authorization.form.username.placeholder": "Please enter username",
|
||||||
|
"access.authorization.form.password.label": "Password",
|
||||||
|
"access.authorization.form.password.placeholder": "Please enter password",
|
||||||
|
"access.authorization.form.access_group.placeholder": "Please select a group",
|
||||||
|
"access.authorization.form.ssh_group.label": "Authorization Configuration Group (used to deploy a single domain certificate to multiple SSH hosts)",
|
||||||
|
"access.authorization.form.ssh_host.label": "Server Host",
|
||||||
|
"access.authorization.form.ssh_host.placeholder": "Please enter Host",
|
||||||
|
"access.authorization.form.ssh_port.label": "SSH Port",
|
||||||
|
"access.authorization.form.ssh_port.placeholder": "Please enter Port",
|
||||||
|
"access.authorization.form.ssh_key.label": "Key (Log in using private key)",
|
||||||
|
"access.authorization.form.ssh_key.placeholder": "Please enter Key",
|
||||||
|
"access.authorization.form.ssh_key_file.placeholder": "Please select file",
|
||||||
|
"access.authorization.form.ssh_key_path.label": "Private Key Save Path",
|
||||||
|
"access.authorization.form.ssh_key_path.placeholder": "Please enter private key save path",
|
||||||
|
"access.authorization.form.ssh_cert_path.label": "Certificate Save Path",
|
||||||
|
"access.authorization.form.ssh_cert_path.placeholder": "Please enter certificate save path",
|
||||||
|
"access.authorization.form.ssh_pre_command.label": "Pre-deployment Command",
|
||||||
|
"access.authorization.form.ssh_pre_command.placeholder": "Command to be executed before deploying the certificate",
|
||||||
|
"access.authorization.form.ssh_command.label": "Command",
|
||||||
|
"access.authorization.form.ssh_command.placeholder": "Please enter command",
|
||||||
|
"access.authorization.form.webhook_url.label": "Webhook URL",
|
||||||
|
"access.authorization.form.webhook_url.placeholder": "Please enter Webhook URL",
|
||||||
|
|
||||||
|
"access.group.tab": "Authorization Group",
|
||||||
|
|
||||||
|
"access.group.nodata": "No deployment authorization configuration yet, please add after starting use.",
|
||||||
|
"access.group.total": "Totally {{total}} deployment authorization configuration",
|
||||||
|
|
||||||
|
"access.group.add": "Add Group",
|
||||||
|
"access.group.delete": "Delete Group",
|
||||||
|
"access.group.delete.confirm": "Are you sure you want to delete the deployment authorization group?",
|
||||||
|
|
||||||
|
"access.group.form.name.label": "Group Name",
|
||||||
|
"access.group.form.name.errmsg.empty": "Please enter group name",
|
||||||
|
|
||||||
|
"access.group.domains": "All Authorizations",
|
||||||
|
"access.group.domains.nodata": "Please add a domain to start deploying the certificate."
|
||||||
|
}
|
||||||
72
ui/src/i18n/locales/en/nls.common.json
Normal file
72
ui/src/i18n/locales/en/nls.common.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"common.save": "Save",
|
||||||
|
"common.save.succeeded.message": "Save Successful",
|
||||||
|
"common.save.failed.message": "Save Failed",
|
||||||
|
"common.add": "Add",
|
||||||
|
"common.edit": "Edit",
|
||||||
|
"common.copy": "Copy",
|
||||||
|
"common.download": "Download",
|
||||||
|
"common.delete": "Delete",
|
||||||
|
"common.delete.succeeded.message": "Delete Successful",
|
||||||
|
"common.delete.failed.message": "Delete Failed",
|
||||||
|
"common.next": "Next",
|
||||||
|
"common.confirm": "Confirm",
|
||||||
|
"common.cancel": "Cancel",
|
||||||
|
"common.submit": "Submit",
|
||||||
|
"common.update": "Update",
|
||||||
|
"common.update.succeeded.message": "Update Successful",
|
||||||
|
"common.update.failed.message": "Update Failed",
|
||||||
|
|
||||||
|
"common.text.domain": "Domain",
|
||||||
|
"common.text.domain.empty": "No Domain",
|
||||||
|
"common.text.ip": "IP Address",
|
||||||
|
"common.text.ip.empty": "No IP address",
|
||||||
|
"common.text.dns": "Domain Name Server",
|
||||||
|
"common.text.dns.empty": "No DNS",
|
||||||
|
"common.text.ca": "Certificate Authority",
|
||||||
|
"common.text.provider": "Provider",
|
||||||
|
"common.text.name": "Name",
|
||||||
|
"common.text.created_at": "Created At",
|
||||||
|
"common.text.updated_at": "Updated At",
|
||||||
|
"common.text.operations": "Operations",
|
||||||
|
"common.text.nodata": "No data available",
|
||||||
|
|
||||||
|
"common.menu.settings": "Settings",
|
||||||
|
"common.menu.logout": "Logout",
|
||||||
|
"common.menu.document": "Document",
|
||||||
|
|
||||||
|
"common.pagination.next": "Next",
|
||||||
|
"common.pagination.prev": "Previous",
|
||||||
|
"common.pagination.more": "More pages",
|
||||||
|
|
||||||
|
"common.theme.light": "Light",
|
||||||
|
"common.theme.dark": "Dark",
|
||||||
|
"common.theme.system": "System",
|
||||||
|
|
||||||
|
"common.errmsg.string_max": "Please enter no more than {{max}} characters",
|
||||||
|
"common.errmsg.email_invalid": "Please enter a valid email address",
|
||||||
|
"common.errmsg.email_empty": "Please enter email",
|
||||||
|
"common.errmsg.email_duplicate": "Email already exists",
|
||||||
|
"common.errmsg.domain_invalid": "Please enter domain",
|
||||||
|
"common.errmsg.host_invalid": "Please enter the correct domain name or IP",
|
||||||
|
"common.errmsg.ip_invalid": "Please enter IP",
|
||||||
|
"common.errmsg.url_invalid": "Please enter a valid URL",
|
||||||
|
|
||||||
|
"common.provider.aliyun": "Alibaba Cloud",
|
||||||
|
"common.provider.aliyun.cdn": "Alibaba Cloud-CDN",
|
||||||
|
"common.provider.aliyun.oss": "Alibaba Cloud-OSS",
|
||||||
|
"common.provider.aliyun.dcdn": "Alibaba Cloud-DCDN",
|
||||||
|
"common.provider.tencent": "Tencent",
|
||||||
|
"common.provider.tencent.cdn": "Tencent-CDN",
|
||||||
|
"common.provider.huaweicloud": "Huawei Cloud",
|
||||||
|
"common.provider.qiniu": "Qiniu",
|
||||||
|
"common.provider.qiniu.cdn": "Qiniu-CDN",
|
||||||
|
"common.provider.cloudflare": "Cloudflare",
|
||||||
|
"common.provider.namesilo": "Namesilo",
|
||||||
|
"common.provider.godaddy": "GoDaddy",
|
||||||
|
"common.provider.local": "Local Deployment",
|
||||||
|
"common.provider.ssh": "SSH Deployment",
|
||||||
|
"common.provider.webhook": "Webhook",
|
||||||
|
"common.provider.dingtalk": "DingTalk",
|
||||||
|
"common.provider.telegram": "Telegram"
|
||||||
|
}
|
||||||
11
ui/src/i18n/locales/en/nls.dashboard.json
Normal file
11
ui/src/i18n/locales/en/nls.dashboard.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"dashboard.page.title": "Dashboard",
|
||||||
|
|
||||||
|
"dashboard.statistics.all": "All",
|
||||||
|
"dashboard.statistics.near_expired": "About to Expire",
|
||||||
|
"dashboard.statistics.enabled": "Enabled",
|
||||||
|
"dashboard.statistics.disabled": "Not Enabled",
|
||||||
|
"dashboard.statistics.unit": "",
|
||||||
|
|
||||||
|
"dashboard.history": "Deployment History"
|
||||||
|
}
|
||||||
65
ui/src/i18n/locales/en/nls.domain.json
Normal file
65
ui/src/i18n/locales/en/nls.domain.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"domain.page.title": "Domain List",
|
||||||
|
|
||||||
|
"domain.nodata": "Please add a domain to start deploying the certificate.",
|
||||||
|
|
||||||
|
"domain.add": "Add Domain",
|
||||||
|
"domain.edit": "Edit Domain",
|
||||||
|
"domain.delete": "Delete Domain",
|
||||||
|
"domain.delete.confirm": "Are you sure you want to delete this domain?",
|
||||||
|
"domain.history": "Deployment History",
|
||||||
|
"domain.deploy": "Deploy Now",
|
||||||
|
"domain.deploy.started.message": "Deploy Started",
|
||||||
|
"domain.deploy.started.tips": "Deployment initiated, please check the deployment log later.",
|
||||||
|
"domain.deploy.failed.message": "Execution Failed",
|
||||||
|
"domain.deploy.failed.tips": "Execution failed, please check the details in <1>Deployment History</1>.",
|
||||||
|
"domain.deploy_forced": "Force Deployment",
|
||||||
|
|
||||||
|
"domain.props.expiry": "Validity Period",
|
||||||
|
"domain.props.expiry.date1": "Valid for {{date}} days",
|
||||||
|
"domain.props.expiry.date2": "Expiry on {{date}}",
|
||||||
|
"domain.props.last_execution_status": "Last Execution Status",
|
||||||
|
"domain.props.last_execution_stage": "Last Execution Stage",
|
||||||
|
"domain.props.last_execution_time": "Last Execution Time",
|
||||||
|
"domain.props.enable": "Enable",
|
||||||
|
"domain.props.enable.enabled": "Enable",
|
||||||
|
"domain.props.enable.disabled": "Disable",
|
||||||
|
|
||||||
|
"domain.application.tab": "Apply Settings",
|
||||||
|
"domain.application.form.domain.added.message": "Domain added successfully",
|
||||||
|
"domain.application.form.domain.changed.message": "Domain updated successfully",
|
||||||
|
"domain.application.form.email.label": "Email",
|
||||||
|
"domain.application.form.email.tips": "(A email is required to apply for a certificate)",
|
||||||
|
"domain.application.form.email.add": "Add Email",
|
||||||
|
"domain.application.form.email.list": "Email List",
|
||||||
|
"domain.application.form.email.errmsg.empty": "Please select email",
|
||||||
|
"domain.application.form.access.label": "DNS Provider Authorization Configuration",
|
||||||
|
"domain.application.form.access.placeholder": "Please select DNS provider authorization configuration",
|
||||||
|
"domain.application.form.access.errmsg.empty": "Please select DNS provider authorization configuration",
|
||||||
|
"domain.application.form.access.list": "Provider Authorization Configurations",
|
||||||
|
"domain.application.form.timeout.label": "Timeout",
|
||||||
|
"domain.application.form.timeoue.placeholder": "Timeout (seconds)",
|
||||||
|
"domain.application.unsaved.message": "Please save applyment configuration first",
|
||||||
|
|
||||||
|
"domain.deployment.tab": "Deploy Settings",
|
||||||
|
"domain.deployment.nodata": "Deployment not added yet",
|
||||||
|
"domain.deployment.form.type.label": "Deploy Method",
|
||||||
|
"domain.deployment.form.type.placeholder": "Please select deploy method",
|
||||||
|
"domain.deployment.form.type.list": "Deploy Method List",
|
||||||
|
"domain.deployment.form.access.label": "Access Configuration",
|
||||||
|
"domain.deployment.form.access.placeholder": "Please select provider authorization configuration",
|
||||||
|
"domain.deployment.form.access.list": "Provider Authorization Configurations",
|
||||||
|
"domain.deployment.form.cdn_domain.label": "Deploy to domain",
|
||||||
|
"domain.deployment.form.cdn_domain.placeholder": "Please enter CDN domain",
|
||||||
|
"domain.deployment.form.oss_endpoint.label": "Endpoint",
|
||||||
|
"domain.deployment.form.oss_bucket": "Bucket",
|
||||||
|
"domain.deployment.form.oss_bucket.placeholder": "Please enter Bucket",
|
||||||
|
"domain.deployment.form.variables.label": "Variable",
|
||||||
|
"domain.deployment.form.variables.key": "Name",
|
||||||
|
"domain.deployment.form.variables.value": "Value",
|
||||||
|
"domain.deployment.form.variables.empty": "Variable not added yet",
|
||||||
|
"domain.deployment.form.variables.key.required": "Variable name cannot be empty",
|
||||||
|
"domain.deployment.form.variables.value.required": "Variable value cannot be empty",
|
||||||
|
"domain.deployment.form.variables.key.placeholder": "Variable name",
|
||||||
|
"domain.deployment.form.variables.value.placeholder": "Variable value"
|
||||||
|
}
|
||||||
15
ui/src/i18n/locales/en/nls.history.json
Normal file
15
ui/src/i18n/locales/en/nls.history.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"history.page.title": "Deployment",
|
||||||
|
|
||||||
|
"history.nodata": "You have not created any deployments yet, please add a domain to start deployment!",
|
||||||
|
|
||||||
|
"history.props.domain": "Domain",
|
||||||
|
"history.props.status": "Status",
|
||||||
|
"history.props.stage": "Stage",
|
||||||
|
"history.props.stage.progress.check": "Check",
|
||||||
|
"history.props.stage.progress.apply": "Apply",
|
||||||
|
"history.props.stage.progress.deploy": "Deploy",
|
||||||
|
"history.props.last_execution_time": "Last Execution Time",
|
||||||
|
|
||||||
|
"history.log": "Log"
|
||||||
|
}
|
||||||
9
ui/src/i18n/locales/en/nls.login.json
Normal file
9
ui/src/i18n/locales/en/nls.login.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"login.username.label": "Username",
|
||||||
|
"login.username.placeholder": "Username/Email",
|
||||||
|
"login.username.errmsg.invalid": "Please enter a valid email address",
|
||||||
|
"login.password.label": "Password",
|
||||||
|
"login.password.placeholder": "Password",
|
||||||
|
"login.password.errmsg.invalid": "Password should be at least 10 characters",
|
||||||
|
"login.submit": "Log In"
|
||||||
|
}
|
||||||
41
ui/src/i18n/locales/en/nls.settings.json
Normal file
41
ui/src/i18n/locales/en/nls.settings.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"settings.page.title": "Settings",
|
||||||
|
|
||||||
|
"settings.account.relogin.message": "Please login again",
|
||||||
|
|
||||||
|
"settings.account.tab": "Account",
|
||||||
|
"settings.account.email.label": "Email",
|
||||||
|
"settings.account.email.placeholder": "Please enter email",
|
||||||
|
"settings.account.email.errmsg.invalid": "Please enter a valid email address",
|
||||||
|
"settings.account.email.changed.message": "Account email altered successfully",
|
||||||
|
"settings.account.email.failed.message": "Account email alteration failed",
|
||||||
|
|
||||||
|
"settings.password.tab": "Password",
|
||||||
|
"settings.password.current_password.label": "Current Password",
|
||||||
|
"settings.password.current_password.placeholder": "Please enter the current password",
|
||||||
|
"settings.password.new_password.label": "New Password",
|
||||||
|
"settings.password.new_password.placeholder": "Please enter the new password",
|
||||||
|
"settings.password.confirm_password.label": "Confirm Password",
|
||||||
|
"settings.password.confirm_password.placeholder": "Please enter the new password again",
|
||||||
|
"settings.password.password.errmsg.length": "Password should be at least 10 characters",
|
||||||
|
"settings.password.password.errmsg.not_matched": "Passwords do not match",
|
||||||
|
"settings.password.changed.message": "Password changed successfully",
|
||||||
|
"settings.password.failed.message": "Password change failed",
|
||||||
|
|
||||||
|
"settings.notification.tab": "Notification",
|
||||||
|
"settings.notification.template.label": "Template",
|
||||||
|
"settings.notification.template.saved.message": "Notification template saved successfully",
|
||||||
|
"settings.notification.template.variables.tips.title": "Optional variables ({COUNT}: number of expiring soon)",
|
||||||
|
"settings.notification.template.variables.tips.content": "Optional variables ({COUNT}: number of expiring soon. {DOMAINS}: Domain list)",
|
||||||
|
"settings.notification.config.enable": "Enable",
|
||||||
|
"settings.notification.config.saved.message": "Configuration saved successfully",
|
||||||
|
"settings.notification.config.failed.message": "Configuration save failed",
|
||||||
|
"settings.notification.dingtalk.secret.placeholder": "Signature for signed addition",
|
||||||
|
"settings.notification.url.errmsg.invalid": "Invalid Url format",
|
||||||
|
|
||||||
|
"settings.ca.tab": "Certificate Authority",
|
||||||
|
"settings.ca.provider.errmsg.empty": "Please select a Certificate Authority",
|
||||||
|
"settings.ca.eab_kid.errmsg.empty": "Please enter EAB_KID",
|
||||||
|
"settings.ca.eab_hmac_key.errmsg.empty": "Please enter EAB_HMAC_KEY.",
|
||||||
|
"settings.ca.eab_kid_hmac_key.errmsg.empty": "Please enter EAB_KID and EAB_HMAC_KEY"
|
||||||
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
import { Resource } from 'i18next'
|
import { Resource } from "i18next";
|
||||||
|
|
||||||
import zh from './zh.json'
|
import zh from "./zh";
|
||||||
import en from './en.json'
|
import en from "./en";
|
||||||
|
|
||||||
const resources: Resource = {
|
const resources: Resource = {
|
||||||
zh: {
|
zh: {
|
||||||
name: '简体中文',
|
name: "简体中文",
|
||||||
translation: zh
|
translation: zh,
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
name: 'English',
|
name: "English",
|
||||||
translation: en
|
translation: en,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export default resources;
|
export default resources;
|
||||||
|
|||||||
@@ -1,216 +0,0 @@
|
|||||||
{
|
|
||||||
"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",
|
|
||||||
"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.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": "加签的签名"
|
|
||||||
}
|
|
||||||
17
ui/src/i18n/locales/zh/index.ts
Normal file
17
ui/src/i18n/locales/zh/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import nlsCommon from "./nls.common.json";
|
||||||
|
import nlsLogin from "./nls.login.json";
|
||||||
|
import nlsDashboard from "./nls.dashboard.json";
|
||||||
|
import nlsSettings from "./nls.settings.json";
|
||||||
|
import nlsDomain from "./nls.domain.json";
|
||||||
|
import nlsAccess from "./nls.access.json";
|
||||||
|
import nlsHistory from "./nls.history.json";
|
||||||
|
|
||||||
|
export default Object.freeze({
|
||||||
|
...nlsCommon,
|
||||||
|
...nlsLogin,
|
||||||
|
...nlsDashboard,
|
||||||
|
...nlsSettings,
|
||||||
|
...nlsDomain,
|
||||||
|
...nlsAccess,
|
||||||
|
...nlsHistory,
|
||||||
|
});
|
||||||
78
ui/src/i18n/locales/zh/nls.access.json
Normal file
78
ui/src/i18n/locales/zh/nls.access.json
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"access.page.title": "授权管理",
|
||||||
|
|
||||||
|
"access.authorization.tab": "授权",
|
||||||
|
"access.authorization.nodata": "请添加授权开始部署证书吧。",
|
||||||
|
|
||||||
|
"access.authorization.add": "新增授权",
|
||||||
|
"access.authorization.edit": "编辑授权",
|
||||||
|
"access.authorization.copy": "复制授权",
|
||||||
|
"access.authorization.delete": "删除授权",
|
||||||
|
"access.authorization.delete.confirm": "确定要删除授权吗?",
|
||||||
|
|
||||||
|
"access.authorization.form.type.label": "服务商",
|
||||||
|
"access.authorization.form.type.placeholder": "请选择服务商",
|
||||||
|
"access.authorization.form.type.list": "服务商列表",
|
||||||
|
"access.authorization.form.name.label": "名称",
|
||||||
|
"access.authorization.form.name.placeholder": "请输入授权名称",
|
||||||
|
"access.authorization.form.config.label": "配置类型",
|
||||||
|
"access.authorization.form.region.label": "Region",
|
||||||
|
"access.authorization.form.region.placeholder": "请输入区域",
|
||||||
|
"access.authorization.form.access_key_id.label": "AccessKeyId",
|
||||||
|
"access.authorization.form.access_key_id.placeholder": "请输入 AccessKeyId",
|
||||||
|
"access.authorization.form.access_key_secret.label": "AccessKeySecret",
|
||||||
|
"access.authorization.form.access_key_secret.placeholder": "请输入 AccessKeySecret",
|
||||||
|
"access.authorization.form.access_key.label": "AccessKey",
|
||||||
|
"access.authorization.form.access_key.placeholder": "请输入 AccessKey",
|
||||||
|
"access.authorization.form.secret_id.label": "SecretId",
|
||||||
|
"access.authorization.form.secret_id.placeholder": "请输入 SecretId",
|
||||||
|
"access.authorization.form.secret_key.label": "SecretKey",
|
||||||
|
"access.authorization.form.secret_key.placeholder": "请输入 SecretKey",
|
||||||
|
"access.authorization.form.cloud_dns_api_token.label": "CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.authorization.form.cloud_dns_api_token.placeholder": "请输入 CLOUD_DNS_API_TOKEN",
|
||||||
|
"access.authorization.form.godaddy_api_key.label": "GO_DADDY_API_KEY",
|
||||||
|
"access.authorization.form.godaddy_api_key.placeholder": "请输入 GO_DADDY_API_KEY",
|
||||||
|
"access.authorization.form.godaddy_api_secret.label": "GO_DADDY_API_SECRET",
|
||||||
|
"access.authorization.form.godaddy_api_secret.placeholder": "请输入 GO_DADDY_API_SECRET",
|
||||||
|
"access.authorization.form.namesilo_api_key.label": "NAMESILO_API_KEY",
|
||||||
|
"access.authorization.form.namesilo_api_key.placeholder": "请输入 NAMESILO_API_KEY",
|
||||||
|
"access.authorization.form.username.label": "用户名",
|
||||||
|
"access.authorization.form.username.placeholder": "请输入用户名",
|
||||||
|
"access.authorization.form.password.label": "密码",
|
||||||
|
"access.authorization.form.password.placeholder": "请输入密码",
|
||||||
|
"access.authorization.form.access_group.placeholder": "请选择分组",
|
||||||
|
"access.authorization.form.ssh_group.label": "授权配置组(用于将一个域名证书部署到多个 SSH 主机)",
|
||||||
|
"access.authorization.form.ssh_host.label": "服务器 Host",
|
||||||
|
"access.authorization.form.ssh_host.placeholder": "请输入 Host",
|
||||||
|
"access.authorization.form.ssh_port.label": "SSH 端口",
|
||||||
|
"access.authorization.form.ssh_port.placeholder": "请输入 Port",
|
||||||
|
"access.authorization.form.ssh_key.label": "Key(使用私钥登录)",
|
||||||
|
"access.authorization.form.ssh_key.placeholder": "请输入 Key",
|
||||||
|
"access.authorization.form.ssh_key_file.placeholder": "请选择文件",
|
||||||
|
"access.authorization.form.ssh_key.label.passphrase": "私钥密码",
|
||||||
|
"access.authorization.form.ssh_key_path.label": "私钥保存路径",
|
||||||
|
"access.authorization.form.ssh_key_path.placeholder": "请输入私钥保存路径",
|
||||||
|
"access.authorization.form.ssh_cert_path.label": "证书保存路径",
|
||||||
|
"access.authorization.form.ssh_cert_path.placeholder": "请输入证书保存路径",
|
||||||
|
"access.authorization.form.ssh_pre_command.label": "前置 Command",
|
||||||
|
"access.authorization.form.ssh_pre_command.placeholder": "在部署证书前执行的前置命令",
|
||||||
|
"access.authorization.form.ssh_command.label": "Command",
|
||||||
|
"access.authorization.form.ssh_command.placeholder": "请输入要执行的命令",
|
||||||
|
"access.authorization.form.webhook_url.label": "Webhook URL",
|
||||||
|
"access.authorization.form.webhook_url.placeholder": "请输入 Webhook URL",
|
||||||
|
|
||||||
|
"access.group.tab": "授权组",
|
||||||
|
|
||||||
|
"access.group.nodata": "暂无部署授权配置,请添加后开始使用吧",
|
||||||
|
"access.group.total": "共有 {{total}} 个部署授权配置",
|
||||||
|
|
||||||
|
"access.group.add": "添加授权组",
|
||||||
|
"access.group.delete": "删除组",
|
||||||
|
"access.group.delete.confirm": "确定要删除部署授权组吗?",
|
||||||
|
|
||||||
|
"access.group.form.name.label": "组名",
|
||||||
|
"access.group.form.name.errmsg.empty": "请输入组名",
|
||||||
|
|
||||||
|
"access.group.domains": "所有授权",
|
||||||
|
"access.group.domains.nodata": "请添加域名开始部署证书吧。"
|
||||||
|
}
|
||||||
72
ui/src/i18n/locales/zh/nls.common.json
Normal file
72
ui/src/i18n/locales/zh/nls.common.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"common.add": "新增",
|
||||||
|
"common.save": "保存",
|
||||||
|
"common.save.succeeded.message": "保存成功",
|
||||||
|
"common.save.failed.message": "保存失败",
|
||||||
|
"common.edit": "编辑",
|
||||||
|
"common.copy": "复制",
|
||||||
|
"common.download": "下载",
|
||||||
|
"common.delete": "刪除",
|
||||||
|
"common.delete.succeeded.message": "删除成功",
|
||||||
|
"common.delete.failed.message": "删除失败",
|
||||||
|
"common.next": "下一步",
|
||||||
|
"common.confirm": "确认",
|
||||||
|
"common.cancel": "取消",
|
||||||
|
"common.submit": "提交",
|
||||||
|
"common.update": "更新",
|
||||||
|
"common.update.succeeded.message": "修改成功",
|
||||||
|
"common.update.failed.message": "修改失败",
|
||||||
|
|
||||||
|
"common.text.domain": "域名",
|
||||||
|
"common.text.domain.empty": "无域名",
|
||||||
|
"common.text.ip": "IP 地址",
|
||||||
|
"common.text.ip.empty": "无 IP 地址",
|
||||||
|
"common.text.dns": "DNS(域名服务器)",
|
||||||
|
"common.text.dns.empty": "无 DNS 地址",
|
||||||
|
"common.text.ca": "CA(证书颁发机构)",
|
||||||
|
"common.text.name": "名称",
|
||||||
|
"common.text.provider": "服务商",
|
||||||
|
"common.text.created_at": "创建时间",
|
||||||
|
"common.text.updated_at": "更新时间",
|
||||||
|
"common.text.operations": "操作",
|
||||||
|
"common.text.nodata": "暂无数据",
|
||||||
|
|
||||||
|
"common.menu.settings": "系统设置",
|
||||||
|
"common.menu.logout": "退出登录",
|
||||||
|
"common.menu.document": "文档",
|
||||||
|
|
||||||
|
"common.pagination.next": "下一页",
|
||||||
|
"common.pagination.prev": "上一页",
|
||||||
|
"common.pagination.more": "更多",
|
||||||
|
|
||||||
|
"common.theme.light": "浅色",
|
||||||
|
"common.theme.dark": "暗黑",
|
||||||
|
"common.theme.system": "跟随系统",
|
||||||
|
|
||||||
|
"common.errmsg.string_max": "请输入不超过 {{max}} 个字符",
|
||||||
|
"common.errmsg.email_empty": "请输入邮箱",
|
||||||
|
"common.errmsg.email_invalid": "请输入正确的邮箱",
|
||||||
|
"common.errmsg.email_duplicate": "邮箱已存在",
|
||||||
|
"common.errmsg.domain_invalid": "请输入正确的域名",
|
||||||
|
"common.errmsg.host_invalid": "请输入正确的域名或 IP 地址",
|
||||||
|
"common.errmsg.ip_invalid": "请输入正确的 IP 地址",
|
||||||
|
"common.errmsg.url_invalid": "请输入正确的 URL",
|
||||||
|
|
||||||
|
"common.provider.tencent": "腾讯云",
|
||||||
|
"common.provider.tencent.cdn": "腾讯云-CDN",
|
||||||
|
"common.provider.aliyun": "阿里云",
|
||||||
|
"common.provider.aliyun.cdn": "阿里云-CDN",
|
||||||
|
"common.provider.aliyun.oss": "阿里云-OSS",
|
||||||
|
"common.provider.aliyun.dcdn": "阿里云-DCDN",
|
||||||
|
"common.provider.huaweicloud": "华为云",
|
||||||
|
"common.provider.qiniu": "七牛云",
|
||||||
|
"common.provider.qiniu.cdn": "七牛云-CDN",
|
||||||
|
"common.provider.cloudflare": "Cloudflare",
|
||||||
|
"common.provider.namesilo": "Namesilo",
|
||||||
|
"common.provider.godaddy": "GoDaddy",
|
||||||
|
"common.provider.local": "本地部署",
|
||||||
|
"common.provider.ssh": "SSH 部署",
|
||||||
|
"common.provider.webhook": "Webhook",
|
||||||
|
"common.provider.dingtalk": "钉钉",
|
||||||
|
"common.provider.telegram": "Telegram"
|
||||||
|
}
|
||||||
11
ui/src/i18n/locales/zh/nls.dashboard.json
Normal file
11
ui/src/i18n/locales/zh/nls.dashboard.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"dashboard.page.title": "仪表盘",
|
||||||
|
|
||||||
|
"dashboard.statistics.all": "所有",
|
||||||
|
"dashboard.statistics.near_expired": "即将过期",
|
||||||
|
"dashboard.statistics.enabled": "启用中",
|
||||||
|
"dashboard.statistics.disabled": "未启用",
|
||||||
|
"dashboard.statistics.unit": "个",
|
||||||
|
|
||||||
|
"dashboard.history": "部署历史"
|
||||||
|
}
|
||||||
65
ui/src/i18n/locales/zh/nls.domain.json
Normal file
65
ui/src/i18n/locales/zh/nls.domain.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"domain.page.title": "域名列表",
|
||||||
|
|
||||||
|
"domain.nodata": "请添加域名开始部署证书吧。",
|
||||||
|
|
||||||
|
"domain.add": "新增域名",
|
||||||
|
"domain.edit": "编辑域名",
|
||||||
|
"domain.delete": "删除域名",
|
||||||
|
"domain.delete.confirm": "确定要删除域名吗?",
|
||||||
|
"domain.history": "部署历史",
|
||||||
|
"domain.deploy": "立即部署",
|
||||||
|
"domain.deploy.started.message": "开始部署",
|
||||||
|
"domain.deploy.started.tips": "已发起部署,请稍后查看部署日志。",
|
||||||
|
"domain.deploy.failed.message": "执行失败",
|
||||||
|
"domain.deploy.failed.tips": "执行失败,请在 <1>部署历史</1> 查看详情。",
|
||||||
|
"domain.deploy_forced": "强行部署",
|
||||||
|
|
||||||
|
"domain.props.expiry": "有效期限",
|
||||||
|
"domain.props.expiry.date1": "有效期 {{date}} 天",
|
||||||
|
"domain.props.expiry.date2": "{{date}} 到期",
|
||||||
|
"domain.props.last_execution_status": "最近执行状态",
|
||||||
|
"domain.props.last_execution_stage": "最近执行阶段",
|
||||||
|
"domain.props.last_execution_time": "最近执行时间",
|
||||||
|
"domain.props.enable": "是否启用",
|
||||||
|
"domain.props.enable.enabled": "启用",
|
||||||
|
"domain.props.enable.disabled": "禁用",
|
||||||
|
|
||||||
|
"domain.application.tab": "申请配置",
|
||||||
|
"domain.application.form.domain.added.message": "域名添加成功",
|
||||||
|
"domain.application.form.domain.changed.message": "域名编辑成功",
|
||||||
|
"domain.application.form.email.label": "邮箱",
|
||||||
|
"domain.application.form.email.tips": "(申请证书需要提供邮箱)",
|
||||||
|
"domain.application.form.email.add": "添加邮箱",
|
||||||
|
"domain.application.form.email.list": "邮箱列表",
|
||||||
|
"domain.application.form.email.errmsg.empty": "请选择邮箱",
|
||||||
|
"domain.application.form.access.label": "DNS 服务商授权配置",
|
||||||
|
"domain.application.form.access.placeholder": "请选择 DNS 服务商授权配置",
|
||||||
|
"domain.application.form.access.errmsg.empty": "请选择 DNS 服务商授权配置",
|
||||||
|
"domain.application.form.access.list": "已有的 DNS 服务商授权配置",
|
||||||
|
"domain.application.form.timeout.label": "超时时间",
|
||||||
|
"domain.application.form.timeoue.placeholder": "超时时间(单位:秒)",
|
||||||
|
"domain.application.unsaved.message": "请先保存申请配置",
|
||||||
|
|
||||||
|
"domain.deployment.tab": "部署配置",
|
||||||
|
"domain.deployment.nodata": "暂无部署配置,请添加后开始部署证书吧",
|
||||||
|
"domain.deployment.form.type.label": "部署方式",
|
||||||
|
"domain.deployment.form.type.placeholder": "请选择部署方式",
|
||||||
|
"domain.deployment.form.type.list": "支持的部署方式",
|
||||||
|
"domain.deployment.form.access.label": "授权配置",
|
||||||
|
"domain.deployment.form.access.placeholder": "请选择授权配置",
|
||||||
|
"domain.deployment.form.access.list": "已有的服务商授权配置",
|
||||||
|
"domain.deployment.form.cdn_domain.label": "部署到域名",
|
||||||
|
"domain.deployment.form.cdn_domain.placeholder": "请输入 CDN 域名",
|
||||||
|
"domain.deployment.form.oss_endpoint.label": "Endpoint",
|
||||||
|
"domain.deployment.form.oss_bucket": "存储桶",
|
||||||
|
"domain.deployment.form.oss_bucket.placeholder": "请输入存储桶名",
|
||||||
|
"domain.deployment.form.variables.label": "变量",
|
||||||
|
"domain.deployment.form.variables.key": "变量名",
|
||||||
|
"domain.deployment.form.variables.value": "值",
|
||||||
|
"domain.deployment.form.variables.empty": "尚未添加变量",
|
||||||
|
"domain.deployment.form.variables.key.required": "变量名不能为空",
|
||||||
|
"domain.deployment.form.variables.value.required": "变量值不能为空",
|
||||||
|
"domain.deployment.form.variables.key.placeholder": "请输入变量名",
|
||||||
|
"domain.deployment.form.variables.value.placeholder": "请输入变量值"
|
||||||
|
}
|
||||||
15
ui/src/i18n/locales/zh/nls.history.json
Normal file
15
ui/src/i18n/locales/zh/nls.history.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"history.page.title": "部署",
|
||||||
|
|
||||||
|
"history.nodata": "你暂未创建任何部署,请先添加域名进行部署吧!",
|
||||||
|
|
||||||
|
"history.props.domain": "域名",
|
||||||
|
"history.props.status": "状态",
|
||||||
|
"history.props.stage": "阶段",
|
||||||
|
"history.props.stage.progress.check": "检查",
|
||||||
|
"history.props.stage.progress.apply": "获取",
|
||||||
|
"history.props.stage.progress.deploy": "部署",
|
||||||
|
"history.props.last_execution_time": "最近执行时间",
|
||||||
|
|
||||||
|
"history.log": "日志"
|
||||||
|
}
|
||||||
9
ui/src/i18n/locales/zh/nls.login.json
Normal file
9
ui/src/i18n/locales/zh/nls.login.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"login.username.label": "用户名",
|
||||||
|
"login.username.placeholder": "请输入用户名/邮箱",
|
||||||
|
"login.username.errmsg.invalid": "请输入正确的用户名/邮箱",
|
||||||
|
"login.password.label": "密码",
|
||||||
|
"login.password.placeholder": "请输入密码",
|
||||||
|
"login.password.errmsg.invalid": "密码至少 10 个字符",
|
||||||
|
"login.submit": "登录"
|
||||||
|
}
|
||||||
41
ui/src/i18n/locales/zh/nls.settings.json
Normal file
41
ui/src/i18n/locales/zh/nls.settings.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"settings.page.title": "系统设置",
|
||||||
|
|
||||||
|
"settings.account.relogin.message": "请重新登录",
|
||||||
|
|
||||||
|
"settings.account.tab": "账号",
|
||||||
|
"settings.account.email.label": "登录邮箱",
|
||||||
|
"settings.account.email.errmsg.invalid": "请输入正确的邮箱地址",
|
||||||
|
"settings.account.email.placeholder": "请输入邮箱",
|
||||||
|
"settings.account.email.changed.message": "修改账户邮箱成功",
|
||||||
|
"settings.account.email.failed.message": "修改账户邮箱失败",
|
||||||
|
|
||||||
|
"settings.password.tab": "密码",
|
||||||
|
"settings.password.password.errmsg.length": "密码至少10个字符",
|
||||||
|
"settings.password.password.errmsg.not_matched": "两次密码不一致",
|
||||||
|
"settings.password.current_password.label": "当前密码",
|
||||||
|
"settings.password.current_password.placeholder": "请输入旧密码",
|
||||||
|
"settings.password.new_password.label": "新密码",
|
||||||
|
"settings.password.new_password.placeholder": "请输入新密码",
|
||||||
|
"settings.password.confirm_password.label": "确认密码",
|
||||||
|
"settings.password.confirm_password.placeholder": "请再次输入新密码",
|
||||||
|
"settings.password.changed.message": "修改密码成功",
|
||||||
|
"settings.password.failed.message": "修改密码失败",
|
||||||
|
|
||||||
|
"settings.notification.tab": "消息推送",
|
||||||
|
"settings.notification.template.label": "内容模板",
|
||||||
|
"settings.notification.template.saved.message": "通知模板保存成功",
|
||||||
|
"settings.notification.template.variables.tips.title": "可选的变量({COUNT}: 即将过期张数)",
|
||||||
|
"settings.notification.template.variables.tips.content": "可选的变量({COUNT}: 即将过期张数;{DOMAINS}: 域名列表)",
|
||||||
|
"settings.notification.config.enable": "是否启用",
|
||||||
|
"settings.notification.config.saved.message": "配置保存成功",
|
||||||
|
"settings.notification.config.failed.message": "配置保存失败",
|
||||||
|
"settings.notification.dingtalk.secret.placeholder": "加签的签名",
|
||||||
|
"settings.notification.url.errmsg.invalid": "URL 格式不正确",
|
||||||
|
|
||||||
|
"settings.ca.tab": "证书颁发机构(CA)",
|
||||||
|
"settings.ca.provider.errmsg.empty": "请选择证书分发机构",
|
||||||
|
"settings.ca.eab_kid.errmsg.empty": "请输入EAB_KID",
|
||||||
|
"settings.ca.eab_hmac_key.errmsg.empty": "请输入EAB_HMAC_KEY",
|
||||||
|
"settings.ca.eab_kid_hmac_key.errmsg.empty": "请输入EAB_KID和EAB_HMAC_KEY"
|
||||||
|
}
|
||||||
@@ -14,8 +14,6 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||||
@@ -30,7 +28,7 @@ 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()
|
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" />;
|
||||||
@@ -73,7 +71,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Home className="h-4 w-4" />
|
<Home className="h-4 w-4" />
|
||||||
{t('dashboard')}
|
{t("dashboard.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/domains"
|
to="/domains"
|
||||||
@@ -83,7 +81,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Earth className="h-4 w-4" />
|
<Earth className="h-4 w-4" />
|
||||||
{t('domain.management.name')}
|
{t("domain.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/access"
|
to="/access"
|
||||||
@@ -93,7 +91,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Server className="h-4 w-4" />
|
<Server className="h-4 w-4" />
|
||||||
{t('menu.auth.management')}
|
{t("access.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
@@ -104,7 +102,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<History className="h-4 w-4" />
|
<History className="h-4 w-4" />
|
||||||
{t('deployment.log.name')}
|
{t("history.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,7 +139,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Home className="h-5 w-5" />
|
<Home className="h-5 w-5" />
|
||||||
{t('dashboard')}
|
{t("dashboard.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/domains"
|
to="/domains"
|
||||||
@@ -151,7 +149,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Earth className="h-5 w-5" />
|
<Earth className="h-5 w-5" />
|
||||||
{t('domain.management.name')}
|
{t("domain.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/access"
|
to="/access"
|
||||||
@@ -161,7 +159,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Server className="h-5 w-5" />
|
<Server className="h-5 w-5" />
|
||||||
{t('menu.auth.management')}
|
{t("access.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
@@ -172,7 +170,7 @@ export default function Dashboard() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<History className="h-5 w-5" />
|
<History className="h-5 w-5" />
|
||||||
{t('deployment.log.name')}
|
{t("history.page.title")}
|
||||||
</Link>
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
@@ -192,15 +190,11 @@ export default function Dashboard() {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuLabel>{t('account')}</DropdownMenuLabel>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
|
|
||||||
<DropdownMenuItem onClick={handleSettingClick}>
|
<DropdownMenuItem onClick={handleSettingClick}>
|
||||||
{t('setting')}
|
{t("common.menu.settings")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<DropdownMenuItem onClick={handleLogoutClick}>
|
<DropdownMenuItem onClick={handleLogoutClick}>
|
||||||
{t('logout')}
|
{t("common.menu.logout")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@@ -22,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")}
|
{t("settings.page.title")}
|
||||||
</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}>
|
||||||
@@ -35,8 +35,9 @@ const SettingLayout = () => {
|
|||||||
className="px-5"
|
className="px-5"
|
||||||
>
|
>
|
||||||
<UserRound size={14} />
|
<UserRound size={14} />
|
||||||
<div className="ml-1">{t("account")}</div>
|
<div className="ml-1">{t("settings.account.tab")}</div>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="password"
|
value="password"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -45,7 +46,7 @@ const SettingLayout = () => {
|
|||||||
className="px-5"
|
className="px-5"
|
||||||
>
|
>
|
||||||
<KeyRound size={14} />
|
<KeyRound size={14} />
|
||||||
<div className="ml-1">{t("password")}</div>
|
<div className="ml-1">{t("settings.password.tab")}</div>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
@@ -56,8 +57,9 @@ const SettingLayout = () => {
|
|||||||
className="px-5"
|
className="px-5"
|
||||||
>
|
>
|
||||||
<Megaphone size={14} />
|
<Megaphone size={14} />
|
||||||
<div className="ml-1">{t("setting.notify.menu")}</div>
|
<div className="ml-1">{t("settings.notification.tab")}</div>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="ssl-provider"
|
value="ssl-provider"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -66,7 +68,7 @@ const SettingLayout = () => {
|
|||||||
className="px-5"
|
className="px-5"
|
||||||
>
|
>
|
||||||
<ShieldCheck size={14} />
|
<ShieldCheck size={14} />
|
||||||
<div className="ml-1">{t("ca")}</div>
|
<div className="ml-1">{t("settings.ca.tab")}</div>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value={tabValue}>
|
<TabsContent value={tabValue}>
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ import {
|
|||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
AlertDialogContent, AlertDialogDescription, AlertDialogFooter,
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogTrigger
|
AlertDialogTrigger,
|
||||||
} from "@/components/ui/alert-dialog.tsx";
|
} from "@/components/ui/alert-dialog.tsx";
|
||||||
|
|
||||||
const Access = () => {
|
const Access = () => {
|
||||||
@@ -56,9 +58,9 @@ 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">{t("access.management")}</div>
|
<div className="text-muted-foreground">{t("access.page.title")}</div>
|
||||||
{tab != "access_group" ? (
|
{tab != "access_group" ? (
|
||||||
<AccessEdit trigger={<Button>{t("access.add")}</Button>} op="add" />
|
<AccessEdit trigger={<Button>{t("access.authorization.add")}</Button>} op="add" />
|
||||||
) : (
|
) : (
|
||||||
<AccessGroupEdit trigger={<Button>{t("access.group.add")}</Button>} />
|
<AccessGroupEdit trigger={<Button>{t("access.group.add")}</Button>} />
|
||||||
)}
|
)}
|
||||||
@@ -76,7 +78,7 @@ const Access = () => {
|
|||||||
handleTabItemClick("access");
|
handleTabItemClick("access");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("access.management")}
|
{t("access.authorization.tab")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="access_group"
|
value="access_group"
|
||||||
@@ -84,7 +86,7 @@ const Access = () => {
|
|||||||
handleTabItemClick("access_group");
|
handleTabItemClick("access_group");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("access.group.management")}
|
{t("access.group.tab")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="access">
|
<TabsContent value="access">
|
||||||
@@ -95,10 +97,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")}
|
{t("access.authorization.nodata")}
|
||||||
</div>
|
</div>
|
||||||
<AccessEdit
|
<AccessEdit
|
||||||
trigger={<Button>{t("access.add")}</Button>}
|
trigger={<Button>{t("access.authorization.add")}</Button>}
|
||||||
op="add"
|
op="add"
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
/>
|
/>
|
||||||
@@ -106,15 +108,12 @@ 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">{t("name")}</div>
|
<div className="w-48">{t("common.text.name")}</div>
|
||||||
<div className="w-48">{t("access.type")}</div>
|
<div className="w-48">{t("common.text.provider")}</div>
|
||||||
|
|
||||||
<div className="w-60">{t("create.time")}</div>
|
<div className="w-60">{t("common.text.created_at")}</div>
|
||||||
<div className="w-60">{t("update.time")}</div>
|
<div className="w-60">{t("common.text.updated_at")}</div>
|
||||||
<div className="grow">{t("operation")}</div>
|
<div className="grow">{t("common.text.operations")}</div>
|
||||||
</div>
|
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
|
||||||
{t("access.list")}
|
|
||||||
</div>
|
</div>
|
||||||
{accesses
|
{accesses
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
@@ -140,18 +139,16 @@ const Access = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="sm:w-60 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-60 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")}
|
{t("common.edit")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
op="edit"
|
op="edit"
|
||||||
@@ -159,40 +156,40 @@ const Access = () => {
|
|||||||
/>
|
/>
|
||||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||||
<AccessEdit
|
<AccessEdit
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant={"link"} className="p-0">
|
<Button variant={"link"} className="p-0">
|
||||||
{t("copy")}
|
{t("common.copy")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
op="copy"
|
op="copy"
|
||||||
data={access}
|
data={access}
|
||||||
/>
|
/>
|
||||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button variant={"link"} size={"sm"}>
|
<Button variant={"link"} className="p-0">
|
||||||
{t('delete')}
|
{t("common.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')}
|
{t("access.authorization.delete")}
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
{t('access.delete.confirm')}
|
{t("access.authorization.delete.confirm")}
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel className="dark:text-gray-200">
|
<AlertDialogCancel className="dark:text-gray-200">
|
||||||
{t('cancel')}
|
{t("common.cancel")}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleDelete(access);
|
handleDelete(access);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('confirm')}
|
{t("common.confirm")}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
|
|||||||
@@ -57,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">{t("dashboard")}</div>
|
<div className="text-muted-foreground">{t("dashboard.page.title")}</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">
|
||||||
@@ -66,7 +66,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">
|
<div className="text-muted-foreground font-semibold">
|
||||||
{t("dashboard.all")}
|
{t("dashboard.statistics.all")}
|
||||||
</div>
|
</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">
|
||||||
@@ -79,7 +79,7 @@ const Dashboard = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
{t("dashboard.unit")}
|
{t("dashboard.statistics.unit")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,7 +91,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">
|
<div className="text-muted-foreground font-semibold">
|
||||||
{t("dashboard.near.expired")}
|
{t("dashboard.statistics.near_expired")}
|
||||||
</div>
|
</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">
|
||||||
@@ -104,7 +104,7 @@ const Dashboard = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
{t("dashboard.unit")}
|
{t("dashboard.statistics.unit")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,7 +120,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">
|
<div className="text-muted-foreground font-semibold">
|
||||||
{t("dashboard.enabled")}
|
{t("dashboard.statistics.enabled")}
|
||||||
</div>
|
</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">
|
||||||
@@ -133,7 +133,7 @@ const Dashboard = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
{t("dashboard.unit")}
|
{t("dashboard.statistics.unit")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,7 +145,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground font-semibold">
|
<div className="text-muted-foreground font-semibold">
|
||||||
{t("dashboard.not.enabled")}
|
{t("dashboard.statistics.disabled")}
|
||||||
</div>
|
</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">
|
||||||
@@ -161,28 +161,32 @@ const Dashboard = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||||
{t("dashboard.unit")}
|
{t("dashboard.statistics.unit")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="my-4">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="text-muted-foreground mt-5 text-sm">
|
<div className="text-muted-foreground mt-5 text-sm">
|
||||||
{t("deployment.log.name")}
|
{t("dashboard.history")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{deployments?.length == 0 ? (
|
{deployments?.length == 0 ? (
|
||||||
<>
|
<>
|
||||||
<Alert className="max-w-[40em] mt-10">
|
<Alert className="max-w-[40em] mt-10">
|
||||||
<AlertTitle>{t("no.data")}</AlertTitle>
|
<AlertTitle>{t("common.text.nodata")}</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"> {t("deployment.log.empty")}</div>
|
<div className="ml-2"> {t("history.nodata")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex justify-end">
|
<div className="mt-2 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
@@ -199,18 +203,15 @@ 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">{t("domain")}</div>
|
<div className="w-48">{t("history.props.domain")}</div>
|
||||||
|
|
||||||
<div className="w-24">{t("deployment.log.status")}</div>
|
<div className="w-24">{t("history.props.status")}</div>
|
||||||
<div className="w-56">{t("deployment.log.stage")}</div>
|
<div className="w-56">{t("history.props.stage")}</div>
|
||||||
<div className="w-56 sm:ml-2 text-center">
|
<div className="w-56 sm:ml-2 text-center">
|
||||||
{t("deployment.log.last.execution.time")}
|
{t("history.props.last_execution_time")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grow">{t("operation")}</div>
|
<div className="grow">{t("common.text.operations")}</div>
|
||||||
</div>
|
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
|
||||||
{t("deployment.log.name")}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{deployments?.map((deployment) => (
|
{deployments?.map((deployment) => (
|
||||||
@@ -244,14 +245,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")}
|
{t("history.log")}
|
||||||
</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")}
|
{t("history.log")}
|
||||||
</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]">
|
||||||
|
|||||||
@@ -23,36 +23,43 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Domain, targetTypeKeys, targetTypeMap } from "@/domain/domain";
|
import { DeployConfig, Domain } from "@/domain/domain";
|
||||||
import { save, get } from "@/repository/domains";
|
import { save, get } from "@/repository/domains";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
import { PbErrorData } from "@/domain/base";
|
import { PbErrorData } from "@/domain/base";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import { AccessEdit } from "@/components/certimate/AccessEdit";
|
import { AccessEdit } from "@/components/certimate/AccessEdit";
|
||||||
import { accessTypeMap } from "@/domain/access";
|
import { accessTypeMap } from "@/domain/access";
|
||||||
import EmailsEdit from "@/components/certimate/EmailsEdit";
|
import EmailsEdit from "@/components/certimate/EmailsEdit";
|
||||||
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 { useTranslation } from "react-i18next";
|
||||||
import StringList from "@/components/certimate/StringList";
|
import StringList from "@/components/certimate/StringList";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import DeployList from "@/components/certimate/DeployList";
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from "@/components/ui/breadcrumb";
|
||||||
|
|
||||||
const Edit = () => {
|
const Edit = () => {
|
||||||
const {
|
const {
|
||||||
config: { accesses, emails, accessGroups },
|
config: { accesses, emails },
|
||||||
} = useConfig();
|
} = useConfig();
|
||||||
|
|
||||||
const [domain, setDomain] = useState<Domain>();
|
const [domain, setDomain] = useState<Domain>({} as Domain);
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [tab, setTab] = useState<"base" | "advance">("base");
|
const [tab, setTab] = useState<"apply" | "deploy">("apply");
|
||||||
|
|
||||||
const [targetType, setTargetType] = useState(domain ? domain.targetType : "");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Parsing query parameters
|
// Parsing query parameters
|
||||||
@@ -62,7 +69,6 @@ const Edit = () => {
|
|||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const data = await get(id);
|
const data = await get(id);
|
||||||
setDomain(data);
|
setDomain(data);
|
||||||
setTargetType(data.targetType);
|
|
||||||
};
|
};
|
||||||
fetchData();
|
fetchData();
|
||||||
}
|
}
|
||||||
@@ -71,19 +77,14 @@ const Edit = () => {
|
|||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
domain: z.string().min(1, {
|
domain: z.string().min(1, {
|
||||||
message: "domain.not.empty.verify.message",
|
message: "common.errmsg.domain_invalid",
|
||||||
}),
|
}),
|
||||||
email: z.string().email("email.valid.message").optional(),
|
email: z.string().email("common.errmsg.email_invalid").optional(),
|
||||||
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
|
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
|
||||||
message: "domain.management.edit.dns.access.not.empty.message",
|
message: "domain.application.form.access.errmsg.empty",
|
||||||
}),
|
}),
|
||||||
targetAccess: z.string().optional(),
|
|
||||||
targetType: z.string().regex(/^[a-zA-Z0-9-]+$/, {
|
|
||||||
message: "domain.management.edit.target.type.not.empty.message",
|
|
||||||
}),
|
|
||||||
variables: z.string().optional(),
|
|
||||||
group: z.string().optional(),
|
|
||||||
nameservers: z.string().optional(),
|
nameservers: z.string().optional(),
|
||||||
|
timeout: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
@@ -93,11 +94,8 @@ const Edit = () => {
|
|||||||
domain: "",
|
domain: "",
|
||||||
email: "",
|
email: "",
|
||||||
access: "",
|
access: "",
|
||||||
targetAccess: "",
|
|
||||||
targetType: "",
|
|
||||||
variables: "",
|
|
||||||
group: "",
|
|
||||||
nameservers: "",
|
nameservers: "",
|
||||||
|
timeout: 60,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,74 +104,81 @@ const Edit = () => {
|
|||||||
form.reset({
|
form.reset({
|
||||||
id: domain.id,
|
id: domain.id,
|
||||||
domain: domain.domain,
|
domain: domain.domain,
|
||||||
email: domain.email,
|
email: domain.applyConfig?.email,
|
||||||
access: domain.access,
|
access: domain.applyConfig?.access,
|
||||||
targetAccess: domain.targetAccess,
|
nameservers: domain.applyConfig?.nameservers,
|
||||||
targetType: domain.targetType,
|
timeout: domain.applyConfig?.timeout,
|
||||||
variables: domain.variables,
|
|
||||||
group: domain.group,
|
|
||||||
nameservers: domain.nameservers,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [domain, form]);
|
}, [domain, form]);
|
||||||
|
|
||||||
const targetAccesses = accesses.filter((item) => {
|
|
||||||
if (item.usage == "apply") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetType == "") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const types = targetType.split("-");
|
|
||||||
return item.configType === types[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
const group = data.group == "emptyId" ? "" : data.group;
|
console.log(data);
|
||||||
const targetAccess =
|
|
||||||
data.targetAccess === "emptyId" ? "" : data.targetAccess;
|
|
||||||
if (group == "" && targetAccess == "") {
|
|
||||||
form.setError("group", {
|
|
||||||
type: "manual",
|
|
||||||
message: "domain.management.edit.target.access.verify.msg",
|
|
||||||
});
|
|
||||||
form.setError("targetAccess", {
|
|
||||||
type: "manual",
|
|
||||||
message: "domain.management.edit.target.access.verify.msg",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const req: Domain = {
|
const req: Domain = {
|
||||||
id: data.id as string,
|
id: data.id as string,
|
||||||
crontab: "0 0 * * *",
|
crontab: "0 0 * * *",
|
||||||
domain: data.domain,
|
domain: data.domain,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
access: data.access,
|
access: data.access,
|
||||||
group: group,
|
applyConfig: {
|
||||||
targetAccess: targetAccess,
|
email: data.email ?? "",
|
||||||
targetType: data.targetType,
|
access: data.access,
|
||||||
variables: data.variables,
|
nameservers: data.nameservers,
|
||||||
nameservers: data.nameservers,
|
timeout: data.timeout,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await save(req);
|
const resp = await save(req);
|
||||||
let description = t("domain.management.edit.succeed.tips");
|
let description = t("domain.application.form.domain.changed.message");
|
||||||
if (req.id == "") {
|
if (req.id == "") {
|
||||||
description = t("domain.management.add.succeed.tips");
|
description = t("domain.application.form.domain.added.message");
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: t("succeed"),
|
title: t("common.save.succeeded.message"),
|
||||||
description,
|
description,
|
||||||
});
|
});
|
||||||
navigate("/domains");
|
|
||||||
|
if (!domain?.id) setTab("deploy");
|
||||||
|
setDomain({ ...resp });
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handelOnDeployListChange = async (list: DeployConfig[]) => {
|
||||||
|
const req = {
|
||||||
|
...domain,
|
||||||
|
deployConfig: list,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const resp = await save(req);
|
||||||
|
let description = t("domain.application.form.domain.changed.message");
|
||||||
|
if (req.id == "") {
|
||||||
|
description = t("domain.application.form.domain.added.message");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: t("common.save.succeeded.message"),
|
||||||
|
description,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!domain?.id) setTab("deploy");
|
||||||
|
setDomain({ ...resp });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = e as ClientResponseError;
|
const err = e as ClientResponseError;
|
||||||
|
|
||||||
@@ -195,403 +200,281 @@ const Edit = () => {
|
|||||||
<div className="">
|
<div className="">
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<div className=" h-5 text-muted-foreground">
|
<div className=" h-5 text-muted-foreground">
|
||||||
{domain?.id ? t("domain.edit") : t("domain.add")}
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink href="#/domains">
|
||||||
|
{t("domain.page.title")}
|
||||||
|
</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>
|
||||||
|
{domain?.id ? t("domain.edit") : t("domain.add")}
|
||||||
|
</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
</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 md:mt-5">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"cursor-pointer text-right",
|
"cursor-pointer text-right",
|
||||||
tab === "base" ? "text-primary" : ""
|
tab === "apply" ? "text-primary" : ""
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTab("base");
|
setTab("apply");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("basic.setting")}
|
{t("domain.application.tab")}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"cursor-pointer text-right",
|
"cursor-pointer text-right",
|
||||||
tab === "advance" ? "text-primary" : ""
|
tab === "deploy" ? "text-primary" : ""
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTab("advance");
|
if (!domain?.id) {
|
||||||
|
toast({
|
||||||
|
title: t("domain.application.unsaved.message"),
|
||||||
|
description: t("domain.application.unsaved.message"),
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTab("deploy");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("advanced.setting")}
|
{t("domain.deployment.tab")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
<div className="w-full md:w-[35em] bg-gray-100 dark:bg-gray-900 p-5 rounded mt-3 md:mt-0">
|
<div
|
||||||
<Form {...form}>
|
className={cn(
|
||||||
<form
|
"w-full md:w-[35em] p-5 rounded mt-3 md:mt-0",
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
tab == "deploy" && "hidden"
|
||||||
className="space-y-8 dark:text-stone-200"
|
)}
|
||||||
>
|
>
|
||||||
<FormField
|
<Form {...form}>
|
||||||
control={form.control}
|
<form
|
||||||
name="domain"
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
render={({ field }) => (
|
className="space-y-8 dark:text-stone-200"
|
||||||
<FormItem hidden={tab != "base"}>
|
>
|
||||||
<>
|
{/* 域名 */}
|
||||||
<StringList
|
<FormField
|
||||||
value={field.value}
|
control={form.control}
|
||||||
valueType="domain"
|
name="domain"
|
||||||
onValueChange={(domain: string) => {
|
render={({ field }) => (
|
||||||
form.setValue("domain", domain);
|
<FormItem>
|
||||||
}}
|
<>
|
||||||
/>
|
<StringList
|
||||||
</>
|
value={field.value}
|
||||||
|
valueType="domain"
|
||||||
<FormMessage />
|
onValueChange={(domain: string) => {
|
||||||
</FormItem>
|
form.setValue("domain", domain);
|
||||||
)}
|
}}
|
||||||
/>
|
/>
|
||||||
<FormField
|
</>
|
||||||
control={form.control}
|
<FormMessage />
|
||||||
name="email"
|
</FormItem>
|
||||||
render={({ field }) => (
|
)}
|
||||||
<FormItem hidden={tab != "base"}>
|
/>
|
||||||
<FormLabel className="flex w-full justify-between">
|
{/* 邮箱 */}
|
||||||
<div>
|
<FormField
|
||||||
{t("email") +
|
control={form.control}
|
||||||
t("domain.management.edit.email.description")}
|
name="email"
|
||||||
</div>
|
render={({ field }) => (
|
||||||
<EmailsEdit
|
<FormItem>
|
||||||
trigger={
|
<FormLabel className="flex w-full justify-between">
|
||||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
<div>
|
||||||
<Plus size={14} />
|
{t("domain.application.form.email.label") +
|
||||||
{t("add")}
|
" " +
|
||||||
</div>
|
t("domain.application.form.email.tips")}
|
||||||
}
|
</div>
|
||||||
/>
|
<EmailsEdit
|
||||||
</FormLabel>
|
trigger={
|
||||||
<FormControl>
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
<Select
|
<Plus size={14} />
|
||||||
{...field}
|
{t("common.add")}
|
||||||
value={field.value}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
form.setValue("email", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={t(
|
|
||||||
"domain.management.edit.email.not.empty.message"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectGroup>
|
|
||||||
<SelectLabel>{t("email.list")}</SelectLabel>
|
|
||||||
{(emails.content as EmailsSetting).emails.map(
|
|
||||||
(item) => (
|
|
||||||
<SelectItem key={item} value={item}>
|
|
||||||
<div>{item}</div>
|
|
||||||
</SelectItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="access"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem hidden={tab != "base"}>
|
|
||||||
<FormLabel className="flex w-full justify-between">
|
|
||||||
<div>
|
|
||||||
{t("domain.management.edit.dns.access.label")}
|
|
||||||
</div>
|
|
||||||
<AccessEdit
|
|
||||||
trigger={
|
|
||||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
|
||||||
<Plus size={14} />
|
|
||||||
{t("add")}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
op="add"
|
|
||||||
/>
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
value={field.value}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
form.setValue("access", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={t(
|
|
||||||
"domain.management.edit.access.not.empty.message"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectGroup>
|
|
||||||
<SelectLabel>
|
|
||||||
{t("domain.management.edit.access.label")}
|
|
||||||
</SelectLabel>
|
|
||||||
{accesses
|
|
||||||
.filter((item) => item.usage != "deploy")
|
|
||||||
.map((item) => (
|
|
||||||
<SelectItem key={item.id} value={item.id}>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<img
|
|
||||||
className="w-6"
|
|
||||||
src={
|
|
||||||
accessTypeMap.get(
|
|
||||||
item.configType
|
|
||||||
)?.[1]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div>{item.name}</div>
|
|
||||||
</div>
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="targetType"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem hidden={tab != "base"}>
|
|
||||||
<FormLabel>
|
|
||||||
{t("domain.management.edit.target.type")}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setTargetType(value);
|
|
||||||
form.setValue("targetType", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={t(
|
|
||||||
"domain.management.edit.target.type.not.empty.message"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectGroup>
|
|
||||||
<SelectLabel>
|
|
||||||
{t("domain.management.edit.target.type")}
|
|
||||||
</SelectLabel>
|
|
||||||
{targetTypeKeys.map((key) => (
|
|
||||||
<SelectItem key={key} value={key}>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<img
|
|
||||||
className="w-6"
|
|
||||||
src={targetTypeMap.get(key)?.[1]}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
{t(targetTypeMap.get(key)?.[0] || "")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="targetAccess"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem hidden={tab != "base"}>
|
|
||||||
<FormLabel className="w-full flex justify-between">
|
|
||||||
<div>{t("domain.management.edit.target.access")}</div>
|
|
||||||
<AccessEdit
|
|
||||||
trigger={
|
|
||||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
|
||||||
<Plus size={14} />
|
|
||||||
{t("add")}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
op="add"
|
|
||||||
/>
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
form.setValue("targetAccess", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={t(
|
|
||||||
"domain.management.edit.target.access.not.empty.message"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectGroup>
|
|
||||||
<SelectLabel>
|
|
||||||
{t(
|
|
||||||
"domain.management.edit.target.access.content.label"
|
|
||||||
)}{" "}
|
|
||||||
{form.getValues().targetAccess}
|
|
||||||
</SelectLabel>
|
|
||||||
<SelectItem value="emptyId">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
--
|
|
||||||
</div>
|
|
||||||
</SelectItem>
|
|
||||||
{targetAccesses.map((item) => (
|
|
||||||
<SelectItem key={item.id} value={item.id}>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<img
|
|
||||||
className="w-6"
|
|
||||||
src={
|
|
||||||
accessTypeMap.get(item.configType)?.[1]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div>{item.name}</div>
|
|
||||||
</div>
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="group"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem hidden={tab != "advance" || targetType != "ssh"}>
|
|
||||||
<FormLabel className="w-full flex justify-between">
|
|
||||||
<div>{t("domain.management.edit.group.label")}</div>
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
value={field.value}
|
|
||||||
defaultValue="emptyId"
|
|
||||||
onValueChange={(value) => {
|
|
||||||
form.setValue("group", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={t(
|
|
||||||
"domain.management.edit.group.not.empty.message"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="emptyId">
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"flex items-center space-x-2 rounded cursor-pointer"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
--
|
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
}
|
||||||
{accessGroups
|
/>
|
||||||
.filter((item) => {
|
</FormLabel>
|
||||||
return (
|
<FormControl>
|
||||||
item.expand && item.expand?.access.length > 0
|
<Select
|
||||||
);
|
{...field}
|
||||||
})
|
value={field.value}
|
||||||
.map((item) => (
|
onValueChange={(value) => {
|
||||||
<SelectItem
|
form.setValue("email", value);
|
||||||
value={item.id ? item.id : ""}
|
}}
|
||||||
key={item.id}
|
>
|
||||||
>
|
<SelectTrigger>
|
||||||
<div
|
<SelectValue
|
||||||
className={cn(
|
placeholder={t(
|
||||||
"flex items-center space-x-2 rounded cursor-pointer"
|
"domain.application.form.email.errmsg.empty"
|
||||||
)}
|
)}
|
||||||
>
|
/>
|
||||||
{item.name}
|
</SelectTrigger>
|
||||||
</div>
|
<SelectContent>
|
||||||
</SelectItem>
|
<SelectGroup>
|
||||||
))}
|
<SelectLabel>
|
||||||
</SelectContent>
|
{t("domain.application.form.email.list")}
|
||||||
</Select>
|
</SelectLabel>
|
||||||
</FormControl>
|
{(emails.content as EmailsSetting).emails.map(
|
||||||
|
(item) => (
|
||||||
|
<SelectItem key={item} value={item}>
|
||||||
|
<div>{item}</div>
|
||||||
|
</SelectItem>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
{/* 授权 */}
|
||||||
control={form.control}
|
<FormField
|
||||||
name="variables"
|
control={form.control}
|
||||||
render={({ field }) => (
|
name="access"
|
||||||
<FormItem hidden={tab != "advance"}>
|
render={({ field }) => (
|
||||||
<FormLabel>{t("variables")}</FormLabel>
|
<FormItem>
|
||||||
<FormControl>
|
<FormLabel className="flex w-full justify-between">
|
||||||
<Textarea
|
<div>{t("domain.application.form.access.label")}</div>
|
||||||
placeholder={t(
|
<AccessEdit
|
||||||
"domain.management.edit.variables.placeholder"
|
trigger={
|
||||||
)}
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
{...field}
|
<Plus size={14} />
|
||||||
className="placeholder:whitespace-pre-wrap"
|
{t("common.add")}
|
||||||
/>
|
</div>
|
||||||
</FormControl>
|
}
|
||||||
|
op="add"
|
||||||
|
/>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
{...field}
|
||||||
|
value={field.value}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
form.setValue("access", value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue
|
||||||
|
placeholder={t(
|
||||||
|
"domain.application.form.access.placeholder"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>
|
||||||
|
{t("domain.application.form.access.list")}
|
||||||
|
</SelectLabel>
|
||||||
|
{accesses
|
||||||
|
.filter((item) => item.usage != "deploy")
|
||||||
|
.map((item) => (
|
||||||
|
<SelectItem key={item.id} value={item.id}>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<img
|
||||||
|
className="w-6"
|
||||||
|
src={
|
||||||
|
accessTypeMap.get(
|
||||||
|
item.configType
|
||||||
|
)?.[1]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>{item.name}</div>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
{/* 超时时间 */}
|
||||||
control={form.control}
|
<FormField
|
||||||
name="nameservers"
|
control={form.control}
|
||||||
render={({ field }) => (
|
name="timeout"
|
||||||
<FormItem hidden={tab != "advance"}>
|
render={({ field }) => (
|
||||||
<FormLabel>{t("dns")}</FormLabel>
|
<FormItem>
|
||||||
<FormControl>
|
<FormLabel>
|
||||||
<Textarea
|
{t("domain.application.form.timeout.label")}
|
||||||
placeholder={t(
|
</FormLabel>
|
||||||
"domain.management.edit.dns.placeholder"
|
<FormControl>
|
||||||
)}
|
<Input
|
||||||
{...field}
|
type="number"
|
||||||
className="placeholder:whitespace-pre-wrap"
|
placeholder={t(
|
||||||
/>
|
"ddomain.application.form.timeout.placeholder"
|
||||||
</FormControl>
|
)}
|
||||||
|
{...field}
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
form.setValue(
|
||||||
|
"timeout",
|
||||||
|
parseInt(e.target.value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
{/* nameservers */}
|
||||||
<Button type="submit">{t("save")}</Button>
|
<FormField
|
||||||
</div>
|
control={form.control}
|
||||||
</form>
|
name="nameservers"
|
||||||
</Form>
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<StringList
|
||||||
|
value={field.value ?? ""}
|
||||||
|
onValueChange={(val: string) => {
|
||||||
|
form.setValue("nameservers", val);
|
||||||
|
}}
|
||||||
|
valueType="dns"
|
||||||
|
></StringList>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit">
|
||||||
|
{domain?.id ? t("common.save") : t("common.next")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col space-y-5 w-full md:w-[35em]",
|
||||||
|
tab == "apply" && "hidden"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<DeployList
|
||||||
|
deploys={domain?.deployConfig ?? []}
|
||||||
|
onChange={(list: DeployConfig[]) => {
|
||||||
|
handelOnDeployListChange(list);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ const Home = () => {
|
|||||||
|
|
||||||
const handleRightNowClick = async (domain: Domain) => {
|
const handleRightNowClick = async (domain: Domain) => {
|
||||||
try {
|
try {
|
||||||
unsubscribeId(domain.id);
|
unsubscribeId(domain.id ?? "");
|
||||||
subscribeId(domain.id, (resp) => {
|
subscribeId(domain.id ?? "", (resp) => {
|
||||||
console.log(resp);
|
console.log(resp);
|
||||||
const updatedDomains = domains.map((domain) => {
|
const updatedDomains = domains.map((domain) => {
|
||||||
if (domain.id === resp.id) {
|
if (domain.id === resp.id) {
|
||||||
@@ -129,15 +129,15 @@ const Home = () => {
|
|||||||
await save(domain);
|
await save(domain);
|
||||||
|
|
||||||
toast.toast({
|
toast.toast({
|
||||||
title: t("operation.succeed"),
|
title: t("domain.deploy.started.message"),
|
||||||
description: t("domain.management.start.deploy.succeed.tips"),
|
description: t("domain.deploy.started.tips"),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.toast({
|
toast.toast({
|
||||||
title: t("domain.management.execution.failed"),
|
title: t("domain.deploy.failed.message"),
|
||||||
description: (
|
description: (
|
||||||
// 这里的 text 只是占位作用,实际文案在 src/i18n/locales/[lang].json
|
// 这里的 text 只是占位作用,实际文案在 src/i18n/locales/[lang].json
|
||||||
<Trans i18nKey="domain.management.execution.failed.tips">
|
<Trans i18nKey="domain.deploy.failed.tips">
|
||||||
text1
|
text1
|
||||||
<Link
|
<Link
|
||||||
to={`/history?domain=${domain.id}`}
|
to={`/history?domain=${domain.id}`}
|
||||||
@@ -178,9 +178,7 @@ 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 className="text-muted-foreground">{t("domain.page.title")}</div>
|
||||||
{t("domain.management.name")}
|
|
||||||
</div>
|
|
||||||
<Button onClick={handleCreateClick}>{t("domain.add")}</Button>
|
<Button onClick={handleCreateClick}>{t("domain.add")}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -192,7 +190,7 @@ 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")}
|
{t("domain.nodata")}
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={handleCreateClick} className="mt-3">
|
<Button onClick={handleCreateClick} className="mt-3">
|
||||||
{t("domain.add")}
|
{t("domain.add")}
|
||||||
@@ -202,22 +200,19 @@ const Home = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<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">{t("domain")}</div>
|
<div className="w-36">{t("common.text.domain")}</div>
|
||||||
<div className="w-40">{t("domain.management.expiry.date")}</div>
|
<div className="w-40">{t("domain.props.expiry")}</div>
|
||||||
<div className="w-32">
|
<div className="w-32">
|
||||||
{t("domain.management.last.execution.status")}
|
{t("domain.props.last_execution_status")}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-64">
|
<div className="w-64">
|
||||||
{t("domain.management.last.execution.stage")}
|
{t("domain.props.last_execution_stage")}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-40 sm:ml-2">
|
<div className="w-40 sm:ml-2">
|
||||||
{t("domain.management.last.execution.time")}
|
{t("domain.props.last_execution_time")}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-24">{t("domain.management.enable")}</div>
|
<div className="w-24">{t("domain.props.enable")}</div>
|
||||||
<div className="grow">{t("operation")}</div>
|
<div className="grow">{t("common.text.operations")}</div>
|
||||||
</div>
|
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
|
||||||
{t("domain")}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{domains.map((domain) => (
|
{domains.map((domain) => (
|
||||||
@@ -238,10 +233,10 @@ const Home = () => {
|
|||||||
{domain.expiredAt ? (
|
{domain.expiredAt ? (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
{t("domain.management.expiry.date1", { date: 90 })}
|
{t("domain.props.expiry.date1", { date: 90 })}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{t("domain.management.expiry.date2", {
|
{t("domain.props.expiry.date2", {
|
||||||
date: getDate(domain.expiredAt),
|
date: getDate(domain.expiredAt),
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -282,13 +277,15 @@ const Home = () => {
|
|||||||
<Switch
|
<Switch
|
||||||
checked={domain.enabled}
|
checked={domain.enabled}
|
||||||
onCheckedChange={() => {
|
onCheckedChange={() => {
|
||||||
handelCheckedChange(domain.id);
|
handelCheckedChange(domain.id ?? "");
|
||||||
}}
|
}}
|
||||||
></Switch>
|
></Switch>
|
||||||
</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 ? t("disable") : t("enable")}
|
{domain.enabled
|
||||||
|
? t("domain.props.enable.disabled")
|
||||||
|
: t("domain.props.enable.enabled")}
|
||||||
</div>
|
</div>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -298,9 +295,9 @@ const Home = () => {
|
|||||||
<Button
|
<Button
|
||||||
variant={"link"}
|
variant={"link"}
|
||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleHistoryClick(domain.id)}
|
onClick={() => handleHistoryClick(domain.id ?? "")}
|
||||||
>
|
>
|
||||||
{t("deployment.log.name")}
|
{t("domain.history")}
|
||||||
</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" />
|
||||||
@@ -309,7 +306,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleRightNowClick(domain)}
|
onClick={() => handleRightNowClick(domain)}
|
||||||
>
|
>
|
||||||
{t("domain.management.start.deploying")}
|
{t("domain.deploy")}
|
||||||
</Button>
|
</Button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
@@ -326,7 +323,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleForceClick(domain)}
|
onClick={() => handleForceClick(domain)}
|
||||||
>
|
>
|
||||||
{t("domain.management.forced.deployment")}
|
{t("domain.deploy_forced")}
|
||||||
</Button>
|
</Button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
@@ -337,7 +334,7 @@ const Home = () => {
|
|||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleDownloadClick(domain)}
|
onClick={() => handleDownloadClick(domain)}
|
||||||
>
|
>
|
||||||
{t("download")}
|
{t("common.download")}
|
||||||
</Button>
|
</Button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
@@ -347,7 +344,7 @@ const Home = () => {
|
|||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button variant={"link"} className="p-0">
|
<Button variant={"link"} className="p-0">
|
||||||
{t("delete")}
|
{t("common.delete")}
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</AlertDialogTrigger>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
@@ -356,17 +353,19 @@ const Home = () => {
|
|||||||
{t("domain.delete")}
|
{t("domain.delete")}
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
{t("domain.management.delete.confirm")}
|
{t("domain.delete.confirm")}
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
|
<AlertDialogCancel>
|
||||||
|
{t("common.cancel")}
|
||||||
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleDeleteClick(domain.id);
|
handleDeleteClick(domain.id ?? "");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("confirm")}
|
{t("common.confirm")}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
@@ -376,9 +375,9 @@ const Home = () => {
|
|||||||
<Button
|
<Button
|
||||||
variant={"link"}
|
variant={"link"}
|
||||||
className="p-0"
|
className="p-0"
|
||||||
onClick={() => handleEditClick(domain.id)}
|
onClick={() => handleEditClick(domain.id ?? "")}
|
||||||
>
|
>
|
||||||
{t("edit")}
|
{t("common.edit")}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -40,17 +40,17 @@ const History = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea className="h-[80vh] overflow-hidden">
|
<ScrollArea className="h-[80vh] overflow-hidden">
|
||||||
<div className="text-muted-foreground">{t("deployment.log.name")}</div>
|
<div className="text-muted-foreground">{t("history.page.title")}</div>
|
||||||
{!deployments?.length ? (
|
{!deployments?.length ? (
|
||||||
<>
|
<>
|
||||||
<Alert className="max-w-[40em] mx-auto mt-20">
|
<Alert className="max-w-[40em] mx-auto mt-20">
|
||||||
<AlertTitle>{t("no.data")}</AlertTitle>
|
<AlertTitle>{t("common.text.nodata")}</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"> {t("deployment.log.empty")}</div>
|
<div className="ml-2"> {t("history.nodata")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex justify-end">
|
<div className="mt-2 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
@@ -67,18 +67,15 @@ 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">{t("domain")}</div>
|
<div className="w-48">{t("history.props.domain")}</div>
|
||||||
|
|
||||||
<div className="w-24">{t("deployment.log.status")}</div>
|
<div className="w-24">{t("history.props.status")}</div>
|
||||||
<div className="w-56">{t("deployment.log.stage")}</div>
|
<div className="w-56">{t("history.props.stage")}</div>
|
||||||
<div className="w-56 sm:ml-2 text-center">
|
<div className="w-56 sm:ml-2 text-center">
|
||||||
{t("deployment.log.last.execution.time")}
|
{t("history.props.last_execution_time")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grow">{t("operation")}</div>
|
<div className="grow">{t("common.text.operations")}</div>
|
||||||
</div>
|
|
||||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
|
||||||
{t("deployment.log.name")}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{deployments?.map((deployment) => (
|
{deployments?.map((deployment) => (
|
||||||
@@ -112,14 +109,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")}
|
{t("history.log")}
|
||||||
</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")}
|
{t("history.log")}
|
||||||
</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,7 +1,7 @@
|
|||||||
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 { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -19,15 +19,15 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
username: z.string().email({
|
username: z.string().email({
|
||||||
message: "login.username.no.empty.message",
|
message: "login.username.errmsg.invalid",
|
||||||
}),
|
}),
|
||||||
password: z.string().min(10, {
|
password: z.string().min(10, {
|
||||||
message: "login.password.length.message",
|
message: "login.password.errmsg.invalid",
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
@@ -65,9 +65,9 @@ const Login = () => {
|
|||||||
name="username"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('username')}</FormLabel>
|
<FormLabel>{t("login.username.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="email" {...field} />
|
<Input placeholder={t("login.username.placeholder")} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -80,9 +80,9 @@ const Login = () => {
|
|||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('password')}</FormLabel>
|
<FormLabel>{t("login.password.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="password" {...field} type="password" />
|
<Input placeholder={t("login.password.placeholder")} {...field} type="password" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -90,7 +90,7 @@ const Login = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('login.submit')}</Button>
|
<Button type="submit">{t("login.submit")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ 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("setting.account.email.valid.message"),
|
email: z.string().email("settings.account.email.errmsg.invalid"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const Account = () => {
|
const Account = () => {
|
||||||
@@ -45,8 +45,8 @@ const Account = () => {
|
|||||||
|
|
||||||
getPb().authStore.clear();
|
getPb().authStore.clear();
|
||||||
toast({
|
toast({
|
||||||
title: t("setting.account.email.change.succeed"),
|
title: t("settings.account.email.changed.message"),
|
||||||
description: t("setting.account.log.back.in"),
|
description: t("settings.account.relogin.message"),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
@@ -54,7 +54,7 @@ const Account = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = getErrMessage(e);
|
const message = getErrMessage(e);
|
||||||
toast({
|
toast({
|
||||||
title: t("setting.account.email.change.failed"),
|
title: t("settings.account.email.failed.message"),
|
||||||
description: message,
|
description: message,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
@@ -74,10 +74,10 @@ const Account = () => {
|
|||||||
name="email"
|
name="email"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('email')}</FormLabel>
|
<FormLabel>{t("settings.account.email.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('setting.email.placeholder')}
|
placeholder={t("settings.account.email.placeholder")}
|
||||||
{...field}
|
{...field}
|
||||||
type="email"
|
type="email"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -94,10 +94,10 @@ const Account = () => {
|
|||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
{changed ? (
|
{changed ? (
|
||||||
<Button type="submit">{t('setting.submit')}</Button>
|
<Button type="submit">{t("common.update")}</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button type="submit" disabled variant={"secondary"}>
|
<Button type="submit" disabled variant={"secondary"}>
|
||||||
{t('setting.submit')}
|
{t("common.update")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ const Notify = () => {
|
|||||||
<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>{t('template')}</AccordionTrigger>
|
<AccordionTrigger>
|
||||||
|
{t("settings.notification.template.label")}
|
||||||
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<NotifyTemplate />
|
<NotifyTemplate />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
@@ -30,21 +32,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>{t('ding.talk')}</AccordionTrigger>
|
<AccordionTrigger>{t("common.provider.dingtalk")}</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>{t('telegram')}</AccordionTrigger>
|
<AccordionTrigger>{t("common.provider.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>{t('webhook')}</AccordionTrigger>
|
<AccordionTrigger>{t("common.provider.webhook")}</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<Webhook />
|
<Webhook />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
|
|||||||
@@ -21,17 +21,17 @@ import { z } from "zod";
|
|||||||
const formSchema = z
|
const formSchema = z
|
||||||
.object({
|
.object({
|
||||||
oldPassword: z.string().min(10, {
|
oldPassword: z.string().min(10, {
|
||||||
message: "setting.password.length.message",
|
message: "settings.password.password.errmsg.length",
|
||||||
}),
|
}),
|
||||||
newPassword: z.string().min(10, {
|
newPassword: z.string().min(10, {
|
||||||
message: "setting.password.length.message",
|
message: "settings.password.password.errmsg.length",
|
||||||
}),
|
}),
|
||||||
confirmPassword: z.string().min(10, {
|
confirmPassword: z.string().min(10, {
|
||||||
message: "setting.password.length.message",
|
message: "settings.password.password.errmsg.length",
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.refine((data) => data.newPassword === data.confirmPassword, {
|
.refine((data) => data.newPassword === data.confirmPassword, {
|
||||||
message: "setting.password.not.match",
|
message: "settings.password.password.errmsg.not_matched",
|
||||||
path: ["confirmPassword"],
|
path: ["confirmPassword"],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ const Password = () => {
|
|||||||
|
|
||||||
getPb().authStore.clear();
|
getPb().authStore.clear();
|
||||||
toast({
|
toast({
|
||||||
title: t('setting.password.change.succeed'),
|
title: t("settings.password.changed.message"),
|
||||||
description: t("setting.account.log.back.in"),
|
description: t("settings.account.relogin.message"),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
@@ -77,7 +77,7 @@ const Password = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = getErrMessage(e);
|
const message = getErrMessage(e);
|
||||||
toast({
|
toast({
|
||||||
title: t('setting.password.change.failed'),
|
title: t("settings.password.failed.message"),
|
||||||
description: message,
|
description: message,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
@@ -97,9 +97,17 @@ const Password = () => {
|
|||||||
name="oldPassword"
|
name="oldPassword"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('setting.password.current.password')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("settings.password.current_password.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder={t('setting.password.current.password')} {...field} type="password" />
|
<Input
|
||||||
|
placeholder={t(
|
||||||
|
"settings.password.current_password.placeholder"
|
||||||
|
)}
|
||||||
|
{...field}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -112,10 +120,14 @@ const Password = () => {
|
|||||||
name="newPassword"
|
name="newPassword"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('setting.password.new.password')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("settings.password.new_password.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="newPassword"
|
placeholder={t(
|
||||||
|
"settings.password.new_password.placeholder"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
@@ -131,10 +143,14 @@ const Password = () => {
|
|||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('setting.password.confirm.password')}</FormLabel>
|
<FormLabel>
|
||||||
|
{t("settings.password.confirm_password.label")}
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder="confirmPassword"
|
placeholder={t(
|
||||||
|
"settings.password.confirm_password.placeholder"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
@@ -145,7 +161,7 @@ const Password = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t('setting.submit')}</Button>
|
<Button type="submit">{t("common.update")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const SSLProvider = () => {
|
|||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.enum(["letsencrypt", "zerossl"], {
|
provider: z.enum(["letsencrypt", "zerossl"], {
|
||||||
message: t("setting.ca.not.empty"),
|
message: t("settings.ca.provider.errmsg.empty"),
|
||||||
}),
|
}),
|
||||||
eabKid: z.string().optional(),
|
eabKid: z.string().optional(),
|
||||||
eabHmacKey: z.string().optional(),
|
eabHmacKey: z.string().optional(),
|
||||||
@@ -89,12 +89,12 @@ const SSLProvider = () => {
|
|||||||
if (values.provider === "zerossl") {
|
if (values.provider === "zerossl") {
|
||||||
if (!values.eabKid) {
|
if (!values.eabKid) {
|
||||||
form.setError("eabKid", {
|
form.setError("eabKid", {
|
||||||
message: t("setting.ca.eab_kid_hmac_key.not.empty"),
|
message: t("settings.ca.eab_kid_hmac_key.errmsg.empty"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!values.eabHmacKey) {
|
if (!values.eabHmacKey) {
|
||||||
form.setError("eabHmacKey", {
|
form.setError("eabHmacKey", {
|
||||||
message: t("setting.ca.eab_kid_hmac_key.not.empty"),
|
message: t("settings.ca.eab_kid_hmac_key.errmsg.empty"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!values.eabKid || !values.eabHmacKey) {
|
if (!values.eabKid || !values.eabHmacKey) {
|
||||||
@@ -120,13 +120,13 @@ const SSLProvider = () => {
|
|||||||
try {
|
try {
|
||||||
await update(setting);
|
await update(setting);
|
||||||
toast({
|
toast({
|
||||||
title: t("update.succeed"),
|
title: t("common.update.succeeded.message"),
|
||||||
description: t("update.succeed"),
|
description: t("common.update.succeeded.message"),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = getErrMessage(e);
|
const message = getErrMessage(e);
|
||||||
toast({
|
toast({
|
||||||
title: t("update.failed"),
|
title: t("common.update.failed.message"),
|
||||||
description: message,
|
description: message,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
@@ -146,7 +146,7 @@ const SSLProvider = () => {
|
|||||||
name="provider"
|
name="provider"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t("ca")}</FormLabel>
|
<FormLabel>{t("common.text.ca")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
{...field}
|
{...field}
|
||||||
@@ -202,7 +202,7 @@ const SSLProvider = () => {
|
|||||||
<FormLabel>EAB_KID</FormLabel>
|
<FormLabel>EAB_KID</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t("setting.ca.eab_kid.not.empty")}
|
placeholder={t("settings.ca.eab_kid.errmsg.empty")}
|
||||||
{...field}
|
{...field}
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
@@ -221,7 +221,9 @@ const SSLProvider = () => {
|
|||||||
<FormLabel>EAB_HMAC_KEY</FormLabel>
|
<FormLabel>EAB_HMAC_KEY</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t("setting.ca.eab_hmac_key.not.empty")}
|
placeholder={t(
|
||||||
|
"settings.ca.eab_hmac_key.errmsg.empty"
|
||||||
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
@@ -238,7 +240,7 @@ const SSLProvider = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit">{t("setting.submit")}</Button>
|
<Button type="submit">{t("common.update")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
Reference in New Issue
Block a user