Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4382474449 | ||
|
|
678ef9c232 | ||
|
|
3d535320b9 | ||
|
|
77d3e40ffb | ||
|
|
5dca64d3d3 | ||
|
|
02d582b564 | ||
|
|
8e906cbf23 | ||
|
|
411b7bbfe2 | ||
|
|
3093fc6b02 | ||
|
|
3c4b7d251a | ||
|
|
f87a1be192 | ||
|
|
9b91cbd67e | ||
|
|
9b5e1052a1 | ||
|
|
0d47d7cfd0 | ||
|
|
ef87975c80 | ||
|
|
9db757fbbb | ||
|
|
f6ef305441 | ||
|
|
004c6a8506 |
@@ -78,12 +78,14 @@ go run main.go serve
|
||||
| 腾讯云 | √ | √ | 可签发在腾讯云注册的域名;可部署到腾讯云 CDN |
|
||||
| 华为云 | √ | | 可签发在华为云注册的域名 |
|
||||
| 七牛云 | | √ | 可部署到七牛云 CDN |
|
||||
| AWS | √ | | 可签发在 AWS Route53 托管的域名 |
|
||||
| CloudFlare | √ | | 可签发在 CloudFlare 注册的域名;CloudFlare 服务自带 SSL 证书 |
|
||||
| GoDaddy | √ | | 可签发在 GoDaddy 注册的域名 |
|
||||
| Namesilo | √ | | 可签发在 Namesilo 注册的域名 |
|
||||
| 本地部署 | | √ | 可部署到本地服务器 |
|
||||
| SSH | | √ | 可部署到 SSH 服务器 |
|
||||
| Webhook | | √ | 可部署时回调到 Webhook |
|
||||
| Kubernetes | | √ | 可部署到 Kubernetes Secret |
|
||||
|
||||
## 四、系统截图
|
||||
|
||||
|
||||
@@ -77,12 +77,14 @@ password:1234567890
|
||||
| Tencent Cloud | √ | √ | Supports domains registered on Tencent Cloud; supports deployment to Tencent Cloud CDN |
|
||||
| Huawei Cloud | √ | | Supports domains registered on Huawei Cloud |
|
||||
| Qiniu Cloud | | √ | Supports deployment to Qiniu Cloud CDN |
|
||||
| AWS | √ | | Supports domains managed on AWS Route53 |
|
||||
| CloudFlare | √ | | Supports domains registered on CloudFlare; CloudFlare services come with SSL certificates |
|
||||
| GoDaddy | √ | | Supports domains registered on GoDaddy |
|
||||
| Namesilo | √ | | Supports domains registered on Namesilo |
|
||||
| Local Deploy | | √ | Supports deployment to local servers |
|
||||
| SSH | | √ | Supports deployment to SSH servers |
|
||||
| Webhook | | √ | Supports callback to Webhook |
|
||||
| Kubernetes | | √ | Supports deployment to Kubernetes Secret |
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
||||
29
go.mod
29
go.mod
@@ -22,6 +22,8 @@ require (
|
||||
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.27.0
|
||||
k8s.io/apimachinery v0.31.1
|
||||
k8s.io/client-go v0.31.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -29,12 +31,39 @@ require (
|
||||
github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect
|
||||
github.com/alibabacloud-go/tea-oss-sdk v1.1.3 // indirect
|
||||
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.43.2 // indirect
|
||||
github.com/blinkbean/dingtalk v1.1.3 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-lark/lark v1.14.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 // indirect
|
||||
github.com/imdario/mergo v0.3.6 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/api v0.31.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
72
go.sum
72
go.sum
@@ -117,6 +117,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 h1:rfprUlsd
|
||||
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/route53 v1.43.2 h1:957e1/SwXIfPi/0OUJkH9YnPZRe9G6Kisd/xUhF7AUE=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.43.2/go.mod h1:343vcjcyOTuHTBBgUrOxPM36/jE96qLZnGL447ldrB0=
|
||||
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=
|
||||
@@ -157,6 +159,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/elastic/go-sysinfo v1.0.2/go.mod h1:O/D5m1VpYLwGjCYzEt63g3Z1uO3jXfwyzzjiW90t8cY=
|
||||
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -168,6 +172,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
|
||||
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
|
||||
@@ -178,10 +184,19 @@ github.com/go-acme/lego/v4 v4.19.2/go.mod h1:wtDe3dDkmV4/oI2nydpNXSJpvV10J9RCyZ6
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
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-lark/lark v1.14.1 h1:qWYQTk6wLwf/08u8WbdNAHNmfqavdOvmsENlQ+Cb8aY=
|
||||
github.com/go-lark/lark v1.14.1/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
|
||||
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=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
@@ -193,11 +208,15 @@ github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPF
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
|
||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/gojek/heimdall/v7 v7.0.3 h1:+5sAhl8S0m+qRRL8IVeHCJudFh/XkG3wyO++nvOg+gc=
|
||||
github.com/gojek/heimdall/v7 v7.0.3/go.mod h1:Z43HtMid7ysSjmsedPTXAki6jcdcNVnjn5pmsTyiMic=
|
||||
github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf h1:5xRGbUdOmZKoDXkGx5evVLehuCMpuO1hl701bEQqXOM=
|
||||
@@ -226,6 +245,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
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/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
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=
|
||||
@@ -234,12 +255,15 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.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.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||
@@ -263,6 +287,8 @@ github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4Dvx
|
||||
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/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
@@ -271,8 +297,12 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
@@ -280,6 +310,8 @@ 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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
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=
|
||||
@@ -296,6 +328,8 @@ github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQ
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f/go.mod h1:aEt7p9Rvh67BYApmZwNDPpgircTO2kgdmDUoF/1QmwA=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
@@ -319,6 +353,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
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=
|
||||
@@ -326,6 +362,10 @@ github.com/nikoksr/notify v1.0.0 h1:qe9/6FRsWdxBgQgWcpvQ0sv8LRGJZDpRB4TkL2uNdO8=
|
||||
github.com/nikoksr/notify v1.0.0/go.mod h1:hPaaDt30d6LAA7/5nb0e48Bp/MctDfycCSs8VEgN29I=
|
||||
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
|
||||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -352,8 +392,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
@@ -405,12 +445,15 @@ 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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
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.2.1/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=
|
||||
@@ -465,6 +508,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.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=
|
||||
@@ -480,6 +524,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
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-20201021035429-f5854403a974/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=
|
||||
@@ -503,6 +548,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
@@ -574,6 +620,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||
@@ -627,6 +675,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
@@ -643,6 +693,18 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
|
||||
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
|
||||
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
|
||||
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
|
||||
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
|
||||
@@ -670,3 +732,9 @@ modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package applicant
|
||||
|
||||
import (
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/app"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
@@ -16,15 +18,13 @@ import (
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
|
||||
"certimate/internal/domain"
|
||||
"certimate/internal/utils/app"
|
||||
)
|
||||
|
||||
const (
|
||||
configTypeAliyun = "aliyun"
|
||||
configTypeTencent = "tencent"
|
||||
configTypeHuaweicloud = "huaweicloud"
|
||||
configTypeAws = "aws"
|
||||
configTypeCloudflare = "cloudflare"
|
||||
configTypeNamesilo = "namesilo"
|
||||
configTypeGodaddy = "godaddy"
|
||||
@@ -127,6 +127,8 @@ func Get(record *models.Record) (Applicant, error) {
|
||||
return NewTencent(option), nil
|
||||
case configTypeHuaweicloud:
|
||||
return NewHuaweiCloud(option), nil
|
||||
case configTypeAws:
|
||||
return NewAws(option), nil
|
||||
case configTypeCloudflare:
|
||||
return NewCloudflare(option), nil
|
||||
case configTypeNamesilo:
|
||||
|
||||
39
internal/applicant/aws.go
Normal file
39
internal/applicant/aws.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package applicant
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/route53"
|
||||
|
||||
"certimate/internal/domain"
|
||||
)
|
||||
|
||||
type aws struct {
|
||||
option *ApplyOption
|
||||
}
|
||||
|
||||
func NewAws(option *ApplyOption) Applicant {
|
||||
return &aws{
|
||||
option: option,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *aws) Apply() (*Certificate, error) {
|
||||
access := &domain.AwsAccess{}
|
||||
json.Unmarshal([]byte(t.option.Access), access)
|
||||
|
||||
os.Setenv("AWS_REGION", access.Region)
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", access.AccessKeyId)
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", access.SecretAccessKey)
|
||||
os.Setenv("AWS_HOSTED_ZONE_ID", access.HostedZoneId)
|
||||
os.Setenv("AWS_PROPAGATION_TIMEOUT", fmt.Sprintf("%d", t.option.Timeout))
|
||||
|
||||
dnsProvider, err := route53.NewDNSProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return apply(t.option, dnsProvider)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
|
||||
"certimate/internal/domain"
|
||||
)
|
||||
|
||||
type aliyun struct {
|
||||
client *oss.Client
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewAliyun(option *DeployerOption) (Deployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
a := &aliyun{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}
|
||||
client, err := a.createClient(access.AccessKeyId, access.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.client = client
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *aliyun) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", a.option.AceessRecord.GetString("name"), a.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (a *aliyun) GetInfo() []string {
|
||||
return a.infos
|
||||
}
|
||||
|
||||
func (a *aliyun) Deploy(ctx context.Context) error {
|
||||
err := a.client.PutBucketCnameWithCertificate(getDeployString(a.option.DeployConfig, "bucket"), oss.PutBucketCname{
|
||||
Cname: getDeployString(a.option.DeployConfig, "domain"),
|
||||
CertificateConfiguration: &oss.CertificateConfiguration{
|
||||
Certificate: a.option.Certificate.Certificate,
|
||||
PrivateKey: a.option.Certificate.PrivateKey,
|
||||
Force: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deploy aliyun oss error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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, fmt.Errorf("create aliyun client error: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
@@ -14,63 +14,65 @@ import (
|
||||
"certimate/internal/utils/rand"
|
||||
)
|
||||
|
||||
type AliyunCdn struct {
|
||||
type AliyunCDNDeployer struct {
|
||||
client *cdn20180510.Client
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewAliyunCdn(option *DeployerOption) (*AliyunCdn, error) {
|
||||
func NewAliyunCdnDeployer(option *DeployerOption) (*AliyunCDNDeployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
a := &AliyunCdn{
|
||||
|
||||
d := &AliyunCDNDeployer{
|
||||
option: option,
|
||||
}
|
||||
client, err := a.createClient(access.AccessKeyId, access.AccessKeySecret)
|
||||
|
||||
client, err := d.createClient(access.AccessKeyId, access.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AliyunCdn{
|
||||
return &AliyunCDNDeployer{
|
||||
client: client,
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *AliyunCdn) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", a.option.AceessRecord.GetString("name"), a.option.AceessRecord.Id)
|
||||
func (d *AliyunCDNDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (a *AliyunCdn) GetInfo() []string {
|
||||
return a.infos
|
||||
func (d *AliyunCDNDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (a *AliyunCdn) Deploy(ctx context.Context) error {
|
||||
certName := fmt.Sprintf("%s-%s-%s", a.option.Domain, a.option.DomainId, rand.RandStr(6))
|
||||
func (d *AliyunCDNDeployer) Deploy(ctx context.Context) error {
|
||||
certName := fmt.Sprintf("%s-%s-%s", d.option.Domain, d.option.DomainId, rand.RandStr(6))
|
||||
setCdnDomainSSLCertificateRequest := &cdn20180510.SetCdnDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(getDeployString(a.option.DeployConfig, "domain")),
|
||||
DomainName: tea.String(getDeployString(d.option.DeployConfig, "domain")),
|
||||
CertName: tea.String(certName),
|
||||
CertType: tea.String("upload"),
|
||||
SSLProtocol: tea.String("on"),
|
||||
SSLPub: tea.String(a.option.Certificate.Certificate),
|
||||
SSLPri: tea.String(a.option.Certificate.PrivateKey),
|
||||
SSLPub: tea.String(d.option.Certificate.Certificate),
|
||||
SSLPri: tea.String(d.option.Certificate.PrivateKey),
|
||||
CertRegion: tea.String("cn-hangzhou"),
|
||||
}
|
||||
|
||||
runtime := &util.RuntimeOptions{}
|
||||
|
||||
resp, err := a.client.SetCdnDomainSSLCertificateWithOptions(setCdnDomainSSLCertificateRequest, runtime)
|
||||
resp, err := d.client.SetCdnDomainSSLCertificateWithOptions(setCdnDomainSSLCertificateRequest, runtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.infos = append(a.infos, toStr("cdn设置证书", resp))
|
||||
d.infos = append(d.infos, toStr("cdn设置证书", resp))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AliyunCdn) createClient(accessKeyId, accessKeySecret string) (_result *cdn20180510.Client, _err error) {
|
||||
func (d *AliyunCDNDeployer) createClient(accessKeyId, accessKeySecret string) (_result *cdn20180510.Client, _err error) {
|
||||
config := &openapi.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
|
||||
@@ -19,63 +19,65 @@ import (
|
||||
"certimate/internal/utils/rand"
|
||||
)
|
||||
|
||||
type AliyunEsa struct {
|
||||
type AliyunESADeployer struct {
|
||||
client *dcdn20180115.Client
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewAliyunEsa(option *DeployerOption) (*AliyunEsa, error) {
|
||||
func NewAliyunEsaDeployer(option *DeployerOption) (*AliyunESADeployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
a := &AliyunEsa{
|
||||
|
||||
d := &AliyunESADeployer{
|
||||
option: option,
|
||||
}
|
||||
client, err := a.createClient(access.AccessKeyId, access.AccessKeySecret)
|
||||
|
||||
client, err := d.createClient(access.AccessKeyId, access.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AliyunEsa{
|
||||
return &AliyunESADeployer{
|
||||
client: client,
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *AliyunEsa) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", a.option.AceessRecord.GetString("name"), a.option.AceessRecord.Id)
|
||||
func (d *AliyunESADeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (a *AliyunEsa) GetInfo() []string {
|
||||
return a.infos
|
||||
func (d *AliyunESADeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (a *AliyunEsa) Deploy(ctx context.Context) error {
|
||||
certName := fmt.Sprintf("%s-%s-%s", a.option.Domain, a.option.DomainId, rand.RandStr(6))
|
||||
func (d *AliyunESADeployer) Deploy(ctx context.Context) error {
|
||||
certName := fmt.Sprintf("%s-%s-%s", d.option.Domain, d.option.DomainId, rand.RandStr(6))
|
||||
setDcdnDomainSSLCertificateRequest := &dcdn20180115.SetDcdnDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(getDeployString(a.option.DeployConfig, "domain")),
|
||||
DomainName: tea.String(getDeployString(d.option.DeployConfig, "domain")),
|
||||
CertName: tea.String(certName),
|
||||
CertType: tea.String("upload"),
|
||||
SSLProtocol: tea.String("on"),
|
||||
SSLPub: tea.String(a.option.Certificate.Certificate),
|
||||
SSLPri: tea.String(a.option.Certificate.PrivateKey),
|
||||
SSLPub: tea.String(d.option.Certificate.Certificate),
|
||||
SSLPri: tea.String(d.option.Certificate.PrivateKey),
|
||||
CertRegion: tea.String("cn-hangzhou"),
|
||||
}
|
||||
|
||||
runtime := &util.RuntimeOptions{}
|
||||
|
||||
resp, err := a.client.SetDcdnDomainSSLCertificateWithOptions(setDcdnDomainSSLCertificateRequest, runtime)
|
||||
resp, err := d.client.SetDcdnDomainSSLCertificateWithOptions(setDcdnDomainSSLCertificateRequest, runtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.infos = append(a.infos, toStr("dcdn设置证书", resp))
|
||||
d.infos = append(d.infos, toStr("dcdn设置证书", resp))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AliyunEsa) createClient(accessKeyId, accessKeySecret string) (_result *dcdn20180115.Client, _err error) {
|
||||
func (d *AliyunESADeployer) createClient(accessKeyId, accessKeySecret string) (_result *dcdn20180115.Client, _err error) {
|
||||
config := &openapi.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
|
||||
70
internal/deployer/aliyun_oss.go
Normal file
70
internal/deployer/aliyun_oss.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
|
||||
"certimate/internal/domain"
|
||||
)
|
||||
|
||||
type AliyunOSSDeployer struct {
|
||||
client *oss.Client
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewAliyunOssDeployer(option *DeployerOption) (Deployer, error) {
|
||||
access := &domain.AliyunAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
d := &AliyunOSSDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}
|
||||
|
||||
client, err := d.createClient(access.AccessKeyId, access.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.client = client
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d *AliyunOSSDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *AliyunOSSDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (d *AliyunOSSDeployer) Deploy(ctx context.Context) error {
|
||||
err := d.client.PutBucketCnameWithCertificate(getDeployString(d.option.DeployConfig, "bucket"), oss.PutBucketCname{
|
||||
Cname: getDeployString(d.option.DeployConfig, "domain"),
|
||||
CertificateConfiguration: &oss.CertificateConfiguration{
|
||||
Certificate: d.option.Certificate.Certificate,
|
||||
PrivateKey: d.option.Certificate.PrivateKey,
|
||||
Force: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deploy aliyun oss error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AliyunOSSDeployer) createClient(accessKeyId, accessKeySecret string) (*oss.Client, error) {
|
||||
client, err := oss.New(
|
||||
getDeployString(d.option.DeployConfig, "endpoint"),
|
||||
accessKeyId,
|
||||
accessKeySecret,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create aliyun client error: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
@@ -15,14 +15,15 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
targetAliyunOss = "aliyun-oss"
|
||||
targetAliyunCdn = "aliyun-cdn"
|
||||
targetAliyunEsa = "aliyun-dcdn"
|
||||
targetSSH = "ssh"
|
||||
targetWebhook = "webhook"
|
||||
targetTencentCdn = "tencent-cdn"
|
||||
targetAliyunOSS = "aliyun-oss"
|
||||
targetAliyunCDN = "aliyun-cdn"
|
||||
targetAliyunESA = "aliyun-dcdn"
|
||||
targetTencentCDN = "tencent-cdn"
|
||||
targetQiniuCdn = "qiniu-cdn"
|
||||
targetLocal = "local"
|
||||
targetSSH = "ssh"
|
||||
targetWebhook = "webhook"
|
||||
targetK8sSecret = "k8s-secret"
|
||||
)
|
||||
|
||||
type DeployerOption struct {
|
||||
@@ -60,7 +61,6 @@ func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error
|
||||
}
|
||||
|
||||
for _, deployConfig := range deployConfigs {
|
||||
|
||||
deployer, err := getWithDeployConfig(record, cert, deployConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -96,23 +96,24 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
|
||||
}
|
||||
|
||||
switch deployConfig.Type {
|
||||
case targetAliyunOss:
|
||||
return NewAliyun(option)
|
||||
case targetAliyunCdn:
|
||||
return NewAliyunCdn(option)
|
||||
case targetAliyunEsa:
|
||||
return NewAliyunEsa(option)
|
||||
case targetSSH:
|
||||
return NewSSH(option)
|
||||
case targetWebhook:
|
||||
return NewWebhook(option)
|
||||
case targetTencentCdn:
|
||||
return NewTencentCdn(option)
|
||||
case targetAliyunOSS:
|
||||
return NewAliyunOssDeployer(option)
|
||||
case targetAliyunCDN:
|
||||
return NewAliyunCdnDeployer(option)
|
||||
case targetAliyunESA:
|
||||
return NewAliyunEsaDeployer(option)
|
||||
case targetTencentCDN:
|
||||
return NewTencentCDNDeployer(option)
|
||||
case targetQiniuCdn:
|
||||
|
||||
return NewQiNiu(option)
|
||||
return NewQiniuCDNDeployer(option)
|
||||
case targetLocal:
|
||||
return NewLocal(option), nil
|
||||
return NewLocalDeployer(option)
|
||||
case targetSSH:
|
||||
return NewSSHDeployer(option)
|
||||
case targetWebhook:
|
||||
return NewWebhookDeployer(option)
|
||||
case targetK8sSecret:
|
||||
return NewK8sSecretDeployer(option)
|
||||
}
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
110
internal/deployer/k8s_secret.go
Normal file
110
internal/deployer/k8s_secret.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
k8sMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
type KubernetesAccess struct {
|
||||
KubeConfig string `json:"kubeConfig"`
|
||||
}
|
||||
|
||||
type K8sSecretDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewK8sSecretDeployer(option *DeployerOption) (Deployer, error) {
|
||||
return &K8sSecretDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
|
||||
access := &KubernetesAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := d.createClient(access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("kubeClient 创建成功", nil))
|
||||
|
||||
namespace := getDeployString(d.option.DeployConfig, "namespace")
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
secretName := getDeployString(d.option.DeployConfig, "secretName")
|
||||
if secretName == "" {
|
||||
return fmt.Errorf("k8s secret name is empty")
|
||||
}
|
||||
|
||||
secretDataKeyForCrt := getDeployString(d.option.DeployConfig, "secretDataKeyForCrt")
|
||||
if secretDataKeyForCrt == "" {
|
||||
namespace = "tls.crt"
|
||||
}
|
||||
|
||||
secretDataKeyForKey := getDeployString(d.option.DeployConfig, "secretDataKeyForKey")
|
||||
if secretDataKeyForKey == "" {
|
||||
namespace = "tls.key"
|
||||
}
|
||||
|
||||
// 获取 Secret 实例
|
||||
secret, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, k8sMetaV1.GetOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get k8s secret: %w", err)
|
||||
}
|
||||
|
||||
// 更新 Secret Data
|
||||
secret.Data[secretDataKeyForCrt] = []byte(d.option.Certificate.Certificate)
|
||||
secret.Data[secretDataKeyForKey] = []byte(d.option.Certificate.PrivateKey)
|
||||
_, err = client.CoreV1().Secrets(namespace).Update(context.TODO(), secret, k8sMetaV1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update k8s secret: %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("证书已更新到 K8s Secret", nil))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *K8sSecretDeployer) createClient(access *KubernetesAccess) (*kubernetes.Clientset, error) {
|
||||
kubeConfig, err := clientcmd.Load([]byte(access.KubeConfig))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
&clientcmd.ClientConfigLoadingRules{ExplicitPath: ""},
|
||||
&clientcmd.ConfigOverrides{CurrentContext: kubeConfig.CurrentContext},
|
||||
)
|
||||
config, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
@@ -10,35 +10,35 @@ import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type localAccess struct{}
|
||||
type LocalAccess struct{}
|
||||
|
||||
type local struct {
|
||||
type LocalDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewLocal(option *DeployerOption) *local {
|
||||
return &local{
|
||||
func NewLocalDeployer(option *DeployerOption) (Deployer, error) {
|
||||
return &LocalDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *local) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", l.option.AceessRecord.GetString("name"), l.option.AceessRecord.Id)
|
||||
func (d *LocalDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (l *local) GetInfo() []string {
|
||||
func (d *LocalDeployer) GetInfo() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (l *local) Deploy(ctx context.Context) error {
|
||||
access := &localAccess{}
|
||||
if err := json.Unmarshal([]byte(l.option.Access), access); err != nil {
|
||||
func (d *LocalDeployer) Deploy(ctx context.Context) error {
|
||||
access := &LocalAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preCommand := getDeployString(l.option.DeployConfig, "preCommand")
|
||||
preCommand := getDeployString(d.option.DeployConfig, "preCommand")
|
||||
|
||||
if preCommand != "" {
|
||||
if err := execCmd(preCommand); err != nil {
|
||||
@@ -46,18 +46,18 @@ func (l *local) Deploy(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 复制文件
|
||||
if err := copyFile(l.option.Certificate.Certificate, getDeployString(l.option.DeployConfig, "certPath")); err != nil {
|
||||
// 复制证书文件
|
||||
if err := copyFile(getDeployString(d.option.DeployConfig, "certPath"), d.option.Certificate.Certificate); err != nil {
|
||||
return fmt.Errorf("复制证书失败: %w", err)
|
||||
}
|
||||
|
||||
if err := copyFile(l.option.Certificate.PrivateKey, getDeployString(l.option.DeployConfig, "keyPath")); err != nil {
|
||||
// 复制私钥文件
|
||||
if err := copyFile(getDeployString(d.option.DeployConfig, "keyPath"), d.option.Certificate.PrivateKey); err != nil {
|
||||
return fmt.Errorf("复制私钥失败: %w", err)
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
|
||||
if err := execCmd(getDeployString(l.option.DeployConfig, "command")); err != nil {
|
||||
if err := execCmd(getDeployString(d.option.DeployConfig, "command")); err != nil {
|
||||
return fmt.Errorf("执行命令失败: %w", err)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func execCmd(command string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(content string, path string) error {
|
||||
func copyFile(path string, content string) error {
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
// 如果目录不存在,创建目录
|
||||
|
||||
@@ -16,17 +16,17 @@ import (
|
||||
|
||||
const qiniuGateway = "http://api.qiniu.com"
|
||||
|
||||
type qiuniu struct {
|
||||
type QiniuCDNDeployer struct {
|
||||
option *DeployerOption
|
||||
info []string
|
||||
credentials *auth.Credentials
|
||||
}
|
||||
|
||||
func NewQiNiu(option *DeployerOption) (*qiuniu, error) {
|
||||
func NewQiniuCDNDeployer(option *DeployerOption) (*QiniuCDNDeployer, error) {
|
||||
access := &domain.QiniuAccess{}
|
||||
json.Unmarshal([]byte(option.Access), access)
|
||||
|
||||
return &qiuniu{
|
||||
return &QiniuCDNDeployer{
|
||||
option: option,
|
||||
info: make([]string, 0),
|
||||
|
||||
@@ -34,41 +34,39 @@ func NewQiNiu(option *DeployerOption) (*qiuniu, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *qiuniu) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", a.option.AceessRecord.GetString("name"), a.option.AceessRecord.Id)
|
||||
func (d *QiniuCDNDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (q *qiuniu) GetInfo() []string {
|
||||
return q.info
|
||||
func (d *QiniuCDNDeployer) GetInfo() []string {
|
||||
return d.info
|
||||
}
|
||||
|
||||
func (q *qiuniu) Deploy(ctx context.Context) error {
|
||||
func (d *QiniuCDNDeployer) Deploy(ctx context.Context) error {
|
||||
// 上传证书
|
||||
certId, err := q.uploadCert()
|
||||
certId, err := d.uploadCert()
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploadCert failed: %w", err)
|
||||
}
|
||||
|
||||
// 获取域名信息
|
||||
domainInfo, err := q.getDomainInfo()
|
||||
domainInfo, err := d.getDomainInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getDomainInfo failed: %w", err)
|
||||
}
|
||||
|
||||
// 判断域名是否启用 https
|
||||
|
||||
if domainInfo.Https != nil && domainInfo.Https.CertID != "" {
|
||||
// 启用了 https
|
||||
// 修改域名证书
|
||||
err = q.modifyDomainCert(certId)
|
||||
err = d.modifyDomainCert(certId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("modifyDomainCert failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
// 没启用 https
|
||||
// 启用 https
|
||||
|
||||
err = q.enableHttps(certId)
|
||||
err = d.enableHttps(certId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("enableHttps failed: %w", err)
|
||||
}
|
||||
@@ -77,10 +75,10 @@ func (q *qiuniu) Deploy(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *qiuniu) enableHttps(certId string) error {
|
||||
path := fmt.Sprintf("/domain/%s/sslize", getDeployString(q.option.DeployConfig, "domain"))
|
||||
func (d *QiniuCDNDeployer) enableHttps(certId string) error {
|
||||
path := fmt.Sprintf("/domain/%s/sslize", getDeployString(d.option.DeployConfig, "domain"))
|
||||
|
||||
body := &modifyDomainCertReq{
|
||||
body := &qiniuModifyDomainCertReq{
|
||||
CertID: certId,
|
||||
ForceHttps: true,
|
||||
Http2Enable: true,
|
||||
@@ -91,7 +89,7 @@ func (q *qiuniu) enableHttps(certId string) error {
|
||||
return fmt.Errorf("enable https failed: %w", err)
|
||||
}
|
||||
|
||||
_, err = q.req(qiniuGateway+path, http.MethodPut, bytes.NewReader(bodyBytes))
|
||||
_, err = d.req(qiniuGateway+path, http.MethodPut, bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return fmt.Errorf("enable https failed: %w", err)
|
||||
}
|
||||
@@ -99,19 +97,19 @@ func (q *qiuniu) enableHttps(certId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type domainInfo struct {
|
||||
Https *modifyDomainCertReq `json:"https"`
|
||||
type qiniuDomainInfo struct {
|
||||
Https *qiniuModifyDomainCertReq `json:"https"`
|
||||
}
|
||||
|
||||
func (q *qiuniu) getDomainInfo() (*domainInfo, error) {
|
||||
path := fmt.Sprintf("/domain/%s", getDeployString(q.option.DeployConfig, "domain"))
|
||||
func (d *QiniuCDNDeployer) getDomainInfo() (*qiniuDomainInfo, error) {
|
||||
path := fmt.Sprintf("/domain/%s", getDeployString(d.option.DeployConfig, "domain"))
|
||||
|
||||
res, err := q.req(qiniuGateway+path, http.MethodGet, nil)
|
||||
res, err := d.req(qiniuGateway+path, http.MethodGet, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("req failed: %w", err)
|
||||
}
|
||||
|
||||
resp := &domainInfo{}
|
||||
resp := &qiniuDomainInfo{}
|
||||
err = json.Unmarshal(res, resp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json.Unmarshal failed: %w", err)
|
||||
@@ -120,25 +118,25 @@ func (q *qiuniu) getDomainInfo() (*domainInfo, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type uploadCertReq struct {
|
||||
type qiniuUploadCertReq struct {
|
||||
Name string `json:"name"`
|
||||
CommonName string `json:"common_name"`
|
||||
Pri string `json:"pri"`
|
||||
Ca string `json:"ca"`
|
||||
}
|
||||
|
||||
type uploadCertResp struct {
|
||||
type qiniuUploadCertResp struct {
|
||||
CertID string `json:"certID"`
|
||||
}
|
||||
|
||||
func (q *qiuniu) uploadCert() (string, error) {
|
||||
func (d *QiniuCDNDeployer) uploadCert() (string, error) {
|
||||
path := "/sslcert"
|
||||
|
||||
body := &uploadCertReq{
|
||||
Name: getDeployString(q.option.DeployConfig, "domain"),
|
||||
CommonName: getDeployString(q.option.DeployConfig, "domain"),
|
||||
Pri: q.option.Certificate.PrivateKey,
|
||||
Ca: q.option.Certificate.Certificate,
|
||||
body := &qiniuUploadCertReq{
|
||||
Name: getDeployString(d.option.DeployConfig, "domain"),
|
||||
CommonName: getDeployString(d.option.DeployConfig, "domain"),
|
||||
Pri: d.option.Certificate.PrivateKey,
|
||||
Ca: d.option.Certificate.Certificate,
|
||||
}
|
||||
|
||||
bodyBytes, err := json.Marshal(body)
|
||||
@@ -146,11 +144,11 @@ func (q *qiuniu) uploadCert() (string, error) {
|
||||
return "", fmt.Errorf("json.Marshal failed: %w", err)
|
||||
}
|
||||
|
||||
res, err := q.req(qiniuGateway+path, http.MethodPost, bytes.NewReader(bodyBytes))
|
||||
res, err := d.req(qiniuGateway+path, http.MethodPost, bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("req failed: %w", err)
|
||||
}
|
||||
resp := &uploadCertResp{}
|
||||
resp := &qiniuUploadCertResp{}
|
||||
err = json.Unmarshal(res, resp)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("json.Unmarshal failed: %w", err)
|
||||
@@ -159,16 +157,16 @@ func (q *qiuniu) uploadCert() (string, error) {
|
||||
return resp.CertID, nil
|
||||
}
|
||||
|
||||
type modifyDomainCertReq struct {
|
||||
type qiniuModifyDomainCertReq struct {
|
||||
CertID string `json:"certId"`
|
||||
ForceHttps bool `json:"forceHttps"`
|
||||
Http2Enable bool `json:"http2Enable"`
|
||||
}
|
||||
|
||||
func (q *qiuniu) modifyDomainCert(certId string) error {
|
||||
path := fmt.Sprintf("/domain/%s/httpsconf", getDeployString(q.option.DeployConfig, "domain"))
|
||||
func (d *QiniuCDNDeployer) modifyDomainCert(certId string) error {
|
||||
path := fmt.Sprintf("/domain/%s/httpsconf", getDeployString(d.option.DeployConfig, "domain"))
|
||||
|
||||
body := &modifyDomainCertReq{
|
||||
body := &qiniuModifyDomainCertReq{
|
||||
CertID: certId,
|
||||
ForceHttps: true,
|
||||
Http2Enable: true,
|
||||
@@ -179,7 +177,7 @@ func (q *qiuniu) modifyDomainCert(certId string) error {
|
||||
return fmt.Errorf("json.Marshal failed: %w", err)
|
||||
}
|
||||
|
||||
_, err = q.req(qiniuGateway+path, http.MethodPut, bytes.NewReader(bodyBytes))
|
||||
_, err = d.req(qiniuGateway+path, http.MethodPut, bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return fmt.Errorf("req failed: %w", err)
|
||||
}
|
||||
@@ -187,12 +185,12 @@ func (q *qiuniu) modifyDomainCert(certId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *qiuniu) req(url, method string, body io.Reader) ([]byte, error) {
|
||||
func (d *QiniuCDNDeployer) req(url, method string, body io.Reader) ([]byte, error) {
|
||||
req := xhttp.BuildReq(url, method, body, map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
})
|
||||
|
||||
if err := q.credentials.AddToken(auth.TokenQBox, req); err != nil {
|
||||
if err := d.credentials.AddToken(auth.TokenQBox, req); err != nil {
|
||||
return nil, fmt.Errorf("credentials.AddToken failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func Test_qiuniu_uploadCert(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
q, _ := NewQiNiu(tt.fields.option)
|
||||
q, _ := NewQiniuCDNDeployer(tt.fields.option)
|
||||
got, err := q.uploadCert()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("qiuniu.uploadCert() error = %v, wantErr %v", err, tt.wantErr)
|
||||
@@ -78,7 +78,7 @@ func Test_qiuniu_modifyDomainCert(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
q, _ := NewQiNiu(tt.fields.option)
|
||||
q, _ := NewQiniuCDNDeployer(tt.fields.option)
|
||||
if err := q.modifyDomainCert(tt.args.certId); (err != nil) != tt.wantErr {
|
||||
t.Errorf("qiuniu.modifyDomainCert() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
@@ -12,12 +12,7 @@ import (
|
||||
sshPkg "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type ssh struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
type sshAccess struct {
|
||||
type SSHAccess struct {
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
Username string `json:"username"`
|
||||
@@ -26,71 +21,76 @@ type sshAccess struct {
|
||||
KeyPassphrase string `json:"keyPassphrase"`
|
||||
}
|
||||
|
||||
func NewSSH(option *DeployerOption) (Deployer, error) {
|
||||
return &ssh{
|
||||
type SSHDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewSSHDeployer(option *DeployerOption) (Deployer, error) {
|
||||
return &SSHDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *ssh) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", a.option.AceessRecord.GetString("name"), a.option.AceessRecord.Id)
|
||||
func (d *SSHDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (s *ssh) GetInfo() []string {
|
||||
return s.infos
|
||||
func (d *SSHDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (s *ssh) Deploy(ctx context.Context) error {
|
||||
access := &sshAccess{}
|
||||
if err := json.Unmarshal([]byte(s.option.Access), access); err != nil {
|
||||
func (d *SSHDeployer) Deploy(ctx context.Context) error {
|
||||
access := &SSHAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 连接
|
||||
client, err := s.getClient(access)
|
||||
client, err := d.createClient(access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s.infos = append(s.infos, toStr("ssh连接成功", nil))
|
||||
d.infos = append(d.infos, toStr("ssh连接成功", nil))
|
||||
|
||||
// 执行前置命令
|
||||
preCommand := getDeployString(s.option.DeployConfig, "preCommand")
|
||||
preCommand := getDeployString(d.option.DeployConfig, "preCommand")
|
||||
if preCommand != "" {
|
||||
stdout, stderr, err := s.sshExecCommand(client, preCommand)
|
||||
stdout, stderr, err := d.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, getDeployString(s.option.DeployConfig, "certPath")); err != nil {
|
||||
if err := d.upload(client, d.option.Certificate.Certificate, getDeployString(d.option.DeployConfig, "certPath")); err != nil {
|
||||
return fmt.Errorf("failed to upload certificate: %w", err)
|
||||
}
|
||||
|
||||
s.infos = append(s.infos, toStr("ssh上传证书成功", nil))
|
||||
d.infos = append(d.infos, toStr("ssh上传证书成功", nil))
|
||||
|
||||
// 上传私钥
|
||||
if err := s.upload(client, s.option.Certificate.PrivateKey, getDeployString(s.option.DeployConfig, "keyPath")); err != nil {
|
||||
if err := d.upload(client, d.option.Certificate.PrivateKey, getDeployString(d.option.DeployConfig, "keyPath")); err != nil {
|
||||
return fmt.Errorf("failed to upload private key: %w", err)
|
||||
}
|
||||
|
||||
s.infos = append(s.infos, toStr("ssh上传私钥成功", nil))
|
||||
d.infos = append(d.infos, toStr("ssh上传私钥成功", nil))
|
||||
|
||||
// 执行命令
|
||||
stdout, stderr, err := s.sshExecCommand(client, getDeployString(s.option.DeployConfig, "command"))
|
||||
stdout, stderr, err := d.sshExecCommand(client, getDeployString(d.option.DeployConfig, "command"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
|
||||
}
|
||||
|
||||
s.infos = append(s.infos, toStr("ssh执行命令成功", stdout))
|
||||
d.infos = append(d.infos, toStr("ssh执行命令成功", stdout))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ssh) sshExecCommand(client *sshPkg.Client, command string) (string, string, error) {
|
||||
func (d *SSHDeployer) sshExecCommand(client *sshPkg.Client, command string) (string, string, error) {
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to create ssh session: %w", err)
|
||||
@@ -105,7 +105,7 @@ func (s *ssh) sshExecCommand(client *sshPkg.Client, command string) (string, str
|
||||
return stdoutBuf.String(), stderrBuf.String(), err
|
||||
}
|
||||
|
||||
func (s *ssh) upload(client *sshPkg.Client, content, path string) error {
|
||||
func (d *SSHDeployer) upload(client *sshPkg.Client, content, path string) error {
|
||||
sftpCli, err := sftp.NewClient(client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create sftp client: %w", err)
|
||||
@@ -130,7 +130,7 @@ func (s *ssh) upload(client *sshPkg.Client, content, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ssh) getClient(access *sshAccess) (*sshPkg.Client, error) {
|
||||
func (d *SSHDeployer) createClient(access *SSHAccess) (*sshPkg.Client, error) {
|
||||
var authMethod sshPkg.AuthMethod
|
||||
|
||||
if access.Key != "" {
|
||||
|
||||
@@ -16,13 +16,13 @@ import (
|
||||
"certimate/internal/utils/rand"
|
||||
)
|
||||
|
||||
type tencentCdn struct {
|
||||
type TencentCDNDeployer struct {
|
||||
option *DeployerOption
|
||||
credential *common.Credential
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewTencentCdn(option *DeployerOption) (Deployer, error) {
|
||||
func NewTencentCDNDeployer(option *DeployerOption) (Deployer, error) {
|
||||
access := &domain.TencentAccess{}
|
||||
if err := json.Unmarshal([]byte(option.Access), access); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal tencent access: %w", err)
|
||||
@@ -33,47 +33,47 @@ func NewTencentCdn(option *DeployerOption) (Deployer, error) {
|
||||
access.SecretKey,
|
||||
)
|
||||
|
||||
return &tencentCdn{
|
||||
return &TencentCDNDeployer{
|
||||
option: option,
|
||||
credential: credential,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *tencentCdn) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", a.option.AceessRecord.GetString("name"), a.option.AceessRecord.Id)
|
||||
func (d *TencentCDNDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (t *tencentCdn) GetInfo() []string {
|
||||
return t.infos
|
||||
func (d *TencentCDNDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
func (t *tencentCdn) Deploy(ctx context.Context) error {
|
||||
func (d *TencentCDNDeployer) Deploy(ctx context.Context) error {
|
||||
// 上传证书
|
||||
certId, err := t.uploadCert()
|
||||
certId, err := d.uploadCert()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload certificate: %w", err)
|
||||
}
|
||||
t.infos = append(t.infos, toStr("上传证书", certId))
|
||||
d.infos = append(d.infos, toStr("上传证书", certId))
|
||||
|
||||
if err := t.deploy(certId); err != nil {
|
||||
if err := d.deploy(certId); err != nil {
|
||||
return fmt.Errorf("failed to deploy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tencentCdn) uploadCert() (string, error) {
|
||||
func (d *TencentCDNDeployer) uploadCert() (string, error) {
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com"
|
||||
|
||||
client, _ := ssl.NewClient(t.credential, "", cpf)
|
||||
client, _ := ssl.NewClient(d.credential, "", cpf)
|
||||
|
||||
request := ssl.NewUploadCertificateRequest()
|
||||
|
||||
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.CertificatePublicKey = common.StringPtr(d.option.Certificate.Certificate)
|
||||
request.CertificatePrivateKey = common.StringPtr(d.option.Certificate.PrivateKey)
|
||||
request.Alias = common.StringPtr(d.option.Domain + "_" + rand.RandStr(6))
|
||||
request.Repeatable = common.BoolPtr(false)
|
||||
|
||||
response, err := client.UploadCertificate(request)
|
||||
@@ -84,11 +84,11 @@ func (t *tencentCdn) uploadCert() (string, error) {
|
||||
return *response.Response.CertificateId, nil
|
||||
}
|
||||
|
||||
func (t *tencentCdn) deploy(certId string) error {
|
||||
func (d *TencentCDNDeployer) deploy(certId string) error {
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com"
|
||||
// 实例化要请求产品的client对象,clientProfile是可选的
|
||||
client, _ := ssl.NewClient(t.credential, "", cpf)
|
||||
client, _ := ssl.NewClient(d.credential, "", cpf)
|
||||
|
||||
// 实例化一个请求对象,每个接口都会对应一个request对象
|
||||
request := ssl.NewDeployCertificateInstanceRequest()
|
||||
@@ -98,9 +98,9 @@ func (t *tencentCdn) deploy(certId string) error {
|
||||
request.Status = common.Int64Ptr(1)
|
||||
|
||||
// 如果是泛域名就从cdn列表下获取SSL证书中的可用域名
|
||||
domain := getDeployString(t.option.DeployConfig, "domain")
|
||||
domain := getDeployString(d.option.DeployConfig, "domain")
|
||||
if strings.Contains(domain, "*") {
|
||||
list, errGetList := t.getDomainList()
|
||||
list, errGetList := d.getDomainList()
|
||||
if errGetList != nil {
|
||||
return fmt.Errorf("failed to get certificate domain list: %w", errGetList)
|
||||
}
|
||||
@@ -117,18 +117,18 @@ func (t *tencentCdn) deploy(certId string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deploy certificate: %w", err)
|
||||
}
|
||||
t.infos = append(t.infos, toStr("部署证书", resp.Response))
|
||||
d.infos = append(d.infos, toStr("部署证书", resp.Response))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tencentCdn) getDomainList() ([]string, error) {
|
||||
func (d *TencentCDNDeployer) getDomainList() ([]string, error) {
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.Endpoint = "cdn.tencentcloudapi.com"
|
||||
client, _ := cdn.NewClient(t.credential, "", cpf)
|
||||
client, _ := cdn.NewClient(d.credential, "", cpf)
|
||||
|
||||
request := cdn.NewDescribeCertDomainsRequest()
|
||||
|
||||
cert := base64.StdEncoding.EncodeToString([]byte(t.option.Certificate.Certificate))
|
||||
cert := base64.StdEncoding.EncodeToString([]byte(d.option.Certificate.Certificate))
|
||||
request.Cert = &cert
|
||||
|
||||
response, err := client.DescribeCertDomains(request)
|
||||
|
||||
@@ -10,48 +10,48 @@ import (
|
||||
xhttp "certimate/internal/utils/http"
|
||||
)
|
||||
|
||||
type webhookAccess struct {
|
||||
type WebhookAccess struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type hookData struct {
|
||||
type WebhookDeployer struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewWebhookDeployer(option *DeployerOption) (Deployer, error) {
|
||||
return &WebhookDeployer{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *WebhookDeployer) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", d.option.AceessRecord.GetString("name"), d.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (d *WebhookDeployer) GetInfo() []string {
|
||||
return d.infos
|
||||
}
|
||||
|
||||
type webhookData struct {
|
||||
Domain string `json:"domain"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Variables map[string]string `json:"variables"`
|
||||
}
|
||||
|
||||
type webhook struct {
|
||||
option *DeployerOption
|
||||
infos []string
|
||||
}
|
||||
|
||||
func NewWebhook(option *DeployerOption) (Deployer, error) {
|
||||
return &webhook{
|
||||
option: option,
|
||||
infos: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *webhook) GetID() string {
|
||||
return fmt.Sprintf("%s-%s", a.option.AceessRecord.GetString("name"), a.option.AceessRecord.Id)
|
||||
}
|
||||
|
||||
func (w *webhook) GetInfo() []string {
|
||||
return w.infos
|
||||
}
|
||||
|
||||
func (w *webhook) Deploy(ctx context.Context) error {
|
||||
access := &webhookAccess{}
|
||||
if err := json.Unmarshal([]byte(w.option.Access), access); err != nil {
|
||||
func (d *WebhookDeployer) Deploy(ctx context.Context) error {
|
||||
access := &WebhookAccess{}
|
||||
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
|
||||
return fmt.Errorf("failed to parse hook access config: %w", err)
|
||||
}
|
||||
|
||||
data := &hookData{
|
||||
Domain: w.option.Domain,
|
||||
Certificate: w.option.Certificate.Certificate,
|
||||
PrivateKey: w.option.Certificate.PrivateKey,
|
||||
Variables: getDeployVariables(w.option.DeployConfig),
|
||||
data := &webhookData{
|
||||
Domain: d.option.Domain,
|
||||
Certificate: d.option.Certificate.Certificate,
|
||||
PrivateKey: d.option.Certificate.PrivateKey,
|
||||
Variables: getDeployVariables(d.option.DeployConfig),
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(data)
|
||||
@@ -63,7 +63,7 @@ func (w *webhook) Deploy(ctx context.Context) error {
|
||||
return fmt.Errorf("failed to send hook request: %w", err)
|
||||
}
|
||||
|
||||
w.infos = append(w.infos, toStr("webhook response", string(resp)))
|
||||
d.infos = append(d.infos, toStr("webhook response", string(resp)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ type HuaweiCloudAccess struct {
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
}
|
||||
|
||||
type AwsAccess struct {
|
||||
Region string `json:"region"`
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
HostedZoneId string `json:"hostedZoneId"`
|
||||
}
|
||||
|
||||
type CloudflareAccess struct {
|
||||
DnsApiToken string `json:"dnsApiToken"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"certimate/internal/utils/app"
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
@@ -8,15 +9,15 @@ import (
|
||||
notifyPackage "github.com/nikoksr/notify"
|
||||
"github.com/nikoksr/notify/service/dingding"
|
||||
"github.com/nikoksr/notify/service/http"
|
||||
"github.com/nikoksr/notify/service/lark"
|
||||
"github.com/nikoksr/notify/service/telegram"
|
||||
|
||||
"certimate/internal/utils/app"
|
||||
)
|
||||
|
||||
const (
|
||||
notifyChannelDingtalk = "dingtalk"
|
||||
notifyChannelWebhook = "webhook"
|
||||
notifyChannelTelegram = "telegram"
|
||||
notifyChannelLark = "lark"
|
||||
)
|
||||
|
||||
func Send(title, content string) error {
|
||||
@@ -67,6 +68,8 @@ func getNotifiers() ([]notifyPackage.Notifier, error) {
|
||||
notifiers = append(notifiers, temp)
|
||||
case notifyChannelDingtalk:
|
||||
notifiers = append(notifiers, getDingTalkNotifier(v))
|
||||
case notifyChannelLark:
|
||||
notifiers = append(notifiers, getLarkNotifier(v))
|
||||
case notifyChannelWebhook:
|
||||
notifiers = append(notifiers, getWebhookNotifier(v))
|
||||
}
|
||||
@@ -108,6 +111,10 @@ func getDingTalkNotifier(conf map[string]any) notifyPackage.Notifier {
|
||||
})
|
||||
}
|
||||
|
||||
func getLarkNotifier(conf map[string]any) notifyPackage.Notifier {
|
||||
return lark.NewWebhookService(getString(conf, "webhookUrl"))
|
||||
}
|
||||
|
||||
func getString(conf map[string]any, key string) string {
|
||||
if _, ok := conf[key]; !ok {
|
||||
return ""
|
||||
|
||||
93
migrations/1729160433_updated_access.go
Normal file
93
migrations/1729160433_updated_access.go
Normal file
@@ -0,0 +1,93 @@
|
||||
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",
|
||||
"aws",
|
||||
"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",
|
||||
"qiniu",
|
||||
"cloudflare",
|
||||
"namesilo",
|
||||
"godaddy",
|
||||
"local",
|
||||
"ssh",
|
||||
"webhook"
|
||||
]
|
||||
}
|
||||
}`), edit_configType); err != nil {
|
||||
return err
|
||||
}
|
||||
collection.Schema.AddField(edit_configType)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
})
|
||||
}
|
||||
95
migrations/1729241998_updated_access.go
Normal file
95
migrations/1729241998_updated_access.go
Normal file
@@ -0,0 +1,95 @@
|
||||
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",
|
||||
"aws",
|
||||
"cloudflare",
|
||||
"namesilo",
|
||||
"godaddy",
|
||||
"local",
|
||||
"ssh",
|
||||
"webhook",
|
||||
"k8s"
|
||||
]
|
||||
}
|
||||
}`), 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",
|
||||
"qiniu",
|
||||
"aws",
|
||||
"cloudflare",
|
||||
"namesilo",
|
||||
"godaddy",
|
||||
"local",
|
||||
"ssh",
|
||||
"webhook"
|
||||
]
|
||||
}
|
||||
}`), edit_configType); err != nil {
|
||||
return err
|
||||
}
|
||||
collection.Schema.AddField(edit_configType)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
})
|
||||
}
|
||||
329
ui/dist/assets/index-BTjl7ZFn.js
vendored
329
ui/dist/assets/index-BTjl7ZFn.js
vendored
File diff suppressed because one or more lines are too long
329
ui/dist/assets/index-C9KsIpHj.js
vendored
Normal file
329
ui/dist/assets/index-C9KsIpHj.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
ui/dist/imgs/providers/aliyun.svg
vendored
2
ui/dist/imgs/providers/aliyun.svg
vendored
@@ -1 +1 @@
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9731" width="64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M1020.586667 361.813333c0-92.16-75.093333-167.253333-167.253334-167.253333h-266.24l23.893334 95.573333 228.693333 51.2c20.48 3.413333 37.546667 23.893333 37.546667 44.373334v245.76c0 20.48-17.066667 40.96-37.546667 44.373333l-228.693333 51.2-27.306667 92.16h266.24c92.16 0 167.253333-75.093333 167.253333-167.253333 3.413333 0 3.413333-290.133333 3.413334-290.133334zM187.733333 672.426667c-20.48-3.413333-37.546667-23.893333-37.546666-44.373334v-245.76c0-20.48 17.066667-40.96 37.546666-44.373333l228.693334-51.2 23.893333-95.573333H174.08C81.92 191.146667 6.826667 266.24 6.826667 358.4V648.533333c0 92.16 75.093333 167.253333 167.253333 167.253334h266.24l-23.893333-95.573334c0 3.413333-228.693333-47.786667-228.693334-47.786666z m215.04-211.626667h218.453334v88.746667h-218.453334v-88.746667z" fill="#ff6b01" p-id="9732"></path></svg>
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5262" width="200" height="200"><path d="M512 64a448 448 0 1 1 0 896A448 448 0 0 1 512 64z" fill="#FF6A00" p-id="5263"></path><path d="M324.8 602.624a26.752 26.752 0 0 1-21.312-25.92v-142.72a27.712 27.712 0 0 1 21.376-25.984l132.416-28.672 13.952-56.896H317.312a97.6 97.6 0 0 0-98.24 96.96v169.344c0.384 54.08 44.16 97.856 98.24 98.176h153.92l-13.888-56.512-132.544-27.776zM710.4 322.432c54.016 0.128 97.92 43.584 98.56 97.6v170.176a98.368 98.368 0 0 1-98.56 98.048H555.328l14.08-56.832 132.608-28.736a27.84 27.84 0 0 0 21.376-25.92v-142.72a26.88 26.88 0 0 0-21.376-25.984l-132.544-28.8-14.08-56.832zM570.368 497.92v13.952H457.28v-13.952h113.088z" fill="#FFFFFF" p-id="5264"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1017 B After Width: | Height: | Size: 785 B |
1
ui/dist/imgs/providers/aws.svg
vendored
Normal file
1
ui/dist/imgs/providers/aws.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
1
ui/dist/imgs/providers/k8s.svg
vendored
Normal file
1
ui/dist/imgs/providers/k8s.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
2
ui/dist/index.html
vendored
2
ui/dist/index.html
vendored
@@ -5,7 +5,7 @@
|
||||
<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-BTjl7ZFn.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-C9KsIpHj.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-YqBWA4KK.css">
|
||||
</head>
|
||||
<body class="bg-background">
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9731" width="64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M1020.586667 361.813333c0-92.16-75.093333-167.253333-167.253334-167.253333h-266.24l23.893334 95.573333 228.693333 51.2c20.48 3.413333 37.546667 23.893333 37.546667 44.373334v245.76c0 20.48-17.066667 40.96-37.546667 44.373333l-228.693333 51.2-27.306667 92.16h266.24c92.16 0 167.253333-75.093333 167.253333-167.253333 3.413333 0 3.413333-290.133333 3.413334-290.133334zM187.733333 672.426667c-20.48-3.413333-37.546667-23.893333-37.546666-44.373334v-245.76c0-20.48 17.066667-40.96 37.546666-44.373333l228.693334-51.2 23.893333-95.573333H174.08C81.92 191.146667 6.826667 266.24 6.826667 358.4V648.533333c0 92.16 75.093333 167.253333 167.253333 167.253334h266.24l-23.893333-95.573334c0 3.413333-228.693333-47.786667-228.693334-47.786666z m215.04-211.626667h218.453334v88.746667h-218.453334v-88.746667z" fill="#ff6b01" p-id="9732"></path></svg>
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5262" width="200" height="200"><path d="M512 64a448 448 0 1 1 0 896A448 448 0 0 1 512 64z" fill="#FF6A00" p-id="5263"></path><path d="M324.8 602.624a26.752 26.752 0 0 1-21.312-25.92v-142.72a27.712 27.712 0 0 1 21.376-25.984l132.416-28.672 13.952-56.896H317.312a97.6 97.6 0 0 0-98.24 96.96v169.344c0.384 54.08 44.16 97.856 98.24 98.176h153.92l-13.888-56.512-132.544-27.776zM710.4 322.432c54.016 0.128 97.92 43.584 98.56 97.6v170.176a98.368 98.368 0 0 1-98.56 98.048H555.328l14.08-56.832 132.608-28.736a27.84 27.84 0 0 0 21.376-25.92v-142.72a26.88 26.88 0 0 0-21.376-25.984l-132.544-28.8-14.08-56.832zM570.368 497.92v13.952H457.28v-13.952h113.088z" fill="#FFFFFF" p-id="5264"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1017 B After Width: | Height: | Size: 785 B |
1
ui/public/imgs/providers/aws.svg
Normal file
1
ui/public/imgs/providers/aws.svg
Normal file
File diff suppressed because one or more lines are too long
1
ui/public/imgs/providers/k8s.svg
Normal file
1
ui/public/imgs/providers/k8s.svg
Normal file
File diff suppressed because one or more lines are too long
240
ui/src/components/certimate/AccessAwsForm.tsx
Normal file
240
ui/src/components/certimate/AccessAwsForm.tsx
Normal file
@@ -0,0 +1,240 @@
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import z from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { Access, accessFormType, AwsConfig, getUsageByConfigType } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfig } from "@/providers/config";
|
||||
|
||||
type AccessAwsFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
|
||||
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.secret_access_key.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
hostedZoneId: z
|
||||
.string()
|
||||
.min(0, "access.authorization.form.aws_hosted_zone_id.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
});
|
||||
|
||||
let config: AwsConfig = {
|
||||
region: "cn-north-1",
|
||||
accessKeyId: "",
|
||||
secretAccessKey: "",
|
||||
hostedZoneId: "",
|
||||
};
|
||||
if (data) config = data.config as AwsConfig;
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || "",
|
||||
configType: "aws",
|
||||
region: config.region,
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
hostedZoneId: config.hostedZoneId,
|
||||
},
|
||||
});
|
||||
|
||||
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,
|
||||
hostedZoneId: data.hostedZoneId,
|
||||
},
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
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.secret_access_key.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("access.authorization.form.secret_access_key.placeholder")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="hostedZoneId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("access.authorization.form.aws_hosted_zone_id.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("access.authorization.form.aws_hosted_zone_id.placeholder")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessAwsForm;
|
||||
@@ -10,12 +10,14 @@ import AccessAliyunForm from "./AccessAliyunForm";
|
||||
import AccessTencentForm from "./AccessTencentForm";
|
||||
import AccessHuaweicloudForm from "./AccessHuaweicloudForm";
|
||||
import AccessQiniuForm from "./AccessQiniuForm";
|
||||
import AccessAwsForm from "./AccessAwsForm";
|
||||
import AccessCloudflareForm from "./AccessCloudflareForm";
|
||||
import AccessNamesiloForm from "./AccessNamesiloForm";
|
||||
import AccessGodaddyForm from "./AccessGodaddyForm";
|
||||
import AccessLocalForm from "./AccessLocalForm";
|
||||
import AccessSSHForm from "./AccessSSHForm";
|
||||
import AccessWebhookForm from "./AccessWebhookForm";
|
||||
import AccessKubernetesForm from "./AccessKubernetesForm";
|
||||
import { Access, accessTypeMap } from "@/domain/access";
|
||||
|
||||
type AccessEditProps = {
|
||||
@@ -79,6 +81,17 @@ const AccessEdit = ({ trigger, op, data, className }: AccessEditProps) => {
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "aws":
|
||||
form = (
|
||||
<AccessAwsForm
|
||||
data={data}
|
||||
op={op}
|
||||
onAfterReq={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "cloudflare":
|
||||
form = (
|
||||
<AccessCloudflareForm
|
||||
@@ -145,6 +158,17 @@ const AccessEdit = ({ trigger, op, data, className }: AccessEditProps) => {
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "k8s":
|
||||
form = (
|
||||
<AccessKubernetesForm
|
||||
data={data}
|
||||
op={op}
|
||||
onAfterReq={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const getOptionCls = (val: string) => {
|
||||
|
||||
@@ -38,7 +38,7 @@ const AccessHuaweicloudForm = ({ data, op, onAfterReq }: AccessHuaweicloudFormPr
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
secretAccessKey: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.access_key_secret.placeholder")
|
||||
.min(1, "access.authorization.form.secret_access_key.placeholder")
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
});
|
||||
|
||||
@@ -193,9 +193,9 @@ const AccessHuaweicloudForm = ({ data, op, onAfterReq }: AccessHuaweicloudFormPr
|
||||
name="secretAccessKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("access.authorization.form.access_key_secret.label")}</FormLabel>
|
||||
<FormLabel>{t("access.authorization.form.secret_access_key.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("access.authorization.form.access_key_secret.placeholder")} {...field} />
|
||||
<Input placeholder={t("access.authorization.form.secret_access_key.placeholder")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
195
ui/src/components/certimate/AccessKubernetesForm.tsx
Normal file
195
ui/src/components/certimate/AccessKubernetesForm.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
import { Access, accessFormType, getUsageByConfigType, KubernetesConfig } from "@/domain/access";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { readFileContent } from "@/lib/file";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfig } from "@/providers/config";
|
||||
|
||||
type AccessKubernetesFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfig();
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [fileName, setFileName] = useState("");
|
||||
|
||||
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,
|
||||
kubeConfig: z
|
||||
.string()
|
||||
.min(1, "access.authorization.form.k8s_kubeconfig.placeholder")
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 })),
|
||||
kubeConfigFile: z.any().optional(),
|
||||
});
|
||||
|
||||
let config: KubernetesConfig & { kubeConfigFile?: string } = {
|
||||
kubeConfig: "",
|
||||
kubeConfigFile: "",
|
||||
};
|
||||
if (data) config = data.config as typeof config;
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
id: data?.id,
|
||||
name: data?.name || "",
|
||||
configType: "k8s",
|
||||
kubeConfig: config.kubeConfig,
|
||||
kubeConfigFile: config.kubeConfigFile,
|
||||
},
|
||||
});
|
||||
|
||||
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: {
|
||||
kubeConfig: data.kubeConfig,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
req.id = op == "copy" ? "" : req.id;
|
||||
const rs = await save(req);
|
||||
|
||||
onAfterReq();
|
||||
|
||||
req.id = rs.id;
|
||||
req.created = rs.created;
|
||||
req.updated = rs.updated;
|
||||
if (data.id && op == "edit") {
|
||||
updateAccess(req);
|
||||
} else {
|
||||
addAccess(req);
|
||||
}
|
||||
} 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 handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
const savedFile = file;
|
||||
setFileName(savedFile.name);
|
||||
const content = await readFileContent(savedFile);
|
||||
form.setValue("kubeConfig", content);
|
||||
};
|
||||
|
||||
const handleSelectFileClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="max-w-[35em] mx-auto mt-10">
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.stopPropagation();
|
||||
form.handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
className="space-y-3"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("access.authorization.form.name.placeholder")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="kubeConfig"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden>
|
||||
<FormLabel>{t("access.authorization.form.k8s_kubeconfig.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("access.authorization.form.k8s_kubeconfig.placeholder")} {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="kubeConfigFile"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("access.authorization.form.k8s_kubeconfig.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<div>
|
||||
<Button type={"button"} variant={"secondary"} size={"sm"} className="w-48" onClick={handleSelectFileClick}>
|
||||
{fileName ? fileName : t("access.authorization.form.k8s_kubeconfig_file.placeholder")}
|
||||
</Button>
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.k8s_kubeconfig.placeholder")}
|
||||
{...field}
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
hidden
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessKubernetesForm;
|
||||
@@ -140,8 +140,10 @@ const DeployItem = ({ item, onDelete, onSave }: DeployItemProps) => {
|
||||
config: { accesses },
|
||||
} = useConfig();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const access = accesses.find((access) => access.id === item.access);
|
||||
const getImg = () => {
|
||||
|
||||
const getTypeIcon = () => {
|
||||
if (!access) {
|
||||
return "";
|
||||
}
|
||||
@@ -173,7 +175,7 @@ const DeployItem = ({ item, onDelete, onSave }: DeployItemProps) => {
|
||||
<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>
|
||||
<img src={getTypeIcon()} className="w-9"></img>
|
||||
</div>
|
||||
<div className="text-stone-600 flex-col flex space-y-0 dark:text-stone-200">
|
||||
<div>{getTypeName()}</div>
|
||||
@@ -239,7 +241,8 @@ const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogPro
|
||||
|
||||
let t;
|
||||
if (temp && temp.length > 1) {
|
||||
t = temp[1];
|
||||
// TODO: code smell, maybe a dictionary is better
|
||||
t = temp[0] === "k8s" ? temp[0] : temp[1];
|
||||
} else {
|
||||
t = locDeployConfig.type;
|
||||
}
|
||||
@@ -419,7 +422,7 @@ const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogPro
|
||||
);
|
||||
};
|
||||
|
||||
type TargetType = "ssh" | "cdn" | "webhook" | "local" | "oss" | "dcdn";
|
||||
type TargetType = "oss" | "cdn" | "dcdn" | "local" | "ssh" | "webhook" | "k8s";
|
||||
|
||||
type DeployEditProps = {
|
||||
type: TargetType;
|
||||
@@ -428,18 +431,20 @@ type DeployEditProps = {
|
||||
const DeployEdit = ({ type }: DeployEditProps) => {
|
||||
const getDeploy = () => {
|
||||
switch (type) {
|
||||
case "ssh":
|
||||
return <DeployToSSH />;
|
||||
case "local":
|
||||
return <DeployToSSH />;
|
||||
case "cdn":
|
||||
return <DeployToCDN />;
|
||||
case "dcdn":
|
||||
return <DeployToCDN />;
|
||||
case "oss":
|
||||
return <DeployToOSS />;
|
||||
case "ssh":
|
||||
return <DeployToSSH />;
|
||||
case "local":
|
||||
return <DeployToSSH />;
|
||||
case "webhook":
|
||||
return <DeployToWebhook />;
|
||||
case "k8s":
|
||||
return <DeployToKubernetes />;
|
||||
default:
|
||||
return <DeployToCDN />;
|
||||
}
|
||||
@@ -474,9 +479,9 @@ const DeployToSSH = () => {
|
||||
<>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div>
|
||||
<Label>{t("access.authorization.form.ssh_cert_path.label")}</Label>
|
||||
<Label>{t("domain.deployment.form.ssh_cert_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.ssh_cert_path.label")}
|
||||
placeholder={t("domain.deployment.form.ssh_cert_path.label")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.certPath}
|
||||
onChange={(e) => {
|
||||
@@ -491,9 +496,9 @@ const DeployToSSH = () => {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>{t("access.authorization.form.ssh_key_path.label")}</Label>
|
||||
<Label>{t("domain.deployment.form.ssh_key_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("access.authorization.form.ssh_key_path.placeholder")}
|
||||
placeholder={t("domain.deployment.form.ssh_key_path.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.keyPath}
|
||||
onChange={(e) => {
|
||||
@@ -509,11 +514,11 @@ const DeployToSSH = () => {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("access.authorization.form.ssh_pre_command.label")}</Label>
|
||||
<Label>{t("domain.deployment.form.ssh_pre_command.label")}</Label>
|
||||
<Textarea
|
||||
className="mt-1"
|
||||
value={data?.config?.preCommand}
|
||||
placeholder={t("access.authorization.form.ssh_pre_command.placeholder")}
|
||||
placeholder={t("domain.deployment.form.ssh_pre_command.placeholder")}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
if (!draft.config) {
|
||||
@@ -527,11 +532,11 @@ const DeployToSSH = () => {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("access.authorization.form.ssh_command.label")}</Label>
|
||||
<Label>{t("domain.deployment.form.ssh_command.label")}</Label>
|
||||
<Textarea
|
||||
className="mt-1"
|
||||
value={data?.config?.command}
|
||||
placeholder={t("access.authorization.form.ssh_command.placeholder")}
|
||||
placeholder={t("domain.deployment.form.ssh_command.placeholder")}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
if (!draft.config) {
|
||||
@@ -548,70 +553,30 @@ const DeployToSSH = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const DeployToCDN = () => {
|
||||
const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
|
||||
const DeployToWebhook = () => {
|
||||
const { deploy: data, setDeploy } = useDeployEditContext();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { setError } = useDeployEditContext();
|
||||
|
||||
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: "",
|
||||
});
|
||||
<>
|
||||
<KVList
|
||||
variables={data?.config?.variables}
|
||||
onValueChange={(variables: KVType[]) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
if (!draft.config) {
|
||||
draft.config = {};
|
||||
}
|
||||
|
||||
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>
|
||||
draft.config.variables = variables;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -681,6 +646,7 @@ const DeployToOSS = () => {
|
||||
<Label>{t("domain.deployment.form.oss_endpoint.label")}</Label>
|
||||
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.oss_endpoint.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.endpoint}
|
||||
onChange={(e) => {
|
||||
@@ -697,7 +663,7 @@ const DeployToOSS = () => {
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.endpoint}</div>
|
||||
|
||||
<Label>{t("domain.deployment.form.oss_bucket")}</Label>
|
||||
<Label>{t("domain.deployment.form.oss_bucket.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.oss_bucket.placeholder")}
|
||||
className="w-full mt-1"
|
||||
@@ -729,9 +695,9 @@ const DeployToOSS = () => {
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.bucket}</div>
|
||||
|
||||
<Label>{t("domain.deployment.form.cdn_domain.label")}</Label>
|
||||
<Label>{t("domain.deployment.form.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.cdn_domain.label")}
|
||||
placeholder={t("domain.deployment.form.domain.label")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.domain}
|
||||
onChange={(e) => {
|
||||
@@ -765,29 +731,161 @@ const DeployToOSS = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const DeployToWebhook = () => {
|
||||
const { deploy: data, setDeploy } = useDeployEditContext();
|
||||
const DeployToCDN = () => {
|
||||
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.domain.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.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 DeployToKubernetes = () => {
|
||||
const { t } = useTranslation();
|
||||
const { setError } = useDeployEditContext();
|
||||
|
||||
useEffect(() => {
|
||||
setError({});
|
||||
}, []);
|
||||
|
||||
const { deploy: data, setDeploy } = useDeployEditContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!data.id) {
|
||||
setDeploy({
|
||||
...data,
|
||||
config: {
|
||||
namespace: "default",
|
||||
secretName: "",
|
||||
secretDataKeyForCrt: "tls.crt",
|
||||
secretDataKeyForKey: "tls.key",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
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);
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_namespace.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_namespace.label")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.namespace}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.namespace = e.target.value;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_secret_name.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_secret_name.label")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.secretName}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.secretName = e.target.value;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_secret_data_key_for_crt.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_secret_data_key_for_crt.label")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.secretDataKeyForCrt}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.secretDataKeyForCrt = e.target.value;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.k8s_secret_data_key_for_key.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.k8s_secret_data_key_for_key.label")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.secretDataKeyForKey}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.secretDataKeyForKey = e.target.value;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
136
ui/src/components/notify/Lark.tsx
Normal file
136
ui/src/components/notify/Lark.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import { Input } from "../ui/input";
|
||||
import { Button } from "../ui/button";
|
||||
import { Switch } from "../ui/switch";
|
||||
import { Label } from "../ui/label";
|
||||
import { useNotify } from "@/providers/notify";
|
||||
import { NotifyChannelLark, NotifyChannels } from "@/domain/settings";
|
||||
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";
|
||||
|
||||
type LarkSetting = {
|
||||
id: string;
|
||||
name: string;
|
||||
data: NotifyChannelLark;
|
||||
};
|
||||
|
||||
const Lark = () => {
|
||||
const { config, setChannels } = useNotify();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [lark, setLark] = useState<LarkSetting>({
|
||||
id: config.id ?? "",
|
||||
name: "notifyChannels",
|
||||
data: {
|
||||
webhookUrl: "",
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const getDetailLark = () => {
|
||||
const df: NotifyChannelLark = {
|
||||
webhookUrl: "",
|
||||
enabled: false,
|
||||
};
|
||||
if (!config.content) {
|
||||
return df;
|
||||
}
|
||||
const chanels = config.content as NotifyChannels;
|
||||
if (!chanels.lark) {
|
||||
return df;
|
||||
}
|
||||
|
||||
return chanels.lark as NotifyChannelLark;
|
||||
};
|
||||
const data = getDetailLark();
|
||||
setLark({
|
||||
id: config.id ?? "",
|
||||
name: "lark",
|
||||
data,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleSaveClick = async () => {
|
||||
try {
|
||||
const resp = await update({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
...config.content,
|
||||
lark: {
|
||||
...lark.data,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
description: `${t(
|
||||
"settings.notification.config.failed.message"
|
||||
)}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input
|
||||
placeholder="Webhook Url"
|
||||
value={lark.data.webhookUrl}
|
||||
onChange={(e) => {
|
||||
setLark({
|
||||
...lark,
|
||||
data: {
|
||||
...lark.data,
|
||||
webhookUrl: e.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center space-x-1 mt-2">
|
||||
<Switch
|
||||
id="airplane-mode"
|
||||
checked={lark.data.enabled}
|
||||
onCheckedChange={() => {
|
||||
setLark({
|
||||
...lark,
|
||||
data: {
|
||||
...lark.data,
|
||||
enabled: !lark.data.enabled,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="airplane-mode">
|
||||
{t("settings.notification.config.enable")}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end mt-2">
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Lark;
|
||||
@@ -5,12 +5,14 @@ export const accessTypeMap: Map<string, [string, string]> = new Map([
|
||||
["tencent", ["common.provider.tencent", "/imgs/providers/tencent.svg"]],
|
||||
["huaweicloud", ["common.provider.huaweicloud", "/imgs/providers/huaweicloud.svg"]],
|
||||
["qiniu", ["common.provider.qiniu", "/imgs/providers/qiniu.svg"]],
|
||||
["aws", ["common.provider.aws", "/imgs/providers/aws.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"]],
|
||||
["k8s", ["common.provider.kubernetes", "/imgs/providers/k8s.svg"]],
|
||||
]);
|
||||
|
||||
export const getProviderInfo = (t: string) => {
|
||||
@@ -23,12 +25,14 @@ export const accessFormType = z.union(
|
||||
z.literal("tencent"),
|
||||
z.literal("huaweicloud"),
|
||||
z.literal("qiniu"),
|
||||
z.literal("aws"),
|
||||
z.literal("cloudflare"),
|
||||
z.literal("namesilo"),
|
||||
z.literal("godaddy"),
|
||||
z.literal("local"),
|
||||
z.literal("ssh"),
|
||||
z.literal("webhook"),
|
||||
z.literal("k8s"),
|
||||
],
|
||||
{ message: "access.authorization.form.type.placeholder" }
|
||||
);
|
||||
@@ -46,12 +50,14 @@ export type Access = {
|
||||
| TencentConfig
|
||||
| HuaweicloudConfig
|
||||
| QiniuConfig
|
||||
| AwsConfig
|
||||
| CloudflareConfig
|
||||
| NamesiloConfig
|
||||
| GodaddyConfig
|
||||
| LocalConfig
|
||||
| SSHConfig
|
||||
| WebhookConfig;
|
||||
| WebhookConfig
|
||||
| KubernetesConfig;
|
||||
deleted?: string;
|
||||
created?: string;
|
||||
updated?: string;
|
||||
@@ -78,6 +84,13 @@ export type QiniuConfig = {
|
||||
secretKey: string;
|
||||
};
|
||||
|
||||
export type AwsConfig = {
|
||||
region: string;
|
||||
accessKeyId: string;
|
||||
secretAccessKey: string;
|
||||
hostedZoneId?: string;
|
||||
};
|
||||
|
||||
export type CloudflareConfig = {
|
||||
dnsApiToken: string;
|
||||
};
|
||||
@@ -107,6 +120,10 @@ export type WebhookConfig = {
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type KubernetesConfig = {
|
||||
kubeConfig: string;
|
||||
};
|
||||
|
||||
export const getUsageByConfigType = (configType: string): AccessUsage => {
|
||||
switch (configType) {
|
||||
case "aliyun":
|
||||
@@ -118,8 +135,10 @@ export const getUsageByConfigType = (configType: string): AccessUsage => {
|
||||
case "local":
|
||||
case "ssh":
|
||||
case "webhook":
|
||||
case "k8s":
|
||||
return "deploy";
|
||||
|
||||
case "aws":
|
||||
case "cloudflare":
|
||||
case "namesilo":
|
||||
case "godaddy":
|
||||
|
||||
@@ -75,6 +75,7 @@ export const targetTypeMap: Map<string, [string, string]> = new Map([
|
||||
["local", ["common.provider.local", "/imgs/providers/local.svg"]],
|
||||
["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]],
|
||||
["webhook", ["common.provider.webhook", "/imgs/providers/webhook.svg"]],
|
||||
["k8s-secret", ["common.provider.kubernetes.secret", "/imgs/providers/k8s.svg"]],
|
||||
]);
|
||||
|
||||
export const targetTypeKeys = Array.from(targetTypeMap.keys());
|
||||
|
||||
@@ -19,11 +19,12 @@ export type NotifyTemplate = {
|
||||
|
||||
export type NotifyChannels = {
|
||||
dingtalk?: NotifyChannel;
|
||||
lark?: NotifyChannel;
|
||||
telegram?: NotifyChannel;
|
||||
webhook?: NotifyChannel;
|
||||
};
|
||||
|
||||
export type NotifyChannel = NotifyChannelDingTalk | NotifyChannelTelegram | NotifyChannelWebhook;
|
||||
export type NotifyChannel = NotifyChannelDingTalk | NotifyChannelLark | NotifyChannelTelegram | NotifyChannelWebhook;
|
||||
|
||||
export type NotifyChannelDingTalk = {
|
||||
accessToken: string;
|
||||
@@ -31,6 +32,11 @@ export type NotifyChannelDingTalk = {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type NotifyChannelLark = {
|
||||
webhookUrl: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type NotifyChannelTelegram = {
|
||||
apiToken: string;
|
||||
chatId: string;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const version = "Certimate v0.2.2";
|
||||
export const version = "Certimate v0.2.4";
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
"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.secret_access_key.label": "SecretAccessKey",
|
||||
"access.authorization.form.secret_access_key.placeholder": "Please enter SecretAccessKey",
|
||||
"access.authorization.form.aws_hosted_zone_id.label": "AWS Hosted Zone ID",
|
||||
"access.authorization.form.aws_hosted_zone_id.placeholder": "Please enter AWS Hosted Zone ID",
|
||||
"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",
|
||||
@@ -55,16 +59,11 @@
|
||||
"access.authorization.form.ssh_key_file.placeholder": "Please select file",
|
||||
"access.authorization.form.ssh_key_passphrase.label": "Key Passphrase (Log-in using private key)",
|
||||
"access.authorization.form.ssh_key_passphrase.placeholder": "Please enter Key Passphrase",
|
||||
"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.authorization.form.k8s_kubeconfig.label": "KubeConfig",
|
||||
"access.authorization.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig",
|
||||
"access.authorization.form.k8s_kubeconfig_file.placeholder": "Please select file",
|
||||
|
||||
"access.group.tab": "Authorization Group",
|
||||
|
||||
|
||||
@@ -61,12 +61,16 @@
|
||||
"common.provider.huaweicloud": "Huawei Cloud",
|
||||
"common.provider.qiniu": "Qiniu",
|
||||
"common.provider.qiniu.cdn": "Qiniu - CDN",
|
||||
"common.provider.aws": "AWS",
|
||||
"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.kubernetes": "Kubernetes",
|
||||
"common.provider.kubernetes.secret": "Kubernetes - Secret",
|
||||
"common.provider.dingtalk": "DingTalk",
|
||||
"common.provider.telegram": "Telegram"
|
||||
"common.provider.telegram": "Telegram",
|
||||
"common.provider.lark": "Lark"
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"dashboard.statistics.disabled": "Not Enabled",
|
||||
"dashboard.statistics.unit": "",
|
||||
|
||||
"dashboard.history": "Deployment History"
|
||||
"dashboard.history": "Recently Deployment History"
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"domain.application.form.advanced_settings.label": "Advanced Settings",
|
||||
"domain.application.form.key_algorithm.label": "Certificate Key Algorithm",
|
||||
"domain.application.form.key_algorithm.placeholder": "Please select certificate key algorithm",
|
||||
"domain.application.form.timeout.label": "DNS Propagation Timeout (seconds)",
|
||||
"domain.application.form.timeout.label": "DNS Propagation Timeout (Seconds)",
|
||||
"domain.application.form.timeoue.placeholder": "Please enter maximum waiting time for DNS propagation",
|
||||
"domain.application.unsaved.message": "Please save applyment configuration first",
|
||||
|
||||
@@ -51,11 +51,28 @@
|
||||
"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.domain.label": "Deploy to domain (Single domain only, not wildcard domain)",
|
||||
"domain.deployment.form.domain.placeholder": "Please enter domain to be deployed",
|
||||
"domain.deployment.form.ssh_key_path.label": "Private Key Save Path",
|
||||
"domain.deployment.form.ssh_key_path.placeholder": "Please enter private key save path",
|
||||
"domain.deployment.form.ssh_cert_path.label": "Certificate Save Path",
|
||||
"domain.deployment.form.ssh_cert_path.placeholder": "Please enter certificate save path",
|
||||
"domain.deployment.form.ssh_pre_command.label": "Pre-deployment Command",
|
||||
"domain.deployment.form.ssh_pre_command.placeholder": "Command to be executed before deploying the certificate",
|
||||
"domain.deployment.form.ssh_command.label": "Command",
|
||||
"domain.deployment.form.ssh_command.placeholder": "Please enter command",
|
||||
"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.oss_endpoint.placeholder": "Please enter endpoint",
|
||||
"domain.deployment.form.oss_bucket.label": "Bucket",
|
||||
"domain.deployment.form.oss_bucket.placeholder": "Please enter bucket",
|
||||
"domain.deployment.form.k8s_namespace.label": "Namespace",
|
||||
"domain.deployment.form.k8s_namespace.placeholder": "Please enter namespace",
|
||||
"domain.deployment.form.k8s_secret_name.label": "Secret Name",
|
||||
"domain.deployment.form.k8s_secret_name.placeholder": "Please enter secret name",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_key.label": "Secret Data Key for PublicKey",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_key.placeholder": "Please enter secret data key for public key",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_crt.label": "Secret Data Key for Certificate",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_crt.placeholder": "Please enter secret data key for certificate",
|
||||
"domain.deployment.form.variables.label": "Variable",
|
||||
"domain.deployment.form.variables.key": "Name",
|
||||
"domain.deployment.form.variables.value": "Value",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"history.page.title": "Deployment",
|
||||
"history.page.title": "Deployment History",
|
||||
|
||||
"history.nodata": "You have not created any deployments yet, please add a domain to start deployment!",
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
"access.authorization.form.secret_id.placeholder": "请输入 SecretId",
|
||||
"access.authorization.form.secret_key.label": "SecretKey",
|
||||
"access.authorization.form.secret_key.placeholder": "请输入 SecretKey",
|
||||
"access.authorization.form.secret_access_key.label": "SecretAccessKey",
|
||||
"access.authorization.form.secret_access_key.placeholder": "请输入 SecretAccessKey",
|
||||
"access.authorization.form.aws_hosted_zone_id.label": "AWS 托管区域 ID",
|
||||
"access.authorization.form.aws_hosted_zone_id.placeholder": "请输入 AWS Hosted Zone ID",
|
||||
"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",
|
||||
@@ -55,16 +59,11 @@
|
||||
"access.authorization.form.ssh_key_file.placeholder": "请选择文件",
|
||||
"access.authorization.form.ssh_key_passphrase.label": "Key 口令(使用私钥登录)",
|
||||
"access.authorization.form.ssh_key_passphrase.placeholder": "请输入 Key 口令",
|
||||
"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.authorization.form.k8s_kubeconfig.label": "KubeConfig",
|
||||
"access.authorization.form.k8s_kubeconfig.placeholder": "请输入 KubeConfig",
|
||||
"access.authorization.form.k8s_kubeconfig_file.placeholder": "请选择文件",
|
||||
|
||||
"access.group.tab": "授权组",
|
||||
|
||||
|
||||
@@ -61,12 +61,16 @@
|
||||
"common.provider.huaweicloud": "华为云",
|
||||
"common.provider.qiniu": "七牛云",
|
||||
"common.provider.qiniu.cdn": "七牛云 - CDN",
|
||||
"common.provider.aws": "AWS",
|
||||
"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.kubernetes": "Kubernetes",
|
||||
"common.provider.kubernetes.secret": "Kubernetes - Secret",
|
||||
"common.provider.dingtalk": "钉钉",
|
||||
"common.provider.telegram": "Telegram"
|
||||
"common.provider.telegram": "Telegram",
|
||||
"common.provider.lark": "飞书"
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"dashboard.statistics.disabled": "未启用",
|
||||
"dashboard.statistics.unit": "个",
|
||||
|
||||
"dashboard.history": "部署历史"
|
||||
"dashboard.history": "最近部署"
|
||||
}
|
||||
|
||||
@@ -51,11 +51,28 @@
|
||||
"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.domain.label": "部署到域名(仅支持单个域名;不支持泛域名)",
|
||||
"domain.deployment.form.domain.placeholder": "请输入部署到的域名",
|
||||
"domain.deployment.form.ssh_key_path.label": "私钥保存路径",
|
||||
"domain.deployment.form.ssh_key_path.placeholder": "请输入私钥保存路径",
|
||||
"domain.deployment.form.ssh_cert_path.label": "证书保存路径",
|
||||
"domain.deployment.form.ssh_cert_path.placeholder": "请输入证书保存路径",
|
||||
"domain.deployment.form.ssh_pre_command.label": "前置命令",
|
||||
"domain.deployment.form.ssh_pre_command.placeholder": "在部署证书前执行的命令",
|
||||
"domain.deployment.form.ssh_command.label": "命令",
|
||||
"domain.deployment.form.ssh_command.placeholder": "请输入要执行的命令",
|
||||
"domain.deployment.form.oss_endpoint.label": "Endpoint",
|
||||
"domain.deployment.form.oss_bucket": "存储桶",
|
||||
"domain.deployment.form.oss_endpoint.placeholder": "请输入 Endpoint",
|
||||
"domain.deployment.form.oss_bucket.label": "存储桶",
|
||||
"domain.deployment.form.oss_bucket.placeholder": "请输入存储桶名",
|
||||
"domain.deployment.form.k8s_namespace.label": "命名空间",
|
||||
"domain.deployment.form.k8s_namespace.placeholder": "请输入 K8S 命名空间",
|
||||
"domain.deployment.form.k8s_secret_name.label": "Secret 名称",
|
||||
"domain.deployment.form.k8s_secret_name.placeholder": "请输入 K8S Secret 名称",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_key.label": "Secret 数据键(用于存放公钥的 Key)",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_key.placeholder": "请输入 K8S Secret 中用于存放公钥的数据键",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_crt.label": "Secret 数据键(用于存放证书的 Key)",
|
||||
"domain.deployment.form.k8s_secret_data_key_for_crt.placeholder": "请输入 K8S Secret 中用于存放证书的数据键",
|
||||
"domain.deployment.form.variables.label": "变量",
|
||||
"domain.deployment.form.variables.key": "变量名",
|
||||
"domain.deployment.form.variables.value": "值",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"history.page.title": "部署",
|
||||
"history.page.title": "部署历史",
|
||||
|
||||
"history.nodata": "你暂未创建任何部署,请先添加域名进行部署吧!",
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
|
||||
import DingTalk from "@/components/notify/DingTalk";
|
||||
import Lark from "@/components/notify/Lark";
|
||||
import NotifyTemplate from "@/components/notify/NotifyTemplate";
|
||||
import Telegram from "@/components/notify/Telegram";
|
||||
import Webhook from "@/components/notify/Webhook";
|
||||
@@ -32,6 +33,13 @@ const Notify = () => {
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="item-3" className="dark:border-stone-200">
|
||||
<AccordionTrigger>{t("common.provider.lark")}</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Lark />
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="item-4" className="dark:border-stone-200">
|
||||
<AccordionTrigger>{t("common.provider.telegram")}</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
||||
Reference in New Issue
Block a user