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)
|
||||
* [域名变量及部署授权组介绍](https://docs.certimate.me/blog/multi-deployer)
|
||||
|
||||
@@ -32,6 +33,11 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
|
||||
./certimate serve
|
||||
```
|
||||
|
||||
或运行以下命令自动给 Certimate 自身添加证书
|
||||
```bash
|
||||
./certimate serve 你的域名
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> MacOS 在执行二进制文件时会提示:无法打开“certimate”,因为Apple无法检查其是否包含恶意软件。可在系统设置> 隐私与安全性> 安全性 中点击 "仍然允许",然后再次尝试执行二进制文件。
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@ You can download the precompiled binary files directly from the [Releases page](
|
||||
./certimate serve
|
||||
```
|
||||
|
||||
Or run the following command to automatically add a certificate to Certimate itself.
|
||||
|
||||
```bash
|
||||
./certimate serve yourDomain
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> When executing the binary file on macOS, you may see a prompt saying: “Cannot open ‘certimate’ because Apple cannot check it for malicious software.” You can go to System Preferences > Security & Privacy > General, then click “Allow Anyway,” and try executing the binary file again.
|
||||
|
||||
|
||||
91
go.mod
91
go.mod
@@ -1,16 +1,16 @@
|
||||
module certimate
|
||||
|
||||
go 1.22
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.5
|
||||
toolchain go1.23.2
|
||||
|
||||
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/darabonba-openapi/v2 v2.0.9
|
||||
github.com/alibabacloud-go/tea v1.2.2
|
||||
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/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||
github.com/nikoksr/notify v1.0.0
|
||||
@@ -18,9 +18,10 @@ require (
|
||||
github.com/pocketbase/dbx v1.10.1
|
||||
github.com/pocketbase/pocketbase v0.22.18
|
||||
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
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/crypto v0.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -30,8 +31,10 @@ require (
|
||||
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // 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/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -44,39 +47,39 @@ require (
|
||||
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-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/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/aws/protocol/eventstream v1.6.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.5 // 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.33 // 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.13 // 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/endpoints/v2 v2.6.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // 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/checksum v1.3.17 // 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/s3shared v1.17.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // 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.17 // 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.17 // 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.19 // 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.17 // 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.7 // 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.7 // indirect
|
||||
github.com/aws/smithy-go v1.20.4 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.5.6 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.97.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.104.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 // 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/goccy/go-json v0.10.3 // 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/uuid v1.6.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/inconshreveable/mousetrap v1.1.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-sqlite3 v1.14.22 // 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/reflect2 v1.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // 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/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 // indirect
|
||||
github.com/tjfoc/gmsm v1.3.2 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
gocloud.dev v0.37.0 // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.25.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
|
||||
google.golang.org/api v0.189.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a // indirect
|
||||
google.golang.org/grpc v1.65.0 // indirect
|
||||
google.golang.org/api v0.197.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/grpc v1.66.1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // 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.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
||||
cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
|
||||
cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
||||
cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
|
||||
cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
|
||||
cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U=
|
||||
cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||
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/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/compute/metadata v0.5.1 h1:NM6oZeZNlYjiwYje+sYFjEpP0Q0zCan1bmQW/KmIrGs=
|
||||
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/go.mod h1:biXoiLWYIKntto2joP+62sd9uW5EpkZmKIvfNcTWlnQ=
|
||||
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/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/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/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.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.9 h1:fxMCrZatZfXq5nLcgkmWBXmU3FLC1OR+m/SqVtMqflk=
|
||||
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.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
|
||||
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.62.712/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15 h1:r2uwBUQhLhcPzaWz9tRJqc8MjYwHb+oF2+Q6467BF14=
|
||||
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.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28=
|
||||
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/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-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||
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.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
|
||||
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.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g=
|
||||
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.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU=
|
||||
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.33 h1:Nof9o/MsmH4oa0s2q9a0k7tMz5x/Yj5k06lDODWz3BU=
|
||||
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.32 h1:7Cxhp/BnT2RcGy4VisJ9miUPecY+lyE9I8JvcZofn9I=
|
||||
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.13 h1:pfQ2sqNpMVK6xz2RbqLEL0GH87JOwSxPV2rzm8Zsb74=
|
||||
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/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.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
|
||||
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.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
|
||||
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.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
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.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg=
|
||||
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.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
||||
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.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw=
|
||||
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.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
|
||||
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.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg=
|
||||
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.58.2/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE=
|
||||
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.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
|
||||
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.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
|
||||
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.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
|
||||
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
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.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE=
|
||||
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.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU=
|
||||
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.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
|
||||
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.17/go.mod h1:NcWPxQzGM1USQggaTVwz6VpqMZPX1CvDJLDh6jnOCa4=
|
||||
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.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc=
|
||||
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.19/go.mod h1:Vx+GucNSsdhaxs3aZIKfSUjKVGsxN25nX2SRcdhuw08=
|
||||
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.19/go.mod h1:SCWkEdRq8/7EK60NcvvQ6NXKuTcchAD4ROAsC37VEZE=
|
||||
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.17/go.mod h1:VaMx6302JHax2vHJWgRo+5n9zvbacs3bLU/23DNQrTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2 h1:Kp6PWAlXwP1UvIflkIP6MFZYBNDCa4mFCGtxrpICVOg=
|
||||
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.7 h1:pIaGg+08llrP7Q5aiz9ICWbY8cqhTkyy+0SHvfzQpTc=
|
||||
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.7 h1:/Cfdu0XV3mONYKaOt1Gr0k1KvQzkzPyiKUdlWJqy+J4=
|
||||
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.7 h1:NKTa1eqZYw8tiHSRGpP0VtTdub/8KNk8sDkNPFaOKDE=
|
||||
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.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
|
||||
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/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto=
|
||||
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/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
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.97.0/go.mod h1:JXRwuTfHpe5xFg8xytc2w0XC6LcrFsBVMS4WlVaiGg8=
|
||||
github.com/cloudflare/cloudflare-go v0.104.0 h1:R/lB0dZupaZbOgibAH/BRrkFbZ6Acn/WsKg2iX2xXuY=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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/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.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.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/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
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/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
|
||||
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.17.4/go.mod h1:dU94SvPNqimEeb7EVilGGSnS0nU1O5Exir0pQ4QFL4U=
|
||||
github.com/go-acme/lego/v4 v4.19.2 h1:Y8hrmMvWETdqzzkRly7m98xtPJJivWFsgWi8fcvZo+Y=
|
||||
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-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||
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 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||
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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
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.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.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.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.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.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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
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.3.0/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/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/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||
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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
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/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.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||
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/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-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
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/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/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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
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/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/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/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
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-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
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.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
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.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
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/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
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/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
||||
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.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/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
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.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.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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||
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/common v1.0.992 h1:266lOve+E8vzhnrb/Mr05Ee+oxXD9C82JiusY/AZqXw=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1017 h1:OymmfmyFkvHirY3WHsoRT3cdTEsqygLbMn8jM41erK4=
|
||||
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/dnspod v1.0.898 h1:LoYv5u+gUoFpU/AmIuTRG/2KiEkdm9gCC0dTvk8WITQ=
|
||||
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.1002/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
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/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.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/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
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/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
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.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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/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.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
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.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
||||
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.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
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/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
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-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-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-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.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
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-20180807140117-3d87b88a115f/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.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
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.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
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-20180826012351-8a410e7b638d/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-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-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-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.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
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.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
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.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
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.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
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-20181108010431-42b317875d0f/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-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-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-20220520151302-bc2c85ada10a/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.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.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.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-20210927222741-03fcf44c2211/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.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.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
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.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.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.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
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.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.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.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
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-20180917221912-90fa682c2a6e/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.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.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||
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-20191011141410-1b5146add898/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/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=
|
||||
google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
|
||||
google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
|
||||
google.golang.org/api v0.197.0 h1:x6CwqQLsFiA5JKAiGyGBjc2bNtHtLddhJCE2IKuhhcQ=
|
||||
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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
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-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-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a h1:hqK4+jJZXCU4pW7jsAdGOVFIfLHQeV7LaizZKnZ84HI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU=
|
||||
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
|
||||
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-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
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.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.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
|
||||
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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
@@ -3,6 +3,7 @@ package applicant
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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_SECRET_KEY", access.AccessKeySecret)
|
||||
os.Setenv("ALICLOUD_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", a.option.Timeout))
|
||||
dnsProvider, err := alidns.NewDNSProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package applicant
|
||||
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/app"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
@@ -20,8 +21,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
configTypeTencent = "tencent"
|
||||
configTypeAliyun = "aliyun"
|
||||
configTypeTencent = "tencent"
|
||||
configTypeHuaweicloud = "huaweicloud"
|
||||
configTypeCloudflare = "cloudflare"
|
||||
configTypeNamesilo = "namesilo"
|
||||
configTypeGodaddy = "godaddy"
|
||||
@@ -45,6 +47,8 @@ var sslProviderUrls = map[string]string{
|
||||
|
||||
const defaultEmail = "536464346@qq.com"
|
||||
|
||||
const defaultTimeout = 60
|
||||
|
||||
type Certificate struct {
|
||||
CertUrl string `json:"certUrl"`
|
||||
CertStableUrl string `json:"certStableUrl"`
|
||||
@@ -59,6 +63,7 @@ type ApplyOption struct {
|
||||
Domain string `json:"domain"`
|
||||
Access string `json:"access"`
|
||||
Nameservers string `json:"nameservers"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
}
|
||||
|
||||
type MyUser struct {
|
||||
@@ -82,8 +87,22 @@ type Applicant interface {
|
||||
}
|
||||
|
||||
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 == "" {
|
||||
email = defaultEmail
|
||||
}
|
||||
@@ -91,13 +110,16 @@ func Get(record *models.Record) (Applicant, error) {
|
||||
Email: email,
|
||||
Domain: record.GetString("domain"),
|
||||
Access: access.GetString("config"),
|
||||
Nameservers: record.GetString("nameservers"),
|
||||
Nameservers: applyConfig.Nameservers,
|
||||
Timeout: applyConfig.Timeout,
|
||||
}
|
||||
switch access.GetString("configType") {
|
||||
case configTypeTencent:
|
||||
return NewTencent(option), nil
|
||||
case configTypeAliyun:
|
||||
return NewAliyun(option), nil
|
||||
case configTypeTencent:
|
||||
return NewTencent(option), nil
|
||||
case configTypeHuaweicloud:
|
||||
return NewHuaweiCloud(option), nil
|
||||
case configTypeCloudflare:
|
||||
return NewCloudflare(option), nil
|
||||
case configTypeNamesilo:
|
||||
|
||||
@@ -3,6 +3,7 @@ package applicant
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
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)
|
||||
|
||||
os.Setenv("CLOUDFLARE_DNS_API_TOKEN", access.DnsApiToken)
|
||||
os.Setenv("CLOUDFLARE_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", c.option.Timeout))
|
||||
|
||||
provider, err := cf.NewDNSProvider()
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package applicant
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
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_SECRET", access.ApiSecret)
|
||||
os.Setenv("GODADDY_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", a.option.Timeout))
|
||||
|
||||
dnsProvider, err := godaddyProvider.NewDNSProvider()
|
||||
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 (
|
||||
"certimate/internal/domain"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
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)
|
||||
|
||||
os.Setenv("NAMESILO_API_KEY", access.ApiKey)
|
||||
os.Setenv("NAMESILO_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", a.option.Timeout))
|
||||
|
||||
dnsProvider, err := namesiloProvider.NewDNSProvider()
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package applicant
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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_KEY", access.SecretKey)
|
||||
os.Setenv("TENCENTCLOUD_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", t.option.Timeout))
|
||||
|
||||
dnsProvider, err := tencentcloud.NewDNSProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"certimate/internal/applicant"
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/rand"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cas20200407 "github.com/alibabacloud-go/cas-20200407/v2/client"
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
util "github.com/alibabacloud-go/tea-utils/v2/service"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
)
|
||||
|
||||
type aliyun struct {
|
||||
client *cas20200407.Client
|
||||
client *oss.Client
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
@@ -47,160 +40,31 @@ func (a *aliyun) GetInfo() []string {
|
||||
}
|
||||
|
||||
func (a *aliyun) Deploy(ctx context.Context) error {
|
||||
|
||||
// 查询有没有对应的资源
|
||||
resource, err := a.resource()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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,
|
||||
err := a.client.PutBucketCnameWithCertificate(getDeployString(a.option.DeployConfig, "bucket"), oss.PutBucketCname{
|
||||
Cname: getDeployString(a.option.DeployConfig, "domain"),
|
||||
CertificateConfiguration: &oss.CertificateConfiguration{
|
||||
Certificate: a.option.Certificate.Certificate,
|
||||
PrivateKey: a.option.Certificate.PrivateKey,
|
||||
Force: true,
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("deploy aliyun oss error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (a *aliyun) contacts() ([]*cas20200407.ListContactResponseBodyContactList, error) {
|
||||
listContactRequest := &cas20200407.ListContactRequest{}
|
||||
runtime := &util.RuntimeOptions{}
|
||||
|
||||
resp, err := a.client.ListContactWithOptions(listContactRequest, runtime)
|
||||
func (a *aliyun) createClient(accessKeyId, accessKeySecret string) (*oss.Client, error) {
|
||||
client, err := oss.New(
|
||||
getDeployString(a.option.DeployConfig, "endpoint"),
|
||||
accessKeyId,
|
||||
accessKeySecret,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("create aliyun client error: %w", err)
|
||||
}
|
||||
if resp.Body.TotalCount == 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
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package deployer
|
||||
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/rand"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -46,9 +47,9 @@ func (a *AliyunCdn) GetInfo() []string {
|
||||
|
||||
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{
|
||||
DomainName: tea.String(a.option.Domain),
|
||||
DomainName: tea.String(getDeployString(a.option.DeployConfig, "domain")),
|
||||
CertName: tea.String(certName),
|
||||
CertType: tea.String("upload"),
|
||||
SSLProtocol: tea.String("on"),
|
||||
|
||||
@@ -7,6 +7,7 @@ package deployer
|
||||
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/rand"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -51,9 +52,9 @@ func (a *AliyunEsa) GetInfo() []string {
|
||||
|
||||
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{
|
||||
DomainName: tea.String(a.option.Domain),
|
||||
DomainName: tea.String(getDeployString(a.option.DeployConfig, "domain")),
|
||||
CertName: tea.String(certName),
|
||||
CertType: tea.String("upload"),
|
||||
SSLProtocol: tea.String("on"),
|
||||
|
||||
@@ -2,8 +2,8 @@ package deployer
|
||||
|
||||
import (
|
||||
"certimate/internal/applicant"
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/app"
|
||||
"certimate/internal/utils/variables"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -30,6 +30,7 @@ type DeployerOption struct {
|
||||
Product string `json:"product"`
|
||||
Access string `json:"access"`
|
||||
AceessRecord *models.Record `json:"-"`
|
||||
DeployConfig domain.DeployConfig `json:"deployConfig"`
|
||||
Certificate applicant.Certificate `json:"certificate"`
|
||||
Variables map[string]string `json:"variables"`
|
||||
}
|
||||
@@ -42,52 +43,29 @@ type Deployer interface {
|
||||
|
||||
func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error) {
|
||||
rs := make([]Deployer, 0)
|
||||
|
||||
if record.GetString("targetAccess") != "" {
|
||||
singleDeployer, err := Get(record, cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rs = append(rs, singleDeployer)
|
||||
}
|
||||
|
||||
if record.GetString("group") != "" {
|
||||
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...)
|
||||
|
||||
}
|
||||
|
||||
if record.GetString("deployConfig") == "" {
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
}
|
||||
deployConfigs := make([]domain.DeployConfig, 0)
|
||||
|
||||
func getByGroup(record *models.Record, cert *applicant.Certificate, accesses ...*models.Record) ([]Deployer, error) {
|
||||
err := record.UnmarshalJSONField("deployConfig", &deployConfigs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析部署配置失败: %w", err)
|
||||
}
|
||||
|
||||
rs := make([]Deployer, 0)
|
||||
if len(deployConfigs) == 0 {
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
for _, deployConfig := range deployConfigs {
|
||||
|
||||
deployer, err := getWithDeployConfig(record, cert, deployConfig)
|
||||
|
||||
for _, access := range accesses {
|
||||
deployer, err := getWithAccess(record, cert, access)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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{
|
||||
DomainId: record.Id,
|
||||
Domain: record.GetString("domain"),
|
||||
Product: getProduct(record),
|
||||
Product: getProduct(deployConfig.Type),
|
||||
Access: access.GetString("config"),
|
||||
AceessRecord: access,
|
||||
Variables: variables.Parse2Map(record.GetString("variables")),
|
||||
DeployConfig: deployConfig,
|
||||
}
|
||||
if cert != nil {
|
||||
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:
|
||||
return NewAliyun(option)
|
||||
case targetAliyunCdn:
|
||||
@@ -136,16 +120,8 @@ func getWithAccess(record *models.Record, cert *applicant.Certificate, access *m
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func Get(record *models.Record, cert *applicant.Certificate) (Deployer, error) {
|
||||
|
||||
access := record.ExpandedOne("targetAccess")
|
||||
|
||||
return getWithAccess(record, cert, access)
|
||||
}
|
||||
|
||||
func getProduct(record *models.Record) string {
|
||||
targetType := record.GetString("targetType")
|
||||
rs := strings.Split(targetType, "-")
|
||||
func getProduct(t string) string {
|
||||
rs := strings.Split(t, "-")
|
||||
if len(rs) < 2 {
|
||||
return ""
|
||||
}
|
||||
@@ -159,3 +135,39 @@ func toStr(tag string, data any) string {
|
||||
byts, _ := json.Marshal(data)
|
||||
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 {
|
||||
Command string `json:"command"`
|
||||
CertPath string `json:"certPath"`
|
||||
KeyPath string `json:"keyPath"`
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
|
||||
if err := execCmd(access.Command); err != nil {
|
||||
if err := execCmd(getDeployString(l.option.DeployConfig, "command")); err != nil {
|
||||
return fmt.Errorf("执行命令失败: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ func (q *qiuniu) Deploy(ctx context.Context) 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{
|
||||
CertID: certId,
|
||||
@@ -104,7 +104,7 @@ type domainInfo struct {
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
@@ -135,8 +135,8 @@ func (q *qiuniu) uploadCert() (string, error) {
|
||||
path := "/sslcert"
|
||||
|
||||
body := &uploadCertReq{
|
||||
Name: q.option.Domain,
|
||||
CommonName: q.option.Domain,
|
||||
Name: getDeployString(q.option.DeployConfig, "domain"),
|
||||
CommonName: getDeployString(q.option.DeployConfig, "domain"),
|
||||
Pri: q.option.Certificate.PrivateKey,
|
||||
Ca: q.option.Certificate.Certificate,
|
||||
}
|
||||
@@ -166,7 +166,7 @@ type modifyDomainCertReq struct {
|
||||
}
|
||||
|
||||
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{
|
||||
CertID: certId,
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
xpath "path"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
sshPkg "golang.org/x/crypto/ssh"
|
||||
@@ -24,10 +23,6 @@ type sshAccess struct {
|
||||
Password string `json:"password"`
|
||||
Key string `json:"key"`
|
||||
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) {
|
||||
@@ -50,16 +45,6 @@ func (s *ssh) Deploy(ctx context.Context) error {
|
||||
if err := json.Unmarshal([]byte(s.option.Access), access); err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
@@ -70,29 +55,30 @@ func (s *ssh) Deploy(ctx context.Context) error {
|
||||
s.infos = append(s.infos, toStr("ssh连接成功", nil))
|
||||
|
||||
// 执行前置命令
|
||||
if access.PreCommand != "" {
|
||||
err, stdout, stderr := s.sshExecCommand(client, access.PreCommand)
|
||||
preCommand := getDeployString(s.option.DeployConfig, "preCommand")
|
||||
if preCommand != "" {
|
||||
err, stdout, stderr := s.sshExecCommand(client, preCommand)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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/utils/rand"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"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/profile"
|
||||
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.CertificatePrivateKey = common.StringPtr(t.option.Certificate.PrivateKey)
|
||||
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)
|
||||
if err != nil {
|
||||
@@ -93,10 +96,24 @@ func (t *tencentCdn) deploy(certId string) error {
|
||||
request := ssl.NewDeployCertificateInstanceRequest()
|
||||
|
||||
request.CertificateId = common.StringPtr(certId)
|
||||
request.InstanceIdList = common.StringPtrs([]string{t.option.Domain})
|
||||
request.ResourceType = common.StringPtr("cdn")
|
||||
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, err := client.DeployCertificateInstance(request)
|
||||
|
||||
@@ -106,3 +123,26 @@ func (t *tencentCdn) deploy(certId string) error {
|
||||
t.infos = append(t.infos, toStr("部署证书", resp.Response))
|
||||
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
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type hookData struct {
|
||||
Domain string `json:"domain"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Variables map[string]string `json:"variables"`
|
||||
}
|
||||
|
||||
type webhook struct {
|
||||
@@ -50,6 +51,7 @@ func (w *webhook) Deploy(ctx context.Context) error {
|
||||
Domain: w.option.Domain,
|
||||
Certificate: w.option.Certificate.Certificate,
|
||||
PrivateKey: w.option.Certificate.PrivateKey,
|
||||
Variables: getDeployVariables(w.option.DeployConfig),
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(data)
|
||||
|
||||
@@ -10,6 +10,12 @@ type TencentAccess struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
}
|
||||
|
||||
type HuaweiCloudAccess struct {
|
||||
Region string `json:"region"`
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
}
|
||||
|
||||
type CloudflareAccess struct {
|
||||
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/utils/app"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -41,18 +40,6 @@ func deploy(ctx context.Context, record *models.Record) error {
|
||||
return err
|
||||
}
|
||||
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")
|
||||
expiredAt := currRecord.GetDateTime("expiredAt").Time()
|
||||
@@ -106,6 +93,13 @@ func deploy(ctx context.Context, record *models.Record) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 没有部署配置,也算成功
|
||||
if len(deployers) == 0 {
|
||||
history.record(deployPhase, "没有部署配置", &RecordInfo{Info: []string{"没有部署配置"}})
|
||||
history.setWholeSuccess(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, deployer := range deployers {
|
||||
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" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Certimate - Your Trusted SSL Automation Partner</title>
|
||||
<script type="module" crossorigin src="/assets/index-47M77bCI.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-ClQTEWmX.css">
|
||||
<script type="module" crossorigin src="/assets/index-DipHpsma.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CV_7sKTK.css">
|
||||
</head>
|
||||
<body class="bg-background">
|
||||
<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-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.6.1",
|
||||
"immer": "^10.1.1",
|
||||
"jszip": "^3.10.1",
|
||||
"lucide-react": "^0.417.0",
|
||||
"moment": "^2.30.1",
|
||||
"nanoid": "^5.0.7",
|
||||
"pocketbase": "^0.21.4",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -3780,6 +3782,15 @@
|
||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||
"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": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@@ -4159,9 +4170,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.0.7.tgz",
|
||||
"integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -4169,10 +4180,10 @@
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
@@ -4561,6 +4572,23 @@
|
||||
"resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"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": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
|
||||
@@ -29,22 +29,24 @@
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"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",
|
||||
"lucide-react": "^0.417.0",
|
||||
"moment": "^2.30.1",
|
||||
"nanoid": "^5.0.7",
|
||||
"pocketbase": "^0.21.4",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-i18next": "^15.0.2",
|
||||
"react-router-dom": "^6.25.1",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^0.9.1",
|
||||
"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"
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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";
|
||||
|
||||
export default function LocaleToggle() {
|
||||
const { i18n } = useTranslation()
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@@ -22,7 +22,7 @@ export default function LocaleToggle() {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{Object.keys(i18n.store.data).map(key => (
|
||||
{Object.keys(i18n.store.data).map((key) => (
|
||||
<DropdownMenuItem onClick={() => i18n.changeLanguage(key)}>
|
||||
{i18n.store.data[key].name as string}
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Moon, Sun } from "lucide-react";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -14,7 +14,6 @@ export function ThemeToggle() {
|
||||
const { setTheme } = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -26,13 +25,13 @@ export function ThemeToggle() {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => setTheme("light")}>
|
||||
{t('theme.light')}
|
||||
{t("common.theme.light")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
||||
{t('theme.dark')}
|
||||
{t("common.theme.dark")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("system")}>
|
||||
{t('theme.system')}
|
||||
{t("common.theme.system")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -15,7 +15,12 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
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 { useConfig } from "@/providers/config";
|
||||
|
||||
@@ -35,10 +40,19 @@ const AccessAliyunForm = ({
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
configType: accessFormType,
|
||||
accessKeyId: z.string().min(1, 'access.form.access.key.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
accessSecretId: z.string().min(1, 'access.form.access.key.secret.not.empty').max(64, t('zod.rule.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 })),
|
||||
accessSecretId: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.access_key_secret.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
});
|
||||
|
||||
let config: AliyunConfig = {
|
||||
@@ -51,7 +65,7 @@ const AccessAliyunForm = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "aliyun",
|
||||
accessKeyId: config.accessKeyId,
|
||||
accessSecretId: config.accessKeySecret,
|
||||
@@ -117,9 +131,12 @@ const AccessAliyunForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -132,7 +149,7 @@ const AccessAliyunForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -147,7 +164,7 @@ const AccessAliyunForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -162,9 +179,12 @@ const AccessAliyunForm = ({
|
||||
name="accessKeyId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.access.key.id')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.access_key_id.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.access.key.id.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.access_key_id.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -177,9 +197,12 @@ const AccessAliyunForm = ({
|
||||
name="accessSecretId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.access.key.secret')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.access_key_secret.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.access.key.secret.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.access_key_secret.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -190,7 +213,7 @@ const AccessAliyunForm = ({
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -15,7 +15,12 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
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 { useConfig } from "@/providers/config";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
@@ -34,9 +39,15 @@ const AccessCloudflareForm = ({
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
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 = {
|
||||
@@ -48,7 +59,7 @@ const AccessCloudflareForm = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "cloudflare",
|
||||
dnsApiToken: config.dnsApiToken,
|
||||
},
|
||||
@@ -111,9 +122,12 @@ const AccessCloudflareForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -126,7 +140,7 @@ const AccessCloudflareForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -141,7 +155,7 @@ const AccessCloudflareForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -156,9 +170,14 @@ const AccessCloudflareForm = ({
|
||||
name="dnsApiToken"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.cloud.dns.api.token')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.cloud_dns_api_token.label")}</FormLabel>
|
||||
<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>
|
||||
|
||||
<FormMessage />
|
||||
@@ -167,7 +186,7 @@ const AccessCloudflareForm = ({
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -10,16 +10,10 @@ import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import AccessTencentForm from "./AccessTencentForm";
|
||||
|
||||
import { Label } from "../ui/label";
|
||||
|
||||
import { Access, accessTypeMap } from "@/domain/access";
|
||||
import AccessAliyunForm from "./AccessAliyunForm";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import AccessSSHForm from "./AccessSSHForm";
|
||||
import WebhookForm from "./AccessWebhookFrom";
|
||||
import { Label } from "../ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -29,11 +23,16 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "../ui/select";
|
||||
import AccessCloudflareForm from "./AccessCloudflareForm";
|
||||
import AccessAliyunForm from "./AccessAliyunForm";
|
||||
import AccessTencentForm from "./AccessTencentForm";
|
||||
import AccessHuaweicloudForm from "./AccessHuaweicloudForm";
|
||||
import AccessQiniuForm from "./AccessQiniuForm";
|
||||
import AccessCloudflareForm from "./AccessCloudflareForm";
|
||||
import AccessNamesiloForm from "./AccessNamesiloForm";
|
||||
import AccessGodaddyFrom from "./AccessGodaddyForm";
|
||||
import AccessLocalForm from "./AccessLocalForm";
|
||||
import AccessSSHForm from "./AccessSSHForm";
|
||||
import AccessWebhookForm from "./AccessWebhookFrom";
|
||||
|
||||
type TargetConfigEditProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
@@ -56,17 +55,6 @@ export function AccessEdit({
|
||||
|
||||
let form = <> </>;
|
||||
switch (configType) {
|
||||
case "tencent":
|
||||
form = (
|
||||
<AccessTencentForm
|
||||
data={data}
|
||||
op={op}
|
||||
onAfterReq={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "aliyun":
|
||||
form = (
|
||||
<AccessAliyunForm
|
||||
@@ -78,9 +66,9 @@ export function AccessEdit({
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "ssh":
|
||||
case "tencent":
|
||||
form = (
|
||||
<AccessSSHForm
|
||||
<AccessTencentForm
|
||||
data={data}
|
||||
op={op}
|
||||
onAfterReq={() => {
|
||||
@@ -89,20 +77,9 @@ export function AccessEdit({
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "webhook":
|
||||
case "huaweicloud":
|
||||
form = (
|
||||
<WebhookForm
|
||||
data={data}
|
||||
op={op}
|
||||
onAfterReq={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "cloudflare":
|
||||
form = (
|
||||
<AccessCloudflareForm
|
||||
<AccessHuaweicloudForm
|
||||
data={data}
|
||||
op={op}
|
||||
onAfterReq={() => {
|
||||
@@ -122,6 +99,17 @@ export function AccessEdit({
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "cloudflare":
|
||||
form = (
|
||||
<AccessCloudflareForm
|
||||
data={data}
|
||||
op={op}
|
||||
onAfterReq={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "namesilo":
|
||||
form = (
|
||||
<AccessNamesiloForm
|
||||
@@ -155,6 +143,28 @@ export function AccessEdit({
|
||||
/>
|
||||
);
|
||||
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) => {
|
||||
@@ -168,11 +178,17 @@ export function AccessEdit({
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||
<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>
|
||||
<ScrollArea className="max-h-[80vh]">
|
||||
<div className="container py-3">
|
||||
<Label>{t('access.type')}</Label>
|
||||
<Label>{t("access.authorization.form.type.label")}</Label>
|
||||
|
||||
<Select
|
||||
onValueChange={(val) => {
|
||||
@@ -181,11 +197,11 @@ export function AccessEdit({
|
||||
defaultValue={configType}
|
||||
>
|
||||
<SelectTrigger className="mt-3">
|
||||
<SelectValue placeholder={t('access.type.not.empty')} />
|
||||
<SelectValue placeholder={t("access.authorization.form.type.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>{t('access.type')}</SelectLabel>
|
||||
<SelectLabel>{t("access.authorization.form.type.list")}</SelectLabel>
|
||||
{typeKeys.map((key) => (
|
||||
<SelectItem value={key} key={key}>
|
||||
<div
|
||||
@@ -198,7 +214,7 @@ export function AccessEdit({
|
||||
src={accessTypeMap.get(key)?.[1]}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
<div>{t(accessTypeMap.get(key)?.[0] || '')}</div>
|
||||
<div>{t(accessTypeMap.get(key)?.[0] || "")}</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
|
||||
@@ -39,10 +39,19 @@ const AccessGodaddyFrom = ({
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
configType: accessFormType,
|
||||
apiKey: z.string().min(1, 'access.form.go.daddy.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
apiSecret: z.string().min(1, 'access.form.go.daddy.api.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
apiKey: z
|
||||
.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 = {
|
||||
@@ -55,7 +64,7 @@ const AccessGodaddyFrom = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "godaddy",
|
||||
apiKey: config.apiKey,
|
||||
apiSecret: config.apiSecret,
|
||||
@@ -120,9 +129,12 @@ const AccessGodaddyFrom = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -135,7 +147,7 @@ const AccessGodaddyFrom = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -150,7 +162,7 @@ const AccessGodaddyFrom = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -165,9 +177,12 @@ const AccessGodaddyFrom = ({
|
||||
name="apiKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.go.daddy.api.key')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.godaddy_api_key.label")}</FormLabel>
|
||||
<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>
|
||||
|
||||
<FormMessage />
|
||||
@@ -180,9 +195,14 @@ const AccessGodaddyFrom = ({
|
||||
name="apiSecret"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.go.daddy.api.secret')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.godaddy_api_secret.label")}</FormLabel>
|
||||
<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>
|
||||
|
||||
<FormMessage />
|
||||
@@ -191,7 +211,7 @@ const AccessGodaddyFrom = ({
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -39,7 +39,10 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
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>>({
|
||||
@@ -80,7 +83,7 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('access.group.add')}</DialogTitle>
|
||||
<DialogTitle>{t("access.group.add")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="container py-3">
|
||||
@@ -97,9 +100,13 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.group.name')}</FormLabel>
|
||||
<FormLabel>{t("access.group.form.name.label")}</FormLabel>
|
||||
<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>
|
||||
|
||||
<FormMessage />
|
||||
@@ -108,7 +115,7 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -48,7 +48,7 @@ const AccessGroupList = () => {
|
||||
reloadAccessGroups();
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t('delete.failed'),
|
||||
title: t("common.delete.failed.message"),
|
||||
description: getErrMessage(e),
|
||||
variant: "destructive",
|
||||
});
|
||||
@@ -69,10 +69,10 @@ const AccessGroupList = () => {
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||
{t('access.group.domain.empty')}
|
||||
{t("access.group.domains.nodata")}
|
||||
</div>
|
||||
<AccessGroupEdit
|
||||
trigger={<Button>{t('access.group.add')}</Button>}
|
||||
trigger={<Button>{t("access.group.add")}</Button>}
|
||||
className="mt-3"
|
||||
/>
|
||||
</div>
|
||||
@@ -86,7 +86,11 @@ const AccessGroupList = () => {
|
||||
<CardHeader>
|
||||
<CardTitle>{accessGroup.name}</CardTitle>
|
||||
<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>
|
||||
</CardHeader>
|
||||
<CardContent className="min-h-[180px]">
|
||||
@@ -120,9 +124,7 @@ const AccessGroupList = () => {
|
||||
<div>
|
||||
<Group size={40} />
|
||||
</div>
|
||||
<div className="ml-2">
|
||||
{t('access.group.empty')}
|
||||
</div>
|
||||
<div className="ml-2">{t("access.group.nodata")}</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -149,7 +151,7 @@ const AccessGroupList = () => {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t('access.all')}
|
||||
{t("access.group.domains")}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
@@ -164,7 +166,7 @@ const AccessGroupList = () => {
|
||||
>
|
||||
<div>
|
||||
<Button size="sm" onClick={handleAddAccess}>
|
||||
{t('access.add')}
|
||||
{t("access.authorization.add")}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
@@ -173,21 +175,21 @@ const AccessGroupList = () => {
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant={"destructive"} size={"sm"}>
|
||||
{t('delete')}
|
||||
{t("common.delete")}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="dark:text-gray-200">
|
||||
{t('access.group.delete')}
|
||||
{t("access.group.delete")}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t('access.group.delete.confirm')}
|
||||
{t("access.group.delete.confirm")}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel className="dark:text-gray-200">
|
||||
{t('cancel')}
|
||||
{t("common.cancel")}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
@@ -196,7 +198,7 @@ const AccessGroupList = () => {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t('confirm')}
|
||||
{t("common.confirm")}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</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 {
|
||||
Access,
|
||||
accessFormType,
|
||||
getUsageByConfigType,
|
||||
LocalConfig,
|
||||
SSHConfig,
|
||||
} from "@/domain/access";
|
||||
import { Access, accessFormType, getUsageByConfigType } from "@/domain/access";
|
||||
import { useConfig } from "@/providers/config";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -20,7 +14,7 @@ import {
|
||||
} from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { Button } from "../ui/button";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
|
||||
import { save } from "@/repository/access";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
@@ -39,30 +33,19 @@ const AccessLocalForm = ({
|
||||
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
configType: accessFormType,
|
||||
|
||||
command: z.string().min(1, 'access.form.ssh.command.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||
certPath: z.string().min(0, 'access.form.ssh.cert.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||
keyPath: z.string().min(0, 'access.form.ssh.key.path.not.empty').max(2048, t('zod.rule.string.max', { max: 2048 })),
|
||||
});
|
||||
|
||||
let config: LocalConfig = {
|
||||
command: "sudo service nginx restart",
|
||||
certPath: "/etc/nginx/ssl/certificate.crt",
|
||||
keyPath: "/etc/nginx/ssl/private.key",
|
||||
};
|
||||
if (data) config = data.config as SSHConfig;
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "local",
|
||||
certPath: config.certPath,
|
||||
keyPath: config.keyPath,
|
||||
command: config.command,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -73,11 +56,7 @@ const AccessLocalForm = ({
|
||||
configType: data.configType,
|
||||
usage: getUsageByConfigType(data.configType),
|
||||
|
||||
config: {
|
||||
command: data.command,
|
||||
certPath: data.certPath,
|
||||
keyPath: data.keyPath,
|
||||
},
|
||||
config: {},
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -128,9 +107,12 @@ const AccessLocalForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -143,7 +125,7 @@ const AccessLocalForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -158,7 +140,7 @@ const AccessLocalForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</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 />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -15,7 +15,12 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
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 { useConfig } from "@/providers/config";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
@@ -34,9 +39,15 @@ const AccessNamesiloForm = ({
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
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 = {
|
||||
@@ -48,7 +59,7 @@ const AccessNamesiloForm = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "namesilo",
|
||||
apiKey: config.apiKey,
|
||||
},
|
||||
@@ -110,9 +121,12 @@ const AccessNamesiloForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -125,7 +139,7 @@ const AccessNamesiloForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -140,7 +154,7 @@ const AccessNamesiloForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -155,9 +169,12 @@ const AccessNamesiloForm = ({
|
||||
name="apiKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.namesilo.api.key')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.namesilo_api_key.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.namesilo.api.key.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.namesilo_api_key.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -166,7 +183,7 @@ const AccessNamesiloForm = ({
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -15,7 +15,12 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
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 { useConfig } from "@/providers/config";
|
||||
|
||||
@@ -35,10 +40,13 @@ const AccessQiniuForm = ({
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
configType: accessFormType,
|
||||
accessKey: z.string().min(1, 'access.form.access.key.not.empty').max(64),
|
||||
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64),
|
||||
accessKey: z.string().min(1, "access.authorization.form.access_key.placeholder").max(64),
|
||||
secretKey: z.string().min(1, "access.authorization.form.secret_key.placeholder").max(64),
|
||||
});
|
||||
|
||||
let config: QiniuConfig = {
|
||||
@@ -51,7 +59,7 @@ const AccessQiniuForm = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "qiniu",
|
||||
accessKey: config.accessKey,
|
||||
secretKey: config.secretKey,
|
||||
@@ -116,9 +124,12 @@ const AccessQiniuForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -131,7 +142,7 @@ const AccessQiniuForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -146,7 +157,7 @@ const AccessQiniuForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -161,9 +172,12 @@ const AccessQiniuForm = ({
|
||||
name="accessKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.access.key')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.access_key.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.access.key.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.access_key.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -176,9 +190,12 @@ const AccessQiniuForm = ({
|
||||
name="secretKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.secret.key')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.secret_key.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.secret_key.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -189,7 +206,7 @@ const AccessQiniuForm = ({
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
} from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { Button } from "../ui/button";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { save } from "@/repository/access";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
@@ -66,27 +65,37 @@ const AccessSSHForm = ({
|
||||
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
configType: accessFormType,
|
||||
host: z.string().refine(
|
||||
(str) => {
|
||||
return ipReg.test(str) || domainReg.test(str);
|
||||
},
|
||||
{
|
||||
message: "zod.rule.ssh.host",
|
||||
message: "common.errmsg.host_invalid",
|
||||
}
|
||||
),
|
||||
group: z.string().optional(),
|
||||
port: z.string().min(1, 'access.form.ssh.port.not.empty').max(5, t('zod.rule.string.max', { max: 5 })),
|
||||
username: z.string().min(1, 'username.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
password: z.string().min(0, 'password.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
key: z.string().min(0, 'access.form.ssh.key.not.empty').max(20480, t('zod.rule.string.max', { max: 20480 })),
|
||||
port: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.ssh_port.placeholder")
|
||||
.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(),
|
||||
|
||||
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 = {
|
||||
@@ -96,10 +105,6 @@ const AccessSSHForm = ({
|
||||
password: "",
|
||||
key: "",
|
||||
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;
|
||||
|
||||
@@ -107,7 +112,7 @@ const AccessSSHForm = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "ssh",
|
||||
group: data?.group,
|
||||
host: config.host,
|
||||
@@ -116,10 +121,6 @@ const AccessSSHForm = ({
|
||||
password: config.password,
|
||||
key: config.key,
|
||||
keyFile: config.keyFile,
|
||||
certPath: config.certPath,
|
||||
keyPath: config.keyPath,
|
||||
command: config.command,
|
||||
preCommand: config.preCommand,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -139,10 +140,6 @@ const AccessSSHForm = ({
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
key: data.key,
|
||||
command: data.command,
|
||||
preCommand: data.preCommand,
|
||||
certPath: data.certPath,
|
||||
keyPath: data.keyPath,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -228,9 +225,16 @@ const AccessSSHForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.name.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t(
|
||||
"access.authorization.form.name.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -244,12 +248,12 @@ const AccessSSHForm = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<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
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t('add')}
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@@ -264,7 +268,11 @@ const AccessSSHForm = ({
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t('access.group.not.empty')} />
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"access.authorization.form.access_group.placeholder"
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="emptyId">
|
||||
@@ -304,7 +312,9 @@ const AccessSSHForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.config.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -319,7 +329,9 @@ const AccessSSHForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.config.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -334,9 +346,16 @@ const AccessSSHForm = ({
|
||||
name="host"
|
||||
render={({ field }) => (
|
||||
<FormItem className="grow">
|
||||
<FormLabel>{t('access.form.ssh.host')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.ssh_host.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.ssh.host.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t(
|
||||
"access.authorization.form.ssh_host.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -349,10 +368,14 @@ const AccessSSHForm = ({
|
||||
name="port"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.ssh.port')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.ssh_port.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('access.form.ssh.port.not.empty')}
|
||||
placeholder={t(
|
||||
"access.authorization.form.ssh_port.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
type="number"
|
||||
/>
|
||||
@@ -369,9 +392,16 @@ const AccessSSHForm = ({
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('username')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.username.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('username.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t(
|
||||
"access.authorization.form.username.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -384,10 +414,14 @@ const AccessSSHForm = ({
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('password')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.password.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('password.not.empty')}
|
||||
placeholder={t(
|
||||
"access.authorization.form.password.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
type="password"
|
||||
/>
|
||||
@@ -403,9 +437,16 @@ const AccessSSHForm = ({
|
||||
name="key"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden>
|
||||
<FormLabel>{t('access.form.ssh.key')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.ssh_key.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.ssh.key.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t(
|
||||
"access.authorization.form.ssh_key.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -418,7 +459,9 @@ const AccessSSHForm = ({
|
||||
name="keyFile"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.ssh.key')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("access.authorization.form.ssh_key.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<div>
|
||||
<Button
|
||||
@@ -428,10 +471,16 @@ const AccessSSHForm = ({
|
||||
className="w-48"
|
||||
onClick={handleSelectFileClick}
|
||||
>
|
||||
{fileName ? fileName : t('access.form.ssh.key.file.not.empty')}
|
||||
{fileName
|
||||
? fileName
|
||||
: t(
|
||||
"access.authorization.form.ssh_key_file.placeholder"
|
||||
)}
|
||||
</Button>
|
||||
<Input
|
||||
placeholder={t('access.form.ssh.key.not.empty')}
|
||||
placeholder={t(
|
||||
"access.authorization.form.ssh_key.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
ref={fileInputRef}
|
||||
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 />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -15,7 +15,12 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
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 { useConfig } from "@/providers/config";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
@@ -34,10 +39,19 @@ const AccessTencentForm = ({
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
configType: accessFormType,
|
||||
secretId: z.string().min(1, 'access.form.secret.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
secretId: z
|
||||
.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 = {
|
||||
@@ -50,7 +64,7 @@ const AccessTencentForm = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "tencent",
|
||||
secretId: config.secretId,
|
||||
secretKey: config.secretKey,
|
||||
@@ -113,9 +127,12 @@ const AccessTencentForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -128,7 +145,7 @@ const AccessTencentForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -143,7 +160,7 @@ const AccessTencentForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -158,9 +175,12 @@ const AccessTencentForm = ({
|
||||
name="secretId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.secret.id')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.secret_id.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.secret.id.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.secret_id.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -173,9 +193,12 @@ const AccessTencentForm = ({
|
||||
name="secretKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.secret.key')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.secret_key.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.secret_key.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -184,7 +207,7 @@ const AccessTencentForm = ({
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -15,7 +15,12 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
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 { useConfig } from "@/providers/config";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
@@ -34,9 +39,12 @@ const WebhookForm = ({
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.name.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
configType: accessFormType,
|
||||
url: z.string().url('zod.rule.url'),
|
||||
url: z.string().url("common.errmsg.url_invalid"),
|
||||
});
|
||||
|
||||
let config: WebhookConfig = {
|
||||
@@ -48,7 +56,7 @@ const WebhookForm = ({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || '',
|
||||
name: data?.name || "",
|
||||
configType: "webhook",
|
||||
url: config.url,
|
||||
},
|
||||
@@ -110,9 +118,12 @@ const WebhookForm = ({
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('name')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.name.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.name.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -125,7 +136,7 @@ const WebhookForm = ({
|
||||
name="id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -140,7 +151,7 @@ const WebhookForm = ({
|
||||
name="configType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="hidden">
|
||||
<FormLabel>{t('access.form.config.field')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -155,9 +166,12 @@ const WebhookForm = ({
|
||||
name="url"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('access.form.webhook.url')}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.webhook_url.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('access.form.webhook.url.not.empty')} {...field} />
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.webhook_url.placeholder")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -166,7 +180,7 @@ const WebhookForm = ({
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</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 { cn } from "@/lib/utils"
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { Separator } from "../ui/separator";
|
||||
|
||||
@@ -16,58 +15,52 @@ const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
|
||||
let step = 0;
|
||||
|
||||
if (phase === "check") {
|
||||
step = 1
|
||||
step = 1;
|
||||
} else if (phase === "apply") {
|
||||
step = 2
|
||||
step = 2;
|
||||
} else if (phase === "deploy") {
|
||||
step = 3
|
||||
step = 3;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<div className={
|
||||
cn(
|
||||
<div
|
||||
className={cn(
|
||||
"text-xs text-nowrap",
|
||||
step === 1 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
||||
step > 1 ? "text-green-600" : "",
|
||||
)
|
||||
}>
|
||||
{t('deploy.progress.check')}
|
||||
step === 1 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||
step > 1 ? "text-green-600" : ""
|
||||
)}
|
||||
>
|
||||
{t("history.props.stage.progress.check")}
|
||||
</div>
|
||||
<Separator className={
|
||||
cn(
|
||||
"h-1 grow max-w-[60px]",
|
||||
step > 1 ? "bg-green-600" : "",
|
||||
)
|
||||
} />
|
||||
<div className={
|
||||
cn(
|
||||
<Separator
|
||||
className={cn("h-1 grow max-w-[60px]", step > 1 ? "bg-green-600" : "")}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"text-xs text-nowrap",
|
||||
step < 2 ? "text-muted-foreground" : "",
|
||||
step === 2 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
||||
step > 2 ? "text-green-600" : "",
|
||||
)
|
||||
}>
|
||||
{t('deploy.progress.apply')}
|
||||
step === 2 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||
step > 2 ? "text-green-600" : ""
|
||||
)}
|
||||
>
|
||||
{t("history.props.stage.progress.apply")}
|
||||
</div>
|
||||
<Separator className={
|
||||
cn(
|
||||
"h-1 grow max-w-[60px]",
|
||||
step > 2 ? "bg-green-600" : "",
|
||||
)
|
||||
} />
|
||||
<div className={
|
||||
cn(
|
||||
<Separator
|
||||
className={cn("h-1 grow max-w-[60px]", step > 2 ? "bg-green-600" : "")}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"text-xs text-nowrap",
|
||||
step < 3 ? "text-muted-foreground" : "",
|
||||
step === 3 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
|
||||
step > 3 ? "text-green-600" : "",
|
||||
)
|
||||
}>
|
||||
{t('deploy.progress.deploy')}
|
||||
step === 3 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
|
||||
step > 3 ? "text-green-600" : ""
|
||||
)}
|
||||
>
|
||||
{t("history.props.stage.progress.deploy")}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default DeployProgress;
|
||||
|
||||
@@ -43,7 +43,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
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>>({
|
||||
@@ -56,7 +56,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
if ((emails.content as EmailsSetting).emails.includes(data.email)) {
|
||||
form.setError("email", {
|
||||
message: "email.already.exist",
|
||||
message: "common.errmsg.email_duplicate",
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('email.add')}</DialogTitle>
|
||||
<DialogTitle>{t("domain.application.form.email.add")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="container py-3">
|
||||
@@ -120,9 +120,13 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('email')}</FormLabel>
|
||||
<FormLabel>{t("domain.application.form.email.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('email.not.empty.message')} {...field} type="email" />
|
||||
<Input
|
||||
placeholder={t("common.errmsg.email_empty")}
|
||||
{...field}
|
||||
type="email"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -131,7 +135,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('save')}</Button>
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</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 = {
|
||||
className?: string;
|
||||
value: string;
|
||||
valueType?: "domain" | "ip";
|
||||
valueType?: ValueType;
|
||||
onValueChange: (value: string) => void;
|
||||
};
|
||||
|
||||
const titles: Record<string, string> = {
|
||||
domain: "domain",
|
||||
ip: "IP",
|
||||
domain: "common.text.domain",
|
||||
ip: "common.text.ip",
|
||||
dns: "common.text.dns",
|
||||
};
|
||||
|
||||
const StringList = ({
|
||||
@@ -89,7 +90,7 @@ const StringList = ({
|
||||
<div className="flex items-center text-primary">
|
||||
<Plus size={16} className="cursor-pointer " />
|
||||
|
||||
<div className="text-sm ">{t("add")}</div>
|
||||
<div className="text-sm ">{t("common.add")}</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@@ -100,18 +101,20 @@ const StringList = ({
|
||||
when={list.length > 0}
|
||||
fallback={
|
||||
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
|
||||
<div className="text-muted-foreground">暂未添加域名</div>
|
||||
<div className="text-muted-foreground">
|
||||
{t('common.text.' + valueType + '.empty')}
|
||||
</div>
|
||||
|
||||
<StringEdit
|
||||
value={""}
|
||||
trigger={t("add")}
|
||||
trigger={t("common.add")}
|
||||
onValueChange={addVal}
|
||||
valueType={valueType}
|
||||
/>
|
||||
</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) => (
|
||||
<div key={index} className="flex justify-between items-center">
|
||||
<div>{item}</div>
|
||||
@@ -122,7 +125,7 @@ const StringList = ({
|
||||
trigger={
|
||||
<Edit
|
||||
size={16}
|
||||
className="cursor-pointer text-gray-600"
|
||||
className="cursor-pointer text-gray-600 dark:text-white"
|
||||
/>
|
||||
}
|
||||
value={item}
|
||||
@@ -150,7 +153,7 @@ const StringList = ({
|
||||
|
||||
export default StringList;
|
||||
|
||||
type ValueType = "domain" | "ip";
|
||||
type ValueType = "domain" | "dns" | "host";
|
||||
|
||||
type StringEditProps = {
|
||||
value: string;
|
||||
@@ -179,14 +182,15 @@ const StringEdit = ({
|
||||
const domainSchema = z
|
||||
.string()
|
||||
.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> = {
|
||||
domain: domainSchema,
|
||||
ip: ipSchema,
|
||||
dns: ipSchema,
|
||||
host: ipSchema,
|
||||
};
|
||||
|
||||
const onSaveClick = useCallback(() => {
|
||||
@@ -213,12 +217,15 @@ const StringEdit = ({
|
||||
}}
|
||||
>
|
||||
<DialogTrigger className="text-primary">{trigger}</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogContent className="dark:text-white">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t(titles[valueType])}</DialogTitle>
|
||||
<DialogTitle className="dark:text-white">
|
||||
{t(titles[valueType])}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Input
|
||||
value={currentValue}
|
||||
className="dark:text-white"
|
||||
onChange={(e) => {
|
||||
setCurrentValue(e.target.value);
|
||||
}}
|
||||
@@ -233,7 +240,7 @@ const StringEdit = ({
|
||||
onSaveClick();
|
||||
}}
|
||||
>
|
||||
{op === "add" ? t("add") : t("confirm")}
|
||||
{op === "add" ? t("common.add") : t("common.confirm")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Separator } from "../ui/separator";
|
||||
import { version } from "@/domain/version";
|
||||
|
||||
const Version = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="fixed right-0 bottom-0 w-full flex justify-between p-5">
|
||||
@@ -17,7 +17,7 @@ const Version = () => {
|
||||
className="flex items-center"
|
||||
>
|
||||
<BookOpen size={16} />
|
||||
<div className="ml-1">{t('document')}</div>
|
||||
<div className="ml-1">{t("common.menu.document")}</div>
|
||||
</a>
|
||||
<Separator orientation="vertical" className="mx-2" />
|
||||
<a
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useEffect, useState } from "react";
|
||||
import { update } from "@/repository/settings";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { useToast } from "../ui/use-toast";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type DingTalkSetting = {
|
||||
id: string;
|
||||
@@ -72,15 +72,17 @@ const DingTalk = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t('save.succeed'),
|
||||
description: t('setting.notify.config.save.succeed'),
|
||||
title: t("common.save.succeeded.message"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
|
||||
toast({
|
||||
title: t('save.failed'),
|
||||
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
||||
title: t("common.save.failed.message"),
|
||||
description: `${t(
|
||||
"settings.notification.config.failed.message"
|
||||
)}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
@@ -102,7 +104,7 @@ const DingTalk = () => {
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
placeholder={t('access.form.ding.access.token.placeholder')}
|
||||
placeholder={t("settings.notification.dingtalk.secret.placeholder")}
|
||||
className="mt-2"
|
||||
value={dingtalk.data.secret}
|
||||
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 className="flex justify-end mt-2">
|
||||
@@ -138,7 +142,7 @@ const DingTalk = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t('save')}
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@/domain/settings";
|
||||
import { getSetting, update } from "@/repository/settings";
|
||||
import { useToast } from "../ui/use-toast";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const NotifyTemplate = () => {
|
||||
const [id, setId] = useState("");
|
||||
@@ -68,8 +68,8 @@ const NotifyTemplate = () => {
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t('save.succeed'),
|
||||
description: t('setting.notify.template.save.succeed'),
|
||||
title: t("common.save.succeeded.message"),
|
||||
description: t("settings.notification.template.saved.message"),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ const NotifyTemplate = () => {
|
||||
/>
|
||||
|
||||
<div className="text-muted-foreground text-sm mt-1">
|
||||
{t('setting.notify.template.variables.tips.title')}
|
||||
{t("settings.notification.template.variables.tips.title")}
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
@@ -94,10 +94,10 @@ const NotifyTemplate = () => {
|
||||
}}
|
||||
></Textarea>
|
||||
<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 className="flex justify-end mt-2">
|
||||
<Button onClick={handleSaveClick}>{t('save')}</Button>
|
||||
<Button onClick={handleSaveClick}>{t("common.save")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -50,7 +50,7 @@ const Telegram = () => {
|
||||
const data = getDetailTelegram();
|
||||
setTelegram({
|
||||
id: config.id ?? "",
|
||||
name: "telegram",
|
||||
name: "common.provider.telegram",
|
||||
data,
|
||||
});
|
||||
}, [config]);
|
||||
@@ -72,15 +72,17 @@ const Telegram = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t('save.succeed'),
|
||||
description: t('setting.notify.config.save.succeed'),
|
||||
title: t("common.save.succeeded.message"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
|
||||
toast({
|
||||
title: t('save.failed'),
|
||||
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
||||
title: t("common.save.failed.message"),
|
||||
description: `${t(
|
||||
"settings.notification.config.failed.message"
|
||||
)}: ${msg}`,
|
||||
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 className="flex justify-end mt-2">
|
||||
@@ -139,7 +143,7 @@ const Telegram = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t('save')}
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { update } from "@/repository/settings";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { useToast } from "../ui/use-toast";
|
||||
import { isValidURL } from "@/lib/url";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type WebhookSetting = {
|
||||
id: string;
|
||||
@@ -61,8 +61,8 @@ const Webhook = () => {
|
||||
webhook.data.url = webhook.data.url.trim();
|
||||
if (!isValidURL(webhook.data.url)) {
|
||||
toast({
|
||||
title: t('save.failed'),
|
||||
description: t('setting.notify.config.save.failed.url.not.valid'),
|
||||
title: t("common.save.failed.message"),
|
||||
description: t("settings.notification.url.errmsg.invalid"),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
@@ -81,15 +81,17 @@ const Webhook = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t('save.succeed'),
|
||||
description: t('setting.notify.config.save.succeed'),
|
||||
title: t("common.save.succeeded.message"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
|
||||
toast({
|
||||
title: t('save.failed'),
|
||||
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
|
||||
title: t("common.save.failed.message"),
|
||||
description: `${t(
|
||||
"settings.notification.config.failed.message"
|
||||
)}: ${msg}`,
|
||||
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 className="flex justify-end mt-2">
|
||||
@@ -134,7 +138,7 @@ const Webhook = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t('save')}
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from "react"
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
import * as React from "react";
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
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<
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
@@ -15,8 +15,8 @@ const AccordionItem = React.forwardRef<
|
||||
className={cn("border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AccordionItem.displayName = "AccordionItem"
|
||||
));
|
||||
AccordionItem.displayName = "AccordionItem";
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
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" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
))
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
@@ -49,8 +49,8 @@ const AccordionContent = React.forwardRef<
|
||||
>
|
||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||
</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 { cva, type VariantProps } from "class-variance-authority"
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
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",
|
||||
@@ -17,7 +17,7 @@ const alertVariants = cva(
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
@@ -29,8 +29,8 @@ const Alert = React.forwardRef<
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
));
|
||||
Alert.displayName = "Alert";
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
@@ -41,8 +41,8 @@ const AlertTitle = React.forwardRef<
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
));
|
||||
AlertTitle.displayName = "AlertTitle";
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
@@ -53,7 +53,7 @@ const AlertDescription = React.forwardRef<
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...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,
|
||||
...props
|
||||
}: React.ComponentProps<typeof PaginationLink>) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<PaginationLink
|
||||
@@ -74,9 +74,9 @@ const PaginationPrevious = ({
|
||||
{...props}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
<span>{t('pagination.prev')}</span>
|
||||
<span>{t("common.pagination.prev")}</span>
|
||||
</PaginationLink>
|
||||
)
|
||||
);
|
||||
};
|
||||
PaginationPrevious.displayName = "PaginationPrevious";
|
||||
|
||||
@@ -84,7 +84,7 @@ const PaginationNext = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof PaginationLink>) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<PaginationLink
|
||||
@@ -93,26 +93,30 @@ const PaginationNext = ({
|
||||
className={cn("gap-1 pr-2.5", className)}
|
||||
{...props}
|
||||
>
|
||||
<span>{t('pagination.next')}</span>
|
||||
<span>{t("common.pagination.next")}</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</PaginationLink>
|
||||
)
|
||||
);
|
||||
};
|
||||
PaginationNext.displayName = "PaginationNext";
|
||||
|
||||
const PaginationEllipsis = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) => (
|
||||
}: React.ComponentProps<"span">) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<span className="sr-only">More pages</span>
|
||||
<span className="sr-only">{t("common.pagination.more")}</span>
|
||||
</span>
|
||||
);
|
||||
);
|
||||
};
|
||||
PaginationEllipsis.displayName = "PaginationEllipsis";
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,76 +1,73 @@
|
||||
// Inspired by react-hot-toast library
|
||||
import * as React from "react"
|
||||
import * as React from "react";
|
||||
|
||||
import type {
|
||||
ToastActionElement,
|
||||
ToastProps,
|
||||
} from "@/components/ui/toast"
|
||||
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
|
||||
|
||||
const TOAST_LIMIT = 1
|
||||
const TOAST_REMOVE_DELAY = 1000000
|
||||
const TOAST_LIMIT = 1;
|
||||
const TOAST_REMOVE_DELAY = 1000000;
|
||||
|
||||
type ToasterToast = ToastProps & {
|
||||
id: string
|
||||
title?: React.ReactNode
|
||||
description?: React.ReactNode
|
||||
action?: ToastActionElement
|
||||
}
|
||||
id: string;
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
action?: ToastActionElement;
|
||||
};
|
||||
|
||||
const actionTypes = {
|
||||
ADD_TOAST: "ADD_TOAST",
|
||||
UPDATE_TOAST: "UPDATE_TOAST",
|
||||
DISMISS_TOAST: "DISMISS_TOAST",
|
||||
REMOVE_TOAST: "REMOVE_TOAST",
|
||||
} as const
|
||||
} as const;
|
||||
|
||||
let count = 0
|
||||
let count = 0;
|
||||
|
||||
function genId() {
|
||||
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
||||
return count.toString()
|
||||
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
||||
return count.toString();
|
||||
}
|
||||
|
||||
type ActionType = typeof actionTypes
|
||||
type ActionType = typeof actionTypes;
|
||||
|
||||
type Action =
|
||||
| {
|
||||
type: ActionType["ADD_TOAST"]
|
||||
toast: ToasterToast
|
||||
type: ActionType["ADD_TOAST"];
|
||||
toast: ToasterToast;
|
||||
}
|
||||
| {
|
||||
type: ActionType["UPDATE_TOAST"]
|
||||
toast: Partial<ToasterToast>
|
||||
type: ActionType["UPDATE_TOAST"];
|
||||
toast: Partial<ToasterToast>;
|
||||
}
|
||||
| {
|
||||
type: ActionType["DISMISS_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
type: ActionType["DISMISS_TOAST"];
|
||||
toastId?: ToasterToast["id"];
|
||||
}
|
||||
| {
|
||||
type: ActionType["REMOVE_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
type: ActionType["REMOVE_TOAST"];
|
||||
toastId?: ToasterToast["id"];
|
||||
};
|
||||
|
||||
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) => {
|
||||
if (toastTimeouts.has(toastId)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toastTimeouts.delete(toastId)
|
||||
toastTimeouts.delete(toastId);
|
||||
dispatch({
|
||||
type: "REMOVE_TOAST",
|
||||
toastId: toastId,
|
||||
})
|
||||
}, TOAST_REMOVE_DELAY)
|
||||
});
|
||||
}, TOAST_REMOVE_DELAY);
|
||||
|
||||
toastTimeouts.set(toastId, timeout)
|
||||
}
|
||||
toastTimeouts.set(toastId, timeout);
|
||||
};
|
||||
|
||||
export const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
@@ -78,7 +75,7 @@ export const reducer = (state: State, action: Action): State => {
|
||||
return {
|
||||
...state,
|
||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||
}
|
||||
};
|
||||
|
||||
case "UPDATE_TOAST":
|
||||
return {
|
||||
@@ -86,19 +83,19 @@ export const reducer = (state: State, action: Action): State => {
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
case "DISMISS_TOAST": {
|
||||
const { toastId } = action
|
||||
const { toastId } = action;
|
||||
|
||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||
// but I'll keep it here for simplicity
|
||||
if (toastId) {
|
||||
addToRemoveQueue(toastId)
|
||||
addToRemoveQueue(toastId);
|
||||
} else {
|
||||
state.toasts.forEach((toast) => {
|
||||
addToRemoveQueue(toast.id)
|
||||
})
|
||||
addToRemoveQueue(toast.id);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -111,44 +108,44 @@ export const reducer = (state: State, action: Action): State => {
|
||||
}
|
||||
: t
|
||||
),
|
||||
}
|
||||
};
|
||||
}
|
||||
case "REMOVE_TOAST":
|
||||
if (action.toastId === undefined) {
|
||||
return {
|
||||
...state,
|
||||
toasts: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
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) {
|
||||
memoryState = reducer(memoryState, action)
|
||||
memoryState = reducer(memoryState, action);
|
||||
listeners.forEach((listener) => {
|
||||
listener(memoryState)
|
||||
})
|
||||
listener(memoryState);
|
||||
});
|
||||
}
|
||||
|
||||
type Toast = Omit<ToasterToast, "id">
|
||||
type Toast = Omit<ToasterToast, "id">;
|
||||
|
||||
function toast({ ...props }: Toast) {
|
||||
const id = genId()
|
||||
const id = genId();
|
||||
|
||||
const update = (props: ToasterToast) =>
|
||||
dispatch({
|
||||
type: "UPDATE_TOAST",
|
||||
toast: { ...props, id },
|
||||
})
|
||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
||||
});
|
||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
||||
|
||||
dispatch({
|
||||
type: "ADD_TOAST",
|
||||
@@ -157,36 +154,36 @@ function toast({ ...props }: Toast) {
|
||||
id,
|
||||
open: true,
|
||||
onOpenChange: (open) => {
|
||||
if (!open) dismiss()
|
||||
if (!open) dismiss();
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
return {
|
||||
id: id,
|
||||
dismiss,
|
||||
update,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function useToast() {
|
||||
const [state, setState] = React.useState<State>(memoryState)
|
||||
const [state, setState] = React.useState<State>(memoryState);
|
||||
|
||||
React.useEffect(() => {
|
||||
listeners.push(setState)
|
||||
listeners.push(setState);
|
||||
return () => {
|
||||
const index = listeners.indexOf(setState)
|
||||
const index = listeners.indexOf(setState);
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1)
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}, [state])
|
||||
};
|
||||
}, [state]);
|
||||
|
||||
return {
|
||||
...state,
|
||||
toast,
|
||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { useToast, toast }
|
||||
export { useToast, toast };
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const accessTypeMap: Map<string, [string, string]> = new Map([
|
||||
["tencent", ["tencent", "/imgs/providers/tencent.svg"]],
|
||||
["aliyun", ["aliyun", "/imgs/providers/aliyun.svg"]],
|
||||
["cloudflare", ["cloudflare", "/imgs/providers/cloudflare.svg"]],
|
||||
["namesilo", ["namesilo", "/imgs/providers/namesilo.svg"]],
|
||||
["godaddy", ["go.daddy", "/imgs/providers/godaddy.svg"]],
|
||||
["qiniu", ["qiniu", "/imgs/providers/qiniu.svg"]],
|
||||
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
|
||||
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
|
||||
["local", ["local.deployment", "/imgs/providers/local.svg"]],
|
||||
["aliyun", ["common.provider.aliyun", "/imgs/providers/aliyun.svg"]],
|
||||
["tencent", ["common.provider.tencent", "/imgs/providers/tencent.svg"]],
|
||||
["huaweicloud", ["common.provider.huaweicloud", "/imgs/providers/huaweicloud.svg"]],
|
||||
["qiniu", ["common.provider.qiniu", "/imgs/providers/qiniu.svg"]],
|
||||
["cloudflare", ["common.provider.cloudflare", "/imgs/providers/cloudflare.svg"]],
|
||||
["namesilo", ["common.provider.namesilo", "/imgs/providers/namesilo.svg"]],
|
||||
["godaddy", ["common.provider.godaddy", "/imgs/providers/godaddy.svg"]],
|
||||
["local", ["common.provider.local", "/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) => {
|
||||
@@ -20,15 +21,16 @@ export const accessFormType = z.union(
|
||||
[
|
||||
z.literal("aliyun"),
|
||||
z.literal("tencent"),
|
||||
z.literal("ssh"),
|
||||
z.literal("webhook"),
|
||||
z.literal("cloudflare"),
|
||||
z.literal("huaweicloud"),
|
||||
z.literal("qiniu"),
|
||||
z.literal("cloudflare"),
|
||||
z.literal("namesilo"),
|
||||
z.literal("godaddy"),
|
||||
z.literal("local"),
|
||||
z.literal("ssh"),
|
||||
z.literal("webhook"),
|
||||
],
|
||||
{ message: "access.not.empty" }
|
||||
{ message: "access.authorization.form.type.placeholder" }
|
||||
);
|
||||
|
||||
type AccessUsage = "apply" | "deploy" | "all";
|
||||
@@ -40,86 +42,88 @@ export type Access = {
|
||||
usage: AccessUsage;
|
||||
group?: string;
|
||||
config:
|
||||
| TencentConfig
|
||||
| AliyunConfig
|
||||
| SSHConfig
|
||||
| WebhookConfig
|
||||
| CloudflareConfig
|
||||
| TencentConfig
|
||||
| HuaweicloudConfig
|
||||
| QiniuConfig
|
||||
| CloudflareConfig
|
||||
| NamesiloConfig
|
||||
| GodaddyConfig
|
||||
| LocalConfig;
|
||||
|
||||
| LocalConfig
|
||||
| SSHConfig
|
||||
| WebhookConfig;
|
||||
deleted?: string;
|
||||
created?: 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 = {
|
||||
accessKeyId: 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 = {
|
||||
apiKey: string;
|
||||
};
|
||||
|
||||
export type GodaddyConfig = {
|
||||
apiKey: string;
|
||||
apiSecret: string;
|
||||
};
|
||||
|
||||
export type LocalConfig = Record<string, string>;
|
||||
|
||||
export type SSHConfig = {
|
||||
host: string;
|
||||
port: string;
|
||||
preCommand?: string;
|
||||
command: string;
|
||||
username: string;
|
||||
password?: string;
|
||||
key?: string;
|
||||
keyFile?: string;
|
||||
certPath: string;
|
||||
keyPath: string;
|
||||
};
|
||||
|
||||
export type LocalConfig = {
|
||||
command: string;
|
||||
certPath: string;
|
||||
keyPath: string;
|
||||
export type WebhookConfig = {
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const getUsageByConfigType = (configType: string): AccessUsage => {
|
||||
switch (configType) {
|
||||
case "aliyun":
|
||||
case "tencent":
|
||||
case "huaweicloud":
|
||||
return "all";
|
||||
case "ssh":
|
||||
case "webhook":
|
||||
|
||||
case "qiniu":
|
||||
case "local":
|
||||
case "ssh":
|
||||
case "webhook":
|
||||
return "deploy";
|
||||
|
||||
case "cloudflare":
|
||||
case "namesilo":
|
||||
case "godaddy":
|
||||
return "apply";
|
||||
|
||||
default:
|
||||
return "all";
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Deployment, Pahse } from "./deployment";
|
||||
|
||||
export type Domain = {
|
||||
id: string;
|
||||
id?: string;
|
||||
domain: string;
|
||||
email?: string;
|
||||
crontab: string;
|
||||
access: string;
|
||||
targetAccess?: string;
|
||||
targetType: string;
|
||||
targetType?: string;
|
||||
expiredAt?: string;
|
||||
phase?: Pahse;
|
||||
phaseSuccess?: boolean;
|
||||
@@ -31,10 +31,20 @@ export type Domain = {
|
||||
deployConfig?: DeployConfig[];
|
||||
};
|
||||
|
||||
export type KVType = {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type DeployConfig = {
|
||||
id?: string;
|
||||
access: string;
|
||||
type: string;
|
||||
config?: Record<string, string>;
|
||||
config?: {
|
||||
[key: string]: string;
|
||||
} & {
|
||||
variables?: KVType[];
|
||||
};
|
||||
};
|
||||
|
||||
export type ApplyConfig = {
|
||||
@@ -56,14 +66,14 @@ export const getLastDeployment = (domain: Domain): Deployment | undefined => {
|
||||
};
|
||||
|
||||
export const targetTypeMap: Map<string, [string, string]> = new Map([
|
||||
["aliyun-cdn", ["aliyun.cdn", "/imgs/providers/aliyun.svg"]],
|
||||
["aliyun-oss", ["aliyun.oss", "/imgs/providers/aliyun.svg"]],
|
||||
["aliyun-dcdn", ["aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
|
||||
["tencent-cdn", ["tencent.cdn", "/imgs/providers/tencent.svg"]],
|
||||
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
|
||||
["qiniu-cdn", ["qiniu.cdn", "/imgs/providers/qiniu.svg"]],
|
||||
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
|
||||
["local", ["local.deployment", "/imgs/providers/local.svg"]],
|
||||
["aliyun-oss", ["common.provider.aliyun.oss", "/imgs/providers/aliyun.svg"]],
|
||||
["aliyun-cdn", ["common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"]],
|
||||
["aliyun-dcdn", ["common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
|
||||
["tencent-cdn", ["common.provider.tencent.cdn", "/imgs/providers/tencent.svg"]],
|
||||
["qiniu-cdn", ["common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"]],
|
||||
["local", ["common.provider.local", "/imgs/providers/local.svg"]],
|
||||
["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]],
|
||||
["webhook", ["common.provider.webhook", "/imgs/providers/webhook.svg"]],
|
||||
]);
|
||||
|
||||
export const targetTypeKeys = Array.from(targetTypeMap.keys());
|
||||
|
||||
@@ -50,8 +50,8 @@ export type NotifyChannelWebhook = {
|
||||
};
|
||||
|
||||
export const defaultNotifyTemplate: NotifyTemplate = {
|
||||
title: "您有{COUNT}张证书即将过期",
|
||||
content: "有{COUNT}张证书即将过期,域名分别为{DOMAINS},请保持关注!",
|
||||
title: "您有 {COUNT} 张证书即将过期",
|
||||
content: "有 {COUNT} 张证书即将过期,域名分别为 {DOMAINS},请保持关注!",
|
||||
};
|
||||
|
||||
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 { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
|
||||
import resources from './locales'
|
||||
import resources from "./locales";
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'zh',
|
||||
fallbackLng: "zh",
|
||||
debug: true,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
backend: {
|
||||
loadPath: '/locales/{{lng}}.json',
|
||||
}
|
||||
loadPath: "/locales/{{lng}}.json",
|
||||
},
|
||||
});
|
||||
|
||||
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 en from './en.json'
|
||||
import zh from "./zh";
|
||||
import en from "./en";
|
||||
|
||||
const resources: Resource = {
|
||||
zh: {
|
||||
name: '简体中文',
|
||||
translation: zh
|
||||
name: "简体中文",
|
||||
translation: zh,
|
||||
},
|
||||
en: {
|
||||
name: 'English',
|
||||
translation: en
|
||||
}
|
||||
}
|
||||
name: "English",
|
||||
translation: en,
|
||||
},
|
||||
};
|
||||
|
||||
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,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||
@@ -30,7 +28,7 @@ import Version from "@/components/certimate/Version";
|
||||
export default function Dashboard() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!getPb().authStore.isValid || !getPb().authStore.isAdmin) {
|
||||
return <Navigate to="/login" />;
|
||||
@@ -73,7 +71,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<Home className="h-4 w-4" />
|
||||
{t('dashboard')}
|
||||
{t("dashboard.page.title")}
|
||||
</Link>
|
||||
<Link
|
||||
to="/domains"
|
||||
@@ -83,7 +81,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<Earth className="h-4 w-4" />
|
||||
{t('domain.management.name')}
|
||||
{t("domain.page.title")}
|
||||
</Link>
|
||||
<Link
|
||||
to="/access"
|
||||
@@ -93,7 +91,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<Server className="h-4 w-4" />
|
||||
{t('menu.auth.management')}
|
||||
{t("access.page.title")}
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
@@ -104,7 +102,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<History className="h-4 w-4" />
|
||||
{t('deployment.log.name')}
|
||||
{t("history.page.title")}
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -141,7 +139,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<Home className="h-5 w-5" />
|
||||
{t('dashboard')}
|
||||
{t("dashboard.page.title")}
|
||||
</Link>
|
||||
<Link
|
||||
to="/domains"
|
||||
@@ -151,7 +149,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<Earth className="h-5 w-5" />
|
||||
{t('domain.management.name')}
|
||||
{t("domain.page.title")}
|
||||
</Link>
|
||||
<Link
|
||||
to="/access"
|
||||
@@ -161,7 +159,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<Server className="h-5 w-5" />
|
||||
{t('menu.auth.management')}
|
||||
{t("access.page.title")}
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
@@ -172,7 +170,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
>
|
||||
<History className="h-5 w-5" />
|
||||
{t('deployment.log.name')}
|
||||
{t("history.page.title")}
|
||||
</Link>
|
||||
</nav>
|
||||
</SheetContent>
|
||||
@@ -192,15 +190,11 @@ export default function Dashboard() {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>{t('account')}</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItem onClick={handleSettingClick}>
|
||||
{t('setting')}
|
||||
{t("common.menu.settings")}
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onClick={handleLogoutClick}>
|
||||
{t('logout')}
|
||||
{t("common.menu.logout")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -22,7 +22,7 @@ const SettingLayout = () => {
|
||||
<div>
|
||||
<Toaster />
|
||||
<div className="text-muted-foreground border-b dark:border-stone-500 py-5">
|
||||
{t("setting")}
|
||||
{t("settings.page.title")}
|
||||
</div>
|
||||
<div className="w-full mt-5 p-0 md:p-3 flex justify-center">
|
||||
<Tabs defaultValue="account" className="w-full" value={tabValue}>
|
||||
@@ -35,8 +35,9 @@ const SettingLayout = () => {
|
||||
className="px-5"
|
||||
>
|
||||
<UserRound size={14} />
|
||||
<div className="ml-1">{t("account")}</div>
|
||||
<div className="ml-1">{t("settings.account.tab")}</div>
|
||||
</TabsTrigger>
|
||||
|
||||
<TabsTrigger
|
||||
value="password"
|
||||
onClick={() => {
|
||||
@@ -45,7 +46,7 @@ const SettingLayout = () => {
|
||||
className="px-5"
|
||||
>
|
||||
<KeyRound size={14} />
|
||||
<div className="ml-1">{t("password")}</div>
|
||||
<div className="ml-1">{t("settings.password.tab")}</div>
|
||||
</TabsTrigger>
|
||||
|
||||
<TabsTrigger
|
||||
@@ -56,8 +57,9 @@ const SettingLayout = () => {
|
||||
className="px-5"
|
||||
>
|
||||
<Megaphone size={14} />
|
||||
<div className="ml-1">{t("setting.notify.menu")}</div>
|
||||
<div className="ml-1">{t("settings.notification.tab")}</div>
|
||||
</TabsTrigger>
|
||||
|
||||
<TabsTrigger
|
||||
value="ssl-provider"
|
||||
onClick={() => {
|
||||
@@ -66,7 +68,7 @@ const SettingLayout = () => {
|
||||
className="px-5"
|
||||
>
|
||||
<ShieldCheck size={14} />
|
||||
<div className="ml-1">{t("ca")}</div>
|
||||
<div className="ml-1">{t("settings.ca.tab")}</div>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value={tabValue}>
|
||||
|
||||
@@ -16,10 +16,12 @@ import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent, AlertDialogDescription, AlertDialogFooter,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog.tsx";
|
||||
|
||||
const Access = () => {
|
||||
@@ -56,9 +58,9 @@ const Access = () => {
|
||||
return (
|
||||
<div className="">
|
||||
<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" ? (
|
||||
<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>} />
|
||||
)}
|
||||
@@ -76,7 +78,7 @@ const Access = () => {
|
||||
handleTabItemClick("access");
|
||||
}}
|
||||
>
|
||||
{t("access.management")}
|
||||
{t("access.authorization.tab")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="access_group"
|
||||
@@ -84,7 +86,7 @@ const Access = () => {
|
||||
handleTabItemClick("access_group");
|
||||
}}
|
||||
>
|
||||
{t("access.group.management")}
|
||||
{t("access.group.tab")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="access">
|
||||
@@ -95,10 +97,10 @@ const Access = () => {
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||
{t("access.empty")}
|
||||
{t("access.authorization.nodata")}
|
||||
</div>
|
||||
<AccessEdit
|
||||
trigger={<Button>{t("access.add")}</Button>}
|
||||
trigger={<Button>{t("access.authorization.add")}</Button>}
|
||||
op="add"
|
||||
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="w-48">{t("name")}</div>
|
||||
<div className="w-48">{t("access.type")}</div>
|
||||
<div className="w-48">{t("common.text.name")}</div>
|
||||
<div className="w-48">{t("common.text.provider")}</div>
|
||||
|
||||
<div className="w-60">{t("create.time")}</div>
|
||||
<div className="w-60">{t("update.time")}</div>
|
||||
<div className="grow">{t("operation")}</div>
|
||||
</div>
|
||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||
{t("access.list")}
|
||||
<div className="w-60">{t("common.text.created_at")}</div>
|
||||
<div className="w-60">{t("common.text.updated_at")}</div>
|
||||
<div className="grow">{t("common.text.operations")}</div>
|
||||
</div>
|
||||
{accesses
|
||||
.filter((item) => {
|
||||
@@ -140,18 +139,16 @@ const Access = () => {
|
||||
</div>
|
||||
|
||||
<div className="sm:w-60 w-full pt-1 sm:pt-0 flex items-center">
|
||||
{t("created.in")}{" "}
|
||||
{access.created && convertZulu2Beijing(access.created)}
|
||||
</div>
|
||||
<div className="sm:w-60 w-full pt-1 sm:pt-0 flex items-center">
|
||||
{t("updated.in")}{" "}
|
||||
{access.updated && convertZulu2Beijing(access.updated)}
|
||||
</div>
|
||||
<div className="flex items-center grow justify-start pt-1 sm:pt-0">
|
||||
<AccessEdit
|
||||
trigger={
|
||||
<Button variant={"link"} className="p-0">
|
||||
{t("edit")}
|
||||
{t("common.edit")}
|
||||
</Button>
|
||||
}
|
||||
op="edit"
|
||||
@@ -161,7 +158,7 @@ const Access = () => {
|
||||
<AccessEdit
|
||||
trigger={
|
||||
<Button variant={"link"} className="p-0">
|
||||
{t("copy")}
|
||||
{t("common.copy")}
|
||||
</Button>
|
||||
}
|
||||
op="copy"
|
||||
@@ -170,29 +167,29 @@ const Access = () => {
|
||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant={"link"} size={"sm"}>
|
||||
{t('delete')}
|
||||
<Button variant={"link"} className="p-0">
|
||||
{t("common.delete")}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="dark:text-gray-200">
|
||||
{t('access.group.delete')}
|
||||
{t("access.authorization.delete")}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t('access.delete.confirm')}
|
||||
{t("access.authorization.delete.confirm")}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel className="dark:text-gray-200">
|
||||
{t('cancel')}
|
||||
{t("common.cancel")}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
handleDelete(access);
|
||||
}}
|
||||
>
|
||||
{t('confirm')}
|
||||
{t("common.confirm")}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
|
||||
@@ -57,7 +57,7 @@ const Dashboard = () => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<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 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">
|
||||
@@ -66,7 +66,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground font-semibold">
|
||||
{t("dashboard.all")}
|
||||
{t("dashboard.statistics.all")}
|
||||
</div>
|
||||
<div className="flex items-baseline">
|
||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||
@@ -79,7 +79,7 @@ const Dashboard = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||
{t("dashboard.unit")}
|
||||
{t("dashboard.statistics.unit")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,7 +91,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground font-semibold">
|
||||
{t("dashboard.near.expired")}
|
||||
{t("dashboard.statistics.near_expired")}
|
||||
</div>
|
||||
<div className="flex items-baseline">
|
||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||
@@ -104,7 +104,7 @@ const Dashboard = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||
{t("dashboard.unit")}
|
||||
{t("dashboard.statistics.unit")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -120,7 +120,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground font-semibold">
|
||||
{t("dashboard.enabled")}
|
||||
{t("dashboard.statistics.enabled")}
|
||||
</div>
|
||||
<div className="flex items-baseline">
|
||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||
@@ -133,7 +133,7 @@ const Dashboard = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||
{t("dashboard.unit")}
|
||||
{t("dashboard.statistics.unit")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -145,7 +145,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground font-semibold">
|
||||
{t("dashboard.not.enabled")}
|
||||
{t("dashboard.statistics.disabled")}
|
||||
</div>
|
||||
<div className="flex items-baseline">
|
||||
<div className="text-3xl text-stone-700 dark:text-stone-200">
|
||||
@@ -161,28 +161,32 @@ const Dashboard = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1 text-stone-700 dark:text-stone-200">
|
||||
{t("dashboard.unit")}
|
||||
{t("dashboard.statistics.unit")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-4">
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-muted-foreground mt-5 text-sm">
|
||||
{t("deployment.log.name")}
|
||||
{t("dashboard.history")}
|
||||
</div>
|
||||
|
||||
{deployments?.length == 0 ? (
|
||||
<>
|
||||
<Alert className="max-w-[40em] mt-10">
|
||||
<AlertTitle>{t("no.data")}</AlertTitle>
|
||||
<AlertTitle>{t("common.text.nodata")}</AlertTitle>
|
||||
<AlertDescription>
|
||||
<div className="flex items-center mt-5">
|
||||
<div>
|
||||
<Smile className="text-yellow-400" size={36} />
|
||||
</div>
|
||||
<div className="ml-2"> {t("deployment.log.empty")}</div>
|
||||
<div className="ml-2"> {t("history.nodata")}</div>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-end">
|
||||
<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="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-56">{t("deployment.log.stage")}</div>
|
||||
<div className="w-24">{t("history.props.status")}</div>
|
||||
<div className="w-56">{t("history.props.stage")}</div>
|
||||
<div className="w-56 sm:ml-2 text-center">
|
||||
{t("deployment.log.last.execution.time")}
|
||||
{t("history.props.last_execution_time")}
|
||||
</div>
|
||||
|
||||
<div className="grow">{t("operation")}</div>
|
||||
</div>
|
||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||
{t("deployment.log.name")}
|
||||
<div className="grow">{t("common.text.operations")}</div>
|
||||
</div>
|
||||
|
||||
{deployments?.map((deployment) => (
|
||||
@@ -244,14 +245,14 @@ const Dashboard = () => {
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant={"link"} className="p-0">
|
||||
{t("deployment.log.detail.button.text")}
|
||||
{t("history.log")}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="sm:max-w-5xl">
|
||||
<SheetHeader>
|
||||
<SheetTitle>
|
||||
{deployment.expand.domain?.domain}-{deployment.id}
|
||||
{t("deployment.log.detail")}
|
||||
{t("history.log")}
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">
|
||||
|
||||
@@ -23,36 +23,43 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { useConfig } from "@/providers/config";
|
||||
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 { ClientResponseError } from "pocketbase";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
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 { AccessEdit } from "@/components/certimate/AccessEdit";
|
||||
import { accessTypeMap } from "@/domain/access";
|
||||
import EmailsEdit from "@/components/certimate/EmailsEdit";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { EmailsSetting } from "@/domain/settings";
|
||||
import { useTranslation } from "react-i18next";
|
||||
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 {
|
||||
config: { accesses, emails, accessGroups },
|
||||
config: { accesses, emails },
|
||||
} = useConfig();
|
||||
|
||||
const [domain, setDomain] = useState<Domain>();
|
||||
const [domain, setDomain] = useState<Domain>({} as Domain);
|
||||
|
||||
const location = useLocation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [tab, setTab] = useState<"base" | "advance">("base");
|
||||
|
||||
const [targetType, setTargetType] = useState(domain ? domain.targetType : "");
|
||||
const [tab, setTab] = useState<"apply" | "deploy">("apply");
|
||||
|
||||
useEffect(() => {
|
||||
// Parsing query parameters
|
||||
@@ -62,7 +69,6 @@ const Edit = () => {
|
||||
const fetchData = async () => {
|
||||
const data = await get(id);
|
||||
setDomain(data);
|
||||
setTargetType(data.targetType);
|
||||
};
|
||||
fetchData();
|
||||
}
|
||||
@@ -71,19 +77,14 @@ const Edit = () => {
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
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]+$/, {
|
||||
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(),
|
||||
timeout: z.number().optional(),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@@ -93,11 +94,8 @@ const Edit = () => {
|
||||
domain: "",
|
||||
email: "",
|
||||
access: "",
|
||||
targetAccess: "",
|
||||
targetType: "",
|
||||
variables: "",
|
||||
group: "",
|
||||
nameservers: "",
|
||||
timeout: 60,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -106,74 +104,81 @@ const Edit = () => {
|
||||
form.reset({
|
||||
id: domain.id,
|
||||
domain: domain.domain,
|
||||
email: domain.email,
|
||||
access: domain.access,
|
||||
targetAccess: domain.targetAccess,
|
||||
targetType: domain.targetType,
|
||||
variables: domain.variables,
|
||||
group: domain.group,
|
||||
nameservers: domain.nameservers,
|
||||
email: domain.applyConfig?.email,
|
||||
access: domain.applyConfig?.access,
|
||||
nameservers: domain.applyConfig?.nameservers,
|
||||
timeout: domain.applyConfig?.timeout,
|
||||
});
|
||||
}
|
||||
}, [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 navigate = useNavigate();
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const group = data.group == "emptyId" ? "" : data.group;
|
||||
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;
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
const req: Domain = {
|
||||
id: data.id as string,
|
||||
crontab: "0 0 * * *",
|
||||
domain: data.domain,
|
||||
email: data.email,
|
||||
access: data.access,
|
||||
group: group,
|
||||
targetAccess: targetAccess,
|
||||
targetType: data.targetType,
|
||||
variables: data.variables,
|
||||
applyConfig: {
|
||||
email: data.email ?? "",
|
||||
access: data.access,
|
||||
nameservers: data.nameservers,
|
||||
timeout: data.timeout,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
await save(req);
|
||||
let description = t("domain.management.edit.succeed.tips");
|
||||
const resp = await save(req);
|
||||
let description = t("domain.application.form.domain.changed.message");
|
||||
if (req.id == "") {
|
||||
description = t("domain.management.add.succeed.tips");
|
||||
description = t("domain.application.form.domain.added.message");
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t("succeed"),
|
||||
title: t("common.save.succeeded.message"),
|
||||
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) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@@ -195,45 +200,74 @@ const Edit = () => {
|
||||
<div className="">
|
||||
<Toaster />
|
||||
<div className=" h-5 text-muted-foreground">
|
||||
<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 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
|
||||
className={cn(
|
||||
"cursor-pointer text-right",
|
||||
tab === "base" ? "text-primary" : ""
|
||||
tab === "apply" ? "text-primary" : ""
|
||||
)}
|
||||
onClick={() => {
|
||||
setTab("base");
|
||||
setTab("apply");
|
||||
}}
|
||||
>
|
||||
{t("basic.setting")}
|
||||
{t("domain.application.tab")}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"cursor-pointer text-right",
|
||||
tab === "advance" ? "text-primary" : ""
|
||||
tab === "deploy" ? "text-primary" : ""
|
||||
)}
|
||||
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 className="w-full md:w-[35em] bg-gray-100 dark:bg-gray-900 p-5 rounded mt-3 md:mt-0">
|
||||
<div className="flex flex-col">
|
||||
<div
|
||||
className={cn(
|
||||
"w-full md:w-[35em] p-5 rounded mt-3 md:mt-0",
|
||||
tab == "deploy" && "hidden"
|
||||
)}
|
||||
>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-8 dark:text-stone-200"
|
||||
>
|
||||
{/* 域名 */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="domain"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={tab != "base"}>
|
||||
<FormItem>
|
||||
<>
|
||||
<StringList
|
||||
value={field.value}
|
||||
@@ -243,26 +277,27 @@ const Edit = () => {
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* 邮箱 */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={tab != "base"}>
|
||||
<FormItem>
|
||||
<FormLabel className="flex w-full justify-between">
|
||||
<div>
|
||||
{t("email") +
|
||||
t("domain.management.edit.email.description")}
|
||||
{t("domain.application.form.email.label") +
|
||||
" " +
|
||||
t("domain.application.form.email.tips")}
|
||||
</div>
|
||||
<EmailsEdit
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t("add")}
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@@ -278,13 +313,15 @@ const Edit = () => {
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"domain.management.edit.email.not.empty.message"
|
||||
"domain.application.form.email.errmsg.empty"
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>{t("email.list")}</SelectLabel>
|
||||
<SelectLabel>
|
||||
{t("domain.application.form.email.list")}
|
||||
</SelectLabel>
|
||||
{(emails.content as EmailsSetting).emails.map(
|
||||
(item) => (
|
||||
<SelectItem key={item} value={item}>
|
||||
@@ -301,20 +338,19 @@ const Edit = () => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* 授权 */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="access"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={tab != "base"}>
|
||||
<FormItem>
|
||||
<FormLabel className="flex w-full justify-between">
|
||||
<div>
|
||||
{t("domain.management.edit.dns.access.label")}
|
||||
</div>
|
||||
<div>{t("domain.application.form.access.label")}</div>
|
||||
<AccessEdit
|
||||
trigger={
|
||||
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||
<Plus size={14} />
|
||||
{t("add")}
|
||||
{t("common.add")}
|
||||
</div>
|
||||
}
|
||||
op="add"
|
||||
@@ -331,14 +367,14 @@ const Edit = () => {
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"domain.management.edit.access.not.empty.message"
|
||||
"domain.application.form.access.placeholder"
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>
|
||||
{t("domain.management.edit.access.label")}
|
||||
{t("domain.application.form.access.list")}
|
||||
</SelectLabel>
|
||||
{accesses
|
||||
.filter((item) => item.usage != "deploy")
|
||||
@@ -366,198 +402,30 @@ const Edit = () => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* 超时时间 */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="targetType"
|
||||
name="timeout"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={tab != "base"}>
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("domain.management.edit.target.type")}
|
||||
{t("domain.application.form.timeout.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
{...field}
|
||||
onValueChange={(value) => {
|
||||
setTargetType(value);
|
||||
form.setValue("targetType", value);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={t(
|
||||
"domain.management.edit.target.type.not.empty.message"
|
||||
"ddomain.application.form.timeout.placeholder"
|
||||
)}
|
||||
/>
|
||||
</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>
|
||||
</SelectItem>
|
||||
{accessGroups
|
||||
.filter((item) => {
|
||||
return (
|
||||
item.expand && item.expand?.access.length > 0
|
||||
onChange={(e) => {
|
||||
form.setValue(
|
||||
"timeout",
|
||||
parseInt(e.target.value)
|
||||
);
|
||||
})
|
||||
.map((item) => (
|
||||
<SelectItem
|
||||
value={item.id ? item.id : ""}
|
||||
key={item.id}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center space-x-2 rounded cursor-pointer"
|
||||
)}
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="variables"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={tab != "advance"}>
|
||||
<FormLabel>{t("variables")}</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder={t(
|
||||
"domain.management.edit.variables.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
className="placeholder:whitespace-pre-wrap"
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
@@ -566,21 +434,19 @@ const Edit = () => {
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* nameservers */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="nameservers"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={tab != "advance"}>
|
||||
<FormLabel>{t("dns")}</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder={t(
|
||||
"domain.management.edit.dns.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
className="placeholder:whitespace-pre-wrap"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormItem>
|
||||
<StringList
|
||||
value={field.value ?? ""}
|
||||
onValueChange={(val: string) => {
|
||||
form.setValue("nameservers", val);
|
||||
}}
|
||||
valueType="dns"
|
||||
></StringList>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -588,11 +454,28 @@ const Edit = () => {
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("save")}</Button>
|
||||
<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>
|
||||
</>
|
||||
|
||||
@@ -113,8 +113,8 @@ const Home = () => {
|
||||
|
||||
const handleRightNowClick = async (domain: Domain) => {
|
||||
try {
|
||||
unsubscribeId(domain.id);
|
||||
subscribeId(domain.id, (resp) => {
|
||||
unsubscribeId(domain.id ?? "");
|
||||
subscribeId(domain.id ?? "", (resp) => {
|
||||
console.log(resp);
|
||||
const updatedDomains = domains.map((domain) => {
|
||||
if (domain.id === resp.id) {
|
||||
@@ -129,15 +129,15 @@ const Home = () => {
|
||||
await save(domain);
|
||||
|
||||
toast.toast({
|
||||
title: t("operation.succeed"),
|
||||
description: t("domain.management.start.deploy.succeed.tips"),
|
||||
title: t("domain.deploy.started.message"),
|
||||
description: t("domain.deploy.started.tips"),
|
||||
});
|
||||
} catch (e) {
|
||||
toast.toast({
|
||||
title: t("domain.management.execution.failed"),
|
||||
title: t("domain.deploy.failed.message"),
|
||||
description: (
|
||||
// 这里的 text 只是占位作用,实际文案在 src/i18n/locales/[lang].json
|
||||
<Trans i18nKey="domain.management.execution.failed.tips">
|
||||
<Trans i18nKey="domain.deploy.failed.tips">
|
||||
text1
|
||||
<Link
|
||||
to={`/history?domain=${domain.id}`}
|
||||
@@ -178,9 +178,7 @@ const Home = () => {
|
||||
<div className="">
|
||||
<Toaster />
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-muted-foreground">
|
||||
{t("domain.management.name")}
|
||||
</div>
|
||||
<div className="text-muted-foreground">{t("domain.page.title")}</div>
|
||||
<Button onClick={handleCreateClick}>{t("domain.add")}</Button>
|
||||
</div>
|
||||
|
||||
@@ -192,7 +190,7 @@ const Home = () => {
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||
{t("domain.management.empty")}
|
||||
{t("domain.nodata")}
|
||||
</div>
|
||||
<Button onClick={handleCreateClick} className="mt-3">
|
||||
{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="w-36">{t("domain")}</div>
|
||||
<div className="w-40">{t("domain.management.expiry.date")}</div>
|
||||
<div className="w-36">{t("common.text.domain")}</div>
|
||||
<div className="w-40">{t("domain.props.expiry")}</div>
|
||||
<div className="w-32">
|
||||
{t("domain.management.last.execution.status")}
|
||||
{t("domain.props.last_execution_status")}
|
||||
</div>
|
||||
<div className="w-64">
|
||||
{t("domain.management.last.execution.stage")}
|
||||
{t("domain.props.last_execution_stage")}
|
||||
</div>
|
||||
<div className="w-40 sm:ml-2">
|
||||
{t("domain.management.last.execution.time")}
|
||||
{t("domain.props.last_execution_time")}
|
||||
</div>
|
||||
<div className="w-24">{t("domain.management.enable")}</div>
|
||||
<div className="grow">{t("operation")}</div>
|
||||
</div>
|
||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||
{t("domain")}
|
||||
<div className="w-24">{t("domain.props.enable")}</div>
|
||||
<div className="grow">{t("common.text.operations")}</div>
|
||||
</div>
|
||||
|
||||
{domains.map((domain) => (
|
||||
@@ -238,10 +233,10 @@ const Home = () => {
|
||||
{domain.expiredAt ? (
|
||||
<>
|
||||
<div>
|
||||
{t("domain.management.expiry.date1", { date: 90 })}
|
||||
{t("domain.props.expiry.date1", { date: 90 })}
|
||||
</div>
|
||||
<div>
|
||||
{t("domain.management.expiry.date2", {
|
||||
{t("domain.props.expiry.date2", {
|
||||
date: getDate(domain.expiredAt),
|
||||
})}
|
||||
</div>
|
||||
@@ -282,13 +277,15 @@ const Home = () => {
|
||||
<Switch
|
||||
checked={domain.enabled}
|
||||
onCheckedChange={() => {
|
||||
handelCheckedChange(domain.id);
|
||||
handelCheckedChange(domain.id ?? "");
|
||||
}}
|
||||
></Switch>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<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>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -298,9 +295,9 @@ const Home = () => {
|
||||
<Button
|
||||
variant={"link"}
|
||||
className="p-0"
|
||||
onClick={() => handleHistoryClick(domain.id)}
|
||||
onClick={() => handleHistoryClick(domain.id ?? "")}
|
||||
>
|
||||
{t("deployment.log.name")}
|
||||
{t("domain.history")}
|
||||
</Button>
|
||||
<Show when={domain.enabled ? true : false}>
|
||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||
@@ -309,7 +306,7 @@ const Home = () => {
|
||||
className="p-0"
|
||||
onClick={() => handleRightNowClick(domain)}
|
||||
>
|
||||
{t("domain.management.start.deploying")}
|
||||
{t("domain.deploy")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
@@ -326,7 +323,7 @@ const Home = () => {
|
||||
className="p-0"
|
||||
onClick={() => handleForceClick(domain)}
|
||||
>
|
||||
{t("domain.management.forced.deployment")}
|
||||
{t("domain.deploy_forced")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
@@ -337,7 +334,7 @@ const Home = () => {
|
||||
className="p-0"
|
||||
onClick={() => handleDownloadClick(domain)}
|
||||
>
|
||||
{t("download")}
|
||||
{t("common.download")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
@@ -347,7 +344,7 @@ const Home = () => {
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant={"link"} className="p-0">
|
||||
{t("delete")}
|
||||
{t("common.delete")}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
@@ -356,17 +353,19 @@ const Home = () => {
|
||||
{t("domain.delete")}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t("domain.management.delete.confirm")}
|
||||
{t("domain.delete.confirm")}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
|
||||
<AlertDialogCancel>
|
||||
{t("common.cancel")}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
handleDeleteClick(domain.id);
|
||||
handleDeleteClick(domain.id ?? "");
|
||||
}}
|
||||
>
|
||||
{t("confirm")}
|
||||
{t("common.confirm")}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
@@ -376,9 +375,9 @@ const Home = () => {
|
||||
<Button
|
||||
variant={"link"}
|
||||
className="p-0"
|
||||
onClick={() => handleEditClick(domain.id)}
|
||||
onClick={() => handleEditClick(domain.id ?? "")}
|
||||
>
|
||||
{t("edit")}
|
||||
{t("common.edit")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -40,17 +40,17 @@ const History = () => {
|
||||
|
||||
return (
|
||||
<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 ? (
|
||||
<>
|
||||
<Alert className="max-w-[40em] mx-auto mt-20">
|
||||
<AlertTitle>{t("no.data")}</AlertTitle>
|
||||
<AlertTitle>{t("common.text.nodata")}</AlertTitle>
|
||||
<AlertDescription>
|
||||
<div className="flex items-center mt-5">
|
||||
<div>
|
||||
<Smile className="text-yellow-400" size={36} />
|
||||
</div>
|
||||
<div className="ml-2"> {t("deployment.log.empty")}</div>
|
||||
<div className="ml-2"> {t("history.nodata")}</div>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-end">
|
||||
<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="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-56">{t("deployment.log.stage")}</div>
|
||||
<div className="w-24">{t("history.props.status")}</div>
|
||||
<div className="w-56">{t("history.props.stage")}</div>
|
||||
<div className="w-56 sm:ml-2 text-center">
|
||||
{t("deployment.log.last.execution.time")}
|
||||
{t("history.props.last_execution_time")}
|
||||
</div>
|
||||
|
||||
<div className="grow">{t("operation")}</div>
|
||||
</div>
|
||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||
{t("deployment.log.name")}
|
||||
<div className="grow">{t("common.text.operations")}</div>
|
||||
</div>
|
||||
|
||||
{deployments?.map((deployment) => (
|
||||
@@ -112,14 +109,14 @@ const History = () => {
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant={"link"} className="p-0">
|
||||
{t("deployment.log.detail.button.text")}
|
||||
{t("history.log")}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="sm:max-w-5xl">
|
||||
<SheetHeader>
|
||||
<SheetTitle>
|
||||
{deployment.expand.domain?.domain}-{deployment.id}
|
||||
{t("deployment.log.detail")}
|
||||
{t("history.log")}
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<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 { useNavigate } from "react-router-dom";
|
||||
import { z } from "zod";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -19,15 +19,15 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
|
||||
const formSchema = z.object({
|
||||
username: z.string().email({
|
||||
message: "login.username.no.empty.message",
|
||||
message: "login.username.errmsg.invalid",
|
||||
}),
|
||||
password: z.string().min(10, {
|
||||
message: "login.password.length.message",
|
||||
message: "login.password.errmsg.invalid",
|
||||
}),
|
||||
});
|
||||
|
||||
const Login = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
@@ -65,9 +65,9 @@ const Login = () => {
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('username')}</FormLabel>
|
||||
<FormLabel>{t("login.username.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="email" {...field} />
|
||||
<Input placeholder={t("login.username.placeholder")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -80,9 +80,9 @@ const Login = () => {
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('password')}</FormLabel>
|
||||
<FormLabel>{t("login.password.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="password" {...field} type="password" />
|
||||
<Input placeholder={t("login.password.placeholder")} {...field} type="password" />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -90,7 +90,7 @@ const Login = () => {
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('login.submit')}</Button>
|
||||
<Button type="submit">{t("login.submit")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
|
||||
const formSchema = z.object({
|
||||
email: z.string().email("setting.account.email.valid.message"),
|
||||
email: z.string().email("settings.account.email.errmsg.invalid"),
|
||||
});
|
||||
|
||||
const Account = () => {
|
||||
@@ -45,8 +45,8 @@ const Account = () => {
|
||||
|
||||
getPb().authStore.clear();
|
||||
toast({
|
||||
title: t("setting.account.email.change.succeed"),
|
||||
description: t("setting.account.log.back.in"),
|
||||
title: t("settings.account.email.changed.message"),
|
||||
description: t("settings.account.relogin.message"),
|
||||
});
|
||||
setTimeout(() => {
|
||||
navigate("/login");
|
||||
@@ -54,7 +54,7 @@ const Account = () => {
|
||||
} catch (e) {
|
||||
const message = getErrMessage(e);
|
||||
toast({
|
||||
title: t("setting.account.email.change.failed"),
|
||||
title: t("settings.account.email.failed.message"),
|
||||
description: message,
|
||||
variant: "destructive",
|
||||
});
|
||||
@@ -74,10 +74,10 @@ const Account = () => {
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('email')}</FormLabel>
|
||||
<FormLabel>{t("settings.account.email.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('setting.email.placeholder')}
|
||||
placeholder={t("settings.account.email.placeholder")}
|
||||
{...field}
|
||||
type="email"
|
||||
onChange={(e) => {
|
||||
@@ -94,10 +94,10 @@ const Account = () => {
|
||||
|
||||
<div className="flex justify-end">
|
||||
{changed ? (
|
||||
<Button type="submit">{t('setting.submit')}</Button>
|
||||
<Button type="submit">{t("common.update")}</Button>
|
||||
) : (
|
||||
<Button type="submit" disabled variant={"secondary"}>
|
||||
{t('setting.submit')}
|
||||
{t("common.update")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,9 @@ const Notify = () => {
|
||||
<div className="border rounded-sm p-5 shadow-lg">
|
||||
<Accordion type={"multiple"} className="dark:text-stone-200">
|
||||
<AccordionItem value="item-1" className="dark:border-stone-200">
|
||||
<AccordionTrigger>{t('template')}</AccordionTrigger>
|
||||
<AccordionTrigger>
|
||||
{t("settings.notification.template.label")}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<NotifyTemplate />
|
||||
</AccordionContent>
|
||||
@@ -30,21 +32,21 @@ const Notify = () => {
|
||||
<div className="border rounded-md p-5 mt-7 shadow-lg">
|
||||
<Accordion type={"single"} className="dark:text-stone-200">
|
||||
<AccordionItem value="item-2" className="dark:border-stone-200">
|
||||
<AccordionTrigger>{t('ding.talk')}</AccordionTrigger>
|
||||
<AccordionTrigger>{t("common.provider.dingtalk")}</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DingTalk />
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="item-4" className="dark:border-stone-200">
|
||||
<AccordionTrigger>{t('telegram')}</AccordionTrigger>
|
||||
<AccordionTrigger>{t("common.provider.telegram")}</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Telegram />
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="item-5" className="dark:border-stone-200">
|
||||
<AccordionTrigger>{t('webhook')}</AccordionTrigger>
|
||||
<AccordionTrigger>{t("common.provider.webhook")}</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Webhook />
|
||||
</AccordionContent>
|
||||
|
||||
@@ -21,17 +21,17 @@ import { z } from "zod";
|
||||
const formSchema = z
|
||||
.object({
|
||||
oldPassword: z.string().min(10, {
|
||||
message: "setting.password.length.message",
|
||||
message: "settings.password.password.errmsg.length",
|
||||
}),
|
||||
newPassword: z.string().min(10, {
|
||||
message: "setting.password.length.message",
|
||||
message: "settings.password.password.errmsg.length",
|
||||
}),
|
||||
confirmPassword: z.string().min(10, {
|
||||
message: "setting.password.length.message",
|
||||
message: "settings.password.password.errmsg.length",
|
||||
}),
|
||||
})
|
||||
.refine((data) => data.newPassword === data.confirmPassword, {
|
||||
message: "setting.password.not.match",
|
||||
message: "settings.password.password.errmsg.not_matched",
|
||||
path: ["confirmPassword"],
|
||||
});
|
||||
|
||||
@@ -68,8 +68,8 @@ const Password = () => {
|
||||
|
||||
getPb().authStore.clear();
|
||||
toast({
|
||||
title: t('setting.password.change.succeed'),
|
||||
description: t("setting.account.log.back.in"),
|
||||
title: t("settings.password.changed.message"),
|
||||
description: t("settings.account.relogin.message"),
|
||||
});
|
||||
setTimeout(() => {
|
||||
navigate("/login");
|
||||
@@ -77,7 +77,7 @@ const Password = () => {
|
||||
} catch (e) {
|
||||
const message = getErrMessage(e);
|
||||
toast({
|
||||
title: t('setting.password.change.failed'),
|
||||
title: t("settings.password.failed.message"),
|
||||
description: message,
|
||||
variant: "destructive",
|
||||
});
|
||||
@@ -97,9 +97,17 @@ const Password = () => {
|
||||
name="oldPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('setting.password.current.password')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("settings.password.current_password.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t('setting.password.current.password')} {...field} type="password" />
|
||||
<Input
|
||||
placeholder={t(
|
||||
"settings.password.current_password.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
type="password"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -112,10 +120,14 @@ const Password = () => {
|
||||
name="newPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('setting.password.new.password')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("settings.password.new_password.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="newPassword"
|
||||
placeholder={t(
|
||||
"settings.password.new_password.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
type="password"
|
||||
/>
|
||||
@@ -131,10 +143,14 @@ const Password = () => {
|
||||
name="confirmPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('setting.password.confirm.password')}</FormLabel>
|
||||
<FormLabel>
|
||||
{t("settings.password.confirm_password.label")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="confirmPassword"
|
||||
placeholder={t(
|
||||
"settings.password.confirm_password.placeholder"
|
||||
)}
|
||||
{...field}
|
||||
type="password"
|
||||
/>
|
||||
@@ -145,7 +161,7 @@ const Password = () => {
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t('setting.submit')}</Button>
|
||||
<Button type="submit">{t("common.update")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -35,7 +35,7 @@ const SSLProvider = () => {
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.enum(["letsencrypt", "zerossl"], {
|
||||
message: t("setting.ca.not.empty"),
|
||||
message: t("settings.ca.provider.errmsg.empty"),
|
||||
}),
|
||||
eabKid: z.string().optional(),
|
||||
eabHmacKey: z.string().optional(),
|
||||
@@ -89,12 +89,12 @@ const SSLProvider = () => {
|
||||
if (values.provider === "zerossl") {
|
||||
if (!values.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) {
|
||||
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) {
|
||||
@@ -120,13 +120,13 @@ const SSLProvider = () => {
|
||||
try {
|
||||
await update(setting);
|
||||
toast({
|
||||
title: t("update.succeed"),
|
||||
description: t("update.succeed"),
|
||||
title: t("common.update.succeeded.message"),
|
||||
description: t("common.update.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const message = getErrMessage(e);
|
||||
toast({
|
||||
title: t("update.failed"),
|
||||
title: t("common.update.failed.message"),
|
||||
description: message,
|
||||
variant: "destructive",
|
||||
});
|
||||
@@ -146,7 +146,7 @@ const SSLProvider = () => {
|
||||
name="provider"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("ca")}</FormLabel>
|
||||
<FormLabel>{t("common.text.ca")}</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
{...field}
|
||||
@@ -202,7 +202,7 @@ const SSLProvider = () => {
|
||||
<FormLabel>EAB_KID</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t("setting.ca.eab_kid.not.empty")}
|
||||
placeholder={t("settings.ca.eab_kid.errmsg.empty")}
|
||||
{...field}
|
||||
type="text"
|
||||
/>
|
||||
@@ -221,7 +221,9 @@ const SSLProvider = () => {
|
||||
<FormLabel>EAB_HMAC_KEY</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t("setting.ca.eab_hmac_key.not.empty")}
|
||||
placeholder={t(
|
||||
"settings.ca.eab_hmac_key.errmsg.empty"
|
||||
)}
|
||||
{...field}
|
||||
type="text"
|
||||
/>
|
||||
@@ -238,7 +240,7 @@ const SSLProvider = () => {
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("setting.submit")}</Button>
|
||||
<Button type="submit">{t("common.update")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
Reference in New Issue
Block a user