Merge branch 'fudiwei-feat/k8s'

This commit is contained in:
yoan 2024-10-19 08:34:09 +08:00
commit 678ef9c232
37 changed files with 1380 additions and 403 deletions

View File

@ -85,6 +85,7 @@ go run main.go serve
| 本地部署 | | √ | 可部署到本地服务器 |
| SSH | | √ | 可部署到 SSH 服务器 |
| Webhook | | √ | 可部署时回调到 Webhook |
| Kubernetes | | √ | 可部署到 Kubernetes Secret |
## 四、系统截图

View File

@ -84,6 +84,7 @@ password1234567890
| 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

27
go.mod
View File

@ -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 (
@ -31,12 +33,37 @@ require (
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 (

66
go.sum
View File

@ -159,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=
@ -170,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=
@ -186,6 +190,13 @@ 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=
@ -197,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=
@ -230,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=
@ -238,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=
@ -267,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=
@ -279,6 +301,8 @@ 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=
@ -286,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=
@ -302,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=
@ -325,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=
@ -332,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=
@ -358,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=
@ -411,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=
@ -471,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=
@ -486,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=
@ -509,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=
@ -580,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=
@ -633,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=
@ -649,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=
@ -676,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=

View File

@ -1,12 +1,13 @@
package applicant
import (
"certimate/internal/domain"
"encoding/json"
"fmt"
"os"
"github.com/go-acme/lego/v4/providers/dns/route53"
"certimate/internal/domain"
)
type aws struct {

View File

@ -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
}

View File

@ -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),

View File

@ -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),

View 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
}

View File

@ -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")
}

View 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
}

View File

@ -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)
// 如果目录不存在,创建目录

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 != "" {

View File

@ -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)

View File

@ -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
}

View 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-mYQfy72D.js vendored Normal file

File diff suppressed because one or more lines are too long

1
ui/dist/imgs/providers/k8s.svg vendored Normal file

File diff suppressed because one or more lines are too long

29
ui/dist/index.html vendored
View File

@ -1,14 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-j5PIjQ7r.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-YqBWA4KK.css">
</head>
<body class="bg-background">
<div id="root"></div>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-mYQfy72D.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-YqBWA4KK.css">
</head>
<body class="bg-background">
<div id="root"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,7 @@ 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 = {
@ -157,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) => {

View 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;

View File

@ -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>
</>
);
};

View File

@ -12,6 +12,7 @@ export const accessTypeMap: 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", ["common.provider.kubernetes", "/imgs/providers/k8s.svg"]],
]);
export const getProviderInfo = (t: string) => {
@ -31,6 +32,7 @@ export const accessFormType = z.union(
z.literal("local"),
z.literal("ssh"),
z.literal("webhook"),
z.literal("k8s"),
],
{ message: "access.authorization.form.type.placeholder" }
);
@ -54,7 +56,8 @@ export type Access = {
| GodaddyConfig
| LocalConfig
| SSHConfig
| WebhookConfig;
| WebhookConfig
| KubernetesConfig;
deleted?: string;
created?: string;
updated?: string;
@ -117,6 +120,10 @@ export type WebhookConfig = {
url: string;
};
export type KubernetesConfig = {
kubeConfig: string;
};
export const getUsageByConfigType = (configType: string): AccessUsage => {
switch (configType) {
case "aliyun":
@ -128,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":

View File

@ -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());

View File

@ -59,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",

View File

@ -68,6 +68,8 @@
"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.lark": "Lark"

View File

@ -7,5 +7,5 @@
"dashboard.statistics.disabled": "Not Enabled",
"dashboard.statistics.unit": "",
"dashboard.history": "Deployment History"
"dashboard.history": "Recently Deployment History"
}

View File

@ -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",

View File

@ -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!",

View File

@ -59,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": "授权组",

View File

@ -68,6 +68,8 @@
"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.lark": "飞书"

View File

@ -7,5 +7,5 @@
"dashboard.statistics.disabled": "未启用",
"dashboard.statistics.unit": "个",
"dashboard.history": "部署历史"
"dashboard.history": "最近部署"
}

View File

@ -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": "值",

View File

@ -1,5 +1,5 @@
{
"history.page.title": "部署",
"history.page.title": "部署历史",
"history.nodata": "你暂未创建任何部署,请先添加域名进行部署吧!",