diff --git a/go.mod b/go.mod
index 0b8596bd..8e21cd36 100644
--- a/go.mod
+++ b/go.mod
@@ -52,6 +52,7 @@ require (
github.com/libdns/dynv6 v1.1.1
github.com/libdns/libdns v1.1.1
github.com/luthermonson/go-proxmox v0.2.4
+ github.com/minio/minio-go/v7 v7.0.97
github.com/mohuatech/mohuacloud-go-sdk v0.0.0-20251115182757-6fba4d0a4c47
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
github.com/pkg/sftp v1.13.10
@@ -205,22 +206,31 @@ require (
github.com/ganigeorgiev/fexpr v0.5.0 // indirect
github.com/go-acme/esa-20240910/v2 v2.40.3 // indirect
github.com/go-acme/tencentedgdeone v1.1.48 // indirect
+ github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
+ github.com/klauspost/compress v1.18.0 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.11 // indirect
+ github.com/klauspost/crc32 v1.3.0 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.69 // indirect
+ github.com/minio/crc64nvme v1.1.0 // indirect
+ github.com/minio/md5-simd v1.1.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/nrdcg/namesilo v0.5.0 // indirect
+ github.com/philhofer/fwd v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/rs/xid v1.6.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
+ github.com/tinylib/msgp v1.3.0 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
golang.org/x/image v0.34.0 // indirect
golang.org/x/mod v0.31.0 // indirect
diff --git a/go.sum b/go.sum
index 25c96e4e..9c30dc1e 100644
--- a/go.sum
+++ b/go.sum
@@ -144,8 +144,6 @@ github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 h1:A3D8Mp6qf8DfR6Dt5MpS8a
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12/go.mod h1:F5c0E5UB3k8v6neTtw3FBcJ1YCNFzVoL1JPRHTe33u4=
github.com/alibabacloud-go/ga-20191120/v3 v3.1.8 h1:5GF0PXijDhxRQ3gTg9Ee/CVPtglkxuVdz4yIQgYLPgw=
github.com/alibabacloud-go/ga-20191120/v3 v3.1.8/go.mod h1:RVpR9VL4YECKoZCQijTYfPk8k52O61v6hSRekjxF0kw=
-github.com/alibabacloud-go/live-20161101/v2 v2.4.0 h1:TLNjd3HkUyCji4LjXMtse3lKPKBPNCSvll9rAWHOJ7Q=
-github.com/alibabacloud-go/live-20161101/v2 v2.4.0/go.mod h1:1BN//Z4vOkdEplf0pWcpF1GuIqaPJOwYuPCShljY+nI=
github.com/alibabacloud-go/live-20161101/v2 v2.5.0 h1:DxALW3BqBGDrgMu7OHLT5nHVeil440DGKUlPiVN/zbE=
github.com/alibabacloud-go/live-20161101/v2 v2.5.0/go.mod h1:1BN//Z4vOkdEplf0pWcpF1GuIqaPJOwYuPCShljY+nI=
github.com/alibabacloud-go/nlb-20220430/v4 v4.1.0 h1:iuoiJUMz0SUWbLxZdyp/IdbtGKuniftnV776FUwMEyU=
@@ -153,8 +151,6 @@ github.com/alibabacloud-go/nlb-20220430/v4 v4.1.0/go.mod h1:OxNPeLl4eV3s2ZoMcuUo
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28=
github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw=
-github.com/alibabacloud-go/slb-20140515/v4 v4.0.12 h1:J/mnt9MJC5HIzXsWuNnSzp3YgLohkUg3cgrckMJgii0=
-github.com/alibabacloud-go/slb-20140515/v4 v4.0.12/go.mod h1:gWZrz3AD+izASfHjpxTOIJ8N0KMRjbIRzRZr1koy7tA=
github.com/alibabacloud-go/slb-20140515/v4 v4.0.13 h1:MtQUoGTgFqGTebY4lzFTFVsIV7QXeVN13oMzJYqvtYQ=
github.com/alibabacloud-go/slb-20140515/v4 v4.0.13/go.mod h1:gWZrz3AD+izASfHjpxTOIJ8N0KMRjbIRzRZr1koy7tA=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
@@ -246,8 +242,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfm
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
-github.com/baidubce/bce-sdk-go v0.9.254 h1:A7GtBOt7z2lnV7fqlZPZefhcBFg7z6iliUAhEOiIhoE=
-github.com/baidubce/bce-sdk-go v0.9.254/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baidubce/bce-sdk-go v0.9.256 h1:/6UwBzDp+dRFpKRIb5WsvxfSiG4SLOIOghvagOK/q4Y=
github.com/baidubce/bce-sdk-go v0.9.256/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -360,6 +354,8 @@ github.com/go-cmd/cmd v1.4.3/go.mod h1:u3hxg/ry+D5kwh8WvUkHLAMe2zQCaXd00t35WfQaO
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
+github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -596,8 +592,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
-github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
+github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
+github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
github.com/kong/go-kong v0.71.0 h1:unPik6osV1DD3DF+jLs9oMedxWQsnepPYTm1dRQSIa4=
github.com/kong/go-kong v0.71.0/go.mod h1:J0vGB3wsZ2i99zly1zTRe3v7rOKpkhQZRwbcTFP76qM=
github.com/kong/semver/v4 v4.0.1 h1:DIcNR8W3gfx0KabFBADPalxxsp+q/5COwIFkkhrFQ2Y=
@@ -657,8 +658,14 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
+github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q=
+github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
+github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
+github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
+github.com/minio/minio-go/v7 v7.0.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ=
+github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohwdaSmPPSRSk=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -732,6 +739,8 @@ github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuST
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
+github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
+github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -796,6 +805,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
+github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -845,8 +856,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.3.17 h1:daYB3/mHBoYRAUWTkU2PrKSijNOqmPGwyCjSbhpsPfk=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.3.17/go.mod h1:zJGNIT9EJUEGNYwIsB96L9JagH65lhxRriTPmHTjWJk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.3.20 h1:2YLbDjm8I7TRurRJZO7pCsUs6J8uNSGVaP9DWZQPc6U=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.3.20/go.mod h1:elKRHSEOQbiRov7N1xEoy0E8KPwAyc/VMJOVI5G5zEM=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.3.13 h1:UtduU6ahUDJeZlmoUbyoz8Vguipiqk7tHQR4uXW0boA=
@@ -854,38 +863,23 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.3.13/go.mod h1:f
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.25/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.45/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.48/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.2.2/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.4/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.11/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.13/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.16/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.17 h1:pjL2v80TdxvF08WzSO2Tr2TTEOH1O7FQR176uLO+4O0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.17/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.18/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.20 h1:RRq1WvPfe5jy5ImFiFo5HU+tPiCt+qdnwO+T7vVN6dU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.20/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.1.45 h1:xqKjGMJw7CICjs+xAIDWK7l52QQ1rUUlajTfksYkH50=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.1.45/go.mod h1:2KEYKinKeqfdcetd0nLrS3pllb1SJ27ygmiyJ570iH8=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.3.16 h1:ukU4KPGFYgCuTyflIPnXlMQcoW7AoSLtDfQ541gpHXU=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.3.16/go.mod h1:Hpn3mSxaMoQE2/SJf64FgEcK7ypAeSrO1uUwTPadCrw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.3.20 h1:/147pDQzIVqOgt4CGtjRyuWvDvaWXrjmRwvcKbWugXk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.3.20/go.mod h1:YXRHnmmu42wyXIGvVxffVOZtCK0G5xJ23eQzcon1f+w=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.3.4 h1:CkRXWD7DeoUosSQ6tMdoktw2IODUlbrd4KQpaiUDZg0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.3.4/go.mod h1:/Z0n3bA2SQaHclfeTUNtnIfK61ADe/Zk4Auuequgze0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.2.2 h1:VkeBp3dOfCm0INU4oi3Djg4Terms8xrlcbHWI6AQ1so=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.2.2/go.mod h1:uJfDgCw3jYjEPJ/mZ9sMahPt8pd57rct6yGLNiPDKvw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.3.20 h1:VPfMa3hsxMxquv06Bat6wv9tLv3ZjCjgz0/dndwMloQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.3.20/go.mod h1:rDr8tuOVK5CPXe2wiq3Q8RCEVzCE2yKckQgdr1Bvar8=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.3.17 h1:QdoR+rCiHBZuuoivdWaVOIHzvMUPIiSTmUe7OFnUAo8=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.3.17/go.mod h1:EJ82JP35f7Pkh2dm+OvDDQ6/9kViBb0M/WGhiOHmQ10=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.3.18 h1:XFsg8l4rGDkG2HT7xIJsfWvBSe62GQV/MxSh2EBAosU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.3.18/go.mod h1:ttUXlKbDD+f6BOAG1rTiKG0TNNh6/4V7TEEE/0HL/oQ=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.3.17 h1:fL6OrLmOt+cx8ydlqkUngq7M/b8vrxQezINaeKHTZ3Q=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.3.17/go.mod h1:iVBFznh44P2Bq42W0Mxvb6QKA8rne1nyp11O1Wpmwbc=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.3.20 h1:kV0p6/rO4seCojF4hsPBTQq6dMGVJsUB6/5u3Y16YRQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.3.20/go.mod h1:2HdphlkKmQc5TIiaZnUPxB4s1wbriExDS0TSZwkvp+s=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.3.11 h1:+ymQSl/hQaE0quLmGoCTjqwRbXSDebkSPYnC3AigQqA=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.3.11/go.mod h1:3z7CWaLikx7voRr1MVaa785f+y9evC0UzyLWSSwuvgY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.3.20 h1:xrX2I30YY2hLsm302jATQsnq860gBNFSLbGlNgXANug=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.3.20/go.mod h1:NMaQNYoBdz4fUR9fi6/jMTe7xdsJLYnlBxDAu+y5+nw=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
@@ -894,6 +888,8 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
+github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
diff --git a/internal/certacme/certifiers/sp_s3.go b/internal/certacme/certifiers/sp_s3.go
new file mode 100644
index 00000000..f5883e8f
--- /dev/null
+++ b/internal/certacme/certifiers/sp_s3.go
@@ -0,0 +1,32 @@
+package certifiers
+
+import (
+ "fmt"
+
+ "github.com/go-acme/lego/v4/challenge"
+
+ "github.com/certimate-go/certimate/internal/domain"
+ "github.com/certimate-go/certimate/pkg/core/certifier/challengers/http01/s3"
+ xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
+)
+
+func init() {
+ ACMEHttp01Registries.MustRegister(domain.ACMEHttp01ProviderTypeS3, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
+ credentials := domain.AccessConfigForS3{}
+ if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ provider, err := s3.NewChallenger(&s3.ChallengerConfig{
+ Endpoint: credentials.Endpoint,
+ AccessKey: credentials.AccessKey,
+ SecretKey: credentials.SecretKey,
+ SignatureVersion: credentials.SignatureVersion,
+ UsePathStyle: credentials.UsePathStyle,
+ AllowInsecureConnections: credentials.AllowInsecureConnections,
+ Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
+ Bucket: xmaps.GetString(options.ProviderExtendedConfig, "bucket"),
+ })
+ return provider, err
+ })
+}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index e7fb438e..05931768 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -363,6 +363,11 @@ type AccessConfigForMattermost struct {
ChannelId string `json:"channelId,omitempty"`
}
+type AccessConfigForMohua struct {
+ Username string `json:"username"`
+ ApiPassword string `json:"apiPassword"`
+}
+
type AccessConfigForNamecheap struct {
Username string `json:"username"`
ApiKey string `json:"apiKey"`
@@ -429,11 +434,6 @@ type AccessConfigForQiniu struct {
SecretKey string `json:"secretKey"`
}
-type AccessConfigForMohua struct {
- Username string `json:"username"`
- ApiPassword string `json:"apiPassword"`
-}
-
type AccessConfigForRainYun struct {
ApiKey string `json:"apiKey"`
}
@@ -453,6 +453,15 @@ type AccessConfigForRFC2136 struct {
TsigSecret string `json:"tsigSecret,omitempty"`
}
+type AccessConfigForS3 struct {
+ Endpoint string `json:"endpoint"`
+ AccessKey string `json:"accessKey"`
+ SecretKey string `json:"secretKey"`
+ SignatureVersion string `json:"signatureVersion,omitempty"`
+ UsePathStyle bool `json:"usePathStyle,omitempty"`
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
type AccessConfigForSafeLine struct {
ServerUrl string `json:"serverUrl"`
ApiToken string `json:"apiToken"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index 94583f07..2d493d97 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -94,6 +94,7 @@ const (
AccessProviderTypeRainYun = AccessProviderType("rainyun")
AccessProviderTypeRatPanel = AccessProviderType("ratpanel")
AccessProviderTypeRFC2136 = AccessProviderType("rfc2136")
+ AccessProviderTypeS3 = AccessProviderType("s3")
AccessProviderTypeSafeLine = AccessProviderType("safeline")
AccessProviderTypeSectigo = AccessProviderType("sectigo")
AccessProviderTypeSlackBot = AccessProviderType("slackbot")
@@ -236,6 +237,7 @@ NOTICE: If you add new constant, please keep ASCII order.
*/
const (
ACMEHttp01ProviderTypeLocal = ACMEHttp01ProviderType(AccessProviderTypeLocal)
+ ACMEHttp01ProviderTypeS3 = ACMEHttp01ProviderType(AccessProviderTypeS3)
ACMEHttp01ProviderTypeSSH = ACMEHttp01ProviderType(AccessProviderTypeSSH)
)
diff --git a/pkg/core/certifier/challengers/http01/s3/s3.go b/pkg/core/certifier/challengers/http01/s3/s3.go
new file mode 100644
index 00000000..0ee9d6c4
--- /dev/null
+++ b/pkg/core/certifier/challengers/http01/s3/s3.go
@@ -0,0 +1,121 @@
+package s3
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "regexp"
+ "strings"
+
+ "github.com/go-acme/lego/v4/challenge/http01"
+ "github.com/minio/minio-go/v7"
+ "github.com/minio/minio-go/v7/pkg/credentials"
+ "github.com/samber/lo"
+
+ "github.com/certimate-go/certimate/pkg/core/certifier"
+ xhttp "github.com/certimate-go/certimate/pkg/utils/http"
+ xtls "github.com/certimate-go/certimate/pkg/utils/tls"
+)
+
+type ChallengerConfig struct {
+ // S3 Endpoint。
+ Endpoint string `json:"endpoint"`
+ // S3 AccessKey。
+ AccessKey string `json:"accessKey"`
+ // S3 SecretKey。
+ SecretKey string `json:"secretKey"`
+ // S3 签名版本。
+ // 可取值 "v2"、"v4"。
+ // 零值时默认值 "v4"。
+ SignatureVersion string `json:"signatureVersion,omitempty"`
+ // 是否使用路径风格。
+ UsePathStyle bool `json:"usePathStyle,omitempty"`
+ // 存储区域。
+ Region string `json:"region"`
+ // 存储桶名。
+ Bucket string `json:"bucket"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
+func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) {
+ if config == nil {
+ return nil, errors.New("the configuration of the acme challenge provider is nil")
+ }
+
+ var clientCred *credentials.Credentials
+ switch config.SignatureVersion {
+ case "", "v4":
+ clientCred = credentials.NewStaticV4(config.AccessKey, config.SecretKey, "")
+ case "v2":
+ clientCred = credentials.NewStaticV2(config.AccessKey, config.SecretKey, "")
+ default:
+ return nil, fmt.Errorf("unsupported s3 signature version: '%s'", config.SignatureVersion)
+ }
+
+ var clientOpts *minio.Options
+ clientOpts = &minio.Options{
+ Creds: clientCred,
+ Region: config.Region,
+ BucketLookup: lo.If(config.UsePathStyle, minio.BucketLookupPath).Else(minio.BucketLookupDNS),
+ }
+
+ var endpoint string
+ if config.Endpoint != "" {
+ reScheme := regexp.MustCompile("^([^:]+)://")
+ if reScheme.MatchString(config.Endpoint) {
+ temp := strings.Split(config.Endpoint, "://")
+ scheme := temp[0]
+ endpoint = temp[1]
+ clientOpts.Secure = strings.EqualFold(scheme, "https")
+ } else {
+ endpoint = config.Endpoint
+ clientOpts.Secure = true
+ }
+ }
+
+ if clientOpts.Secure && config.AllowInsecureConnections {
+ transport := xhttp.NewDefaultTransport()
+ transport.DisableKeepAlives = true
+ transport.TLSClientConfig = xtls.NewInsecureConfig()
+ clientOpts.Transport = transport
+ }
+
+ client, err := minio.New(endpoint, clientOpts)
+ if err != nil {
+ return nil, err
+ }
+
+ provider := &provider{client: client, bucket: config.Bucket}
+ return provider, nil
+}
+
+type provider struct {
+ client *minio.Client
+ bucket string
+}
+
+func (p *provider) Present(domain, token, keyAuth string) error {
+ objectKey := strings.Trim(http01.ChallengePath(token), "/")
+ putOpts := minio.PutObjectOptions{
+ DisableMultipart: true,
+ }
+ reader := strings.NewReader(keyAuth)
+ _, err := p.client.PutObject(context.Background(), p.bucket, objectKey, reader, reader.Size(), putOpts)
+ if err != nil {
+ return fmt.Errorf("s3: failed to upload token to s3: %w", err)
+ }
+
+ return nil
+}
+
+func (p *provider) CleanUp(domain, token, keyAuth string) error {
+ objectKey := strings.Trim(http01.ChallengePath(token), "/")
+ removeOpts := minio.RemoveObjectOptions{}
+ err := p.client.RemoveObject(context.Background(), p.bucket, objectKey, removeOpts)
+ if err != nil {
+ return fmt.Errorf("s3: could not remove file in s3 bucket after HTTP challenge: %w", err)
+ }
+
+ return nil
+}
diff --git a/ui/public/imgs/providers/s3.svg b/ui/public/imgs/providers/s3.svg
new file mode 100644
index 00000000..76a77a38
--- /dev/null
+++ b/ui/public/imgs/providers/s3.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx
index cea12f8b..68832c13 100644
--- a/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx
+++ b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx
@@ -82,6 +82,7 @@ import AccessConfigFieldsProviderQiniu from "./AccessConfigFieldsProviderQiniu";
import AccessConfigFieldsProviderRainYun from "./AccessConfigFieldsProviderRainYun";
import AccessConfigFieldsProviderRatPanel from "./AccessConfigFieldsProviderRatPanel";
import AccessConfigFieldsProviderRFC2136 from "./AccessConfigFieldsProviderRFC2136";
+import AccessConfigFieldsProviderS3 from "./AccessConfigFieldsProviderS3";
import AccessConfigFieldsProviderSafeLine from "./AccessConfigFieldsProviderSafeLine";
import AccessConfigFieldsProviderSectigo from "./AccessConfigFieldsProviderSectigo";
import AccessConfigFieldsProviderSlackBot from "./AccessConfigFieldsProviderSlackBot";
@@ -189,6 +190,7 @@ const providerComponentMap: Partial {
+ const { i18n, t } = useTranslation();
+
+ const { parentNamePath } = useFormNestedFieldsContext();
+ const formSchema = z.object({
+ [parentNamePath]: getSchema({ i18n }),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+ const initialValues = getInitialValues();
+
+ return (
+ <>
+ }
+ rules={[formRule]}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+ >
+ );
+};
+
+const getInitialValues = (): Nullish>> => {
+ return {
+ endpoint: "",
+ accessKey: "",
+ secretKey: "",
+ signatureVersion: "v4",
+ };
+};
+
+const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+ const { t } = i18n;
+
+ return z.object({
+ endpoint: z.string().refine((v) => isHostname(v) || isUrlWithHttpOrHttps(v), t("access.form.s3_endpoint.placeholder")),
+ accessKey: z.string().nonempty(t("access.form.s3_access_key.placeholder")),
+ secretKey: z.string().nonempty(t("access.form.s3_secret_key.placeholder")),
+ signatureVersion: z.enum(["v2", "v4"]),
+ usePathStyle: z.boolean().nullish(),
+ allowInsecureConnections: z.boolean().nullish(),
+ });
+};
+
+const _default = Object.assign(AccessConfigFieldsProviderS3, {
+ getInitialValues,
+ getSchema,
+});
+
+export default _default;
diff --git a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigFieldsProvider.tsx b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigFieldsProvider.tsx
index c04d6d91..7ed2731d 100644
--- a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigFieldsProvider.tsx
+++ b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigFieldsProvider.tsx
@@ -7,6 +7,7 @@ import BizApplyNodeConfigFieldsProviderAWSRoute53 from "./BizApplyNodeConfigFiel
import BizApplyNodeConfigFieldsProviderHuaweiCloudDNS from "./BizApplyNodeConfigFieldsProviderHuaweiCloudDNS";
import BizApplyNodeConfigFieldsProviderJDCloudDNS from "./BizApplyNodeConfigFieldsProviderJDCloudDNS";
import BizApplyNodeConfigFieldsProviderLocal from "./BizApplyNodeConfigFieldsProviderLocal";
+import BizApplyNodeConfigFieldsProviderS3 from "./BizApplyNodeConfigFieldsProviderS3";
import BizApplyNodeConfigFieldsProviderSSH from "./BizApplyNodeConfigFieldsProviderSSH";
const acmeDns01ProviderComponentMap: Partial>> = {
@@ -29,6 +30,7 @@ const acmeHttp01ProviderComponentMap: Partial {
+ const { i18n, t } = useTranslation();
+
+ const { parentNamePath } = useFormNestedFieldsContext();
+ const formSchema = z.object({
+ [parentNamePath]: getSchema({ i18n }),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+ const initialValues = getInitialValues();
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
+
+const getInitialValues = (): Nullish>> => {
+ return {
+ region: "",
+ bucket: "",
+ };
+};
+
+const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => {
+ const { t } = i18n;
+
+ return z.object({
+ region: z.string().nonempty(t("workflow_node.apply.form.s3_region.placeholder")),
+ bucket: z.string().nonempty(t("workflow_node.apply.form.s3_bucket.placeholder")),
+ });
+};
+
+const _default = Object.assign(BizApplyNodeConfigFieldsProviderS3, {
+ getInitialValues,
+ getSchema,
+});
+
+export default _default;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index b338e5e0..9bf7a43f 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -98,6 +98,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
RAINYUN: "rainyun",
RATPANEL: "ratpanel",
RFC2136: "rfc2136",
+ S3: "s3",
SAFELINE: "safeline",
SECTIGO: "sectigo",
SLACKBOT: "slackbot",
@@ -147,6 +148,7 @@ export const accessProvidersMap: Map
).map(([type, name, builtin]) => [
type,
diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json
index 87d48ea8..318f558b 100644
--- a/ui/src/i18n/locales/en/nls.access.json
+++ b/ui/src/i18n/locales/en/nls.access.json
@@ -534,6 +534,17 @@
"access.form.rfc2136_tsig_key.placeholder": "Please enter TSIG authentication key",
"access.form.rfc2136_tsig_secret.label": "TSIG authentication secret (Optional)",
"access.form.rfc2136_tsig_secret.placeholder": "Please enter TSIG authentication secret",
+ "access.form.s3_endpoint.label": "Endpoint",
+ "access.form.s3_endpoint.placeholder": "Please enter endpoint",
+ "access.form.s3_endpoint.help": "Note: If the protocol is not specified, https:// is used by default.",
+ "access.form.s3_access_key.label": "Access key",
+ "access.form.s3_access_key.placeholder": "Please enter access key",
+ "access.form.s3_secret_key.label": "Secret key",
+ "access.form.s3_secret_key.placeholder": "Please enter secret key",
+ "access.form.s3_signature_version.label": "Signature version",
+ "access.form.s3_signature_version.placeholder": "Please select signature version",
+ "access.form.s3_use_path_style.label": "Use path style addressing",
+ "access.form.s3_use_path_style.tooltip": "- Virtual-hosted style (default): https://<BUCKET>.<ENDPOINT>/<KEY>
- Path style: https://<ENDPOINT>/<BUCKET>/<KEY>
",
"access.form.safeline_server_url.label": "SafeLine server URL",
"access.form.safeline_server_url.placeholder": "Please enter SafeLine server URL",
"access.form.safeline_api_token.label": "SafeLine API token",
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json
index 2931a250..78fa3f6e 100644
--- a/ui/src/i18n/locales/en/nls.provider.json
+++ b/ui/src/i18n/locales/en/nls.provider.json
@@ -159,6 +159,7 @@
"provider.ratpanel_console": "AcePanel - Console itself",
"provider.ratpanel_site": "AcePanel - Website",
"provider.rfc2136": "RFC 2136: Dynamic DNS Updates",
+ "provider.s3": "Object storage (S3-compatible)",
"provider.safeline": "SafeLine",
"provider.safeline_site": "SafeLine - Website",
"provider.sectigo": "Sectigo",
diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json
index 83cbfe94..d7d3cab1 100644
--- a/ui/src/i18n/locales/en/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json
@@ -83,6 +83,10 @@
"workflow_node.apply.form.jdcloud_dns_region_id.label": "JD Cloud region ID",
"workflow_node.apply.form.jdcloud_dns_region_id.placeholder": "Please enter JD Cloud DNS region ID (e.g. cn-north-1)",
"workflow_node.apply.form.jdcloud_dns_region_id.tooltip": "For more information, see https://docs.jdcloud.com/en/common-declaration/api/introduction",
+ "workflow_node.apply.form.s3_region.label": "Object storage (S3-compatible) region",
+ "workflow_node.apply.form.s3_region.placeholder": "Please enter region",
+ "workflow_node.apply.form.s3_bucket.label": "Object storage (S3-compatible) bucket",
+ "workflow_node.apply.form.s3_bucket.placeholder": "Please enter bucket name",
"workflow_node.apply.form.local_webroot_path.label": "Web root path",
"workflow_node.apply.form.local_webroot_path.placeholder": "Please enter web root path",
"workflow_node.apply.form.local_webroot_path.tooltip": "It's the main directory where the website's files are stored on the server.",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index 98264f4f..5e3063bf 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -534,6 +534,17 @@
"access.form.rfc2136_tsig_key.placeholder": "请输入 TSIG 认证密钥 Key",
"access.form.rfc2136_tsig_secret.label": "TSIG 认证密钥 Secret(可选)",
"access.form.rfc2136_tsig_secret.placeholder": "请输入 TSIG 认证密钥 Secret",
+ "access.form.s3_endpoint.label": "服务端点",
+ "access.form.s3_endpoint.placeholder": "请输入服务端点",
+ "access.form.s3_endpoint.help": "注意:如果不指定协议,则默认使用 https://。",
+ "access.form.s3_access_key.label": "AccessKey",
+ "access.form.s3_access_key.placeholder": "请输入 AccessKey",
+ "access.form.s3_secret_key.label": "SecretKey",
+ "access.form.s3_secret_key.placeholder": "请输入 SecretKey",
+ "access.form.s3_signature_version.label": "签名版本",
+ "access.form.s3_signature_version.placeholder": "请选择签名版本",
+ "access.form.s3_use_path_style.label": "使用路径风格地址",
+ "access.form.s3_use_path_style.tooltip": "- 虚拟托管风格(默认):
https://<BUCKET>.<ENDPOINT>/<KEY> - 路径风格:
https://<ENDPOINT>/<BUCKET>/<KEY>
",
"access.form.safeline_server_url.label": "雷池服务地址",
"access.form.safeline_server_url.placeholder": "请输入雷池服务地址",
"access.form.safeline_api_token.label": "雷池 API Token",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index 1b22d9b0..933e7690 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -159,6 +159,7 @@
"provider.ratpanel_console": "耗子面板 - 面板自身",
"provider.ratpanel_site": "耗子面板 - 网站",
"provider.rfc2136": "RFC 2136: Dynamic DNS Updates",
+ "provider.s3": "对象存储(S3 兼容)",
"provider.safeline": "雷池",
"provider.safeline_site": "雷池 - 网站",
"provider.sectigo": "Sectigo",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index 3b2017d1..a25b0a7f 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -83,6 +83,10 @@
"workflow_node.apply.form.jdcloud_dns_region_id.label": "京东云服务地域 ID",
"workflow_node.apply.form.jdcloud_dns_region_id.placeholder": "请输入京东云 DNS 服务地域 ID(例如:cn-north-1)",
"workflow_node.apply.form.jdcloud_dns_region_id.tooltip": "这是什么?请参阅 https://docs.jdcloud.com/cn/common-declaration/api/introduction",
+ "workflow_node.apply.form.s3_region.label": "对象存储区域",
+ "workflow_node.apply.form.s3_region.placeholder": "请输入对象存储区域",
+ "workflow_node.apply.form.s3_bucket.label": "对象存储桶名",
+ "workflow_node.apply.form.s3_bucket.placeholder": "请输入对象存储桶名",
"workflow_node.apply.form.local_webroot_path.label": "网站根目录",
"workflow_node.apply.form.local_webroot_path.placeholder": "请输入网站根目录",
"workflow_node.apply.form.local_webroot_path.tooltip": "即服务器上存储网站文件的主文件夹。",