mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 01:11:55 +08:00
Merge pull request #31 from woodchen-ink:certimate-go-main
Certimate-go-main
This commit is contained in:
commit
aaf60dd771
1
.github/workflows/push_image.yml
vendored
1
.github/workflows/push_image.yml
vendored
@ -14,7 +14,6 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
276
.github/workflows/release_sync_gitee.py
vendored
Normal file
276
.github/workflows/release_sync_gitee.py
vendored
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import mimetypes
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import time
|
||||||
|
from urllib import request
|
||||||
|
from urllib.error import HTTPError
|
||||||
|
|
||||||
|
GITHUB_REPO = "certimate-go/certimate"
|
||||||
|
GITEE_REPO = "certimate-go/certimate"
|
||||||
|
GITEE_TOKEN = os.getenv("GITEE_TOKEN", "")
|
||||||
|
|
||||||
|
SYNC_MARKER = "SYNCING FROM GITHUB, PLEASE WAIT ..."
|
||||||
|
TEMP_DIR = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
def do_httpreq(url, method="GET", headers=None, data=None):
|
||||||
|
req = request.Request(url, data=data, method=method)
|
||||||
|
headers = headers or {}
|
||||||
|
for key, value in headers.items():
|
||||||
|
req.add_header(key, value)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with request.urlopen(req) as resp:
|
||||||
|
resp_data = resp.read().decode("utf-8")
|
||||||
|
if resp_data:
|
||||||
|
try:
|
||||||
|
return json.loads(resp_data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
except HTTPError as e:
|
||||||
|
errmsg = ""
|
||||||
|
if e.readable():
|
||||||
|
try:
|
||||||
|
errmsg = e.read().decode('utf-8')
|
||||||
|
errmsg = errmsg.replace("\r", "\\r").replace("\n", "\\n")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
logging.error(f"Error occurred when sending request: status={e.status}, response={errmsg}")
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
def get_github_stable_release():
|
||||||
|
page = 1
|
||||||
|
while True:
|
||||||
|
releases = do_httpreq(
|
||||||
|
url=f"https://api.github.com/repos/{GITHUB_REPO}/releases?page={page}&per_page=100",
|
||||||
|
headers={"Accept": "application/vnd.github+json"},
|
||||||
|
)
|
||||||
|
if not releases or len(releases) == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
for release in releases:
|
||||||
|
release_name = release.get("name", "")
|
||||||
|
if re.match(r"^v[0-9]", release_name):
|
||||||
|
if any(
|
||||||
|
x in release_name
|
||||||
|
for x in ["alpha", "beta", "rc", "preview", "test", "unstable"]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
return release
|
||||||
|
|
||||||
|
page += 1
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_gitee_release_list():
|
||||||
|
page = 1
|
||||||
|
list = []
|
||||||
|
while True:
|
||||||
|
releases = do_httpreq(
|
||||||
|
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases?access_token={GITEE_TOKEN}&page={page}&per_page=100",
|
||||||
|
)
|
||||||
|
if not releases or len(releases) == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
list.extend(releases)
|
||||||
|
page += 1
|
||||||
|
|
||||||
|
return list
|
||||||
|
|
||||||
|
|
||||||
|
def get_gitee_release_by_tag(tag_name):
|
||||||
|
releases = get_gitee_release_list()
|
||||||
|
for release in releases:
|
||||||
|
if release.get("tag_name") == tag_name:
|
||||||
|
return release
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def delete_gitee_release(release_info):
|
||||||
|
if not release_info:
|
||||||
|
raise ValueError("Release info is invalid")
|
||||||
|
|
||||||
|
release_id = release_info.get("id", "")
|
||||||
|
release_name = release_info.get("tag_name", "")
|
||||||
|
if not release_id:
|
||||||
|
raise ValueError("Release ID is missing")
|
||||||
|
|
||||||
|
attachpage = 1
|
||||||
|
attachfiles = []
|
||||||
|
while True:
|
||||||
|
releases = do_httpreq(
|
||||||
|
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}/attach_files?access_token={GITEE_TOKEN}&page={attachpage}&per_page=100",
|
||||||
|
)
|
||||||
|
if not releases or len(releases) == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
attachfiles.extend(releases)
|
||||||
|
attachpage += 1
|
||||||
|
|
||||||
|
for attachfile in attachfiles:
|
||||||
|
attachfile_id = attachfile.get("id")
|
||||||
|
attachfile_name = attachfile.get("name")
|
||||||
|
logging.info("Trying to delete Gitee attach file: %s/%s", release_name, attachfile_name)
|
||||||
|
do_httpreq(
|
||||||
|
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}/attach_files/{attachfile_id}?access_token={GITEE_TOKEN}",
|
||||||
|
method="DELETE",
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.info("Trying to delete Gitee release: %s", release_name)
|
||||||
|
do_httpreq(
|
||||||
|
url=f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}?access_token={GITEE_TOKEN}",
|
||||||
|
method="DELETE",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_gitee_release(name, tag, body, prerelease, gh_assets):
|
||||||
|
release_info = do_httpreq(
|
||||||
|
f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases?access_token={GITEE_TOKEN}",
|
||||||
|
method="POST",
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
data=json.dumps({
|
||||||
|
"tag_name": tag,
|
||||||
|
"name": name,
|
||||||
|
"body": SYNC_MARKER,
|
||||||
|
"prerelease": prerelease,
|
||||||
|
"target_commitish": "",
|
||||||
|
}).encode("utf-8"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not release_info or "id" not in release_info:
|
||||||
|
return None
|
||||||
|
logging.info("Gitee release created")
|
||||||
|
|
||||||
|
release_id = release_info["id"]
|
||||||
|
|
||||||
|
assets_dir = os.path.join(TEMP_DIR, "assets")
|
||||||
|
os.makedirs(assets_dir, exist_ok=True)
|
||||||
|
|
||||||
|
gh_assets = gh_assets or []
|
||||||
|
for asset in gh_assets:
|
||||||
|
logging.info("Tring to download asset from GitHub: %s", asset["name"])
|
||||||
|
|
||||||
|
opener = request.build_opener()
|
||||||
|
request.install_opener(opener)
|
||||||
|
download_ts = time.time()
|
||||||
|
download_url = asset.get("browser_download_url")
|
||||||
|
download_path = os.path.join(assets_dir, asset["name"])
|
||||||
|
def _hook(blocknum, blocksize, totalsize):
|
||||||
|
nonlocal download_ts
|
||||||
|
TIMESPAN = 5 # print progress every 5sec
|
||||||
|
ts = time.time()
|
||||||
|
pct = min(round(100 * blocknum * blocksize / totalsize, 2), 100)
|
||||||
|
if (ts - download_ts < TIMESPAN) and (pct < 100):
|
||||||
|
return
|
||||||
|
download_ts = ts
|
||||||
|
logging.info(f"Downloading {download_url} >>> {pct}%")
|
||||||
|
|
||||||
|
request.urlretrieve(download_url, download_path, _hook)
|
||||||
|
|
||||||
|
for asset in gh_assets:
|
||||||
|
logging.info("Tring to upload asset to Gitee: %s", asset["name"])
|
||||||
|
|
||||||
|
boundary = '----boundary' + ''.join(random.choice('0123456789abcdef') for _ in range(16))
|
||||||
|
print(f"Using boundary: {boundary}")
|
||||||
|
with open(os.path.join(assets_dir, asset["name"]), 'rb') as f:
|
||||||
|
attachfile_mime = mimetypes.guess_type(asset["name"])[0] or 'application/octet-stream'
|
||||||
|
attachfile_req = []
|
||||||
|
attachfile_req.append(f"--{boundary}")
|
||||||
|
attachfile_req.append(f'Content-Disposition: form-data; name="file"; filename="{asset["name"]}"')
|
||||||
|
attachfile_req.append(f"Content-Type: {attachfile_mime}")
|
||||||
|
attachfile_req.append("")
|
||||||
|
attachfile_req.append(f.read().decode('latin-1'))
|
||||||
|
attachfile_req.append(f"--{boundary}--")
|
||||||
|
attachfile_req.append("")
|
||||||
|
attachfile_req = "\r\n".join(attachfile_req).encode('latin-1')
|
||||||
|
|
||||||
|
do_httpreq(
|
||||||
|
f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}/attach_files?access_token={GITEE_TOKEN}",
|
||||||
|
method="POST",
|
||||||
|
headers={'Content-Type': f'multipart/form-data; boundary={boundary}'},
|
||||||
|
data=attachfile_req,
|
||||||
|
)
|
||||||
|
logging.info("Asset uploaded: %s", asset["name"])
|
||||||
|
|
||||||
|
release_info = do_httpreq(
|
||||||
|
f"https://gitee.com/api/v5/repos/{GITEE_REPO}/releases/{release_id}?access_token={GITEE_TOKEN}",
|
||||||
|
method="PATCH",
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
data=json.dumps({
|
||||||
|
"tag_name": tag,
|
||||||
|
"name": name,
|
||||||
|
"body": f"**此发行版同步自 GitHub,完整变更日志请访问 https://github.com/{GITHUB_REPO}/releases/{tag} 查看。**\n\n**因 Gitee 存储空间容量有限,仅能保留最新一个发行版,如需其余版本请访问 GitHub 获取。**\n\n---\n\n" + body,
|
||||||
|
"prerelease": prerelease,
|
||||||
|
}).encode("utf-8"),
|
||||||
|
)
|
||||||
|
logging.info("Gitee release updated")
|
||||||
|
return release_info
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
# 获取 GitHub 最新稳定发行版
|
||||||
|
github_release = get_github_stable_release()
|
||||||
|
if not github_release:
|
||||||
|
logging.warning("GitHub stable release not found. Foget to release?")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
logging.info("GitHub stable release found: %s", github_release.get('name'))
|
||||||
|
|
||||||
|
# 提取稳定版的信息
|
||||||
|
release_name = github_release.get("name")
|
||||||
|
release_tag = github_release.get("tag_name")
|
||||||
|
release_body = github_release.get("body")
|
||||||
|
release_prerelease = github_release.get("prerelease", False)
|
||||||
|
release_assets = github_release.get("assets", [])
|
||||||
|
|
||||||
|
# 检查 Gitee 是否已有同名发行版
|
||||||
|
gitee_release = get_gitee_release_by_tag(release_tag)
|
||||||
|
if gitee_release and gitee_release.get("body") == SYNC_MARKER:
|
||||||
|
logging.warning("Gitee syncing release found, cleaning up...")
|
||||||
|
delete_gitee_release(gitee_release)
|
||||||
|
elif gitee_release:
|
||||||
|
logging.info("Gitee release already exists, exit.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 同步发行版
|
||||||
|
gitee_release = create_gitee_release(release_name, release_tag, release_body, release_prerelease, release_assets)
|
||||||
|
if not gitee_release:
|
||||||
|
logging.warning("Failed to create Gitee release.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 清除历史发行版
|
||||||
|
gitee_release_list = get_gitee_release_list()
|
||||||
|
for release in gitee_release_list:
|
||||||
|
if release.get("tag_name") == release_tag:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
delete_gitee_release(release)
|
||||||
|
|
||||||
|
logging.info("Sync release completed.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.fatal(str(e))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if os.path.exists(TEMP_DIR):
|
||||||
|
shutil.rmtree(TEMP_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
27
.github/workflows/release_sync_gitee.yml
vendored
Normal file
27
.github/workflows/release_sync_gitee.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
name: Release Sync to Gitee
|
||||||
|
|
||||||
|
on:
|
||||||
|
# release:
|
||||||
|
# types: [published, unpublished, deleted]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-to-gitee:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Python3
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.13"
|
||||||
|
|
||||||
|
- name: Run script
|
||||||
|
env:
|
||||||
|
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
|
||||||
|
run: |
|
||||||
|
cd .github/workflows
|
||||||
|
python ./release_sync_gitee.py
|
7
go.mod
7
go.mod
@ -40,6 +40,7 @@ require (
|
|||||||
github.com/go-viper/mapstructure/v2 v2.3.0
|
github.com/go-viper/mapstructure/v2 v2.3.0
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.155
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.155
|
||||||
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0
|
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0
|
||||||
|
github.com/kong/go-kong v0.66.1
|
||||||
github.com/libdns/dynv6 v1.0.0
|
github.com/libdns/dynv6 v1.0.0
|
||||||
github.com/libdns/libdns v0.2.3
|
github.com/libdns/libdns v0.2.3
|
||||||
github.com/luthermonson/go-proxmox v0.2.2
|
github.com/luthermonson/go-proxmox v0.2.2
|
||||||
@ -109,12 +110,15 @@ require (
|
|||||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/jinzhu/copier v0.3.4 // indirect
|
github.com/jinzhu/copier v0.3.4 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/kong/semver/v4 v4.0.1 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/magefile/mage v1.14.0 // indirect
|
github.com/magefile/mage v1.14.0 // indirect
|
||||||
github.com/mailru/easyjson v0.9.0 // indirect
|
github.com/mailru/easyjson v0.9.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||||
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
||||||
@ -127,6 +131,9 @@ require (
|
|||||||
github.com/qiniu/x v1.10.5 // indirect
|
github.com/qiniu/x v1.10.5 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||||
|
github.com/tidwall/gjson v1.18.0 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
|
26
go.sum
26
go.sum
@ -552,6 +552,8 @@ github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.155/go.mod h1:Y/+YLCFCJtS29i2M
|
|||||||
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
||||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||||
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
@ -596,6 +598,10 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
|
|||||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
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.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
|
github.com/kong/go-kong v0.66.1 h1:UVdemzcCpfXEl6O/VHdf0rT2bXdIO5ykuJbf2z1JTko=
|
||||||
|
github.com/kong/go-kong v0.66.1/go.mod h1:wRMPAXGOB3kn53TF6zN4l2JhIWPUfXDFKNHkMHBB3iQ=
|
||||||
|
github.com/kong/semver/v4 v4.0.1 h1:DIcNR8W3gfx0KabFBADPalxxsp+q/5COwIFkkhrFQ2Y=
|
||||||
|
github.com/kong/semver/v4 v4.0.1/go.mod h1:LImQ0oT15pJvSns/hs2laLca2zcYoHu5EsSNY0J6/QA=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
@ -656,6 +662,8 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
|||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@ -786,6 +794,8 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN
|
|||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
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/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=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||||
|
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
@ -829,8 +839,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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1187 h1:x2q6BAFm2f+9YaE7/lGPWXL7HzRkovjoqOMbdtRdpBw=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1187/go.mod h1:GoIHP0ayv0QOWN4c9aUEaKi74lY/tbeJz7h5i8y2gdU=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1193 h1:zOWZKDVA3kvA5/b+AwKzDtz5ewdiibeKxVqtCFJSTNI=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1193 h1:zOWZKDVA3kvA5/b+AwKzDtz5ewdiibeKxVqtCFJSTNI=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1193/go.mod h1:ufxDBGyS3X/9QKkZzuOFKLNra9FmSfgAHBO/FlFZaTU=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1193/go.mod h1:ufxDBGyS3X/9QKkZzuOFKLNra9FmSfgAHBO/FlFZaTU=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1188 h1:zzaIE12soTfyAgRvBYhb5bYxFXRCelvYXDEfvtkT5Y4=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1188 h1:zzaIE12soTfyAgRvBYhb5bYxFXRCelvYXDEfvtkT5Y4=
|
||||||
@ -840,26 +848,18 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1163/go.mod
|
|||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1182/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1182/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1183/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1183/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1187/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1188/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1188/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1189/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1191/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1191/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1192 h1:3K6aJXXkjBLxqFYnBqAqFW5YqxmwMT0HR2F4gxQiNMU=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1192/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1193 h1:anxhOjL4WrQDqUcX7eT8VEaQITiKWllKwsH1fEt6lBw=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1193 h1:anxhOjL4WrQDqUcX7eT8VEaQITiKWllKwsH1fEt6lBw=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1193/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1193/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.1163 h1:putqrH5n1SVRqFWHOylVqYI5yLQUjRTkHqZPLT2yeVY=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.1163 h1:putqrH5n1SVRqFWHOylVqYI5yLQUjRTkHqZPLT2yeVY=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.1163/go.mod h1:aEWRXlAvovPUUoS3kVB/LVWEQ19WqzTj2lXGvR1YArY=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.1163/go.mod h1:aEWRXlAvovPUUoS3kVB/LVWEQ19WqzTj2lXGvR1YArY=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1192 h1:2430drceaOXASJZyVZ+e7QSzgBfgwSjDEDM5rh4046M=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1192/go.mod h1:JHZLo95Fde/0et2Ag2E5P6VmCZQIq74MClUtanJ4JcY=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1193 h1:VtXqRnzGz3KheXu2msNPvA/fUYQGsVVRC30WgyAUEqg=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1193 h1:VtXqRnzGz3KheXu2msNPvA/fUYQGsVVRC30WgyAUEqg=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1193/go.mod h1:42I1OwaedHR6Yvg7J6UYoOjNYUYfFqwaeEkvx3x+NZc=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1193/go.mod h1:42I1OwaedHR6Yvg7J6UYoOjNYUYfFqwaeEkvx3x+NZc=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172 h1:6SUO0hTie3zxnUEMxmhnS1iRIXpAukSZV27Nrx4NwIk=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172 h1:6SUO0hTie3zxnUEMxmhnS1iRIXpAukSZV27Nrx4NwIk=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172/go.mod h1:tmN4zfu70SD0iee3qfpc09NRLel30zGoAuzIs4X0Kfs=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172/go.mod h1:tmN4zfu70SD0iee3qfpc09NRLel30zGoAuzIs4X0Kfs=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1189 h1:Db7gmkey7On70PAohvrna6RMLZzLHRjbALxPlH5JC3c=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1189/go.mod h1:x+WlMCjbePO7M3R0qzKmrpmieUWrtsRpcKBDpxJNQ5A=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1193 h1:tmACSthp5JLjrdxzng6XFs4gfQcZHBTTVlXR0tO6hSk=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1193 h1:tmACSthp5JLjrdxzng6XFs4gfQcZHBTTVlXR0tO6hSk=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1193/go.mod h1:LWf5UPUl41EQICrq0jswgQEO/BtRQY+CxAI6X+i709o=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1193/go.mod h1:LWf5UPUl41EQICrq0jswgQEO/BtRQY+CxAI6X+i709o=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1191 h1:4l1Db+yFh9HgqNynYbG93khxLtXSBwnXZgNmc88jOE0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1191 h1:4l1Db+yFh9HgqNynYbG93khxLtXSBwnXZgNmc88jOE0=
|
||||||
@ -868,6 +868,12 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1183 h1:3fvxkF
|
|||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1183/go.mod h1:d47RTYqj/2xjIk/lmq8bQ9deUwfEQcWhPQxUgqZnz24=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1183/go.mod h1:d47RTYqj/2xjIk/lmq8bQ9deUwfEQcWhPQxUgqZnz24=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1182 h1:2DaykFM5mXvQBvuhQEU/aOG5amissS31XI1wZh+FeMA=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1182 h1:2DaykFM5mXvQBvuhQEU/aOG5amissS31XI1wZh+FeMA=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1182/go.mod h1:pTAgdVcS28xFIARJXhg10hx2+g/Q9FqVkAkal3ARNfc=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1182/go.mod h1:pTAgdVcS28xFIARJXhg10hx2+g/Q9FqVkAkal3ARNfc=
|
||||||
|
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||||
|
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
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/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||||
|
@ -9,15 +9,21 @@ import (
|
|||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var instance core.App
|
var (
|
||||||
|
instance core.App
|
||||||
var intanceOnce sync.Once
|
intanceOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
func GetApp() core.App {
|
func GetApp() core.App {
|
||||||
intanceOnce.Do(func() {
|
intanceOnce.Do(func() {
|
||||||
instance = pocketbase.NewWithConfig(pocketbase.Config{
|
pb := pocketbase.NewWithConfig(pocketbase.Config{
|
||||||
HideStartBanner: true,
|
HideStartBanner: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
pb.RootCmd.Flags().MarkHidden("encryptionEnv")
|
||||||
|
pb.RootCmd.Flags().MarkHidden("queryTimeout")
|
||||||
|
|
||||||
|
instance = pb
|
||||||
})
|
})
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
@ -63,11 +63,13 @@ import (
|
|||||||
pJDCloudLive "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/jdcloud-live"
|
pJDCloudLive "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/jdcloud-live"
|
||||||
pJDCloudVOD "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/jdcloud-vod"
|
pJDCloudVOD "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/jdcloud-vod"
|
||||||
pK8sSecret "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/k8s-secret"
|
pK8sSecret "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/k8s-secret"
|
||||||
|
pKong "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/kong"
|
||||||
pLeCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/lecdn"
|
pLeCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/lecdn"
|
||||||
pLocal "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/local"
|
pLocal "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/local"
|
||||||
pNetlifySite "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/netlify-site"
|
pNetlifySite "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/netlify-site"
|
||||||
pProxmoxVE "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/proxmoxve"
|
pProxmoxVE "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/proxmoxve"
|
||||||
pQiniuCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-cdn"
|
pQiniuCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-cdn"
|
||||||
|
pQiniuKodo "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-kodo"
|
||||||
pQiniuPili "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-pili"
|
pQiniuPili "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-pili"
|
||||||
pRainYunRCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/rainyun-rcdn"
|
pRainYunRCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/rainyun-rcdn"
|
||||||
pRatPanelConsole "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/ratpanel-console"
|
pRatPanelConsole "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/ratpanel-console"
|
||||||
@ -924,6 +926,24 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy
|
|||||||
return deployer, err
|
return deployer, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case domain.DeploymentProviderTypeKong:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForKong{}
|
||||||
|
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deployer, err := pKong.NewSSLDeployerProvider(&pKong.SSLDeployerProviderConfig{
|
||||||
|
ServerUrl: access.ServerUrl,
|
||||||
|
ApiToken: access.ApiToken,
|
||||||
|
AllowInsecureConnections: access.AllowInsecureConnections,
|
||||||
|
ResourceType: pKong.ResourceType(xmaps.GetString(options.ProviderServiceConfig, "resourceType")),
|
||||||
|
Workspace: xmaps.GetString(options.ProviderServiceConfig, "workspace"),
|
||||||
|
CertificateId: xmaps.GetString(options.ProviderServiceConfig, "certificateId"),
|
||||||
|
})
|
||||||
|
return deployer, err
|
||||||
|
}
|
||||||
|
|
||||||
case domain.DeploymentProviderTypeKubernetesSecret:
|
case domain.DeploymentProviderTypeKubernetesSecret:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForKubernetes{}
|
access := domain.AccessConfigForKubernetes{}
|
||||||
@ -982,7 +1002,7 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch options.Provider {
|
switch options.Provider {
|
||||||
case domain.DeploymentProviderTypeQiniuCDN, domain.DeploymentProviderTypeQiniuKodo:
|
case domain.DeploymentProviderTypeQiniuCDN:
|
||||||
deployer, err := pQiniuCDN.NewSSLDeployerProvider(&pQiniuCDN.SSLDeployerProviderConfig{
|
deployer, err := pQiniuCDN.NewSSLDeployerProvider(&pQiniuCDN.SSLDeployerProviderConfig{
|
||||||
AccessKey: access.AccessKey,
|
AccessKey: access.AccessKey,
|
||||||
SecretKey: access.SecretKey,
|
SecretKey: access.SecretKey,
|
||||||
@ -990,6 +1010,14 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy
|
|||||||
})
|
})
|
||||||
return deployer, err
|
return deployer, err
|
||||||
|
|
||||||
|
case domain.DeploymentProviderTypeQiniuKodo:
|
||||||
|
deployer, err := pQiniuKodo.NewSSLDeployerProvider(&pQiniuKodo.SSLDeployerProviderConfig{
|
||||||
|
AccessKey: access.AccessKey,
|
||||||
|
SecretKey: access.SecretKey,
|
||||||
|
Domain: xmaps.GetString(options.ProviderServiceConfig, "domain"),
|
||||||
|
})
|
||||||
|
return deployer, err
|
||||||
|
|
||||||
case domain.DeploymentProviderTypeQiniuPili:
|
case domain.DeploymentProviderTypeQiniuPili:
|
||||||
deployer, err := pQiniuPili.NewSSLDeployerProvider(&pQiniuPili.SSLDeployerProviderConfig{
|
deployer, err := pQiniuPili.NewSSLDeployerProvider(&pQiniuPili.SSLDeployerProviderConfig{
|
||||||
AccessKey: access.AccessKey,
|
AccessKey: access.AccessKey,
|
||||||
@ -1183,7 +1211,7 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy
|
|||||||
SecretKey: access.SecretKey,
|
SecretKey: access.SecretKey,
|
||||||
Endpoint: xmaps.GetString(options.ProviderServiceConfig, "endpoint"),
|
Endpoint: xmaps.GetString(options.ProviderServiceConfig, "endpoint"),
|
||||||
ZoneId: xmaps.GetString(options.ProviderServiceConfig, "zoneId"),
|
ZoneId: xmaps.GetString(options.ProviderServiceConfig, "zoneId"),
|
||||||
Domain: xmaps.GetString(options.ProviderServiceConfig, "domain"),
|
Domains: xslices.Filter(strings.Split(xmaps.GetString(options.ProviderServiceConfig, "domains"), ";"), func(s string) bool { return s != "" }),
|
||||||
})
|
})
|
||||||
return deployer, err
|
return deployer, err
|
||||||
|
|
||||||
@ -1232,7 +1260,7 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy
|
|||||||
SecretId: access.SecretId,
|
SecretId: access.SecretId,
|
||||||
SecretKey: access.SecretKey,
|
SecretKey: access.SecretKey,
|
||||||
Endpoint: xmaps.GetString(options.ProviderServiceConfig, "endpoint"),
|
Endpoint: xmaps.GetString(options.ProviderServiceConfig, "endpoint"),
|
||||||
CertificiateId: xmaps.GetString(options.ProviderServiceConfig, "certificiateId"),
|
CertificateId: xmaps.GetString(options.ProviderServiceConfig, "certificateId"),
|
||||||
IsReplaced: xmaps.GetBool(options.ProviderServiceConfig, "isReplaced"),
|
IsReplaced: xmaps.GetBool(options.ProviderServiceConfig, "isReplaced"),
|
||||||
ResourceTypes: xslices.Filter(strings.Split(xmaps.GetString(options.ProviderServiceConfig, "resourceTypes"), ";"), func(s string) bool { return s != "" }),
|
ResourceTypes: xslices.Filter(strings.Split(xmaps.GetString(options.ProviderServiceConfig, "resourceTypes"), ";"), func(s string) bool { return s != "" }),
|
||||||
ResourceRegions: xslices.Filter(strings.Split(xmaps.GetString(options.ProviderServiceConfig, "resourceRegions"), ";"), func(s string) bool { return s != "" }),
|
ResourceRegions: xslices.Filter(strings.Split(xmaps.GetString(options.ProviderServiceConfig, "resourceRegions"), ";"), func(s string) bool { return s != "" }),
|
||||||
|
@ -227,6 +227,12 @@ type AccessConfigForJDCloud struct {
|
|||||||
AccessKeySecret string `json:"accessKeySecret"`
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForKong struct {
|
||||||
|
ServerUrl string `json:"serverUrl"`
|
||||||
|
ApiToken string `json:"apiToken,omitempty"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForKubernetes struct {
|
type AccessConfigForKubernetes struct {
|
||||||
KubeConfig string `json:"kubeConfig,omitempty"`
|
KubeConfig string `json:"kubeConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ const (
|
|||||||
AccessProviderTypeHetzner = AccessProviderType("hetzner")
|
AccessProviderTypeHetzner = AccessProviderType("hetzner")
|
||||||
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
|
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
|
||||||
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
|
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
|
||||||
|
AccessProviderTypeKong = AccessProviderType("kong")
|
||||||
AccessProviderTypeKubernetes = AccessProviderType("k8s")
|
AccessProviderTypeKubernetes = AccessProviderType("k8s")
|
||||||
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
|
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
|
||||||
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
|
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
|
||||||
@ -236,6 +237,7 @@ const (
|
|||||||
DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn")
|
DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn")
|
||||||
DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live")
|
DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live")
|
||||||
DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod")
|
DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod")
|
||||||
|
DeploymentProviderTypeKong = DeploymentProviderType(AccessProviderTypeKong)
|
||||||
DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret")
|
DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret")
|
||||||
DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN)
|
DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN)
|
||||||
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
|
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
|
||||||
|
@ -70,11 +70,11 @@ type WorkflowNodeConfigForApply struct {
|
|||||||
ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01
|
ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01
|
||||||
Provider string `json:"provider"` // DNS 提供商
|
Provider string `json:"provider"` // DNS 提供商
|
||||||
ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID
|
ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID
|
||||||
ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置
|
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // DNS 提供商额外配置
|
||||||
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值时使用全局配置)
|
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值时使用全局配置)
|
||||||
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
|
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
|
||||||
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
|
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
|
||||||
KeyAlgorithm string `json:"keyAlgorithm"` // 证书算法
|
KeyAlgorithm string `json:"keyAlgorithm,omitempty"` // 证书算法
|
||||||
ACMEProfile string `json:"acmeProfile,omitempty"` // ACME Profiles Extension
|
ACMEProfile string `json:"acmeProfile,omitempty"` // ACME Profiles Extension
|
||||||
Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
|
Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
|
||||||
DnsPropagationWait int32 `json:"dnsPropagationWait,omitempty"` // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数
|
DnsPropagationWait int32 `json:"dnsPropagationWait,omitempty"` // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数
|
||||||
@ -124,6 +124,7 @@ func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
|||||||
return WorkflowNodeConfigForApply{
|
return WorkflowNodeConfigForApply{
|
||||||
Domains: xmaps.GetString(n.Config, "domains"),
|
Domains: xmaps.GetString(n.Config, "domains"),
|
||||||
ContactEmail: xmaps.GetString(n.Config, "contactEmail"),
|
ContactEmail: xmaps.GetString(n.Config, "contactEmail"),
|
||||||
|
ChallengeType: xmaps.GetString(n.Config, "challengeType"),
|
||||||
Provider: xmaps.GetString(n.Config, "provider"),
|
Provider: xmaps.GetString(n.Config, "provider"),
|
||||||
ProviderAccessId: xmaps.GetString(n.Config, "providerAccessId"),
|
ProviderAccessId: xmaps.GetString(n.Config, "providerAccessId"),
|
||||||
ProviderConfig: xmaps.GetKVMapAny(n.Config, "providerConfig"),
|
ProviderConfig: xmaps.GetKVMapAny(n.Config, "providerConfig"),
|
||||||
|
@ -37,7 +37,7 @@ func NewApplyNode(node *domain.WorkflowNode) *applyNode {
|
|||||||
|
|
||||||
func (n *applyNode) Process(ctx context.Context) error {
|
func (n *applyNode) Process(ctx context.Context) error {
|
||||||
nodeCfg := n.node.GetConfigForApply()
|
nodeCfg := n.node.GetConfigForApply()
|
||||||
n.logger.Info("ready to obtain certificiate ...", slog.Any("config", nodeCfg))
|
n.logger.Info("ready to obtain certificate ...", slog.Any("config", nodeCfg))
|
||||||
|
|
||||||
// 查询上次执行结果
|
// 查询上次执行结果
|
||||||
lastOutput, err := n.outputRepo.GetByNodeId(ctx, n.node.Id)
|
lastOutput, err := n.outputRepo.GetByNodeId(ctx, n.node.Id)
|
||||||
@ -67,7 +67,7 @@ func (n *applyNode) Process(ctx context.Context) error {
|
|||||||
// 申请证书
|
// 申请证书
|
||||||
applyResult, err := applicant.Apply(ctx)
|
applyResult, err := applicant.Apply(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Warn("failed to obtain certificiate")
|
n.logger.Warn("failed to obtain certificate")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
main.go
1
main.go
@ -64,7 +64,6 @@ func main() {
|
|||||||
|
|
||||||
app.OnTerminate().BindFunc(func(e *core.TerminateEvent) error {
|
app.OnTerminate().BindFunc(func(e *core.TerminateEvent) error {
|
||||||
routes.Unregister()
|
routes.Unregister()
|
||||||
slog.Info("[CERTIMATE] Exit!")
|
|
||||||
return e.Next()
|
return e.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -270,12 +270,12 @@ func init() {
|
|||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Config map[string]any `json:"config"`
|
Config map[string]any `json:"config,omitempty"`
|
||||||
Inputs []map[string]any `json:"inputs"`
|
Inputs []map[string]any `json:"inputs,omitempty"`
|
||||||
Outputs []map[string]any `json:"outputs"`
|
Outputs []map[string]any `json:"outputs,omitempty"`
|
||||||
Next *dWorkflowNode `json:"next,omitempty"`
|
Next *dWorkflowNode `json:"next,omitempty"`
|
||||||
Branches []dWorkflowNode `json:"branches,omitempty"`
|
Branches []*dWorkflowNode `json:"branches,omitempty"`
|
||||||
Validated bool `json:"validated"`
|
Validated bool `json:"validated,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, workflowRun := range workflowRuns {
|
for _, workflowRun := range workflowRuns {
|
||||||
|
112
migrations/1751961600_upgrade.go
Normal file
112
migrations/1751961600_upgrade.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(app core.App) error {
|
||||||
|
tracer := NewTracer("(v0.3)1751961600")
|
||||||
|
tracer.Printf("go ...")
|
||||||
|
|
||||||
|
// migrate data
|
||||||
|
{
|
||||||
|
workflows, err := app.FindAllRecords("workflow")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type dWorkflowNode struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Config map[string]any `json:"config,omitempty"`
|
||||||
|
Inputs []map[string]any `json:"inputs,omitempty"`
|
||||||
|
Outputs []map[string]any `json:"outputs,omitempty"`
|
||||||
|
Next *dWorkflowNode `json:"next,omitempty"`
|
||||||
|
Branches []*dWorkflowNode `json:"branches,omitempty"`
|
||||||
|
Validated bool `json:"validated,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
deepChangeFn := func(node *dWorkflowNode) bool {
|
||||||
|
stack := []*dWorkflowNode{node}
|
||||||
|
|
||||||
|
for len(stack) > 0 {
|
||||||
|
current := stack[len(stack)-1]
|
||||||
|
stack = stack[:len(stack)-1]
|
||||||
|
|
||||||
|
if current.Type == "deploy" {
|
||||||
|
configMap := current.Config
|
||||||
|
if configMap != nil {
|
||||||
|
if provider, ok := configMap["provider"]; ok {
|
||||||
|
if provider.(string) == "tencentcloud-eo" {
|
||||||
|
if providerConfig, ok := configMap["providerConfig"]; ok {
|
||||||
|
if providerConfigMap, ok := providerConfig.(map[string]any); ok {
|
||||||
|
if _, ok := providerConfigMap["domain"]; ok {
|
||||||
|
providerConfigMap["domains"] = providerConfigMap["domain"]
|
||||||
|
delete(providerConfigMap, "domain")
|
||||||
|
configMap["providerConfig"] = providerConfigMap
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.Next != nil {
|
||||||
|
stack = append(stack, current.Next)
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.Branches != nil {
|
||||||
|
for i := len(current.Branches) - 1; i >= 0; i-- {
|
||||||
|
stack = append(stack, current.Branches[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, workflow := range workflows {
|
||||||
|
changed := false
|
||||||
|
|
||||||
|
rootNodeContent := &dWorkflowNode{}
|
||||||
|
if err := workflow.UnmarshalJSONField("content", rootNodeContent); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if deepChangeFn(rootNodeContent) {
|
||||||
|
workflow.Set("content", rootNodeContent)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootNodeDraft := &dWorkflowNode{}
|
||||||
|
if err := workflow.UnmarshalJSONField("draft", rootNodeDraft); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if deepChangeFn(rootNodeDraft) {
|
||||||
|
workflow.Set("draft", rootNodeDraft)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
err = app.Save(workflow)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.Printf("record #%s in collection '%s' updated", workflow.Id, workflow.Collection().Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.Printf("done")
|
||||||
|
return nil
|
||||||
|
}, func(app core.App) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
@ -109,12 +109,12 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
|||||||
// 根据部署资源类型决定部署方式
|
// 根据部署资源类型决定部署方式
|
||||||
switch d.config.ResourceType {
|
switch d.config.ResourceType {
|
||||||
case RESOURCE_TYPE_LOADBALANCER:
|
case RESOURCE_TYPE_LOADBALANCER:
|
||||||
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
|
if err := d.deployToLoadbalancer(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case RESOURCE_TYPE_LISTENER:
|
case RESOURCE_TYPE_LISTENER:
|
||||||
if err := d.deployToListener(ctx, upres.CertId); err != nil {
|
if err := d.deployToListener(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,13 +338,13 @@ func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, clo
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听证书 ID 格式:${证书 ID}-${地域}
|
if tea.StringValue(listenerCertificate.CertificateId) == cloudCertId {
|
||||||
certificateId := strings.Split(tea.StringValue(listenerCertificate.CertificateId), "-")[0]
|
|
||||||
if certificateId == cloudCertId {
|
|
||||||
certificateIsAlreadyAssociated = true
|
certificateIsAlreadyAssociated = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听证书 ID 格式:${证书 ID}-${地域}
|
||||||
|
certificateId := strings.Split(tea.StringValue(listenerCertificate.CertificateId), "-")[0]
|
||||||
certificateIdAsInt64, err := strconv.ParseInt(certificateId, 10, 64)
|
certificateIdAsInt64, err := strconv.ParseInt(certificateId, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
|
||||||
|
|
||||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
alislb "github.com/alibabacloud-go/slb-20140515/v4/client"
|
alislb "github.com/alibabacloud-go/slb-20140515/v4/client"
|
||||||
@ -13,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/certimate-go/certimate/pkg/core"
|
"github.com/certimate-go/certimate/pkg/core"
|
||||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-slb"
|
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-slb"
|
||||||
"github.com/certimate-go/certimate/pkg/utils/ifelse"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SSLDeployerProviderConfig struct {
|
type SSLDeployerProviderConfig struct {
|
||||||
@ -61,10 +59,7 @@ func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProv
|
|||||||
AccessKeyId: config.AccessKeyId,
|
AccessKeyId: config.AccessKeyId,
|
||||||
AccessKeySecret: config.AccessKeySecret,
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
ResourceGroupId: config.ResourceGroupId,
|
ResourceGroupId: config.ResourceGroupId,
|
||||||
Region: ifelse.
|
Region: config.Region,
|
||||||
If[string](config.Region == "" || strings.HasPrefix(config.Region, "cn-")).
|
|
||||||
Then("cn-hangzhou").
|
|
||||||
Else("ap-southeast-1"),
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
|
|
||||||
"github.com/certimate-go/certimate/pkg/core"
|
"github.com/certimate-go/certimate/pkg/core"
|
||||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-slb"
|
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||||
"github.com/certimate-go/certimate/pkg/utils/ifelse"
|
"github.com/certimate-go/certimate/pkg/utils/ifelse"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,12 +97,12 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
|||||||
// 根据部署资源类型决定部署方式
|
// 根据部署资源类型决定部署方式
|
||||||
switch d.config.ResourceType {
|
switch d.config.ResourceType {
|
||||||
case RESOURCE_TYPE_LOADBALANCER:
|
case RESOURCE_TYPE_LOADBALANCER:
|
||||||
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
|
if err := d.deployToLoadbalancer(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case RESOURCE_TYPE_LISTENER:
|
case RESOURCE_TYPE_LISTENER:
|
||||||
if err := d.deployToListener(ctx, upres.CertId); err != nil {
|
if err := d.deployToListener(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_SSLDEPLOYER_APISIX_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_SSLDEPLOYER_APISIX_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_SSLDEPLOYER_APISIX_SERVERURL="http://127.0.0.1:9080" \
|
--CERTIMATE_SSLDEPLOYER_APISIX_SERVERURL="http://127.0.0.1:9080" \
|
||||||
--CERTIMATE_SSLDEPLOYER_APISIX_APIKEY="your-api-key" \
|
--CERTIMATE_SSLDEPLOYER_APISIX_APIKEY="your-api-key" \
|
||||||
--CERTIMATE_SSLDEPLOYER_APISIX_CERTIFICATEID="your-cerficiate-id"
|
--CERTIMATE_SSLDEPLOYER_APISIX_CERTIFICATEID="your-certificate-id"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -5,10 +5,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/certimate-go/certimate/pkg/core"
|
"github.com/certimate-go/certimate/pkg/core"
|
||||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/ctcccloud-ao"
|
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/ctcccloud-ao"
|
||||||
ctyunao "github.com/certimate-go/certimate/pkg/sdk3rd/ctyun/ao"
|
ctyunao "github.com/certimate-go/certimate/pkg/sdk3rd/ctyun/ao"
|
||||||
|
xslices "github.com/certimate-go/certimate/pkg/utils/slices"
|
||||||
xtypes "github.com/certimate-go/certimate/pkg/utils/types"
|
xtypes "github.com/certimate-go/certimate/pkg/utils/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -80,7 +82,8 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
|||||||
// 域名基础及加速配置查询
|
// 域名基础及加速配置查询
|
||||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=113&api=13412&data=174&isNormal=1&vid=167
|
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=113&api=13412&data=174&isNormal=1&vid=167
|
||||||
getDomainConfigReq := &ctyunao.GetDomainConfigRequest{
|
getDomainConfigReq := &ctyunao.GetDomainConfigRequest{
|
||||||
Domain: xtypes.ToPtr(d.config.Domain),
|
Domain: xtypes.ToPtr(d.config.Domain),
|
||||||
|
ProductCode: xtypes.ToPtr("020"),
|
||||||
}
|
}
|
||||||
getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq)
|
getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq)
|
||||||
d.logger.Debug("sdk request 'cdn.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp))
|
d.logger.Debug("sdk request 'cdn.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp))
|
||||||
@ -93,7 +96,17 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
|||||||
modifyDomainConfigReq := &ctyunao.ModifyDomainConfigRequest{
|
modifyDomainConfigReq := &ctyunao.ModifyDomainConfigRequest{
|
||||||
Domain: xtypes.ToPtr(d.config.Domain),
|
Domain: xtypes.ToPtr(d.config.Domain),
|
||||||
ProductCode: xtypes.ToPtr(getDomainConfigResp.ReturnObj.ProductCode),
|
ProductCode: xtypes.ToPtr(getDomainConfigResp.ReturnObj.ProductCode),
|
||||||
Origin: getDomainConfigResp.ReturnObj.Origin,
|
Origin: xslices.Map(getDomainConfigResp.ReturnObj.Origin, func(item *ctyunao.DomainOriginConfigWithWeight) *ctyunao.DomainOriginConfig {
|
||||||
|
weight := item.Weight
|
||||||
|
if weight == 0 {
|
||||||
|
weight = 1
|
||||||
|
}
|
||||||
|
return &ctyunao.DomainOriginConfig{
|
||||||
|
Origin: item.Origin,
|
||||||
|
Role: item.Role,
|
||||||
|
Weight: strconv.Itoa(int(weight)),
|
||||||
|
}
|
||||||
|
}),
|
||||||
HttpsStatus: xtypes.ToPtr("on"),
|
HttpsStatus: xtypes.ToPtr("on"),
|
||||||
CertName: xtypes.ToPtr(upres.CertName),
|
CertName: xtypes.ToPtr(upres.CertName),
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_SSLDEPLOYER_FLEXCDN_SERVERURL="http://127.0.0.1:7788" \
|
--CERTIMATE_SSLDEPLOYER_FLEXCDN_SERVERURL="http://127.0.0.1:7788" \
|
||||||
--CERTIMATE_SSLDEPLOYER_FLEXCDN_ACCESSKEYID="your-access-key-id" \
|
--CERTIMATE_SSLDEPLOYER_FLEXCDN_ACCESSKEYID="your-access-key-id" \
|
||||||
--CERTIMATE_SSLDEPLOYER_FLEXCDN_ACCESSKEY="your-access-key" \
|
--CERTIMATE_SSLDEPLOYER_FLEXCDN_ACCESSKEY="your-access-key" \
|
||||||
--CERTIMATE_SSLDEPLOYER_FLEXCDN_CERTIFICATEID="your-cerficiate-id"
|
--CERTIMATE_SSLDEPLOYER_FLEXCDN_CERTIFICATEID="your-certificate-id"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -40,7 +40,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_SSLDEPLOYER_GOEDGE_SERVERURL="http://127.0.0.1:7788" \
|
--CERTIMATE_SSLDEPLOYER_GOEDGE_SERVERURL="http://127.0.0.1:7788" \
|
||||||
--CERTIMATE_SSLDEPLOYER_GOEDGE_ACCESSKEYID="your-access-key-id" \
|
--CERTIMATE_SSLDEPLOYER_GOEDGE_ACCESSKEYID="your-access-key-id" \
|
||||||
--CERTIMATE_SSLDEPLOYER_GOEDGE_ACCESSKEY="your-access-key" \
|
--CERTIMATE_SSLDEPLOYER_GOEDGE_ACCESSKEY="your-access-key" \
|
||||||
--CERTIMATE_SSLDEPLOYER_GOEDGE_CERTIFICATEID="your-cerficiate-id"
|
--CERTIMATE_SSLDEPLOYER_GOEDGE_CERTIFICATEID="your-certificate-id"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -297,21 +297,20 @@ func (d *SSLDeployerProvider) modifyListenerCertificate(ctx context.Context, clo
|
|||||||
return fmt.Errorf("failed to execute sdk request 'elb.ShowCertificate': %w", err)
|
return fmt.Errorf("failed to execute sdk request 'elb.ShowCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, certificate := range *listOldCertificateResp.Certificates {
|
for _, oldCertInfo := range *listOldCertificateResp.Certificates {
|
||||||
oldCertificate := certificate
|
newCertInfo := showNewCertificateResp.Certificate
|
||||||
newCertificate := showNewCertificateResp.Certificate
|
|
||||||
|
|
||||||
if oldCertificate.SubjectAlternativeNames != nil && newCertificate.SubjectAlternativeNames != nil {
|
if oldCertInfo.SubjectAlternativeNames != nil && newCertInfo.SubjectAlternativeNames != nil {
|
||||||
if slices.Equal(*oldCertificate.SubjectAlternativeNames, *newCertificate.SubjectAlternativeNames) {
|
if slices.Equal(*oldCertInfo.SubjectAlternativeNames, *newCertInfo.SubjectAlternativeNames) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if oldCertificate.Domain == newCertificate.Domain {
|
if oldCertInfo.Domain == newCertInfo.Domain {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sniCertIds = append(sniCertIds, certificate.Id)
|
sniCertIds = append(sniCertIds, oldCertInfo.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateListenerReq.Body.Listener.SniContainerRefs = &sniCertIds
|
updateListenerReq.Body.Listener.SniContainerRefs = &sniCertIds
|
||||||
|
8
pkg/core/ssl-deployer/providers/kong/consts.go
Normal file
8
pkg/core/ssl-deployer/providers/kong/consts.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package kong
|
||||||
|
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:替换指定证书。
|
||||||
|
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
|
||||||
|
)
|
143
pkg/core/ssl-deployer/providers/kong/kong.go
Normal file
143
pkg/core/ssl-deployer/providers/kong/kong.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package kong
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/kong/go-kong/kong"
|
||||||
|
|
||||||
|
"github.com/certimate-go/certimate/pkg/core"
|
||||||
|
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||||
|
xhttp "github.com/certimate-go/certimate/pkg/utils/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SSLDeployerProviderConfig struct {
|
||||||
|
// Kong 服务地址。
|
||||||
|
ServerUrl string `json:"serverUrl"`
|
||||||
|
// Kong Admin API Token。
|
||||||
|
ApiToken string `json:"apiToken,omitempty"`
|
||||||
|
// 是否允许不安全的连接。
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType ResourceType `json:"resourceType"`
|
||||||
|
// 工作空间。
|
||||||
|
// 选填。
|
||||||
|
Workspace string `json:"workspace,omitempty"`
|
||||||
|
// 证书 ID。
|
||||||
|
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
|
||||||
|
CertificateId string `json:"certificateId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSLDeployerProvider struct {
|
||||||
|
config *SSLDeployerProviderConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *kong.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("the configuration of the ssl deployer provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSDKClient(config.ServerUrl, config.Workspace, config.ApiToken, config.AllowInsecureConnections)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SSLDeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.New(slog.DiscardHandler)
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case RESOURCE_TYPE_CERTIFICATE:
|
||||||
|
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &core.SSLDeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SSLDeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
if d.config.CertificateId == "" {
|
||||||
|
return errors.New("config `certificateId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析证书内容
|
||||||
|
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新证书
|
||||||
|
// REF: https://developer.konghq.com/api/gateway/admin-ee/3.10/#/operations/upsert-certificate
|
||||||
|
// REF: https://developer.konghq.com/api/gateway/admin-ee/3.10/#/operations/upsert-certificate-in-workspace
|
||||||
|
updateCertificateReq := &kong.Certificate{
|
||||||
|
ID: kong.String(d.config.CertificateId),
|
||||||
|
Cert: kong.String(certPEM),
|
||||||
|
Key: kong.String(privkeyPEM),
|
||||||
|
SNIs: kong.StringSlice(certX509.DNSNames...),
|
||||||
|
}
|
||||||
|
updateCertificateResp, err := d.sdkClient.Certificates.Update(context.TODO(), updateCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'kong.UpdateCertificate'", slog.String("sslId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'kong.UpdateCertificate': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSDKClient(serverUrl, workspace, apiToken string, skipTlsVerify bool) (*kong.Client, error) {
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Transport: xhttp.NewDefaultTransport(),
|
||||||
|
Timeout: http.DefaultClient.Timeout,
|
||||||
|
}
|
||||||
|
if skipTlsVerify {
|
||||||
|
transport := xhttp.NewDefaultTransport()
|
||||||
|
if transport.TLSClientConfig == nil {
|
||||||
|
transport.TLSClientConfig = &tls.Config{}
|
||||||
|
}
|
||||||
|
transport.TLSClientConfig.InsecureSkipVerify = true
|
||||||
|
httpClient.Transport = transport
|
||||||
|
} else {
|
||||||
|
httpClient.Transport = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
httpHeaders := http.Header{}
|
||||||
|
if apiToken != "" {
|
||||||
|
httpHeaders.Set("Kong-Admin-Token", apiToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := kong.NewClient(kong.String(serverUrl), kong.HTTPClientWithHeaders(httpClient, httpHeaders))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if workspace != "" {
|
||||||
|
client.SetWorkspace(workspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
77
pkg/core/ssl-deployer/providers/kong/kong_test.go
Normal file
77
pkg/core/ssl-deployer/providers/kong/kong_test.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package kong_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/kong"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fServerUrl string
|
||||||
|
fApiToken string
|
||||||
|
fCertificateId string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_SSLDEPLOYER_KONG_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "")
|
||||||
|
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
||||||
|
flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./kong_test.go -args \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_KONG_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_KONG_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_KONG_SERVERURL="http://127.0.0.1:9080" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_KONG_APITOKEN="your-admin-token" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_KONG_CERTIFICATEID="your-certificate-id"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("SERVERURL: %v", fServerUrl),
|
||||||
|
fmt.Sprintf("APITOKEN: %v", fApiToken),
|
||||||
|
fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||||
|
ServerUrl: fServerUrl,
|
||||||
|
ApiToken: fApiToken,
|
||||||
|
AllowInsecureConnections: true,
|
||||||
|
ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
|
||||||
|
CertificateId: fCertificateId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
@ -42,7 +42,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_SSLDEPLOYER_LECDN_SERVERURL="http://127.0.0.1:5090" \
|
--CERTIMATE_SSLDEPLOYER_LECDN_SERVERURL="http://127.0.0.1:5090" \
|
||||||
--CERTIMATE_SSLDEPLOYER_LECDN_USERNAME="your-username" \
|
--CERTIMATE_SSLDEPLOYER_LECDN_USERNAME="your-username" \
|
||||||
--CERTIMATE_SSLDEPLOYER_LECDN_PASSWORD="your-password" \
|
--CERTIMATE_SSLDEPLOYER_LECDN_PASSWORD="your-password" \
|
||||||
--CERTIMATE_SSLDEPLOYER_LECDN_CERTIFICATEID="your-cerficiate-id"
|
--CERTIMATE_SSLDEPLOYER_LECDN_CERTIFICATEID="your-certificate-id"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
88
pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo.go
Normal file
88
pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package qiniukodo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/qiniu/go-sdk/v7/auth"
|
||||||
|
|
||||||
|
"github.com/certimate-go/certimate/pkg/core"
|
||||||
|
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/qiniu-sslcert"
|
||||||
|
qiniusdk "github.com/certimate-go/certimate/pkg/sdk3rd/qiniu"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SSLDeployerProviderConfig struct {
|
||||||
|
// 七牛云 AccessKey。
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
|
// 七牛云 SecretKey。
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
|
// 自定义域名(不支持泛域名)。
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSLDeployerProvider struct {
|
||||||
|
config *SSLDeployerProviderConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *qiniusdk.KodoManager
|
||||||
|
sslManager core.SSLManager
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("the configuration of the ssl deployer provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := qiniusdk.NewKodoManager(auth.New(config.AccessKey, config.SecretKey))
|
||||||
|
|
||||||
|
sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{
|
||||||
|
AccessKey: config.AccessKey,
|
||||||
|
SecretKey: config.SecretKey,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SSLDeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
sslManager: sslmgr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.New(slog.DiscardHandler)
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
d.sslManager.SetLogger(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||||
|
if d.config.Domain == "" {
|
||||||
|
return nil, fmt.Errorf("config `domain` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传证书
|
||||||
|
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定空间域名证书
|
||||||
|
bindBucketCertResp, err := d.sdkClient.BindBucketCert(context.TODO(), d.config.Domain, upres.CertId)
|
||||||
|
d.logger.Debug("sdk request 'kodo.BindCert'", slog.String("request.domain", d.config.Domain), slog.String("request.certId", upres.CertId), slog.Any("response", bindBucketCertResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'kodo.BindCert': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &core.SSLDeployResult{}, nil
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package qiniukodo_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-kodo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKey string
|
||||||
|
fSecretKey string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_SSLDEPLOYER_QINIUKODO_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "")
|
||||||
|
flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./qiniu_kodo_test.go -args \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_QINIUKODO_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_QINIUKODO_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_QINIUKODO_ACCESSKEY="your-access-key" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_QINIUKODO_SECRETKEY="your-secret-key" \
|
||||||
|
--CERTIMATE_SSLDEPLOYER_QINIUKODO_DOMAIN="example.com"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEY: %v", fAccessKey),
|
||||||
|
fmt.Sprintf("SECRETKEY: %v", fSecretKey),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||||
|
AccessKey: fAccessKey,
|
||||||
|
SecretKey: fSecretKey,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
@ -37,7 +37,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_SSLDEPLOYER_SAFELINE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_SSLDEPLOYER_SAFELINE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_SSLDEPLOYER_SAFELINE_SERVERURL="http://127.0.0.1:9443" \
|
--CERTIMATE_SSLDEPLOYER_SAFELINE_SERVERURL="http://127.0.0.1:9443" \
|
||||||
--CERTIMATE_SSLDEPLOYER_SAFELINE_APITOKEN="your-api-token" \
|
--CERTIMATE_SSLDEPLOYER_SAFELINE_APITOKEN="your-api-token" \
|
||||||
--CERTIMATE_SSLDEPLOYER_SAFELINE_CERTIFICATEID="your-cerficiate-id"
|
--CERTIMATE_SSLDEPLOYER_SAFELINE_CERTIFICATEID="your-certificate-id"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -419,7 +419,7 @@ func writeFileWithSFTP(sshCli *ssh.Client, path string, data []byte) error {
|
|||||||
}
|
}
|
||||||
defer sftpCli.Close()
|
defer sftpCli.Close()
|
||||||
|
|
||||||
if err := sftpCli.MkdirAll(filepath.Dir(path)); err != nil {
|
if err := sftpCli.MkdirAll(filepath.ToSlash(filepath.Dir(path))); err != nil {
|
||||||
return fmt.Errorf("failed to create remote directory: %w", err)
|
return fmt.Errorf("failed to create remote directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ type SSLDeployerProviderConfig struct {
|
|||||||
Endpoint string `json:"endpoint,omitempty"`
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
// 站点 ID。
|
// 站点 ID。
|
||||||
ZoneId string `json:"zoneId"`
|
ZoneId string `json:"zoneId"`
|
||||||
// 加速域名(支持泛域名)。
|
// 加速域名列表(支持泛域名)。
|
||||||
Domain string `json:"domain"`
|
Domains []string `json:"domains"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSLDeployerProvider struct {
|
type SSLDeployerProvider struct {
|
||||||
@ -82,8 +82,8 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
|||||||
if d.config.ZoneId == "" {
|
if d.config.ZoneId == "" {
|
||||||
return nil, errors.New("config `zoneId` is required")
|
return nil, errors.New("config `zoneId` is required")
|
||||||
}
|
}
|
||||||
if d.config.Domain == "" {
|
if len(d.config.Domains) == 0 {
|
||||||
return nil, errors.New("config `domain` is required")
|
return nil, errors.New("config `domains` is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传证书
|
// 上传证书
|
||||||
@ -99,7 +99,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
|||||||
modifyHostsCertificateReq := tcteo.NewModifyHostsCertificateRequest()
|
modifyHostsCertificateReq := tcteo.NewModifyHostsCertificateRequest()
|
||||||
modifyHostsCertificateReq.ZoneId = common.StringPtr(d.config.ZoneId)
|
modifyHostsCertificateReq.ZoneId = common.StringPtr(d.config.ZoneId)
|
||||||
modifyHostsCertificateReq.Mode = common.StringPtr("sslcert")
|
modifyHostsCertificateReq.Mode = common.StringPtr("sslcert")
|
||||||
modifyHostsCertificateReq.Hosts = common.StringPtrs([]string{d.config.Domain})
|
modifyHostsCertificateReq.Hosts = common.StringPtrs(d.config.Domains)
|
||||||
modifyHostsCertificateReq.ServerCertInfo = []*tcteo.ServerCertInfo{{CertId: common.StringPtr(upres.CertId)}}
|
modifyHostsCertificateReq.ServerCertInfo = []*tcteo.ServerCertInfo{{CertId: common.StringPtr(upres.CertId)}}
|
||||||
modifyHostsCertificateResp, err := d.sdkClient.ModifyHostsCertificate(modifyHostsCertificateReq)
|
modifyHostsCertificateResp, err := d.sdkClient.ModifyHostsCertificate(modifyHostsCertificateReq)
|
||||||
d.logger.Debug("sdk request 'teo.ModifyHostsCertificate'", slog.Any("request", modifyHostsCertificateReq), slog.Any("response", modifyHostsCertificateResp))
|
d.logger.Debug("sdk request 'teo.ModifyHostsCertificate'", slog.Any("request", modifyHostsCertificateReq), slog.Any("response", modifyHostsCertificateResp))
|
||||||
|
@ -17,7 +17,7 @@ var (
|
|||||||
fSecretId string
|
fSecretId string
|
||||||
fSecretKey string
|
fSecretKey string
|
||||||
fZoneId string
|
fZoneId string
|
||||||
fDomain string
|
fDomains string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -28,7 +28,7 @@ func init() {
|
|||||||
flag.StringVar(&fSecretId, argsPrefix+"SECRETID", "", "")
|
flag.StringVar(&fSecretId, argsPrefix+"SECRETID", "", "")
|
||||||
flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "")
|
flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "")
|
||||||
flag.StringVar(&fZoneId, argsPrefix+"ZONEID", "", "")
|
flag.StringVar(&fZoneId, argsPrefix+"ZONEID", "", "")
|
||||||
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
flag.StringVar(&fDomains, argsPrefix+"DOMAINS", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -40,7 +40,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_SECRETID="your-secret-id" \
|
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_SECRETID="your-secret-id" \
|
||||||
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_SECRETKEY="your-secret-key" \
|
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_SECRETKEY="your-secret-key" \
|
||||||
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_ZONEID="your-zone-id" \
|
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_ZONEID="your-zone-id" \
|
||||||
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_DOMAIN="example.com"
|
--CERTIMATE_SSLDEPLOYER_TENCENTCLOUDEO_DOMAINS="example.com"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -53,14 +53,14 @@ func TestDeploy(t *testing.T) {
|
|||||||
fmt.Sprintf("SECRETID: %v", fSecretId),
|
fmt.Sprintf("SECRETID: %v", fSecretId),
|
||||||
fmt.Sprintf("SECRETKEY: %v", fSecretKey),
|
fmt.Sprintf("SECRETKEY: %v", fSecretKey),
|
||||||
fmt.Sprintf("ZONEID: %v", fZoneId),
|
fmt.Sprintf("ZONEID: %v", fZoneId),
|
||||||
fmt.Sprintf("DOMAIN: %v", fDomain),
|
fmt.Sprintf("DOMAINS: %v", fDomains),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||||
SecretId: fSecretId,
|
SecretId: fSecretId,
|
||||||
SecretKey: fSecretKey,
|
SecretKey: fSecretKey,
|
||||||
ZoneId: fZoneId,
|
ZoneId: fZoneId,
|
||||||
Domain: fDomain,
|
Domains: strings.Split(fDomains, ";"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
||||||
|
@ -24,7 +24,7 @@ type SSLDeployerProviderConfig struct {
|
|||||||
// 腾讯云接口端点。
|
// 腾讯云接口端点。
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
// 原证书 ID。
|
// 原证书 ID。
|
||||||
CertificiateId string `json:"certificateId"`
|
CertificateId string `json:"certificateId"`
|
||||||
// 是否替换原有证书(即保持原证书 ID 不变)。
|
// 是否替换原有证书(即保持原证书 ID 不变)。
|
||||||
IsReplaced bool `json:"isReplaced,omitempty"`
|
IsReplaced bool `json:"isReplaced,omitempty"`
|
||||||
// 云资源类型数组。
|
// 云资源类型数组。
|
||||||
@ -80,7 +80,7 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||||
if d.config.CertificiateId == "" {
|
if d.config.CertificateId == "" {
|
||||||
return nil, errors.New("config `certificateId` is required")
|
return nil, errors.New("config `certificateId` is required")
|
||||||
}
|
}
|
||||||
if len(d.config.ResourceTypes) == 0 {
|
if len(d.config.ResourceTypes) == 0 {
|
||||||
@ -120,7 +120,7 @@ func (d *SSLDeployerProvider) executeUpdateCertificateInstance(ctx context.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateCertificateInstanceReq := tcssl.NewUpdateCertificateInstanceRequest()
|
updateCertificateInstanceReq := tcssl.NewUpdateCertificateInstanceRequest()
|
||||||
updateCertificateInstanceReq.OldCertificateId = common.StringPtr(d.config.CertificiateId)
|
updateCertificateInstanceReq.OldCertificateId = common.StringPtr(d.config.CertificateId)
|
||||||
updateCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
|
updateCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
|
||||||
updateCertificateInstanceReq.ResourceTypes = common.StringPtrs(d.config.ResourceTypes)
|
updateCertificateInstanceReq.ResourceTypes = common.StringPtrs(d.config.ResourceTypes)
|
||||||
updateCertificateInstanceReq.ResourceTypesRegions = wrapResourceTypeRegions(d.config.ResourceTypes, d.config.ResourceRegions)
|
updateCertificateInstanceReq.ResourceTypesRegions = wrapResourceTypeRegions(d.config.ResourceTypes, d.config.ResourceRegions)
|
||||||
@ -198,7 +198,7 @@ func (d *SSLDeployerProvider) executeUploadUpdateCertificateInstance(ctx context
|
|||||||
}
|
}
|
||||||
|
|
||||||
uploadUpdateCertificateInstanceReq := tcssl.NewUploadUpdateCertificateInstanceRequest()
|
uploadUpdateCertificateInstanceReq := tcssl.NewUploadUpdateCertificateInstanceRequest()
|
||||||
uploadUpdateCertificateInstanceReq.OldCertificateId = common.StringPtr(d.config.CertificiateId)
|
uploadUpdateCertificateInstanceReq.OldCertificateId = common.StringPtr(d.config.CertificateId)
|
||||||
uploadUpdateCertificateInstanceReq.CertificatePublicKey = common.StringPtr(certPEM)
|
uploadUpdateCertificateInstanceReq.CertificatePublicKey = common.StringPtr(certPEM)
|
||||||
uploadUpdateCertificateInstanceReq.CertificatePrivateKey = common.StringPtr(privkeyPEM)
|
uploadUpdateCertificateInstanceReq.CertificatePrivateKey = common.StringPtr(privkeyPEM)
|
||||||
uploadUpdateCertificateInstanceReq.ResourceTypes = common.StringPtrs(d.config.ResourceTypes)
|
uploadUpdateCertificateInstanceReq.ResourceTypes = common.StringPtrs(d.config.ResourceTypes)
|
||||||
|
@ -93,13 +93,23 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
}
|
}
|
||||||
|
|
||||||
if listUserCertificateOrderResp.Body.CertificateOrderList != nil {
|
if listUserCertificateOrderResp.Body.CertificateOrderList != nil {
|
||||||
for _, certDetail := range listUserCertificateOrderResp.Body.CertificateOrderList {
|
for _, certOrder := range listUserCertificateOrderResp.Body.CertificateOrderList {
|
||||||
if !strings.EqualFold(certX509.SerialNumber.Text(16), *certDetail.SerialNo) {
|
// 先对比证书通用名称
|
||||||
|
if !strings.EqualFold(certX509.Subject.CommonName, tea.StringValue(certOrder.CommonName)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 再对比证书序列号
|
||||||
|
// 注意阿里云 CAS 会在序列号前补零,需去除后再比较
|
||||||
|
oldCertSN := strings.TrimLeft(tea.StringValue(certOrder.SerialNo), "0")
|
||||||
|
newCertSN := strings.TrimLeft(certX509.SerialNumber.Text(16), "0")
|
||||||
|
if !strings.EqualFold(newCertSN, oldCertSN) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后对比证书内容
|
||||||
getUserCertificateDetailReq := &alicas.GetUserCertificateDetailRequest{
|
getUserCertificateDetailReq := &alicas.GetUserCertificateDetailRequest{
|
||||||
CertId: certDetail.CertificateId,
|
CertId: certOrder.CertificateId,
|
||||||
}
|
}
|
||||||
getUserCertificateDetailResp, err := m.sdkClient.GetUserCertificateDetail(getUserCertificateDetailReq)
|
getUserCertificateDetailResp, err := m.sdkClient.GetUserCertificateDetail(getUserCertificateDetailReq)
|
||||||
m.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp))
|
m.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp))
|
||||||
@ -123,8 +133,8 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
if isSameCert {
|
if isSameCert {
|
||||||
m.logger.Info("ssl certificate already exists")
|
m.logger.Info("ssl certificate already exists")
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: fmt.Sprintf("%d", tea.Int64Value(certDetail.CertificateId)),
|
CertId: fmt.Sprintf("%d", tea.Int64Value(certOrder.CertificateId)),
|
||||||
CertName: *certDetail.Name,
|
CertName: *certOrder.Name,
|
||||||
ExtendedData: map[string]any{
|
ExtendedData: map[string]any{
|
||||||
"instanceId": tea.StringValue(getUserCertificateDetailResp.Body.InstanceId),
|
"instanceId": tea.StringValue(getUserCertificateDetailResp.Body.InstanceId),
|
||||||
"certIdentifier": tea.StringValue(getUserCertificateDetailResp.Body.CertIdentifier),
|
"certIdentifier": tea.StringValue(getUserCertificateDetailResp.Body.CertIdentifier),
|
||||||
|
77
pkg/core/ssl-manager/providers/aliyun-cas/aliyun_cas_test.go
Normal file
77
pkg/core/ssl-manager/providers/aliyun-cas/aliyun_cas_test.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package aliyuncas_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fRegion string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_SSLMANAGER_ALIYUNCAS_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./aliyun_cas_test.go -args \
|
||||||
|
--CERTIMATE_SSLMANAGER_ALIYUNCAS_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_SSLMANAGER_ALIYUNCAS_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_SSLMANAGER_ALIYUNCAS_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_SSLMANAGER_ALIYUNCAS_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_SSLMANAGER_ALIYUNCAS_REGION="cn-hangzhou"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
sslmanager, err := provider.NewSSLManagerProvider(&provider.SSLManagerProviderConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := sslmanager.Upload(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sres, _ := json.Marshal(res)
|
||||||
|
t.Logf("ok: %s", string(sres))
|
||||||
|
})
|
||||||
|
}
|
@ -86,16 +86,16 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
if describeServerCertificatesResp.Body.ServerCertificates != nil && describeServerCertificatesResp.Body.ServerCertificates.ServerCertificate != nil {
|
if describeServerCertificatesResp.Body.ServerCertificates != nil && describeServerCertificatesResp.Body.ServerCertificates.ServerCertificate != nil {
|
||||||
fingerprint := sha256.Sum256(certX509.Raw)
|
fingerprint := sha256.Sum256(certX509.Raw)
|
||||||
fingerprintHex := hex.EncodeToString(fingerprint[:])
|
fingerprintHex := hex.EncodeToString(fingerprint[:])
|
||||||
for _, certDetail := range describeServerCertificatesResp.Body.ServerCertificates.ServerCertificate {
|
for _, serverCert := range describeServerCertificatesResp.Body.ServerCertificates.ServerCertificate {
|
||||||
isSameCert := *certDetail.IsAliCloudCertificate == 0 &&
|
isSameCert := *serverCert.IsAliCloudCertificate == 0 &&
|
||||||
strings.EqualFold(fingerprintHex, strings.ReplaceAll(*certDetail.Fingerprint, ":", "")) &&
|
strings.EqualFold(fingerprintHex, strings.ReplaceAll(*serverCert.Fingerprint, ":", "")) &&
|
||||||
strings.EqualFold(certX509.Subject.CommonName, *certDetail.CommonName)
|
strings.EqualFold(certX509.Subject.CommonName, *serverCert.CommonName)
|
||||||
// 如果已存在相同证书,直接返回
|
// 如果已存在相同证书,直接返回
|
||||||
if isSameCert {
|
if isSameCert {
|
||||||
m.logger.Info("ssl certificate already exists")
|
m.logger.Info("ssl certificate already exists")
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: *certDetail.ServerCertificateId,
|
CertId: *serverCert.ServerCertificateId,
|
||||||
CertName: *certDetail.ServerCertificateName,
|
CertName: *serverCert.ServerCertificateName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
// 生成新证书名(需符合 Azure 命名规则)
|
// 生成新证书名(需符合 Azure 命名规则)
|
||||||
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||||
|
|
||||||
// Azure Key Vault 不支持导入带有 Certificiate Chain 的 PEM 证书。
|
// Azure Key Vault 不支持导入带有 Certificate Chain 的 PEM 证书。
|
||||||
// Issue Link: https://github.com/Azure/azure-cli/issues/19017
|
// Issue Link: https://github.com/Azure/azure-cli/issues/19017
|
||||||
// 暂时的解决方法是,将 PEM 证书转换成 PFX 格式,然后再导入。
|
// 暂时的解决方法是,将 PEM 证书转换成 PFX 格式,然后再导入。
|
||||||
certPFX, err := xcert.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, "")
|
certPFX, err := xcert.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, "")
|
||||||
|
@ -20,7 +20,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
argsPrefix := "CERTIMATE_SSLMANAGER_BAIDUCLOUDCAS_"
|
argsPrefix := "CERTIMATE_SSLMANAGER_BAIDUCLOUDCERT_"
|
||||||
|
|
||||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
@ -31,11 +31,11 @@ func init() {
|
|||||||
/*
|
/*
|
||||||
Shell command to run this test:
|
Shell command to run this test:
|
||||||
|
|
||||||
go test -v ./baiducloud_cas_test.go -args \
|
go test -v ./baiducloud_cert_test.go -args \
|
||||||
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCAS_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCERT_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCAS_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCERT_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCAS_ACCESSKEYID="your-access-key-id" \
|
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCERT_ACCESSKEYID="your-access-key-id" \
|
||||||
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCAS_SECRETACCESSKEY="your-access-key-secret"
|
--CERTIMATE_SSLMANAGER_BAIDUCLOUDCERT_SECRETACCESSKEY="your-access-key-secret"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -87,17 +87,17 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
}
|
}
|
||||||
|
|
||||||
if listCertInfoResp.Result.CertInfo != nil {
|
if listCertInfoResp.Result.CertInfo != nil {
|
||||||
for _, certDetail := range listCertInfoResp.Result.CertInfo {
|
for _, certInfo := range listCertInfoResp.Result.CertInfo {
|
||||||
fingerprintSha1 := sha1.Sum(certX509.Raw)
|
fingerprintSha1 := sha1.Sum(certX509.Raw)
|
||||||
fingerprintSha256 := sha256.Sum256(certX509.Raw)
|
fingerprintSha256 := sha256.Sum256(certX509.Raw)
|
||||||
isSameCert := strings.EqualFold(hex.EncodeToString(fingerprintSha1[:]), certDetail.CertFingerprint.Sha1) &&
|
isSameCert := strings.EqualFold(hex.EncodeToString(fingerprintSha1[:]), certInfo.CertFingerprint.Sha1) &&
|
||||||
strings.EqualFold(hex.EncodeToString(fingerprintSha256[:]), certDetail.CertFingerprint.Sha256)
|
strings.EqualFold(hex.EncodeToString(fingerprintSha256[:]), certInfo.CertFingerprint.Sha256)
|
||||||
// 如果已存在相同证书,直接返回
|
// 如果已存在相同证书,直接返回
|
||||||
if isSameCert {
|
if isSameCert {
|
||||||
m.logger.Info("ssl certificate already exists")
|
m.logger.Info("ssl certificate already exists")
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: certDetail.CertId,
|
CertId: certInfo.CertId,
|
||||||
CertName: certDetail.Desc,
|
CertName: certInfo.Desc,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,12 +95,12 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
}
|
}
|
||||||
|
|
||||||
if listCertificatesResp.Certificates != nil {
|
if listCertificatesResp.Certificates != nil {
|
||||||
for _, certDetail := range *listCertificatesResp.Certificates {
|
for _, certInfo := range *listCertificatesResp.Certificates {
|
||||||
var isSameCert bool
|
var isSameCert bool
|
||||||
if certDetail.Certificate == certPEM {
|
if certInfo.Certificate == certPEM {
|
||||||
isSameCert = true
|
isSameCert = true
|
||||||
} else {
|
} else {
|
||||||
oldCertX509, err := xcert.ParseCertificateFromPEM(certDetail.Certificate)
|
oldCertX509, err := xcert.ParseCertificateFromPEM(certInfo.Certificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -112,8 +112,8 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
if isSameCert {
|
if isSameCert {
|
||||||
m.logger.Info("ssl certificate already exists")
|
m.logger.Info("ssl certificate already exists")
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: certDetail.Id,
|
CertId: certInfo.Id,
|
||||||
CertName: certDetail.Name,
|
CertName: certInfo.Name,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
|
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
|
||||||
@ -95,6 +96,17 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
|
|
||||||
if listCertificatesResp.Certificates != nil {
|
if listCertificatesResp.Certificates != nil {
|
||||||
for _, certDetail := range *listCertificatesResp.Certificates {
|
for _, certDetail := range *listCertificatesResp.Certificates {
|
||||||
|
// 先对比证书通用名称
|
||||||
|
if !strings.EqualFold(certX509.Subject.CommonName, certDetail.Domain) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再对比证书有效期
|
||||||
|
if certX509.NotAfter.Local().Format(time.DateTime) != strings.TrimSuffix(certDetail.ExpireTime, ".0") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后对比证书内容
|
||||||
exportCertificateReq := &hcscmmodel.ExportCertificateRequest{
|
exportCertificateReq := &hcscmmodel.ExportCertificateRequest{
|
||||||
CertificateId: certDetail.Id,
|
CertificateId: certDetail.Id,
|
||||||
}
|
}
|
||||||
@ -105,27 +117,27 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'scm.ExportCertificate': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'scm.ExportCertificate': %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
var isSameCert bool
|
|
||||||
if *exportCertificateResp.Certificate == certPEM {
|
|
||||||
isSameCert = true
|
|
||||||
} else {
|
} else {
|
||||||
oldCertX509, err := xcert.ParseCertificateFromPEM(*exportCertificateResp.Certificate)
|
var isSameCert bool
|
||||||
if err != nil {
|
if *exportCertificateResp.Certificate == certPEM {
|
||||||
continue
|
isSameCert = true
|
||||||
|
} else {
|
||||||
|
oldCertX509, err := xcert.ParseCertificateFromPEM(*exportCertificateResp.Certificate)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
isSameCert = xcert.EqualCertificate(certX509, oldCertX509)
|
||||||
}
|
}
|
||||||
|
|
||||||
isSameCert = xcert.EqualCertificate(certX509, oldCertX509)
|
// 如果已存在相同证书,直接返回
|
||||||
}
|
if isSameCert {
|
||||||
|
m.logger.Info("ssl certificate already exists")
|
||||||
// 如果已存在相同证书,直接返回
|
return &core.SSLManageUploadResult{
|
||||||
if isSameCert {
|
CertId: certDetail.Id,
|
||||||
m.logger.Info("ssl certificate already exists")
|
CertName: certDetail.Name,
|
||||||
return &core.SSLManageUploadResult{
|
}, nil
|
||||||
CertId: certDetail.Id,
|
}
|
||||||
CertName: certDetail.Name,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,12 @@ package qiniusslcert
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qiniu/go-sdk/v7/auth"
|
"github.com/qiniu/go-sdk/v7/auth"
|
||||||
@ -24,7 +27,7 @@ type SSLManagerProviderConfig struct {
|
|||||||
type SSLManagerProvider struct {
|
type SSLManagerProvider struct {
|
||||||
config *SSLManagerProviderConfig
|
config *SSLManagerProviderConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
sdkClient *qiniusdk.CdnManager
|
sdkClient *qiniusdk.SslCertManager
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ core.SSLManager = (*SSLManagerProvider)(nil)
|
var _ core.SSLManager = (*SSLManagerProvider)(nil)
|
||||||
@ -64,12 +67,80 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
// 生成新证书名(需符合七牛云命名规则)
|
// 生成新证书名(需符合七牛云命名规则)
|
||||||
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||||
|
|
||||||
|
// 遍历查询已有证书,避免重复上传
|
||||||
|
getSslCertListMarker := ""
|
||||||
|
getSslCertListLimit := int32(200)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
getSslCertListResp, err := m.sdkClient.GetSslCertList(context.TODO(), getSslCertListMarker, getSslCertListLimit)
|
||||||
|
m.logger.Debug("sdk request 'sslcert.GetList'", slog.Any("request.marker", getSslCertListMarker), slog.Any("response", getSslCertListResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'sslcert.GetList': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if getSslCertListResp.Certs != nil {
|
||||||
|
for _, sslCert := range getSslCertListResp.Certs {
|
||||||
|
// 先对比证书通用名称
|
||||||
|
if !strings.EqualFold(certX509.Subject.CommonName, sslCert.CommonName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再对比证书多域名
|
||||||
|
if !slices.Equal(certX509.DNSNames, sslCert.DnsNames) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再对比证书有效期
|
||||||
|
if certX509.NotBefore.Unix() != sslCert.NotBefore || certX509.NotAfter.Unix() != sslCert.NotAfter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后对比证书公钥算法
|
||||||
|
switch certX509.PublicKeyAlgorithm {
|
||||||
|
case x509.RSA:
|
||||||
|
if !strings.EqualFold(sslCert.Encrypt, "RSA") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case x509.ECDSA:
|
||||||
|
if !strings.EqualFold(sslCert.Encrypt, "ECDSA") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case x509.Ed25519:
|
||||||
|
if !strings.EqualFold(sslCert.Encrypt, "ED25519") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// 未知算法,跳过
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||||
|
m.logger.Info("ssl certificate already exists")
|
||||||
|
return &core.SSLManageUploadResult{
|
||||||
|
CertId: sslCert.CertID,
|
||||||
|
CertName: sslCert.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(getSslCertListResp.Certs) < int(getSslCertListLimit) || getSslCertListResp.Marker == "" {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
getSslCertListMarker = getSslCertListResp.Marker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 上传新证书
|
// 上传新证书
|
||||||
// REF: https://developer.qiniu.com/fusion/8593/interface-related-certificate
|
// REF: https://developer.qiniu.com/fusion/8593/interface-related-certificate
|
||||||
uploadSslCertResp, err := m.sdkClient.UploadSslCert(context.TODO(), certName, certX509.Subject.CommonName, certPEM, privkeyPEM)
|
uploadSslCertResp, err := m.sdkClient.UploadSslCert(context.TODO(), certName, certX509.Subject.CommonName, certPEM, privkeyPEM)
|
||||||
m.logger.Debug("sdk request 'cdn.UploadSslCert'", slog.Any("response", uploadSslCertResp))
|
m.logger.Debug("sdk request 'sslcert.Upload'", slog.Any("response", uploadSslCertResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.UploadSslCert': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'sslcert.Upload': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
@ -78,7 +149,7 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSDKClient(accessKey, secretKey string) (*qiniusdk.CdnManager, error) {
|
func createSDKClient(accessKey, secretKey string) (*qiniusdk.SslCertManager, error) {
|
||||||
if secretKey == "" {
|
if secretKey == "" {
|
||||||
return nil, errors.New("invalid qiniu access key")
|
return nil, errors.New("invalid qiniu access key")
|
||||||
}
|
}
|
||||||
@ -88,6 +159,6 @@ func createSDKClient(accessKey, secretKey string) (*qiniusdk.CdnManager, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
credential := auth.New(accessKey, secretKey)
|
credential := auth.New(accessKey, secretKey)
|
||||||
client := qiniusdk.NewCdnManager(credential)
|
client := qiniusdk.NewSslCertManager(credential)
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package qiniusslcert_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/qiniu-sslcert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKey string
|
||||||
|
fSecretKey string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_SSLMANAGER_QINIUSSLCERT_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "")
|
||||||
|
flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./qiniu_sslcert_test.go -args \
|
||||||
|
--CERTIMATE_SSLMANAGER_QINIUSSLCERT_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_SSLMANAGER_QINIUSSLCERT_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_SSLMANAGER_QINIUSSLCERT_ACCESSKEY="your-access-key" \
|
||||||
|
--CERTIMATE_SSLMANAGER_QINIUSSLCERT_SECRETKEY="your-secret-key"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEY: %v", fAccessKey),
|
||||||
|
fmt.Sprintf("SECRETKEY: %v", fSecretKey),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
sslmanager, err := provider.NewSSLManagerProvider(&provider.SSLManagerProviderConfig{
|
||||||
|
AccessKey: fAccessKey,
|
||||||
|
SecretKey: fSecretKey,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := sslmanager.Upload(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sres, _ := json.Marshal(res)
|
||||||
|
t.Logf("ok: %s", string(sres))
|
||||||
|
})
|
||||||
|
}
|
@ -114,19 +114,19 @@ func (m *SSLManagerProvider) findCertIfExists(ctx context.Context, certPEM strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sslCenterListResp.Data != nil && sslCenterListResp.Data.Records != nil {
|
if sslCenterListResp.Data != nil && sslCenterListResp.Data.Records != nil {
|
||||||
for _, sslItem := range sslCenterListResp.Data.Records {
|
for _, sslRecord := range sslCenterListResp.Data.Records {
|
||||||
// 先对比证书的多域名
|
// 先对比证书的多域名
|
||||||
if sslItem.Domain != strings.Join(certX509.DNSNames, ", ") {
|
if sslRecord.Domain != strings.Join(certX509.DNSNames, ", ") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再对比证书的有效期
|
// 再对比证书的有效期
|
||||||
if sslItem.StartDate != certX509.NotBefore.Unix() || sslItem.ExpireDate != certX509.NotAfter.Unix() {
|
if sslRecord.StartDate != certX509.NotBefore.Unix() || sslRecord.ExpireDate != certX509.NotAfter.Unix() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最后对比证书内容
|
// 最后对比证书内容
|
||||||
sslCenterGetResp, err := m.sdkClient.SslCenterGet(sslItem.ID)
|
sslCenterGetResp, err := m.sdkClient.SslCenterGet(sslRecord.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'sslcenter.Get': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'sslcenter.Get': %w", err)
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ func (m *SSLManagerProvider) findCertIfExists(ctx context.Context, certPEM strin
|
|||||||
// 如果已存在相同证书,直接返回
|
// 如果已存在相同证书,直接返回
|
||||||
if isSameCert {
|
if isSameCert {
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: fmt.Sprintf("%d", sslItem.ID),
|
CertId: fmt.Sprintf("%d", sslRecord.ID),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,24 +143,24 @@ func (m *SSLManagerProvider) findCertIfExists(ctx context.Context, certPEM strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if getCertificateListResp.CertificateList != nil {
|
if getCertificateListResp.CertificateList != nil {
|
||||||
for _, certInfo := range getCertificateListResp.CertificateList {
|
for _, certItem := range getCertificateListResp.CertificateList {
|
||||||
// 优刻得未提供可唯一标识证书的字段,只能通过多个字段尝试对比来判断是否为同一证书
|
// 优刻得未提供可唯一标识证书的字段,只能通过多个字段尝试对比来判断是否为同一证书
|
||||||
// 先分别对比证书的多域名、品牌、有效期,再对比签名算法
|
// 先分别对比证书的多域名、品牌、有效期,再对比签名算法
|
||||||
|
|
||||||
if len(certX509.DNSNames) == 0 || certInfo.Domains != strings.Join(certX509.DNSNames, ",") {
|
if len(certX509.DNSNames) == 0 || certItem.Domains != strings.Join(certX509.DNSNames, ",") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(certX509.Issuer.Organization) == 0 || certInfo.Brand != certX509.Issuer.Organization[0] {
|
if len(certX509.Issuer.Organization) == 0 || certItem.Brand != certX509.Issuer.Organization[0] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if int64(certInfo.NotBefore) != certX509.NotBefore.UnixMilli() || int64(certInfo.NotAfter) != certX509.NotAfter.UnixMilli() {
|
if int64(certItem.NotBefore) != certX509.NotBefore.UnixMilli() || int64(certItem.NotAfter) != certX509.NotAfter.UnixMilli() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
getCertificateDetailInfoReq := m.sdkClient.NewGetCertificateDetailInfoRequest()
|
getCertificateDetailInfoReq := m.sdkClient.NewGetCertificateDetailInfoRequest()
|
||||||
getCertificateDetailInfoReq.CertificateID = ucloud.Int(certInfo.CertificateID)
|
getCertificateDetailInfoReq.CertificateID = ucloud.Int(certItem.CertificateID)
|
||||||
if m.config.ProjectId != "" {
|
if m.config.ProjectId != "" {
|
||||||
getCertificateDetailInfoReq.ProjectId = ucloud.String(m.config.ProjectId)
|
getCertificateDetailInfoReq.ProjectId = ucloud.String(m.config.ProjectId)
|
||||||
}
|
}
|
||||||
@ -212,10 +212,10 @@ func (m *SSLManagerProvider) findCertIfExists(ctx context.Context, certPEM strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: fmt.Sprintf("%d", certInfo.CertificateID),
|
CertId: fmt.Sprintf("%d", certItem.CertificateID),
|
||||||
CertName: certInfo.Name,
|
CertName: certItem.Name,
|
||||||
ExtendedData: map[string]any{
|
ExtendedData: map[string]any{
|
||||||
"resourceId": certInfo.CertificateSN,
|
"resourceId": certItem.CertificateSN,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -88,17 +88,17 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
}
|
}
|
||||||
|
|
||||||
if listCertInfoResp.Result.CertInfo != nil {
|
if listCertInfoResp.Result.CertInfo != nil {
|
||||||
for _, certDetail := range listCertInfoResp.Result.CertInfo {
|
for _, certInfo := range listCertInfoResp.Result.CertInfo {
|
||||||
fingerprintSha1 := sha1.Sum(certX509.Raw)
|
fingerprintSha1 := sha1.Sum(certX509.Raw)
|
||||||
fingerprintSha256 := sha256.Sum256(certX509.Raw)
|
fingerprintSha256 := sha256.Sum256(certX509.Raw)
|
||||||
isSameCert := strings.EqualFold(hex.EncodeToString(fingerprintSha1[:]), certDetail.CertFingerprint.Sha1) &&
|
isSameCert := strings.EqualFold(hex.EncodeToString(fingerprintSha1[:]), certInfo.CertFingerprint.Sha1) &&
|
||||||
strings.EqualFold(hex.EncodeToString(fingerprintSha256[:]), certDetail.CertFingerprint.Sha256)
|
strings.EqualFold(hex.EncodeToString(fingerprintSha256[:]), certInfo.CertFingerprint.Sha256)
|
||||||
// 如果已存在相同证书,直接返回
|
// 如果已存在相同证书,直接返回
|
||||||
if isSameCert {
|
if isSameCert {
|
||||||
m.logger.Info("ssl certificate already exists")
|
m.logger.Info("ssl certificate already exists")
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: certDetail.CertId,
|
CertId: certInfo.CertId,
|
||||||
CertName: certDetail.Desc,
|
CertName: certInfo.Desc,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,11 +70,11 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
return nil, fmt.Errorf("failed to execute sdk request 'live.ListCertV2': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'live.ListCertV2': %w", err)
|
||||||
}
|
}
|
||||||
if listCertResp.Result.CertList != nil {
|
if listCertResp.Result.CertList != nil {
|
||||||
for _, certDetail := range listCertResp.Result.CertList {
|
for _, certInfo := range listCertResp.Result.CertList {
|
||||||
// 查询证书详细信息
|
// 查询证书详细信息
|
||||||
// REF: https://www.volcengine.com/docs/6469/1186278#%E6%9F%A5%E7%9C%8B%E8%AF%81%E4%B9%A6%E8%AF%A6%E6%83%85
|
// REF: https://www.volcengine.com/docs/6469/1186278#%E6%9F%A5%E7%9C%8B%E8%AF%81%E4%B9%A6%E8%AF%A6%E6%83%85
|
||||||
describeCertDetailSecretReq := &velive.DescribeCertDetailSecretV2Body{
|
describeCertDetailSecretReq := &velive.DescribeCertDetailSecretV2Body{
|
||||||
ChainID: ve.String(certDetail.ChainID),
|
ChainID: ve.String(certInfo.ChainID),
|
||||||
}
|
}
|
||||||
describeCertDetailSecretResp, err := m.sdkClient.DescribeCertDetailSecretV2(ctx, describeCertDetailSecretReq)
|
describeCertDetailSecretResp, err := m.sdkClient.DescribeCertDetailSecretV2(ctx, describeCertDetailSecretReq)
|
||||||
m.logger.Debug("sdk request 'live.DescribeCertDetailSecretV2'", slog.Any("request", describeCertDetailSecretReq), slog.Any("response", describeCertDetailSecretResp))
|
m.logger.Debug("sdk request 'live.DescribeCertDetailSecretV2'", slog.Any("request", describeCertDetailSecretReq), slog.Any("response", describeCertDetailSecretResp))
|
||||||
@ -99,8 +99,8 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
if isSameCert {
|
if isSameCert {
|
||||||
m.logger.Info("ssl certificate already exists")
|
m.logger.Info("ssl certificate already exists")
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: certDetail.ChainID,
|
CertId: certInfo.ChainID,
|
||||||
CertName: certDetail.CertName,
|
CertName: certInfo.CertName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,16 +71,16 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
}
|
}
|
||||||
|
|
||||||
if listCertificatesResp.Certificates != nil {
|
if listCertificatesResp.Certificates != nil {
|
||||||
for _, certificate := range listCertificatesResp.Certificates {
|
for _, certRecord := range listCertificatesResp.Certificates {
|
||||||
// 对比证书序列号
|
// 对比证书序列号
|
||||||
if !strings.EqualFold(certX509.SerialNumber.Text(16), certificate.Serial) {
|
if !strings.EqualFold(certX509.SerialNumber.Text(16), certRecord.Serial) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再对比证书有效期
|
// 再对比证书有效期
|
||||||
cstzone := time.FixedZone("CST", 8*60*60)
|
cstzone := time.FixedZone("CST", 8*60*60)
|
||||||
oldCertNotBefore, _ := time.ParseInLocation(time.DateTime, certificate.ValidityFrom, cstzone)
|
oldCertNotBefore, _ := time.ParseInLocation(time.DateTime, certRecord.ValidityFrom, cstzone)
|
||||||
oldCertNotAfter, _ := time.ParseInLocation(time.DateTime, certificate.ValidityTo, cstzone)
|
oldCertNotAfter, _ := time.ParseInLocation(time.DateTime, certRecord.ValidityTo, cstzone)
|
||||||
if !certX509.NotBefore.Equal(oldCertNotBefore) || !certX509.NotAfter.Equal(oldCertNotAfter) {
|
if !certX509.NotBefore.Equal(oldCertNotBefore) || !certX509.NotAfter.Equal(oldCertNotAfter) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -88,8 +88,8 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey
|
|||||||
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||||
m.logger.Info("ssl certificate already exists")
|
m.logger.Info("ssl certificate already exists")
|
||||||
return &core.SSLManageUploadResult{
|
return &core.SSLManageUploadResult{
|
||||||
CertId: certificate.CertificateId,
|
CertId: certRecord.CertificateId,
|
||||||
CertName: certificate.Name,
|
CertName: certRecord.Name,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func NewClient(apiToken string) (*Client, error) {
|
|||||||
SetHeader("Accept", "application/json").
|
SetHeader("Accept", "application/json").
|
||||||
SetHeader("Content-Type", "application/json").
|
SetHeader("Content-Type", "application/json").
|
||||||
SetHeader("User-Agent", "certimate").
|
SetHeader("User-Agent", "certimate").
|
||||||
SetHeader("Token", apiToken)
|
SetQueryParam("token", apiToken)
|
||||||
|
|
||||||
return &Client{client}, nil
|
return &Client{client}, nil
|
||||||
}
|
}
|
||||||
|
@ -14,15 +14,15 @@ type GetDomainConfigResponse struct {
|
|||||||
apiResponseBase
|
apiResponseBase
|
||||||
|
|
||||||
ReturnObj *struct {
|
ReturnObj *struct {
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
ProductCode string `json:"product_code"`
|
ProductCode string `json:"product_code"`
|
||||||
Status int32 `json:"status"`
|
Status int32 `json:"status"`
|
||||||
AreaScope int32 `json:"area_scope"`
|
AreaScope int32 `json:"area_scope"`
|
||||||
Cname string `json:"cname"`
|
Cname string `json:"cname"`
|
||||||
Origin []*DomainOriginConfig `json:"origin,omitempty"`
|
Origin []*DomainOriginConfigWithWeight `json:"origin,omitempty"`
|
||||||
HttpsStatus string `json:"https_status"`
|
HttpsStatus string `json:"https_status"`
|
||||||
HttpsBasic *DomainHttpsBasicConfig `json:"https_basic,omitempty"`
|
HttpsBasic *DomainHttpsBasicConfig `json:"https_basic,omitempty"`
|
||||||
CertName string `json:"cert_name"`
|
CertName string `json:"cert_name"`
|
||||||
} `json:"returnObj,omitempty"`
|
} `json:"returnObj,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,12 @@ type CertDetail struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DomainOriginConfig struct {
|
type DomainOriginConfig struct {
|
||||||
|
Origin string `json:"origin"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
Weight string `json:"weight"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainOriginConfigWithWeight struct {
|
||||||
Origin string `json:"origin"`
|
Origin string `json:"origin"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Weight int32 `json:"weight"`
|
Weight int32 `json:"weight"`
|
||||||
|
@ -2,16 +2,12 @@ package qiniu
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qiniu/go-sdk/v7/auth"
|
"github.com/qiniu/go-sdk/v7/auth"
|
||||||
"github.com/qiniu/go-sdk/v7/client"
|
"github.com/qiniu/go-sdk/v7/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
const qiniuHost = "https://api.qiniu.com"
|
|
||||||
|
|
||||||
type CdnManager struct {
|
type CdnManager struct {
|
||||||
client *client.Client
|
client *client.Client
|
||||||
}
|
}
|
||||||
@ -21,16 +17,10 @@ func NewCdnManager(mac *auth.Credentials) *CdnManager {
|
|||||||
mac = auth.Default()
|
mac = auth.Default()
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &client.Client{&http.Client{Transport: newTransport(mac, nil)}}
|
client := &client.Client{Client: &http.Client{Transport: newTransport(mac, nil)}}
|
||||||
return &CdnManager{client: client}
|
return &CdnManager{client: client}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *CdnManager) urlf(pathf string, pathargs ...any) string {
|
|
||||||
path := fmt.Sprintf(pathf, pathargs...)
|
|
||||||
path = strings.TrimPrefix(path, "/")
|
|
||||||
return qiniuHost + "/" + path
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetDomainInfoResponse struct {
|
type GetDomainInfoResponse struct {
|
||||||
Code *int `json:"code,omitempty"`
|
Code *int `json:"code,omitempty"`
|
||||||
Error *string `json:"error,omitempty"`
|
Error *string `json:"error,omitempty"`
|
||||||
@ -52,7 +42,7 @@ type GetDomainInfoResponse struct {
|
|||||||
|
|
||||||
func (m *CdnManager) GetDomainInfo(ctx context.Context, domain string) (*GetDomainInfoResponse, error) {
|
func (m *CdnManager) GetDomainInfo(ctx context.Context, domain string) (*GetDomainInfoResponse, error) {
|
||||||
resp := new(GetDomainInfoResponse)
|
resp := new(GetDomainInfoResponse)
|
||||||
if err := m.client.Call(ctx, resp, http.MethodGet, m.urlf("domain/%s", domain), nil); err != nil {
|
if err := m.client.Call(ctx, resp, http.MethodGet, urlf("domain/%s", domain), nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -76,7 +66,7 @@ func (m *CdnManager) ModifyDomainHttpsConf(ctx context.Context, domain string, c
|
|||||||
Http2Enable: http2Enable,
|
Http2Enable: http2Enable,
|
||||||
}
|
}
|
||||||
resp := new(ModifyDomainHttpsConfResponse)
|
resp := new(ModifyDomainHttpsConfResponse)
|
||||||
if err := m.client.CallWithJson(ctx, resp, http.MethodPut, m.urlf("domain/%s/httpsconf", domain), nil, req); err != nil {
|
if err := m.client.CallWithJson(ctx, resp, http.MethodPut, urlf("domain/%s/httpsconf", domain), nil, req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -100,34 +90,7 @@ func (m *CdnManager) EnableDomainHttps(ctx context.Context, domain string, certI
|
|||||||
Http2Enable: http2Enable,
|
Http2Enable: http2Enable,
|
||||||
}
|
}
|
||||||
resp := new(EnableDomainHttpsResponse)
|
resp := new(EnableDomainHttpsResponse)
|
||||||
if err := m.client.CallWithJson(ctx, resp, http.MethodPut, m.urlf("domain/%s/sslize", domain), nil, req); err != nil {
|
if err := m.client.CallWithJson(ctx, resp, http.MethodPut, urlf("domain/%s/sslize", domain), nil, req); err != nil {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UploadSslCertRequest struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
CommonName string `json:"common_name"`
|
|
||||||
Certificate string `json:"ca"`
|
|
||||||
PrivateKey string `json:"pri"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UploadSslCertResponse struct {
|
|
||||||
Code *int `json:"code,omitempty"`
|
|
||||||
Error *string `json:"error,omitempty"`
|
|
||||||
CertID string `json:"certID"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *CdnManager) UploadSslCert(ctx context.Context, name string, commonName string, certificate string, privateKey string) (*UploadSslCertResponse, error) {
|
|
||||||
req := &UploadSslCertRequest{
|
|
||||||
Name: name,
|
|
||||||
CommonName: commonName,
|
|
||||||
Certificate: certificate,
|
|
||||||
PrivateKey: privateKey,
|
|
||||||
}
|
|
||||||
resp := new(UploadSslCertResponse)
|
|
||||||
if err := m.client.CallWithJson(ctx, resp, http.MethodPost, m.urlf("sslcert"), nil, req); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
44
pkg/sdk3rd/qiniu/kodo.go
Normal file
44
pkg/sdk3rd/qiniu/kodo.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package qiniu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/qiniu/go-sdk/v7/auth"
|
||||||
|
"github.com/qiniu/go-sdk/v7/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KodoManager struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKodoManager(mac *auth.Credentials) *KodoManager {
|
||||||
|
if mac == nil {
|
||||||
|
mac = auth.Default()
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &client.Client{Client: &http.Client{Transport: newTransport(mac, nil)}}
|
||||||
|
return &KodoManager{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BindBucketCertRequest struct {
|
||||||
|
CertID string `json:"certid"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BindBucketCertResponse struct {
|
||||||
|
Code *int `json:"code,omitempty"`
|
||||||
|
Error *string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KodoManager) BindBucketCert(ctx context.Context, domain string, certId string) (*BindBucketCertResponse, error) {
|
||||||
|
req := &BindBucketCertRequest{
|
||||||
|
CertID: certId,
|
||||||
|
Domain: domain,
|
||||||
|
}
|
||||||
|
resp := new(BindBucketCertResponse)
|
||||||
|
if err := m.client.CallWithJson(ctx, resp, http.MethodPut, urlf("cert/bind"), nil, req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
80
pkg/sdk3rd/qiniu/sslcert.go
Normal file
80
pkg/sdk3rd/qiniu/sslcert.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package qiniu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/qiniu/go-sdk/v7/auth"
|
||||||
|
"github.com/qiniu/go-sdk/v7/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SslCertManager struct {
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSslCertManager(mac *auth.Credentials) *SslCertManager {
|
||||||
|
if mac == nil {
|
||||||
|
mac = auth.Default()
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &client.Client{Client: &http.Client{Transport: newTransport(mac, nil)}}
|
||||||
|
return &SslCertManager{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSslCertListResponse struct {
|
||||||
|
Code *int `json:"code,omitempty"`
|
||||||
|
Error *string `json:"error,omitempty"`
|
||||||
|
Certs []*struct {
|
||||||
|
CertID string `json:"certid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CommonName string `json:"common_name"`
|
||||||
|
DnsNames []string `json:"dnsnames"`
|
||||||
|
CreateTime int64 `json:"create_time"`
|
||||||
|
NotBefore int64 `json:"not_before"`
|
||||||
|
NotAfter int64 `json:"not_after"`
|
||||||
|
ProductType string `json:"product_type"`
|
||||||
|
ProductShortName string `json:"product_short_name,omitempty"`
|
||||||
|
OrderId string `json:"orderid,omitempty"`
|
||||||
|
CertType string `json:"cert_type"`
|
||||||
|
Encrypt string `json:"encrypt"`
|
||||||
|
EncryptParameter string `json:"encryptParameter,omitempty"`
|
||||||
|
Enable bool `json:"enable"`
|
||||||
|
} `json:"certs"`
|
||||||
|
Marker string `json:"marker"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SslCertManager) GetSslCertList(ctx context.Context, marker string, limit int32) (*GetSslCertListResponse, error) {
|
||||||
|
resp := new(GetSslCertListResponse)
|
||||||
|
if err := m.client.Call(ctx, resp, http.MethodGet, urlf("sslcert?marker=%s&limit=%d", url.QueryEscape(marker), limit), nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadSslCertRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
CommonName string `json:"common_name"`
|
||||||
|
Certificate string `json:"ca"`
|
||||||
|
PrivateKey string `json:"pri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadSslCertResponse struct {
|
||||||
|
Code *int `json:"code,omitempty"`
|
||||||
|
Error *string `json:"error,omitempty"`
|
||||||
|
CertID string `json:"certID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SslCertManager) UploadSslCert(ctx context.Context, name string, commonName string, certificate string, privateKey string) (*UploadSslCertResponse, error) {
|
||||||
|
req := &UploadSslCertRequest{
|
||||||
|
Name: name,
|
||||||
|
CommonName: commonName,
|
||||||
|
Certificate: certificate,
|
||||||
|
PrivateKey: privateKey,
|
||||||
|
}
|
||||||
|
resp := new(UploadSslCertResponse)
|
||||||
|
if err := m.client.CallWithJson(ctx, resp, http.MethodPost, urlf("sslcert"), nil, req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
14
pkg/sdk3rd/qiniu/util.go
Normal file
14
pkg/sdk3rd/qiniu/util.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package qiniu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const qiniuHost = "https://api.qiniu.com"
|
||||||
|
|
||||||
|
func urlf(pathf string, pathargs ...any) string {
|
||||||
|
path := fmt.Sprintf(pathf, pathargs...)
|
||||||
|
path = strings.TrimPrefix(path, "/")
|
||||||
|
return qiniuHost + "/" + path
|
||||||
|
}
|
@ -103,8 +103,11 @@ func (c *Client) doRequestWithResult(req *resty.Request, res apiResponse) (*rest
|
|||||||
if err := json.Unmarshal(resp.Body(), &res); err != nil {
|
if err := json.Unmarshal(resp.Body(), &res); err != nil {
|
||||||
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w", err)
|
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w", err)
|
||||||
} else {
|
} else {
|
||||||
if tdata := res.GetData(); tdata == nil {
|
tresp := &apiResponseBase{}
|
||||||
return resp, fmt.Errorf("sdkerr: empty data")
|
if err := json.Unmarshal(resp.Body(), &tresp); err != nil {
|
||||||
|
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w", err)
|
||||||
|
} else if tdata := tresp.GetData(); tdata == nil {
|
||||||
|
return resp, fmt.Errorf("sdkerr: received empty data")
|
||||||
} else if terrcode := tdata.GetErrorCode(); terrcode != 0 {
|
} else if terrcode := tdata.GetErrorCode(); terrcode != 0 {
|
||||||
return resp, fmt.Errorf("sdkerr: code='%d', message='%s'", terrcode, tdata.GetMessage())
|
return resp, fmt.Errorf("sdkerr: code='%d', message='%s'", terrcode, tdata.GetMessage())
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package cert
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 比较两个 x509.Certificate 对象,判断它们是否是同一张证书。
|
// 比较两个 x509.Certificate 对象,判断它们是否是同一张证书。
|
||||||
@ -24,3 +25,18 @@ func EqualCertificate(a, b *x509.Certificate) bool {
|
|||||||
a.Issuer.SerialNumber == b.Issuer.SerialNumber &&
|
a.Issuer.SerialNumber == b.Issuer.SerialNumber &&
|
||||||
a.Subject.SerialNumber == b.Subject.SerialNumber
|
a.Subject.SerialNumber == b.Subject.SerialNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodePEM(data []byte) []*pem.Block {
|
||||||
|
blocks := make([]*pem.Block, 0)
|
||||||
|
for {
|
||||||
|
block, rest := pem.Decode(data)
|
||||||
|
if block == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks = append(blocks, block)
|
||||||
|
data = rest
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cert
|
|||||||
import (
|
import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 从 PEM 编码的证书字符串解析并提取服务器证书和中间证书。
|
// 从 PEM 编码的证书字符串解析并提取服务器证书和中间证书。
|
||||||
@ -15,32 +16,27 @@ import (
|
|||||||
// - intermediaCertPEM: 中间证书的 PEM 内容。
|
// - intermediaCertPEM: 中间证书的 PEM 内容。
|
||||||
// - err: 错误。
|
// - err: 错误。
|
||||||
func ExtractCertificatesFromPEM(certPEM string) (_serverCertPEM string, _intermediaCertPEM string, _err error) {
|
func ExtractCertificatesFromPEM(certPEM string) (_serverCertPEM string, _intermediaCertPEM string, _err error) {
|
||||||
pemBlocks := make([]*pem.Block, 0)
|
blocks := decodePEM([]byte(certPEM))
|
||||||
pemData := []byte(certPEM)
|
for i, block := range blocks {
|
||||||
for {
|
if block.Type != "CERTIFICATE" {
|
||||||
block, rest := pem.Decode(pemData)
|
return "", "", fmt.Errorf("invalid PEM block type at %d, expected 'CERTIFICATE', got '%s'", i, block.Type)
|
||||||
if block == nil || block.Type != "CERTIFICATE" {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pemBlocks = append(pemBlocks, block)
|
|
||||||
pemData = rest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serverCertPEM := ""
|
serverCertPEM := ""
|
||||||
intermediaCertPEM := ""
|
intermediaCertPEM := ""
|
||||||
|
|
||||||
if len(pemBlocks) == 0 {
|
if len(blocks) == 0 {
|
||||||
return "", "", errors.New("failed to decode PEM block")
|
return "", "", errors.New("failed to decode PEM block")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pemBlocks) > 0 {
|
if len(blocks) > 0 {
|
||||||
serverCertPEM = string(pem.EncodeToMemory(pemBlocks[0]))
|
serverCertPEM = string(pem.EncodeToMemory(blocks[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pemBlocks) > 1 {
|
if len(blocks) > 1 {
|
||||||
for i := 1; i < len(pemBlocks); i++ {
|
for i := 1; i < len(blocks); i++ {
|
||||||
intermediaCertPEM += string(pem.EncodeToMemory(pemBlocks[i]))
|
intermediaCertPEM += string(pem.EncodeToMemory(blocks[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@ package cert
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/pem"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pavlo-v-chernykh/keystore-go/v4"
|
"github.com/pavlo-v-chernykh/keystore-go/v4"
|
||||||
@ -21,9 +22,19 @@ import (
|
|||||||
// - data: PFX 格式的证书数据。
|
// - data: PFX 格式的证书数据。
|
||||||
// - err: 错误。
|
// - err: 错误。
|
||||||
func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPassword string) ([]byte, error) {
|
func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPassword string) ([]byte, error) {
|
||||||
cert, err := ParseCertificateFromPEM(certPEM)
|
blocks := decodePEM([]byte(certPEM))
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
certs := make([]*x509.Certificate, 0, len(blocks))
|
||||||
|
for i, block := range blocks {
|
||||||
|
if block.Type != "CERTIFICATE" {
|
||||||
|
return nil, fmt.Errorf("invalid PEM block type at %d, expected 'CERTIFICATE', got '%s'", i, block.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certs = append(certs, cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
privkey, err := ParsePrivateKeyFromPEM(privkeyPEM)
|
privkey, err := ParsePrivateKeyFromPEM(privkeyPEM)
|
||||||
@ -31,12 +42,16 @@ func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPass
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pfxData, err := pkcs12.LegacyRC2.Encode(privkey, cert, nil, pfxPassword)
|
var pfxData []byte
|
||||||
if err != nil {
|
if len(certs) == 0 {
|
||||||
return nil, err
|
return nil, errors.New("failed to decode certificate PEM")
|
||||||
|
} else if len(certs) == 1 {
|
||||||
|
pfxData, err = pkcs12.Legacy.Encode(privkey, certs[0], nil, pfxPassword)
|
||||||
|
} else {
|
||||||
|
pfxData, err = pkcs12.Legacy.Encode(privkey, certs[0], certs[1:], pfxPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pfxData, nil
|
return pfxData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将 PEM 编码的证书字符串转换为 JKS 格式。
|
// 将 PEM 编码的证书字符串转换为 JKS 格式。
|
||||||
@ -52,28 +67,33 @@ func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPass
|
|||||||
// - data: JKS 格式的证书数据。
|
// - data: JKS 格式的证书数据。
|
||||||
// - err: 错误。
|
// - err: 错误。
|
||||||
func TransformCertificateFromPEMToJKS(certPEM string, privkeyPEM string, jksAlias string, jksKeypass string, jksStorepass string) ([]byte, error) {
|
func TransformCertificateFromPEMToJKS(certPEM string, privkeyPEM string, jksAlias string, jksKeypass string, jksStorepass string) ([]byte, error) {
|
||||||
certBlock, _ := pem.Decode([]byte(certPEM))
|
certBlocks := decodePEM([]byte(certPEM))
|
||||||
if certBlock == nil {
|
if len(certBlocks) == 0 {
|
||||||
return nil, errors.New("failed to decode certificate PEM")
|
return nil, errors.New("failed to decode certificate PEM")
|
||||||
}
|
}
|
||||||
|
|
||||||
privkeyBlock, _ := pem.Decode([]byte(privkeyPEM))
|
privkeyBlocks := decodePEM([]byte(privkeyPEM))
|
||||||
if privkeyBlock == nil {
|
if len(privkeyBlocks) == 0 {
|
||||||
return nil, errors.New("failed to decode private key PEM")
|
return nil, errors.New("failed to decode private key PEM")
|
||||||
}
|
}
|
||||||
|
|
||||||
ks := keystore.New()
|
|
||||||
entry := keystore.PrivateKeyEntry{
|
entry := keystore.PrivateKeyEntry{
|
||||||
CreationTime: time.Now(),
|
CreationTime: time.Now(),
|
||||||
PrivateKey: privkeyBlock.Bytes,
|
PrivateKey: privkeyBlocks[0].Bytes,
|
||||||
CertificateChain: []keystore.Certificate{
|
CertificateChain: make([]keystore.Certificate, len(certBlocks)),
|
||||||
{
|
}
|
||||||
Type: "X509",
|
for i, certBlock := range certBlocks {
|
||||||
Content: certBlock.Bytes,
|
if certBlock.Type != "CERTIFICATE" {
|
||||||
},
|
return nil, fmt.Errorf("invalid PEM block type at %d, expected 'CERTIFICATE', got '%s'", i, certBlock.Type)
|
||||||
},
|
}
|
||||||
|
|
||||||
|
entry.CertificateChain[i] = keystore.Certificate{
|
||||||
|
Type: "X509",
|
||||||
|
Content: certBlock.Bytes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ks := keystore.New()
|
||||||
if err := ks.SetPrivateKeyEntry(jksAlias, entry, []byte(jksKeypass)); err != nil {
|
if err := ks.SetPrivateKeyEntry(jksAlias, entry, []byte(jksKeypass)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,84 @@
|
|||||||
package ifelse
|
package ifelse
|
||||||
|
|
||||||
|
type branch[T any] struct {
|
||||||
|
cond bool
|
||||||
|
fn func() T
|
||||||
|
}
|
||||||
|
|
||||||
type ifExpr[T any] struct {
|
type ifExpr[T any] struct {
|
||||||
condition bool
|
cond bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type thenExpr[T any] struct {
|
type thenExpr[T any] struct {
|
||||||
condition bool
|
branches []branch[T]
|
||||||
consequent T
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 示例:
|
type elseIfExpr[T any] struct {
|
||||||
|
branches []branch[T]
|
||||||
|
cond bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用法示例:
|
||||||
//
|
//
|
||||||
// result := ifelse.If[T](condition).Then(consequent).Else(alternative)
|
// result := ifelse.If[string](age < 18).Then("child").
|
||||||
|
// ElseIf(age < 60).Then("adult").
|
||||||
|
// ElseIf(age < 120).Then("senior").
|
||||||
|
// Else("invalid")
|
||||||
func If[T any](condition bool) *ifExpr[T] {
|
func If[T any](condition bool) *ifExpr[T] {
|
||||||
return &ifExpr[T]{
|
return &ifExpr[T]{cond: condition}
|
||||||
condition: condition,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ifExpr[T]) Then(consequent T) *thenExpr[T] {
|
func (e *ifExpr[T]) Then(consequent T) *thenExpr[T] {
|
||||||
return &thenExpr[T]{
|
return &thenExpr[T]{
|
||||||
condition: e.condition,
|
branches: []branch[T]{
|
||||||
consequent: consequent,
|
{cond: e.cond, fn: func() T { return consequent }},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ifExpr[T]) ThenFunc(consequent func() T) *thenExpr[T] {
|
||||||
|
return &thenExpr[T]{
|
||||||
|
branches: []branch[T]{
|
||||||
|
{cond: e.cond, fn: consequent},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *thenExpr[T]) ElseIf(condition bool) *elseIfExpr[T] {
|
||||||
|
return &elseIfExpr[T]{
|
||||||
|
branches: e.branches,
|
||||||
|
cond: condition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *elseIfExpr[T]) Then(alternative T) *thenExpr[T] {
|
||||||
|
branch := branch[T]{cond: e.cond, fn: func() T { return alternative }}
|
||||||
|
return &thenExpr[T]{
|
||||||
|
branches: append(e.branches, branch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *elseIfExpr[T]) ThenFunc(alternativeFunc func() T) *thenExpr[T] {
|
||||||
|
branch := branch[T]{cond: e.cond, fn: alternativeFunc}
|
||||||
|
return &thenExpr[T]{
|
||||||
|
branches: append(e.branches, branch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *thenExpr[T]) Else(alternative T) T {
|
func (e *thenExpr[T]) Else(alternative T) T {
|
||||||
if e.condition {
|
for _, b := range e.branches {
|
||||||
return e.consequent
|
if b.cond {
|
||||||
|
return b.fn()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return alternative
|
return alternative
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *thenExpr[T]) ElseFunc(alternativeFunc func() T) T {
|
||||||
|
for _, b := range e.branches {
|
||||||
|
if b.cond {
|
||||||
|
return b.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return alternativeFunc()
|
||||||
|
}
|
||||||
|
196
pkg/utils/ifelse/ifelse_test.go
Normal file
196
pkg/utils/ifelse/ifelse_test.go
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package ifelse_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/certimate-go/certimate/pkg/utils/ifelse"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIfTrue(t *testing.T) {
|
||||||
|
result := ifelse.If[string](true).
|
||||||
|
Then("true branch").
|
||||||
|
Else("false branch")
|
||||||
|
|
||||||
|
if result != "true branch" {
|
||||||
|
t.Errorf("Expected 'true branch', got '%s'", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIfFalse(t *testing.T) {
|
||||||
|
result := ifelse.If[string](false).
|
||||||
|
Then("true branch").
|
||||||
|
Else("false branch")
|
||||||
|
|
||||||
|
if result != "false branch" {
|
||||||
|
t.Errorf("Expected 'false branch', got '%s'", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestElseIfFirstMatch(t *testing.T) {
|
||||||
|
result := ifelse.If[string](false).
|
||||||
|
Then("should not run").
|
||||||
|
ElseIf(true).
|
||||||
|
Then("elseif branch").
|
||||||
|
Else("should not run")
|
||||||
|
|
||||||
|
if result != "elseif branch" {
|
||||||
|
t.Errorf("Expected 'elseif branch', got '%s'", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestElseIfSecondMatch(t *testing.T) {
|
||||||
|
result := ifelse.If[string](false).
|
||||||
|
Then("should not run").
|
||||||
|
ElseIf(false).
|
||||||
|
Then("should not run").
|
||||||
|
ElseIf(true).
|
||||||
|
Then("second elseif").
|
||||||
|
Else("should not run")
|
||||||
|
|
||||||
|
if result != "second elseif" {
|
||||||
|
t.Errorf("Expected 'second elseif', got '%s'", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleConditions(t *testing.T) {
|
||||||
|
result := ifelse.If[string](1 > 2).
|
||||||
|
Then("impossible").
|
||||||
|
ElseIf(2+2 == 5).
|
||||||
|
Then("false math").
|
||||||
|
ElseIf(3*3 == 9).
|
||||||
|
Then("correct math").
|
||||||
|
Else("fallback")
|
||||||
|
|
||||||
|
if result != "correct math" {
|
||||||
|
t.Errorf("Expected 'correct math', got '%s'", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllConditionsFalse(t *testing.T) {
|
||||||
|
result := ifelse.If[int](false).
|
||||||
|
Then(1).
|
||||||
|
ElseIf(false).
|
||||||
|
Then(2).
|
||||||
|
ElseIf(false).
|
||||||
|
Then(3).
|
||||||
|
Else(99)
|
||||||
|
|
||||||
|
if result != 99 {
|
||||||
|
t.Errorf("Expected 99, got %d", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLazyEvaluationThen(t *testing.T) {
|
||||||
|
called := []string{}
|
||||||
|
|
||||||
|
result := ifelse.If[string](true).
|
||||||
|
ThenFunc(func() string {
|
||||||
|
called = append(called, "then")
|
||||||
|
return "then"
|
||||||
|
}).
|
||||||
|
ElseIf(true).
|
||||||
|
ThenFunc(func() string {
|
||||||
|
called = append(called, "elseif")
|
||||||
|
return "elseif"
|
||||||
|
}).
|
||||||
|
ElseFunc(func() string {
|
||||||
|
called = append(called, "else")
|
||||||
|
return "else"
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证结果和调用情况
|
||||||
|
if result != "then" {
|
||||||
|
t.Errorf("Expected 'then', got '%s'", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(called) != 1 || called[0] != "then" {
|
||||||
|
t.Errorf("Expected only 'then' called, got %v", called)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLazyEvaluationElseIf(t *testing.T) {
|
||||||
|
called := []string{}
|
||||||
|
|
||||||
|
result := ifelse.If[string](false).
|
||||||
|
ThenFunc(func() string {
|
||||||
|
called = append(called, "then")
|
||||||
|
return "then"
|
||||||
|
}).
|
||||||
|
ElseIf(true).
|
||||||
|
ThenFunc(func() string {
|
||||||
|
called = append(called, "elseif")
|
||||||
|
return "elseif"
|
||||||
|
}).
|
||||||
|
ElseFunc(func() string {
|
||||||
|
called = append(called, "else")
|
||||||
|
return "else"
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证结果和调用情况
|
||||||
|
if result != "elseif" {
|
||||||
|
t.Errorf("Expected 'elseif', got '%s'", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(called) != 1 || called[0] != "elseif" {
|
||||||
|
t.Errorf("Expected only 'elseif' called, got %v", called)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLazyEvaluationElse(t *testing.T) {
|
||||||
|
called := []string{}
|
||||||
|
|
||||||
|
result := ifelse.If[string](false).
|
||||||
|
ThenFunc(func() string {
|
||||||
|
called = append(called, "then")
|
||||||
|
return "then"
|
||||||
|
}).
|
||||||
|
ElseIf(false).
|
||||||
|
ThenFunc(func() string {
|
||||||
|
called = append(called, "elseif")
|
||||||
|
return "elseif"
|
||||||
|
}).
|
||||||
|
ElseFunc(func() string {
|
||||||
|
called = append(called, "else")
|
||||||
|
return "else"
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证结果和调用情况
|
||||||
|
if result != "else" {
|
||||||
|
t.Errorf("Expected 'else', got '%s'", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(called) != 1 || called[0] != "else" {
|
||||||
|
t.Errorf("Expected only 'else' called, got %v", called)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMixedValueAndFunc(t *testing.T) {
|
||||||
|
result := ifelse.If[int](false).
|
||||||
|
Then(0).
|
||||||
|
ElseIf(false).
|
||||||
|
ThenFunc(func() int {
|
||||||
|
return 1
|
||||||
|
}).
|
||||||
|
ElseIf(true).
|
||||||
|
Then(2).
|
||||||
|
Else(3)
|
||||||
|
|
||||||
|
if result != 2 {
|
||||||
|
t.Errorf("Expected 2, got %d", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexNumericLogic(t *testing.T) {
|
||||||
|
x := 15
|
||||||
|
result := ifelse.If[string](x < 10).
|
||||||
|
Then("single digit").
|
||||||
|
ElseIf(x < 20).
|
||||||
|
Then("teens").
|
||||||
|
ElseIf(x < 30).
|
||||||
|
Then("twenties").
|
||||||
|
Else("older")
|
||||||
|
|
||||||
|
if result != "teens" {
|
||||||
|
t.Errorf("Expected 'teens', got '%s'", result)
|
||||||
|
}
|
||||||
|
}
|
BIN
ui/public/imgs/providers/kong.png
Normal file
BIN
ui/public/imgs/providers/kong.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
@ -50,6 +50,7 @@ import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServices
|
|||||||
import AccessFormHetznerConfig from "./AccessFormHetznerConfig";
|
import AccessFormHetznerConfig from "./AccessFormHetznerConfig";
|
||||||
import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
|
import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
|
||||||
import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig";
|
import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig";
|
||||||
|
import AccessFormKongConfig from "./AccessFormKongConfig";
|
||||||
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
|
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
|
||||||
import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig";
|
import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig";
|
||||||
import AccessFormLeCDNConfig from "./AccessFormLeCDNConfig";
|
import AccessFormLeCDNConfig from "./AccessFormLeCDNConfig";
|
||||||
@ -266,6 +267,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
|||||||
return <AccessFormHuaweiCloudConfig {...nestedFormProps} />;
|
return <AccessFormHuaweiCloudConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.JDCLOUD:
|
case ACCESS_PROVIDERS.JDCLOUD:
|
||||||
return <AccessFormJDCloudConfig {...nestedFormProps} />;
|
return <AccessFormJDCloudConfig {...nestedFormProps} />;
|
||||||
|
case ACCESS_PROVIDERS.KONG:
|
||||||
|
return <AccessFormKongConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.KUBERNETES:
|
case ACCESS_PROVIDERS.KUBERNETES:
|
||||||
return <AccessFormKubernetesConfig {...nestedFormProps} />;
|
return <AccessFormKubernetesConfig {...nestedFormProps} />;
|
||||||
case ACCESS_PROVIDERS.LARKBOT:
|
case ACCESS_PROVIDERS.LARKBOT:
|
||||||
|
71
ui/src/components/access/AccessFormKongConfig.tsx
Normal file
71
ui/src/components/access/AccessFormKongConfig.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input, Switch } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
import { type AccessConfigForKong } from "@/domain/access";
|
||||||
|
|
||||||
|
type AccessFormKongConfigFieldValues = Nullish<AccessConfigForKong>;
|
||||||
|
|
||||||
|
export type AccessFormKongConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: AccessFormKongConfigFieldValues;
|
||||||
|
onValuesChange?: (values: AccessFormKongConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFormModel = (): AccessFormKongConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
serverUrl: "http://<your-host-addr>:8001/",
|
||||||
|
apiToken: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccessFormKongConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormKongConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
serverUrl: z.url(t("common.errmsg.url_invalid")),
|
||||||
|
apiToken: z.string().nullish(),
|
||||||
|
allowInsecureConnections: z.boolean().nullish(),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<Form.Item name="serverUrl" label={t("access.form.kong_server_url.label")} rules={[formRule]}>
|
||||||
|
<Input placeholder={t("access.form.kong_server_url.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="apiToken"
|
||||||
|
label={t("access.form.kong_api_token.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.kong_api_token.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.kong_api_token.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="allowInsecureConnections" label={t("access.form.common_allow_insecure_conns.label")} rules={[formRule]}>
|
||||||
|
<Switch
|
||||||
|
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
|
||||||
|
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccessFormKongConfig;
|
@ -65,6 +65,7 @@ import DeployNodeConfigFormJDCloudALBConfig from "./DeployNodeConfigFormJDCloudA
|
|||||||
import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudCDNConfig";
|
import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudCDNConfig";
|
||||||
import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig";
|
import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig";
|
||||||
import DeployNodeConfigFormJDCloudVODConfig from "./DeployNodeConfigFormJDCloudVODConfig";
|
import DeployNodeConfigFormJDCloudVODConfig from "./DeployNodeConfigFormJDCloudVODConfig";
|
||||||
|
import DeployNodeConfigFormKongConfig from "./DeployNodeConfigFormKongConfig";
|
||||||
import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig";
|
import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig";
|
||||||
import DeployNodeConfigFormLeCDNConfig from "./DeployNodeConfigFormLeCDNConfig";
|
import DeployNodeConfigFormLeCDNConfig from "./DeployNodeConfigFormLeCDNConfig";
|
||||||
import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig";
|
import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig";
|
||||||
@ -304,6 +305,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
return <DeployNodeConfigFormJDCloudLiveConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormJDCloudLiveConfig {...nestedFormProps} />;
|
||||||
case DEPLOYMENT_PROVIDERS.JDCLOUD_VOD:
|
case DEPLOYMENT_PROVIDERS.JDCLOUD_VOD:
|
||||||
return <DeployNodeConfigFormJDCloudVODConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormJDCloudVODConfig {...nestedFormProps} />;
|
||||||
|
case DEPLOYMENT_PROVIDERS.KONG:
|
||||||
|
return <DeployNodeConfigFormKongConfig {...nestedFormProps} />;
|
||||||
case DEPLOYMENT_PROVIDERS.KUBERNETES_SECRET:
|
case DEPLOYMENT_PROVIDERS.KUBERNETES_SECRET:
|
||||||
return <DeployNodeConfigFormKubernetesSecretConfig {...nestedFormProps} />;
|
return <DeployNodeConfigFormKubernetesSecretConfig {...nestedFormProps} />;
|
||||||
case DEPLOYMENT_PROVIDERS.LECDN:
|
case DEPLOYMENT_PROVIDERS.LECDN:
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Form, type FormInstance, Input, Select } from "antd";
|
||||||
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
import Show from "@/components/Show";
|
||||||
|
|
||||||
|
type DeployNodeConfigFormKongConfigFieldValues = Nullish<{
|
||||||
|
resourceType: string;
|
||||||
|
certificateId?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type DeployNodeConfigFormKongConfigProps = {
|
||||||
|
form: FormInstance;
|
||||||
|
formName: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValues?: DeployNodeConfigFormKongConfigFieldValues;
|
||||||
|
onValuesChange?: (values: DeployNodeConfigFormKongConfigFieldValues) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
|
||||||
|
|
||||||
|
const initFormModel = (): DeployNodeConfigFormKongConfigFieldValues => {
|
||||||
|
return {
|
||||||
|
resourceType: RESOURCE_TYPE_CERTIFICATE,
|
||||||
|
certificateId: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const DeployNodeConfigFormKongConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormKongConfigProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, t("workflow_node.deploy.form.kong_resource_type.placeholder")),
|
||||||
|
workspace: z.string().nullish(),
|
||||||
|
certificateId: z
|
||||||
|
.string()
|
||||||
|
.nullish()
|
||||||
|
.refine((v) => fieldResourceType !== RESOURCE_TYPE_CERTIFICATE || !!v?.trim(), t("workflow_node.deploy.form.kong_certificate_id.placeholder")),
|
||||||
|
});
|
||||||
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const fieldResourceType = Form.useWatch("resourceType", formInst);
|
||||||
|
|
||||||
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
|
onValuesChange?.(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={formInst}
|
||||||
|
disabled={disabled}
|
||||||
|
initialValues={initialValues ?? initFormModel()}
|
||||||
|
layout="vertical"
|
||||||
|
name={formName}
|
||||||
|
onValuesChange={handleFormChange}
|
||||||
|
>
|
||||||
|
<Form.Item name="resourceType" label={t("workflow_node.deploy.form.kong_resource_type.label")} rules={[formRule]}>
|
||||||
|
<Select placeholder={t("workflow_node.deploy.form.kong_resource_type.placeholder")}>
|
||||||
|
<Select.Option key={RESOURCE_TYPE_CERTIFICATE} value={RESOURCE_TYPE_CERTIFICATE}>
|
||||||
|
{t("workflow_node.deploy.form.kong_resource_type.option.certificate.label")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="workspace"
|
||||||
|
label={t("workflow_node.deploy.form.kong_workspace.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.kong_workspace.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input allowClear placeholder={t("workflow_node.deploy.form.kong_workspace.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}>
|
||||||
|
<Form.Item
|
||||||
|
name="certificateId"
|
||||||
|
label={t("workflow_node.deploy.form.kong_certificate_id.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.kong_certificate_id.tooltip") }}></span>}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("workflow_node.deploy.form.kong_certificate_id.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Show>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeployNodeConfigFormKongConfig;
|
@ -152,9 +152,11 @@ chmod 755 "$fnCertPath"
|
|||||||
chmod 755 "$fnKeyPath"
|
chmod 755 "$fnKeyPath"
|
||||||
|
|
||||||
# 更新数据库
|
# 更新数据库
|
||||||
|
NEW_EFFECT_DATE=$(openssl x509 -startdate -noout -in "$fnCertPath" | sed "s/^.*=\\(.*\\)$/\\1/")
|
||||||
|
NEW_EFFECT_TIMESTAMP=$(date -d "$NEW_EFFECT_DATE" +%s%3N)
|
||||||
NEW_EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$fnCertPath" | sed "s/^.*=\\(.*\\)$/\\1/")
|
NEW_EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$fnCertPath" | sed "s/^.*=\\(.*\\)$/\\1/")
|
||||||
NEW_EXPIRY_TIMESTAMP=$(date -d "$NEW_EXPIRY_DATE" +%s%3N)
|
NEW_EXPIRY_TIMESTAMP=$(date -d "$NEW_EXPIRY_DATE" +%s%3N)
|
||||||
psql -U postgres -d trim_connect -c "UPDATE cert SET valid_to=$NEW_EXPIRY_TIMESTAMP WHERE domain='$domain'"
|
psql -U postgres -d trim_connect -c "UPDATE cert SET valid_from=$NEW_EFFECT_TIMESTAMP, valid_to=$NEW_EXPIRY_TIMESTAMP WHERE domain='$domain'"
|
||||||
|
|
||||||
# 重启服务
|
# 重启服务
|
||||||
systemctl restart webdav.service
|
systemctl restart webdav.service
|
||||||
|
@ -58,7 +58,7 @@ const DeployNodeConfigFormTencentCloudCDNConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_cdn_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_cdn_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_cdn_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_cdn_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -92,7 +92,7 @@ const DeployNodeConfigFormTencentCloudCLBConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_clb_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_clb_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_clb_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_clb_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -58,7 +58,7 @@ const DeployNodeConfigFormTencentCloudCSSConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_css_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_css_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_css_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_css_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -58,7 +58,7 @@ const DeployNodeConfigFormTencentCloudECDNConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ecdn_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ecdn_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_ecdn_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_ecdn_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -4,11 +4,12 @@ import { createSchemaFieldRule } from "antd-zod";
|
|||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
import { validDomainName } from "@/utils/validators";
|
import { validDomainName } from "@/utils/validators";
|
||||||
|
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
|
||||||
|
|
||||||
type DeployNodeConfigFormTencentCloudEOConfigFieldValues = Nullish<{
|
type DeployNodeConfigFormTencentCloudEOConfigFieldValues = Nullish<{
|
||||||
endpoint?: string;
|
endpoint?: string;
|
||||||
zoneId: string;
|
zoneId: string;
|
||||||
domain: string;
|
domains: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type DeployNodeConfigFormTencentCloudEOConfigProps = {
|
export type DeployNodeConfigFormTencentCloudEOConfigProps = {
|
||||||
@ -23,6 +24,8 @@ const initFormModel = (): DeployNodeConfigFormTencentCloudEOConfigFieldValues =>
|
|||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MULTIPLE_INPUT_SEPARATOR = ";";
|
||||||
|
|
||||||
const DeployNodeConfigFormTencentCloudEOConfig = ({
|
const DeployNodeConfigFormTencentCloudEOConfig = ({
|
||||||
form: formInst,
|
form: formInst,
|
||||||
formName,
|
formName,
|
||||||
@ -37,9 +40,14 @@ const DeployNodeConfigFormTencentCloudEOConfig = ({
|
|||||||
zoneId: z
|
zoneId: z
|
||||||
.string(t("workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder"))
|
.string(t("workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder"))
|
||||||
.nonempty(t("workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder")),
|
.nonempty(t("workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder")),
|
||||||
domain: z
|
domains: z
|
||||||
.string(t("workflow_node.deploy.form.tencentcloud_eo_domain.placeholder"))
|
.string(t("workflow_node.deploy.form.tencentcloud_eo_domains.placeholder"))
|
||||||
.refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
|
.refine((v) => {
|
||||||
|
if (!v) return false;
|
||||||
|
return String(v)
|
||||||
|
.split(MULTIPLE_INPUT_SEPARATOR)
|
||||||
|
.every((e) => validDomainName(e, { allowWildcard: true }));
|
||||||
|
}, t("common.errmsg.domain_invalid")),
|
||||||
});
|
});
|
||||||
const formRule = createSchemaFieldRule(formSchema);
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
@ -62,7 +70,7 @@ const DeployNodeConfigFormTencentCloudEOConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_eo_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_eo_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_eo_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_eo_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -75,12 +83,17 @@ const DeployNodeConfigFormTencentCloudEOConfig = ({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="domain"
|
name="domains"
|
||||||
label={t("workflow_node.deploy.form.tencentcloud_eo_domain.label")}
|
label={t("workflow_node.deploy.form.tencentcloud_eo_domains.label")}
|
||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_eo_domain.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_eo_domains.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_eo_domain.placeholder")} />
|
<MultipleSplitValueInput
|
||||||
|
modalTitle={t("workflow_node.deploy.form.tencentcloud_eo_domains.multiple_input_modal.title")}
|
||||||
|
placeholder={t("workflow_node.deploy.form.tencentcloud_eo_domains.placeholder")}
|
||||||
|
placeholderInModal={t("workflow_node.deploy.form.tencentcloud_eo_domains.multiple_input_modal.placeholder")}
|
||||||
|
splitOptions={{ trim: true, removeEmpty: true }}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
@ -73,7 +73,7 @@ const DeployNodeConfigFormTencentCloudGAAPConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_gaap_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_gaap_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_gaap_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_gaap_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item name="resourceType" label={t("workflow_node.deploy.form.tencentcloud_gaap_resource_type.label")} rules={[formRule]}>
|
<Form.Item name="resourceType" label={t("workflow_node.deploy.form.tencentcloud_gaap_resource_type.label")} rules={[formRule]}>
|
||||||
|
@ -60,7 +60,7 @@ const DeployNodeConfigFormTencentCloudSCFConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_scf_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_scf_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_scf_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_scf_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -52,7 +52,7 @@ const DeployNodeConfigFormTencentCloudSSLConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
@ -75,7 +75,7 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -24,9 +24,7 @@ export type DeployNodeConfigFormTencentCloudSSLUpdateConfigProps = {
|
|||||||
const MULTIPLE_INPUT_SEPARATOR = ";";
|
const MULTIPLE_INPUT_SEPARATOR = ";";
|
||||||
|
|
||||||
const initFormModel = (): DeployNodeConfigFormTencentCloudSSLUpdateConfigFieldValues => {
|
const initFormModel = (): DeployNodeConfigFormTencentCloudSSLUpdateConfigFieldValues => {
|
||||||
return {
|
return {};
|
||||||
isReplaced: true,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeployNodeConfigFormTencentCloudSSLUpdateConfig = ({
|
const DeployNodeConfigFormTencentCloudSSLUpdateConfig = ({
|
||||||
@ -49,12 +47,15 @@ const DeployNodeConfigFormTencentCloudSSLUpdateConfig = ({
|
|||||||
.split(MULTIPLE_INPUT_SEPARATOR)
|
.split(MULTIPLE_INPUT_SEPARATOR)
|
||||||
.every((e) => !!e.trim());
|
.every((e) => !!e.trim());
|
||||||
}, t("workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.placeholder")),
|
}, t("workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.placeholder")),
|
||||||
resourceRegions: z.string(t("workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder")).refine((v) => {
|
resourceRegions: z
|
||||||
if (!v) return false;
|
.string(t("workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder"))
|
||||||
return String(v)
|
.nullish()
|
||||||
.split(MULTIPLE_INPUT_SEPARATOR)
|
.refine((v) => {
|
||||||
.every((e) => !!e.trim());
|
if (!v) return true;
|
||||||
}, t("workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder")),
|
return String(v)
|
||||||
|
.split(MULTIPLE_INPUT_SEPARATOR)
|
||||||
|
.every((e) => !!e.trim());
|
||||||
|
}, t("workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder")),
|
||||||
isReplaced: z.boolean().nullish(),
|
isReplaced: z.boolean().nullish(),
|
||||||
});
|
});
|
||||||
const formRule = createSchemaFieldRule(formSchema);
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
@ -82,7 +83,7 @@ const DeployNodeConfigFormTencentCloudSSLUpdateConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -122,7 +123,12 @@ const DeployNodeConfigFormTencentCloudSSLUpdateConfig = ({
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item name="isReplaced" label={t("workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.label")} rules={[formRule]}>
|
<Form.Item
|
||||||
|
name="isReplaced"
|
||||||
|
label={t("workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.label")}
|
||||||
|
rules={[formRule]}
|
||||||
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.tooltip") }}></span>}
|
||||||
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -64,7 +64,7 @@ const DeployNodeConfigFormTencentCloudVODConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_vod_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_vod_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_vod_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_vod_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -68,7 +68,7 @@ const DeployNodeConfigFormTencentCloudWAFConfig = ({
|
|||||||
rules={[formRule]}
|
rules={[formRule]}
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_waf_endpoint.tooltip") }}></span>}
|
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_waf_endpoint.tooltip") }}></span>}
|
||||||
>
|
>
|
||||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_waf_endpoint.placeholder")} />
|
<Input allowClear placeholder={t("workflow_node.deploy.form.tencentcloud_waf_endpoint.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -45,6 +45,7 @@ export interface AccessModel extends BaseModel {
|
|||||||
| AccessConfigForHetzner
|
| AccessConfigForHetzner
|
||||||
| AccessConfigForHuaweiCloud
|
| AccessConfigForHuaweiCloud
|
||||||
| AccessConfigForJDCloud
|
| AccessConfigForJDCloud
|
||||||
|
| AccessConfigForKong
|
||||||
| AccessConfigForKubernetes
|
| AccessConfigForKubernetes
|
||||||
| AccessConfigForLarkBot
|
| AccessConfigForLarkBot
|
||||||
| AccessConfigForLeCDN
|
| AccessConfigForLeCDN
|
||||||
@ -293,6 +294,12 @@ export type AccessConfigForJDCloud = {
|
|||||||
accessKeySecret: string;
|
accessKeySecret: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AccessConfigForKong = {
|
||||||
|
serverUrl: string;
|
||||||
|
apiToken: string;
|
||||||
|
allowInsecureConnections?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type AccessConfigForKubernetes = {
|
export type AccessConfigForKubernetes = {
|
||||||
kubeConfig?: string;
|
kubeConfig?: string;
|
||||||
};
|
};
|
||||||
|
@ -44,6 +44,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
|||||||
HETZNER: "hetzner",
|
HETZNER: "hetzner",
|
||||||
HUAWEICLOUD: "huaweicloud",
|
HUAWEICLOUD: "huaweicloud",
|
||||||
JDCLOUD: "jdcloud",
|
JDCLOUD: "jdcloud",
|
||||||
|
KONG: "kong",
|
||||||
KUBERNETES: "k8s",
|
KUBERNETES: "k8s",
|
||||||
LARKBOT: "larkbot",
|
LARKBOT: "larkbot",
|
||||||
LECDN: "lecdn",
|
LECDN: "lecdn",
|
||||||
@ -146,6 +147,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
|||||||
[ACCESS_PROVIDERS.CACHEFLY, "provider.cachefly", "/imgs/providers/cachefly.png", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.CACHEFLY, "provider.cachefly", "/imgs/providers/cachefly.png", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.EDGIO, "provider.edgio", "/imgs/providers/edgio.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.EDGIO, "provider.edgio", "/imgs/providers/edgio.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.APISIX, "provider.apisix", "/imgs/providers/apisix.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.APISIX, "provider.apisix", "/imgs/providers/apisix.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
|
[ACCESS_PROVIDERS.KONG, "provider.kong", "/imgs/providers/kong.png", [ACCESS_USAGES.HOSTING]],
|
||||||
[ACCESS_PROVIDERS.PROXMOXVE, "provider.proxmoxve", "/imgs/providers/proxmoxve.svg", [ACCESS_USAGES.HOSTING]],
|
[ACCESS_PROVIDERS.PROXMOXVE, "provider.proxmoxve", "/imgs/providers/proxmoxve.svg", [ACCESS_USAGES.HOSTING]],
|
||||||
|
|
||||||
[ACCESS_PROVIDERS.CLOUDFLARE, "provider.cloudflare", "/imgs/providers/cloudflare.svg", [ACCESS_USAGES.DNS]],
|
[ACCESS_PROVIDERS.CLOUDFLARE, "provider.cloudflare", "/imgs/providers/cloudflare.svg", [ACCESS_USAGES.DNS]],
|
||||||
@ -436,6 +438,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
|
|||||||
JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`,
|
JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`,
|
||||||
JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`,
|
JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`,
|
||||||
JDCLOUD_VOD: `${ACCESS_PROVIDERS.JDCLOUD}-vod`,
|
JDCLOUD_VOD: `${ACCESS_PROVIDERS.JDCLOUD}-vod`,
|
||||||
|
KONG: `${ACCESS_PROVIDERS.KONG}`,
|
||||||
KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`,
|
KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`,
|
||||||
LECDN: `${ACCESS_PROVIDERS.LECDN}`,
|
LECDN: `${ACCESS_PROVIDERS.LECDN}`,
|
||||||
LOCAL: `${ACCESS_PROVIDERS.LOCAL}`,
|
LOCAL: `${ACCESS_PROVIDERS.LOCAL}`,
|
||||||
@ -613,6 +616,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
|||||||
[DEPLOYMENT_PROVIDERS.BAOTAWAF_CONSOLE, "provider.baotawaf.console", DEPLOYMENT_CATEGORIES.OTHER],
|
[DEPLOYMENT_PROVIDERS.BAOTAWAF_CONSOLE, "provider.baotawaf.console", DEPLOYMENT_CATEGORIES.OTHER],
|
||||||
[DEPLOYMENT_PROVIDERS.SAFELINE, "provider.safeline", DEPLOYMENT_CATEGORIES.FIREWALL],
|
[DEPLOYMENT_PROVIDERS.SAFELINE, "provider.safeline", DEPLOYMENT_CATEGORIES.FIREWALL],
|
||||||
[DEPLOYMENT_PROVIDERS.APISIX, "provider.apisix", DEPLOYMENT_CATEGORIES.APIGATEWAY],
|
[DEPLOYMENT_PROVIDERS.APISIX, "provider.apisix", DEPLOYMENT_CATEGORIES.APIGATEWAY],
|
||||||
|
[DEPLOYMENT_PROVIDERS.KONG, "provider.kong", DEPLOYMENT_CATEGORIES.APIGATEWAY],
|
||||||
[DEPLOYMENT_PROVIDERS.PROXMOXVE, "provider.proxmoxve", DEPLOYMENT_CATEGORIES.NAS],
|
[DEPLOYMENT_PROVIDERS.PROXMOXVE, "provider.proxmoxve", DEPLOYMENT_CATEGORIES.NAS],
|
||||||
].map(([type, name, category, builtin]) => [
|
].map(([type, name, category, builtin]) => [
|
||||||
type,
|
type,
|
||||||
|
@ -286,6 +286,11 @@
|
|||||||
"access.form.k8s_kubeconfig.label": "KubeConfig",
|
"access.form.k8s_kubeconfig.label": "KubeConfig",
|
||||||
"access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file",
|
"access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file",
|
||||||
"access.form.k8s_kubeconfig.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>Leave it blank to use the Pod's ServiceAccount.",
|
"access.form.k8s_kubeconfig.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>Leave it blank to use the Pod's ServiceAccount.",
|
||||||
|
"access.form.kong_server_url.label": "Kong admin API server URL",
|
||||||
|
"access.form.kong_server_url.placeholder": "Please enter Kong admin API server URL",
|
||||||
|
"access.form.kong_api_token.label": "Kong admin API token (Optional)",
|
||||||
|
"access.form.kong_api_token.placeholder": "Please enter Kong admin API token",
|
||||||
|
"access.form.kong_api_token.tooltip": "For more information, see <a href=\"https://developer.konghq.com/admin-api/\" target=\"_blank\">https://developer.konghq.com/admin-api/</a>",
|
||||||
"access.form.larkbot_webhook_url.label": "Lark bot Webhook URL",
|
"access.form.larkbot_webhook_url.label": "Lark bot Webhook URL",
|
||||||
"access.form.larkbot_webhook_url.placeholder": "Please enter Lark bot Webhook URL",
|
"access.form.larkbot_webhook_url.placeholder": "Please enter Lark bot Webhook URL",
|
||||||
"access.form.larkbot_webhook_url.tooltip": "For more information, see <a href=\"https://www.feishu.cn/hc/en-US/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/en-US/articles/807992406756</a>",
|
"access.form.larkbot_webhook_url.tooltip": "For more information, see <a href=\"https://www.feishu.cn/hc/en-US/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/en-US/articles/807992406756</a>",
|
||||||
|
@ -101,6 +101,7 @@
|
|||||||
"provider.jdcloud.dns": "JD Cloud - DNS",
|
"provider.jdcloud.dns": "JD Cloud - DNS",
|
||||||
"provider.jdcloud.live": "JD Cloud - Live Video",
|
"provider.jdcloud.live": "JD Cloud - Live Video",
|
||||||
"provider.jdcloud.vod": "JD Cloud - VOD (Video on Demand)",
|
"provider.jdcloud.vod": "JD Cloud - VOD (Video on Demand)",
|
||||||
|
"provider.kong": "Kong",
|
||||||
"provider.kubernetes": "Kubernetes",
|
"provider.kubernetes": "Kubernetes",
|
||||||
"provider.kubernetes.secret": "Kubernetes - Secret",
|
"provider.kubernetes.secret": "Kubernetes - Secret",
|
||||||
"provider.larkbot": "Lark Bot",
|
"provider.larkbot": "Lark Bot",
|
||||||
|
@ -268,8 +268,8 @@
|
|||||||
"workflow_node.deploy.form.aliyun_oss_bucket.label": "Alibaba Cloud OSS bucket",
|
"workflow_node.deploy.form.aliyun_oss_bucket.label": "Alibaba Cloud OSS bucket",
|
||||||
"workflow_node.deploy.form.aliyun_oss_bucket.placeholder": "Please enter Alibaba Cloud OSS bucket name",
|
"workflow_node.deploy.form.aliyun_oss_bucket.placeholder": "Please enter Alibaba Cloud OSS bucket name",
|
||||||
"workflow_node.deploy.form.aliyun_oss_bucket.tooltip": "For more information, see <a href=\"https://oss.console.aliyun.com\" target=\"_blank\">https://oss.console.aliyun.com</a>",
|
"workflow_node.deploy.form.aliyun_oss_bucket.tooltip": "For more information, see <a href=\"https://oss.console.aliyun.com\" target=\"_blank\">https://oss.console.aliyun.com</a>",
|
||||||
"workflow_node.deploy.form.aliyun_oss_domain.label": "Alibaba Cloud OSS domain",
|
"workflow_node.deploy.form.aliyun_oss_domain.label": "Alibaba Cloud OSS custom domain",
|
||||||
"workflow_node.deploy.form.aliyun_oss_domain.placeholder": "Please enter Alibaba Cloud OSS domain name",
|
"workflow_node.deploy.form.aliyun_oss_domain.placeholder": "Please enter Alibaba Cloud OSS bucket custom domain name",
|
||||||
"workflow_node.deploy.form.aliyun_oss_domain.tooltip": "For more information, see <a href=\"https://oss.console.aliyun.com\" target=\"_blank\">https://oss.console.aliyun.com</a>",
|
"workflow_node.deploy.form.aliyun_oss_domain.tooltip": "For more information, see <a href=\"https://oss.console.aliyun.com\" target=\"_blank\">https://oss.console.aliyun.com</a>",
|
||||||
"workflow_node.deploy.form.aliyun_vod_region.label": "Alibaba Cloud VOD region",
|
"workflow_node.deploy.form.aliyun_vod_region.label": "Alibaba Cloud VOD region",
|
||||||
"workflow_node.deploy.form.aliyun_vod_region.placeholder": "Please enter Alibaba Cloud VOD region (e.g. cn-hangzhou)",
|
"workflow_node.deploy.form.aliyun_vod_region.placeholder": "Please enter Alibaba Cloud VOD region (e.g. cn-hangzhou)",
|
||||||
@ -528,6 +528,15 @@
|
|||||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret data key for private key",
|
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret data key for private key",
|
||||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "Please enter Kubernetes Secret data key for private key",
|
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "Please enter Kubernetes Secret data key for private key",
|
||||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/configuration/secret/\" target=\"_blank\">https://kubernetes.io/docs/concepts/configuration/secret/</a>",
|
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/configuration/secret/\" target=\"_blank\">https://kubernetes.io/docs/concepts/configuration/secret/</a>",
|
||||||
|
"workflow_node.deploy.form.kong_resource_type.label": "Resource type",
|
||||||
|
"workflow_node.deploy.form.kong_resource_type.placeholder": "Please select resource type",
|
||||||
|
"workflow_node.deploy.form.kong_resource_type.option.certificate.label": "SSL certificate",
|
||||||
|
"workflow_node.deploy.form.kong_workspace.label": "Kong workspace (Optional)",
|
||||||
|
"workflow_node.deploy.form.kong_workspace.placeholder": "Please enter Kong workspace",
|
||||||
|
"workflow_node.deploy.form.kong_workspace.tooltip": "You can find it on Kong dashboard.",
|
||||||
|
"workflow_node.deploy.form.kong_certificate_id.label": "Kong certificate ID",
|
||||||
|
"workflow_node.deploy.form.kong_certificate_id.placeholder": "Please enter Kong certificate ID",
|
||||||
|
"workflow_node.deploy.form.kong_certificate_id.tooltip": "You can find it on Kong dashboard.",
|
||||||
"workflow_node.deploy.form.lecdn_resource_type.label": "Resource type",
|
"workflow_node.deploy.form.lecdn_resource_type.label": "Resource type",
|
||||||
"workflow_node.deploy.form.lecdn_resource_type.placeholder": "Please select resource type",
|
"workflow_node.deploy.form.lecdn_resource_type.placeholder": "Please select resource type",
|
||||||
"workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "Certificate",
|
"workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "Certificate",
|
||||||
@ -592,8 +601,8 @@
|
|||||||
"workflow_node.deploy.form.qiniu_cdn_domain.label": "Qiniu CDN domain",
|
"workflow_node.deploy.form.qiniu_cdn_domain.label": "Qiniu CDN domain",
|
||||||
"workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "Please enter Qiniu CDN domain name",
|
"workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "Please enter Qiniu CDN domain name",
|
||||||
"workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "For more information, see <a href=\"https://portal.qiniu.com/cdn\" target=\"_blank\">https://portal.qiniu.com/cdn</a>",
|
"workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "For more information, see <a href=\"https://portal.qiniu.com/cdn\" target=\"_blank\">https://portal.qiniu.com/cdn</a>",
|
||||||
"workflow_node.deploy.form.qiniu_kodo_domain.label": "Qiniu Kodo bucket domain",
|
"workflow_node.deploy.form.qiniu_kodo_domain.label": "Qiniu Kodo custom domain",
|
||||||
"workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "Please enter Qiniu Kodo bucket domain name",
|
"workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "Please enter Qiniu Kodo bucket custom domain name",
|
||||||
"workflow_node.deploy.form.qiniu_kodo_domain.tooltip": "For more information, see <a href=\"https://portal.qiniu.com/kodo\" target=\"_blank\">https://portal.qiniu.com/kodo</a>",
|
"workflow_node.deploy.form.qiniu_kodo_domain.tooltip": "For more information, see <a href=\"https://portal.qiniu.com/kodo\" target=\"_blank\">https://portal.qiniu.com/kodo</a>",
|
||||||
"workflow_node.deploy.form.qiniu_pili_hub.label": "Qiniu Pili hub",
|
"workflow_node.deploy.form.qiniu_pili_hub.label": "Qiniu Pili hub",
|
||||||
"workflow_node.deploy.form.qiniu_pili_hub.placeholder": "Please enter Qiniu Pili hub name",
|
"workflow_node.deploy.form.qiniu_pili_hub.placeholder": "Please enter Qiniu Pili hub name",
|
||||||
@ -697,8 +706,8 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_cos_bucket.label": "Tencent Cloud COS bucket",
|
"workflow_node.deploy.form.tencentcloud_cos_bucket.label": "Tencent Cloud COS bucket",
|
||||||
"workflow_node.deploy.form.tencentcloud_cos_bucket.placeholder": "Please enter Tencent Cloud COS bucket name",
|
"workflow_node.deploy.form.tencentcloud_cos_bucket.placeholder": "Please enter Tencent Cloud COS bucket name",
|
||||||
"workflow_node.deploy.form.tencentcloud_cos_bucket.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/cos\" target=\"_blank\">https://console.tencentcloud.com/cos</a>",
|
"workflow_node.deploy.form.tencentcloud_cos_bucket.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/cos\" target=\"_blank\">https://console.tencentcloud.com/cos</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_cos_domain.label": "Tencent Cloud COS domain",
|
"workflow_node.deploy.form.tencentcloud_cos_domain.label": "Tencent Cloud COS custom domain",
|
||||||
"workflow_node.deploy.form.tencentcloud_cos_domain.placeholder": "Please enter Tencent Cloud COS domain name",
|
"workflow_node.deploy.form.tencentcloud_cos_domain.placeholder": "Please enter Tencent Cloud COS bucket custom domain name",
|
||||||
"workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/cos\" target=\"_blank\">https://console.tencentcloud.com/cos</a>",
|
"workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/cos\" target=\"_blank\">https://console.tencentcloud.com/cos</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_endpoint.label": "Tencent Cloud CSS API endpoint (Optional)",
|
"workflow_node.deploy.form.tencentcloud_css_endpoint.label": "Tencent Cloud CSS API endpoint (Optional)",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_endpoint.placeholder": "Please enter Tencent Cloud CSS API endpoint (e.g. live.intl.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_css_endpoint.placeholder": "Please enter Tencent Cloud CSS API endpoint (e.g. live.intl.tencentcloudapi.com)",
|
||||||
@ -718,9 +727,11 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_eo_zone_id.label": "Tencent Cloud EdgeOne zone ID",
|
"workflow_node.deploy.form.tencentcloud_eo_zone_id.label": "Tencent Cloud EdgeOne zone ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder": "Please enter Tencent Cloud EdgeOne zone ID",
|
"workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder": "Please enter Tencent Cloud EdgeOne zone ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_zone_id.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/edgeone\" target=\"_blank\">https://console.tencentcloud.com/edgeone</a>",
|
"workflow_node.deploy.form.tencentcloud_eo_zone_id.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/edgeone\" target=\"_blank\">https://console.tencentcloud.com/edgeone</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_domain.label": "Tencent Cloud EdgeOne domain",
|
"workflow_node.deploy.form.tencentcloud_eo_domains.label": "Tencent Cloud EdgeOne domains",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_domain.placeholder": "Please enter Tencent Cloud EdgeOne domain name",
|
"workflow_node.deploy.form.tencentcloud_eo_domains.placeholder": "Please enter Tencent Cloud EdgeOne domain names (separated by semicolons)",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_domain.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/edgeone\" target=\"_blank\">https://console.tencentcloud.com/edgeone</a>",
|
"workflow_node.deploy.form.tencentcloud_eo_domains.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/edgeone\" target=\"_blank\">https://console.tencentcloud.com/edgeone</a>",
|
||||||
|
"workflow_node.deploy.form.tencentcloud_eo_domains.multiple_input_modal.title": "Change Tencent Cloud EdgeOne domain",
|
||||||
|
"workflow_node.deploy.form.tencentcloud_eo_domains.multiple_input_modal.placeholder": "Please enter Tencent Cloud EdgeOne domain name",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.label": "Tencent Cloud GAAP API endpoint (Optional)",
|
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.label": "Tencent Cloud GAAP API endpoint (Optional)",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.placeholder": "Please enter Tencent Cloud GAAP API endpoint (e.g. gaap.intl.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.placeholder": "Please enter Tencent Cloud GAAP API endpoint (e.g. gaap.intl.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.tooltip": "<ul style=\"margin-left: 1.25em; list-style: disc;\"><li><strong>gaap.intl.tencentcloudapi.com</strong> for Tencent Cloud International</li><li><strong>gaap.tencentcloudapi.com</strong> for Tencent Cloud in China</li></ul>",
|
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.tooltip": "<ul style=\"margin-left: 1.25em; list-style: disc;\"><li><strong>gaap.intl.tencentcloudapi.com</strong> for Tencent Cloud International</li><li><strong>gaap.tencentcloudapi.com</strong> for Tencent Cloud in China</li></ul>",
|
||||||
@ -770,15 +781,16 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.tooltip": "For more information, see <a href=\"https://console.cloud.tencent.com/certoverview\" target=\"_blank\">https://console.cloud.tencent.com/certoverview</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.tooltip": "For more information, see <a href=\"https://console.cloud.tencent.com/certoverview\" target=\"_blank\">https://console.cloud.tencent.com/certoverview</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.label": "Tencent Cloud resource types",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.label": "Tencent Cloud resource types",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.placeholder": "Please enter Tencent Cloud resource types (separated by semicolons)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.placeholder": "Please enter Tencent Cloud resource types (separated by semicolons)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.tooltip": "For more information, see <a href=\"https://www.tencentcloud.com/document/product/1007/57981\" target=\"_blank\">https://www.tencentcloud.com/document/product/1007/57981</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.tooltip": "For more information, see <a href=\"https://www.tencentcloud.com/document/product/1007/57981\" target=\"_blank\">https://www.tencentcloud.com/document/product/1007/57981</a> or <a href=\"https://www.tencentcloud.com/document/product/1007/70503\" target=\"_blank\">https://www.tencentcloud.com/document/product/1007/70503</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.title": "Change Tencent Cloud resource types",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.title": "Change Tencent Cloud resource types",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.placeholder": "Please enter Tencent Cloud resource type",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.placeholder": "Please enter Tencent Cloud resource type",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.label": "Tencent Cloud resource regions (Optional)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.label": "Tencent Cloud resource regions (Optional)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder": "Please enter Tencent Cloud resource regions (separated by semicolons)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder": "Please enter Tencent Cloud resource regions (separated by semicolons)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.tooltip": "For more information, see <a href=\"https://www.tencentcloud.com/document/product/1007/57981\" target=\"_blank\">https://www.tencentcloud.com/document/product/1007/57981</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.tooltip": "For more information, see <a href=\"https://www.tencentcloud.com/document/product/1007/57981\" target=\"_blank\">https://www.tencentcloud.com/document/product/1007/57981</a> or <a href=\"https://www.tencentcloud.com/document/product/1007/70503\" target=\"_blank\">https://www.tencentcloud.com/document/product/1007/70503</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.title": "Change Tencent Cloud resource regions",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.title": "Change Tencent Cloud resource regions",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.placeholder": "Please enter Tencent Cloud resource region",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.placeholder": "Please enter Tencent Cloud resource region",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.label": "Renewal certificate (certificate ID unchanged)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.label": "Renewal certificate (certificate ID unchanged)",
|
||||||
|
"workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.tooltip": "When unchecked, it will invoke <em>UpdateCertificateInstance</em>; otherwise, it will invoke <em>UploadUpdateCertificateInstance</em>.",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_endpoint.label": "Tencent Cloud VOD API endpoint (Optional)",
|
"workflow_node.deploy.form.tencentcloud_vod_endpoint.label": "Tencent Cloud VOD API endpoint (Optional)",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_endpoint.placeholder": "Please enter Tencent Cloud VOD API endpoint (e.g. vod.intl.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_vod_endpoint.placeholder": "Please enter Tencent Cloud VOD API endpoint (e.g. vod.intl.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_endpoint.tooltip": "<ul style=\"margin-left: 1.25em; list-style: disc;\"><li><strong>vod.intl.tencentcloudapi.com</strong> for Tencent Cloud International</li><li><strong>vod.tencentcloudapi.com</strong> for Tencent Cloud in China</li></ul>",
|
"workflow_node.deploy.form.tencentcloud_vod_endpoint.tooltip": "<ul style=\"margin-left: 1.25em; list-style: disc;\"><li><strong>vod.intl.tencentcloudapi.com</strong> for Tencent Cloud International</li><li><strong>vod.tencentcloudapi.com</strong> for Tencent Cloud in China</li></ul>",
|
||||||
@ -812,8 +824,8 @@
|
|||||||
"workflow_node.deploy.form.ucloud_us3_bucket.label": "UCloud US3 bucket",
|
"workflow_node.deploy.form.ucloud_us3_bucket.label": "UCloud US3 bucket",
|
||||||
"workflow_node.deploy.form.ucloud_us3_bucket.placeholder": "Please enter UCloud US3 bucket name",
|
"workflow_node.deploy.form.ucloud_us3_bucket.placeholder": "Please enter UCloud US3 bucket name",
|
||||||
"workflow_node.deploy.form.ucloud_us3_bucket.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ufile\" target=\"_blank\">https://console.ucloud-global.com/ufile</a>",
|
"workflow_node.deploy.form.ucloud_us3_bucket.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ufile\" target=\"_blank\">https://console.ucloud-global.com/ufile</a>",
|
||||||
"workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 domain",
|
"workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 custom domain",
|
||||||
"workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 domain name",
|
"workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 bucket custom domain name",
|
||||||
"workflow_node.deploy.form.ucloud_us3_domain.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ufile\" target=\"_blank\">https://console.ucloud-global.com/ufile</a>",
|
"workflow_node.deploy.form.ucloud_us3_domain.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ufile\" target=\"_blank\">https://console.ucloud-global.com/ufile</a>",
|
||||||
"workflow_node.deploy.form.unicloud_webhost.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the uniCloud, please create a GitHub Issue.",
|
"workflow_node.deploy.form.unicloud_webhost.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the uniCloud, please create a GitHub Issue.",
|
||||||
"workflow_node.deploy.form.unicloud_webhost_space_provider.label": "uniCloud space provider",
|
"workflow_node.deploy.form.unicloud_webhost_space_provider.label": "uniCloud space provider",
|
||||||
@ -830,8 +842,8 @@
|
|||||||
"workflow_node.deploy.form.upyun_cdn_domain.placeholder": "Please enter UPYUN CDN domain name",
|
"workflow_node.deploy.form.upyun_cdn_domain.placeholder": "Please enter UPYUN CDN domain name",
|
||||||
"workflow_node.deploy.form.upyun_cdn_domain.tooltip": "For more information, see <a href=\"https://console.upyun.com/services/cdn/\" target=\"_blank\">https://console.upyun.com/services/cdn/</a>",
|
"workflow_node.deploy.form.upyun_cdn_domain.tooltip": "For more information, see <a href=\"https://console.upyun.com/services/cdn/\" target=\"_blank\">https://console.upyun.com/services/cdn/</a>",
|
||||||
"workflow_node.deploy.form.upyun_file.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.",
|
"workflow_node.deploy.form.upyun_file.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.",
|
||||||
"workflow_node.deploy.form.upyun_file_domain.label": "UPYUN bucket domain",
|
"workflow_node.deploy.form.upyun_file_domain.label": "UPYUN USS custom domain",
|
||||||
"workflow_node.deploy.form.upyun_file_domain.placeholder": "Please enter UPYUN bucket domain name",
|
"workflow_node.deploy.form.upyun_file_domain.placeholder": "Please enter UPYUN USS bucket custom domain name",
|
||||||
"workflow_node.deploy.form.upyun_file_domain.tooltip": "For more information, see <a href=\"https://console.upyun.com/services/file/\" target=\"_blank\">https://console.upyun.com/services/file/</a>",
|
"workflow_node.deploy.form.upyun_file_domain.tooltip": "For more information, see <a href=\"https://console.upyun.com/services/file/\" target=\"_blank\">https://console.upyun.com/services/file/</a>",
|
||||||
"workflow_node.deploy.form.volcengine_alb_region.label": "VolcEngine ALB region",
|
"workflow_node.deploy.form.volcengine_alb_region.label": "VolcEngine ALB region",
|
||||||
"workflow_node.deploy.form.volcengine_alb_region.placeholder": "Please enter VolcEngine ALB region (e.g. cn-beijing)",
|
"workflow_node.deploy.form.volcengine_alb_region.placeholder": "Please enter VolcEngine ALB region (e.g. cn-beijing)",
|
||||||
@ -876,8 +888,8 @@
|
|||||||
"workflow_node.deploy.form.volcengine_imagex_service_id.label": "VolcEngine ImageX service ID",
|
"workflow_node.deploy.form.volcengine_imagex_service_id.label": "VolcEngine ImageX service ID",
|
||||||
"workflow_node.deploy.form.volcengine_imagex_service_id.placeholder": "Please enter VolcEngine ImageX service ID",
|
"workflow_node.deploy.form.volcengine_imagex_service_id.placeholder": "Please enter VolcEngine ImageX service ID",
|
||||||
"workflow_node.deploy.form.volcengine_imagex_service_id.tooltip": "For more information, see <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>",
|
"workflow_node.deploy.form.volcengine_imagex_service_id.tooltip": "For more information, see <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>",
|
||||||
"workflow_node.deploy.form.volcengine_imagex_domain.label": "VolcEngine ImageX domain",
|
"workflow_node.deploy.form.volcengine_imagex_domain.label": "VolcEngine ImageX custom domain",
|
||||||
"workflow_node.deploy.form.volcengine_imagex_domain.placeholder": "Please enter VolcEngine ImageX domain name",
|
"workflow_node.deploy.form.volcengine_imagex_domain.placeholder": "Please enter VolcEngine ImageX custom domain name",
|
||||||
"workflow_node.deploy.form.volcengine_imagex_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>",
|
"workflow_node.deploy.form.volcengine_imagex_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>",
|
||||||
"workflow_node.deploy.form.volcengine_live_domain.label": "VolcEngine Live streaming domain",
|
"workflow_node.deploy.form.volcengine_live_domain.label": "VolcEngine Live streaming domain",
|
||||||
"workflow_node.deploy.form.volcengine_live_domain.placeholder": "Please enter VolcEngine Live streaming domain name",
|
"workflow_node.deploy.form.volcengine_live_domain.placeholder": "Please enter VolcEngine Live streaming domain name",
|
||||||
@ -888,8 +900,8 @@
|
|||||||
"workflow_node.deploy.form.volcengine_tos_bucket.label": "VolcEngine TOS bucket",
|
"workflow_node.deploy.form.volcengine_tos_bucket.label": "VolcEngine TOS bucket",
|
||||||
"workflow_node.deploy.form.volcengine_tos_bucket.placeholder": "Please enter VolcEngine TOS bucket name",
|
"workflow_node.deploy.form.volcengine_tos_bucket.placeholder": "Please enter VolcEngine TOS bucket name",
|
||||||
"workflow_node.deploy.form.volcengine_tos_bucket.tooltip": "For more information, see <a href=\"https://console.volcengine.com/tos\" target=\"_blank\">https://console.volcengine.com/tos</a>",
|
"workflow_node.deploy.form.volcengine_tos_bucket.tooltip": "For more information, see <a href=\"https://console.volcengine.com/tos\" target=\"_blank\">https://console.volcengine.com/tos</a>",
|
||||||
"workflow_node.deploy.form.volcengine_tos_domain.label": "VolcEngine TOS domain",
|
"workflow_node.deploy.form.volcengine_tos_domain.label": "VolcEngine TOS custom domain",
|
||||||
"workflow_node.deploy.form.volcengine_tos_domain.placeholder": "Please enter VolcEngine TOS domain name",
|
"workflow_node.deploy.form.volcengine_tos_domain.placeholder": "Please enter VolcEngine TOS bucket custom domain name",
|
||||||
"workflow_node.deploy.form.volcengine_tos_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/tos\" target=\"_blank\">https://console.volcengine.com/tos</a>",
|
"workflow_node.deploy.form.volcengine_tos_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/tos\" target=\"_blank\">https://console.volcengine.com/tos</a>",
|
||||||
"workflow_node.deploy.form.wangsu_cdn_domains.label": "Wangsu Cloud CDN domains",
|
"workflow_node.deploy.form.wangsu_cdn_domains.label": "Wangsu Cloud CDN domains",
|
||||||
"workflow_node.deploy.form.wangsu_cdn_domains.placeholder": "Please enter Wangsu Cloud CDN domain names (separated by semicolons)",
|
"workflow_node.deploy.form.wangsu_cdn_domains.placeholder": "Please enter Wangsu Cloud CDN domain names (separated by semicolons)",
|
||||||
|
@ -286,6 +286,11 @@
|
|||||||
"access.form.k8s_kubeconfig.label": "KubeConfig",
|
"access.form.k8s_kubeconfig.label": "KubeConfig",
|
||||||
"access.form.k8s_kubeconfig.placeholder": "请输入 KubeConfig 文件内容",
|
"access.form.k8s_kubeconfig.placeholder": "请输入 KubeConfig 文件内容",
|
||||||
"access.form.k8s_kubeconfig.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>为空时,将使用 Pod 的 ServiceAccount 作为凭证。",
|
"access.form.k8s_kubeconfig.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>为空时,将使用 Pod 的 ServiceAccount 作为凭证。",
|
||||||
|
"access.form.kong_server_url.label": "Kong Admin API 服务地址",
|
||||||
|
"access.form.kong_server_url.placeholder": "请输入 Kong Admin API 服务地址",
|
||||||
|
"access.form.kong_api_token.label": "Kong Admin API Token(可选)",
|
||||||
|
"access.form.kong_api_token.placeholder": "请输入 Kong Admin API Token",
|
||||||
|
"access.form.kong_api_token.tooltip": "这是什么?请参阅 <a href=\"https://developer.konghq.com/admin-api/\" target=\"_blank\">https://developer.konghq.com/admin-api/</a>",
|
||||||
"access.form.larkbot_webhook_url.label": "飞书群机器人 Webhook 地址",
|
"access.form.larkbot_webhook_url.label": "飞书群机器人 Webhook 地址",
|
||||||
"access.form.larkbot_webhook_url.placeholder": "请输入飞书群机器人 Webhook 地址",
|
"access.form.larkbot_webhook_url.placeholder": "请输入飞书群机器人 Webhook 地址",
|
||||||
"access.form.larkbot_webhook_url.tooltip": "这是什么?请参阅 <a href=\"https://www.feishu.cn/hc/zh-CN/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/zh-CN/articles/807992406756</a>",
|
"access.form.larkbot_webhook_url.tooltip": "这是什么?请参阅 <a href=\"https://www.feishu.cn/hc/zh-CN/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/zh-CN/articles/807992406756</a>",
|
||||||
|
@ -101,6 +101,7 @@
|
|||||||
"provider.jdcloud.dns": "京东云 - 云解析 DNS",
|
"provider.jdcloud.dns": "京东云 - 云解析 DNS",
|
||||||
"provider.jdcloud.live": "京东云 - 视频直播",
|
"provider.jdcloud.live": "京东云 - 视频直播",
|
||||||
"provider.jdcloud.vod": "京东云 - 视频点播",
|
"provider.jdcloud.vod": "京东云 - 视频点播",
|
||||||
|
"provider.kong": "Kong",
|
||||||
"provider.kubernetes": "Kubernetes",
|
"provider.kubernetes": "Kubernetes",
|
||||||
"provider.kubernetes.secret": "Kubernetes - Secret",
|
"provider.kubernetes.secret": "Kubernetes - Secret",
|
||||||
"provider.larkbot": "飞书群机器人",
|
"provider.larkbot": "飞书群机器人",
|
||||||
|
@ -526,6 +526,15 @@
|
|||||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret 数据键(用于存放私钥的字段)",
|
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret 数据键(用于存放私钥的字段)",
|
||||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "请输入 Kubernetes Secret 中用于存放私钥的数据键",
|
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "请输入 Kubernetes Secret 中用于存放私钥的数据键",
|
||||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/</a>",
|
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/</a>",
|
||||||
|
"workflow_node.deploy.form.kong_resource_type.label": "证书部署方式",
|
||||||
|
"workflow_node.deploy.form.kong_resource_type.placeholder": "请选择证书部署方式",
|
||||||
|
"workflow_node.deploy.form.kong_resource_type.option.certificate.label": "替换指定证书",
|
||||||
|
"workflow_node.deploy.form.kong_workspace.label": "Kong 工作空间(可选)",
|
||||||
|
"workflow_node.deploy.form.kong_workspace.placeholder": "请输入 Kong 工作空间",
|
||||||
|
"workflow_node.deploy.form.kong_workspace.tooltip": "请登录 Kong 控制台查看。",
|
||||||
|
"workflow_node.deploy.form.kong_certificate_id.label": "Kong 证书 ID",
|
||||||
|
"workflow_node.deploy.form.kong_certificate_id.placeholder": "请输入 Kong 证书 ID",
|
||||||
|
"workflow_node.deploy.form.kong_certificate_id.tooltip": "请登录 Kong 控制台查看。",
|
||||||
"workflow_node.deploy.form.lecdn_resource_type.label": "证书部署方式",
|
"workflow_node.deploy.form.lecdn_resource_type.label": "证书部署方式",
|
||||||
"workflow_node.deploy.form.lecdn_resource_type.placeholder": "请选择证书部署方式",
|
"workflow_node.deploy.form.lecdn_resource_type.placeholder": "请选择证书部署方式",
|
||||||
"workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "替换指定证书",
|
"workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "替换指定证书",
|
||||||
@ -590,8 +599,8 @@
|
|||||||
"workflow_node.deploy.form.qiniu_cdn_domain.label": "七牛云 CDN 加速域名",
|
"workflow_node.deploy.form.qiniu_cdn_domain.label": "七牛云 CDN 加速域名",
|
||||||
"workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "请输入七牛云 CDN 加速域名(支持泛域名)",
|
"workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "请输入七牛云 CDN 加速域名(支持泛域名)",
|
||||||
"workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://portal.qiniu.com/cdn\" target=\"_blank\">https://portal.qiniu.com/cdn</a>",
|
"workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://portal.qiniu.com/cdn\" target=\"_blank\">https://portal.qiniu.com/cdn</a>",
|
||||||
"workflow_node.deploy.form.qiniu_kodo_domain.label": "七牛云对象存储加速域名",
|
"workflow_node.deploy.form.qiniu_kodo_domain.label": "七牛云对象存储自定义域名",
|
||||||
"workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "请输入七牛云对象存储加速域名",
|
"workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "请输入七牛云对象存储自定义域名",
|
||||||
"workflow_node.deploy.form.qiniu_kodo_domain.tooltip": "这是什么?请参阅 <a href=\"https://portal.qiniu.com/kodo\" target=\"_blank\">https://portal.qiniu.com/kodo</a>",
|
"workflow_node.deploy.form.qiniu_kodo_domain.tooltip": "这是什么?请参阅 <a href=\"https://portal.qiniu.com/kodo\" target=\"_blank\">https://portal.qiniu.com/kodo</a>",
|
||||||
"workflow_node.deploy.form.qiniu_pili_hub.label": "七牛云视频直播空间名",
|
"workflow_node.deploy.form.qiniu_pili_hub.label": "七牛云视频直播空间名",
|
||||||
"workflow_node.deploy.form.qiniu_pili_hub.placeholder": "请输入七牛云视频直播空间名",
|
"workflow_node.deploy.form.qiniu_pili_hub.placeholder": "请输入七牛云视频直播空间名",
|
||||||
@ -661,13 +670,13 @@
|
|||||||
"workflow_node.deploy.form.ssh_use_scp.tooltip": "如果你的远程服务器不支持 SFTP,请开启此选项回退为 SCP。",
|
"workflow_node.deploy.form.ssh_use_scp.tooltip": "如果你的远程服务器不支持 SFTP,请开启此选项回退为 SCP。",
|
||||||
"workflow_node.deploy.form.tencentcloud_cdn_endpoint.label": "腾讯云 CDN 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_cdn_endpoint.label": "腾讯云 CDN 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_cdn_endpoint.placeholder": "请输入腾讯云 CDN 接口端点(例如:cdn.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_cdn_endpoint.placeholder": "请输入腾讯云 CDN 接口端点(例如:cdn.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_cdn_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/228/30976\" target=\"_blank\">https://cloud.tencent.com/document/product/228/30976</a>",
|
"workflow_node.deploy.form.tencentcloud_cdn_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/228/30976\" target=\"_blank\">https://cloud.tencent.com/document/product/228/30976</a><br><br>国际站用户请填写 <em>cdn.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_cdn_domain.label": "腾讯云 CDN 加速域名",
|
"workflow_node.deploy.form.tencentcloud_cdn_domain.label": "腾讯云 CDN 加速域名",
|
||||||
"workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder": "请输入腾讯云 CDN 加速域名(支持泛域名)",
|
"workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder": "请输入腾讯云 CDN 加速域名(支持泛域名)",
|
||||||
"workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a>",
|
"workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_clb_endpoint.label": "腾讯云 CLB 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_clb_endpoint.label": "腾讯云 CLB 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_clb_endpoint.placeholder": "请输入腾讯云 CLB 接口端点(例如:clb.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_clb_endpoint.placeholder": "请输入腾讯云 CLB 接口端点(例如:clb.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_clb_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/214/30669\" target=\"_blank\">https://cloud.tencent.com/document/product/214/30669</a>",
|
"workflow_node.deploy.form.tencentcloud_clb_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/214/30669\" target=\"_blank\">https://cloud.tencent.com/document/product/214/30669</a><br><br>国际站用户请填写 <em>clb.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_clb_region.label": "腾讯云 CLB 产品地域",
|
"workflow_node.deploy.form.tencentcloud_clb_region.label": "腾讯云 CLB 产品地域",
|
||||||
"workflow_node.deploy.form.tencentcloud_clb_region.placeholder": "请输入腾讯云 CLB 服务地域(例如:ap-guangzhou)",
|
"workflow_node.deploy.form.tencentcloud_clb_region.placeholder": "请输入腾讯云 CLB 服务地域(例如:ap-guangzhou)",
|
||||||
"workflow_node.deploy.form.tencentcloud_clb_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/214/33415\" target=\"_blank\">https://cloud.tencent.com/document/product/214/33415</a>",
|
"workflow_node.deploy.form.tencentcloud_clb_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/214/33415\" target=\"_blank\">https://cloud.tencent.com/document/product/214/33415</a>",
|
||||||
@ -700,28 +709,30 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "这是什么?请参阅 see <a href=\"https://console.cloud.tencent.com/cos\" target=\"_blank\">https://console.cloud.tencent.com/cos</a>",
|
"workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "这是什么?请参阅 see <a href=\"https://console.cloud.tencent.com/cos\" target=\"_blank\">https://console.cloud.tencent.com/cos</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_endpoint.label": "腾讯云云直播接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_css_endpoint.label": "腾讯云云直播接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_endpoint.placeholder": "请输入腾讯云云直播接口端点(例如:live.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_css_endpoint.placeholder": "请输入腾讯云云直播接口端点(例如:live.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/267/20458\" target=\"_blank\">https://cloud.tencent.com/document/product/267/20458</a>",
|
"workflow_node.deploy.form.tencentcloud_css_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/267/20458\" target=\"_blank\">https://cloud.tencent.com/document/product/267/20458</a><br><br>国际站用户请填写 <em>live.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_domain.label": "腾讯云云直播播放域名",
|
"workflow_node.deploy.form.tencentcloud_css_domain.label": "腾讯云云直播播放域名",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_domain.placeholder": "请输入腾讯云云直播播放域名",
|
"workflow_node.deploy.form.tencentcloud_css_domain.placeholder": "请输入腾讯云云直播播放域名",
|
||||||
"workflow_node.deploy.form.tencentcloud_css_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/live\" target=\"_blank\">https://console.cloud.tencent.com/live</a>",
|
"workflow_node.deploy.form.tencentcloud_css_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/live\" target=\"_blank\">https://console.cloud.tencent.com/live</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_ecdn_endpoint.label": "腾讯云 ECDN 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_ecdn_endpoint.label": "腾讯云 ECDN 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ecdn_endpoint.placeholder": "请输入腾讯云 ECDN 接口端点(例如:cdn.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_ecdn_endpoint.placeholder": "请输入腾讯云 ECDN 接口端点(例如:cdn.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ecdn_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/214/30669\" target=\"_blank\">https://cloud.tencent.com/document/product/214/30669</a>",
|
"workflow_node.deploy.form.tencentcloud_ecdn_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/214/30669\" target=\"_blank\">https://cloud.tencent.com/document/product/214/30669</a><br><br>国际站用户请填写 <em>cdn.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_ecdn_domain.label": "腾讯云 ECDN 加速域名",
|
"workflow_node.deploy.form.tencentcloud_ecdn_domain.label": "腾讯云 ECDN 加速域名",
|
||||||
"workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder": "请输入腾讯云 ECDN 加速域名(支持泛域名)",
|
"workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder": "请输入腾讯云 ECDN 加速域名(支持泛域名)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ecdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a>",
|
"workflow_node.deploy.form.tencentcloud_ecdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_endpoint.label": "腾讯云 EdgeOne 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_eo_endpoint.label": "腾讯云 EdgeOne 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_endpoint.placeholder": "请输入腾讯云 EdgeOne 接口端点(例如:teo.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_eo_endpoint.placeholder": "请输入腾讯云 EdgeOne 接口端点(例如:teo.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/1552/80723\" target=\"_blank\">https://cloud.tencent.com/document/product/1552/80723</a>",
|
"workflow_node.deploy.form.tencentcloud_eo_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/1552/80723\" target=\"_blank\">https://cloud.tencent.com/document/product/1552/80723</a><br><br>国际站用户请填写 <em>teo.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_zone_id.label": "腾讯云 EdgeOne 站点 ID",
|
"workflow_node.deploy.form.tencentcloud_eo_zone_id.label": "腾讯云 EdgeOne 站点 ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder": "请输入腾讯云 EdgeOne 站点 ID",
|
"workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder": "请输入腾讯云 EdgeOne 站点 ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_zone_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/edgeone\" target=\"_blank\">https://console.cloud.tencent.com/edgeone</a>",
|
"workflow_node.deploy.form.tencentcloud_eo_zone_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/edgeone\" target=\"_blank\">https://console.cloud.tencent.com/edgeone</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_domain.label": "腾讯云 EdgeOne 加速域名",
|
"workflow_node.deploy.form.tencentcloud_eo_domains.label": "腾讯云 EdgeOne 加速域名",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_domain.placeholder": "请输入腾讯云 EdgeOne 加速域名(支持泛域名)",
|
"workflow_node.deploy.form.tencentcloud_eo_domains.placeholder": "请输入腾讯云 EdgeOne 加速域名(支持泛域名;多个值请用半角分号隔开)",
|
||||||
"workflow_node.deploy.form.tencentcloud_eo_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/edgeone\" target=\"_blank\">https://console.cloud.tencent.com/edgeone</a>",
|
"workflow_node.deploy.form.tencentcloud_eo_domains.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/edgeone\" target=\"_blank\">https://console.cloud.tencent.com/edgeone</a>",
|
||||||
|
"workflow_node.deploy.form.tencentcloud_eo_domains.multiple_input_modal.title": "修改腾讯云 EdgeOne 加速域名",
|
||||||
|
"workflow_node.deploy.form.tencentcloud_eo_domains.multiple_input_modal.placeholder": "请输入腾讯云 EdgeOne 加速域名(支持泛域名)",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.label": "腾讯云 GAAP 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.label": "腾讯云 GAAP 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.placeholder": "请输入腾讯云 GAAP 接口端点(例如:gaap.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.placeholder": "请输入腾讯云 GAAP 接口端点(例如:gaap.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/608/36934\" target=\"_blank\">https://cloud.tencent.com/document/product/608/36934</a>",
|
"workflow_node.deploy.form.tencentcloud_gaap_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/608/36934\" target=\"_blank\">https://cloud.tencent.com/document/product/608/36934</a><br><br>国际站用户请填写 <em>gaap.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_resource_type.label": "证书部署方式",
|
"workflow_node.deploy.form.tencentcloud_gaap_resource_type.label": "证书部署方式",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_resource_type.placeholder": "请选择证书部署方式",
|
"workflow_node.deploy.form.tencentcloud_gaap_resource_type.placeholder": "请选择证书部署方式",
|
||||||
"workflow_node.deploy.form.tencentcloud_gaap_resource_type.option.listener.label": "替换指定监听器的证书",
|
"workflow_node.deploy.form.tencentcloud_gaap_resource_type.option.listener.label": "替换指定监听器的证书",
|
||||||
@ -733,7 +744,7 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_gaap_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/gaap\" target=\"_blank\">https://console.cloud.tencent.com/gaap</a>",
|
"workflow_node.deploy.form.tencentcloud_gaap_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/gaap\" target=\"_blank\">https://console.cloud.tencent.com/gaap</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_scf_endpoint.label": "腾讯云 SCF 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_scf_endpoint.label": "腾讯云 SCF 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_scf_endpoint.placeholder": "请输入腾讯云 SCF 接口端点(例如:scf.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_scf_endpoint.placeholder": "请输入腾讯云 SCF 接口端点(例如:scf.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_scf_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/583/17237\" target=\"_blank\">https://cloud.tencent.com/document/product/583/17237</a>",
|
"workflow_node.deploy.form.tencentcloud_scf_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/583/17237\" target=\"_blank\">https://cloud.tencent.com/document/product/583/17237</a><br><br>国际站用户请填写 <em>scf.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_scf_region.label": "腾讯云 SCF 产品地域",
|
"workflow_node.deploy.form.tencentcloud_scf_region.label": "腾讯云 SCF 产品地域",
|
||||||
"workflow_node.deploy.form.tencentcloud_scf_region.placeholder": "输入腾讯云 SCF 产品地域(例如:ap-guangzhou)",
|
"workflow_node.deploy.form.tencentcloud_scf_region.placeholder": "输入腾讯云 SCF 产品地域(例如:ap-guangzhou)",
|
||||||
"workflow_node.deploy.form.tencentcloud_scf_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/583/17299\" target=\"_blank\">https://cloud.tencent.com/document/product/583/17299</a>",
|
"workflow_node.deploy.form.tencentcloud_scf_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/583/17299\" target=\"_blank\">https://cloud.tencent.com/document/product/583/17299</a>",
|
||||||
@ -742,11 +753,11 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_scf_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/scf\" target=\"_blank\">https://console.cloud.tencent.com/scf</a>",
|
"workflow_node.deploy.form.tencentcloud_scf_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/scf\" target=\"_blank\">https://console.cloud.tencent.com/scf</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_endpoint.label": "腾讯云 SSL 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_ssl_endpoint.label": "腾讯云 SSL 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_endpoint.placeholder": "请输入腾讯云 SSL 接口端点(例如:ssl.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_ssl_endpoint.placeholder": "请输入腾讯云 SSL 接口端点(例如:ssl.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a><br><br>国际站用户请填写 <em>ssl.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_deploy.guide": "小贴士:将通过腾讯云 OpenAPI <em>DeployCertificateInstance</em> 接口创建异步部署任务。此部署目标若执行成功仅代表已创建部署任务,实际部署结果需要你自行前往腾讯云控制台查询。",
|
"workflow_node.deploy.form.tencentcloud_ssl_deploy.guide": "小贴士:将通过腾讯云 OpenAPI <em>DeployCertificateInstance</em> 接口创建异步部署任务。此部署目标若执行成功仅代表已创建部署任务,实际部署结果需要你自行前往腾讯云控制台查询。",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.label": "腾讯云 SSL 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.label": "腾讯云 SSL 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.placeholder": "请输入腾讯云 SSL 接口端点(例如:ssl.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.placeholder": "请输入腾讯云 SSL 接口端点(例如:ssl.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_deploy_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a><br><br>国际站用户请填写 <em>ssl.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_deploy_region.label": "腾讯云云产品地域",
|
"workflow_node.deploy.form.tencentcloud_ssl_deploy_region.label": "腾讯云云产品地域",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_deploy_region.placeholder": "请输入腾讯云云产品地域(例如:ap-guangzhou)",
|
"workflow_node.deploy.form.tencentcloud_ssl_deploy_region.placeholder": "请输入腾讯云云产品地域(例如:ap-guangzhou)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_deploy_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_deploy_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a>",
|
||||||
@ -762,24 +773,25 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_ssl_update.guide": "小贴士:将通过腾讯云 OpenAPI <em>UpdateCertificateInstance</em> 或 <em>UploadUpdateCertificateInstance</em> 接口创建异步部署任务。此部署目标若执行成功仅代表已创建部署任务,实际部署结果需要你自行前往腾讯云控制台查询。",
|
"workflow_node.deploy.form.tencentcloud_ssl_update.guide": "小贴士:将通过腾讯云 OpenAPI <em>UpdateCertificateInstance</em> 或 <em>UploadUpdateCertificateInstance</em> 接口创建异步部署任务。此部署目标若执行成功仅代表已创建部署任务,实际部署结果需要你自行前往腾讯云控制台查询。",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.label": "腾讯云 SSL 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.label": "腾讯云 SSL 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.placeholder": "请输入腾讯云 SSL 接口端点(例如:ssl.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.placeholder": "请输入腾讯云 SSL 接口端点(例如:ssl.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/41659\" target=\"_blank\">https://cloud.tencent.com/document/product/400/41659</a><br><br>国际站用户请填写 <em>ssl.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.label": "腾讯云原证书 ID",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.label": "腾讯云原证书 ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.placeholder": "请输入腾讯云原证书 ID",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.placeholder": "请输入腾讯云原证书 ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/certoverview\" target=\"_blank\">https://console.cloud.tencent.com/certoverview</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_certificate_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/certoverview\" target=\"_blank\">https://console.cloud.tencent.com/certoverview</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.label": "腾讯云云产品资源类型",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.label": "腾讯云云产品资源类型",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.placeholder": "请输入腾讯云云产品资源类型(多个值请用半角分号隔开)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.placeholder": "请输入腾讯云云产品资源类型(多个值请用半角分号隔开)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/91649\" target=\"_blank\">https://cloud.tencent.com/document/product/400/91649</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/91649\" target=\"_blank\">https://cloud.tencent.com/document/product/400/91649</a> 或 <a href=\"https://cloud.tencent.com/document/product/400/119791\" target=\"_blank\">https://cloud.tencent.com/document/product/400/119791</a><br><br>注意,这两个接口的所支持的云产品资源类型有所不同,具体请查看腾讯云官方文档。",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.title": "修改腾讯云云产品资源类型",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.title": "修改腾讯云云产品资源类型",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.placeholder": "请输入腾讯云云产品资源类型",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_types.multiple_input_modal.placeholder": "请输入腾讯云云产品资源类型",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.label": "腾讯云云产品部署地域(可选)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.label": "腾讯云云产品部署地域(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder": "请输入腾讯云云产品部署地域(多个值请用半角分号隔开)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.placeholder": "请输入腾讯云云产品部署地域(多个值请用半角分号隔开)",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/91649\" target=\"_blank\">https://cloud.tencent.com/document/product/400/91649</a>",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/400/91649\" target=\"_blank\">https://cloud.tencent.com/document/product/400/91649</a> 或 <a href=\"https://cloud.tencent.com/document/product/400/119791\" target=\"_blank\">https://cloud.tencent.com/document/product/400/119791</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.title": "修改腾讯云云产品资源类型",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.title": "修改腾讯云云产品部署地域",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.placeholder": "请输入腾讯云云产品资源类型",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_resource_regions.multiple_input_modal.placeholder": "请输入腾讯云云产品部署地域",
|
||||||
"workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.label": "是否更新原证书(即证书 ID 保持不变)",
|
"workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.label": "是否更新原证书(即证书 ID 保持不变)",
|
||||||
|
"workflow_node.deploy.form.tencentcloud_ssl_update_is_replaced.tooltip": "不勾选时,将调用腾讯云 OpenAPI <em>UpdateCertificateInstance</em> 接口;否则,将调用腾讯云 OpenAPI <em>UploadUpdateCertificateInstance</em> 接口。",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_endpoint.label": "腾讯云云点播接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_vod_endpoint.label": "腾讯云云点播接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_endpoint.placeholder": "请输入腾讯云云点播接口端点(例如:vod.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_vod_endpoint.placeholder": "请输入腾讯云云点播接口端点(例如:vod.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/266/31755\" target=\"_blank\">https://cloud.tencent.com/document/product/266/31755</a>",
|
"workflow_node.deploy.form.tencentcloud_vod_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/266/31755\" target=\"_blank\">https://cloud.tencent.com/document/product/266/31755</a><br><br>国际站用户请填写 <em>vod.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_sub_app_id.label": "腾讯云云点播应用 ID",
|
"workflow_node.deploy.form.tencentcloud_vod_sub_app_id.label": "腾讯云云点播应用 ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_sub_app_id.placeholder": "请输入腾讯云云点播应用 ID",
|
"workflow_node.deploy.form.tencentcloud_vod_sub_app_id.placeholder": "请输入腾讯云云点播应用 ID",
|
||||||
"workflow_node.deploy.form.tencentcloud_vod_sub_app_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>",
|
"workflow_node.deploy.form.tencentcloud_vod_sub_app_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>",
|
||||||
@ -788,7 +800,7 @@
|
|||||||
"workflow_node.deploy.form.tencentcloud_vod_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>",
|
"workflow_node.deploy.form.tencentcloud_vod_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>",
|
||||||
"workflow_node.deploy.form.tencentcloud_waf_endpoint.label": "腾讯云 WAF 接口端点(可选)",
|
"workflow_node.deploy.form.tencentcloud_waf_endpoint.label": "腾讯云 WAF 接口端点(可选)",
|
||||||
"workflow_node.deploy.form.tencentcloud_waf_endpoint.placeholder": "请输入腾讯云 WAF 接口端点(例如:waf.tencentcloudapi.com)",
|
"workflow_node.deploy.form.tencentcloud_waf_endpoint.placeholder": "请输入腾讯云 WAF 接口端点(例如:waf.tencentcloudapi.com)",
|
||||||
"workflow_node.deploy.form.tencentcloud_waf_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/627/53611\" target=\"_blank\">https://cloud.tencent.com/document/product/627/53611</a>",
|
"workflow_node.deploy.form.tencentcloud_waf_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/627/53611\" target=\"_blank\">https://cloud.tencent.com/document/product/627/53611</a><br><br>国际站用户请填写 <em>waf.intl.tencentcloudapi.com</em>。",
|
||||||
"workflow_node.deploy.form.tencentcloud_waf_region.label": "腾讯云 WAF 产品地域",
|
"workflow_node.deploy.form.tencentcloud_waf_region.label": "腾讯云 WAF 产品地域",
|
||||||
"workflow_node.deploy.form.tencentcloud_waf_region.placeholder": "请输入腾讯云 WAF 产品地域(例如:ap-guangzhou)",
|
"workflow_node.deploy.form.tencentcloud_waf_region.placeholder": "请输入腾讯云 WAF 产品地域(例如:ap-guangzhou)",
|
||||||
"workflow_node.deploy.form.tencentcloud_waf_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/627/47525\" target=\"_blank\">https://cloud.tencent.com/document/product/627/47525</a>",
|
"workflow_node.deploy.form.tencentcloud_waf_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/627/47525\" target=\"_blank\">https://cloud.tencent.com/document/product/627/47525</a>",
|
||||||
@ -828,8 +840,8 @@
|
|||||||
"workflow_node.deploy.form.upyun_cdn_domain.placeholder": "请输入又拍云 CDN 加速域名(支持泛域名)",
|
"workflow_node.deploy.form.upyun_cdn_domain.placeholder": "请输入又拍云 CDN 加速域名(支持泛域名)",
|
||||||
"workflow_node.deploy.form.upyun_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.upyun.com/services/cdn/\" target=\"_blank\">https://console.upyun.com/services/cdn/</a>",
|
"workflow_node.deploy.form.upyun_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.upyun.com/services/cdn/\" target=\"_blank\">https://console.upyun.com/services/cdn/</a>",
|
||||||
"workflow_node.deploy.form.upyun_file.guide": "小贴士:由于又拍云未公开相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。",
|
"workflow_node.deploy.form.upyun_file.guide": "小贴士:由于又拍云未公开相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。",
|
||||||
"workflow_node.deploy.form.upyun_file_domain.label": "又拍云云存储加速域名",
|
"workflow_node.deploy.form.upyun_file_domain.label": "又拍云云存储自定义域名",
|
||||||
"workflow_node.deploy.form.upyun_file_domain.placeholder": "请输入又拍云云存储加速域名",
|
"workflow_node.deploy.form.upyun_file_domain.placeholder": "请输入又拍云云存储自定义域名",
|
||||||
"workflow_node.deploy.form.upyun_file_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.upyun.com/services/file/\" target=\"_blank\">https://console.upyun.com/services/file/</a>",
|
"workflow_node.deploy.form.upyun_file_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.upyun.com/services/file/\" target=\"_blank\">https://console.upyun.com/services/file/</a>",
|
||||||
"workflow_node.deploy.form.volcengine_alb_resource_type.label": "证书部署方式",
|
"workflow_node.deploy.form.volcengine_alb_resource_type.label": "证书部署方式",
|
||||||
"workflow_node.deploy.form.volcengine_alb_resource_type.placeholder": "请选择证书部署方式",
|
"workflow_node.deploy.form.volcengine_alb_resource_type.placeholder": "请选择证书部署方式",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user