Replace the OSS deployment api

This commit is contained in:
yoan 2024-10-13 11:15:35 +08:00
parent 2ed94bf509
commit 5b30fc8aba
8 changed files with 215 additions and 192 deletions

2
go.mod
View File

@ -5,11 +5,11 @@ go 1.22.0
toolchain go1.23.2 toolchain go1.23.2
require ( require (
github.com/alibabacloud-go/cas-20200407/v2 v2.3.0
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0 github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9 github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9
github.com/alibabacloud-go/tea v1.2.2 github.com/alibabacloud-go/tea v1.2.2
github.com/alibabacloud-go/tea-utils/v2 v2.0.6 github.com/alibabacloud-go/tea-utils/v2 v2.0.6
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/go-acme/lego/v4 v4.19.2 github.com/go-acme/lego/v4 v4.19.2
github.com/gojek/heimdall/v7 v7.0.3 github.com/gojek/heimdall/v7 v7.0.3
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61

5
go.sum
View File

@ -31,12 +31,9 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI= github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/cas-20200407/v2 v2.3.0 h1:nOrp0n2nFZiYN0wIG7S26YVVaMMzOBkX9GJqUvYnGeE=
github.com/alibabacloud-go/cas-20200407/v2 v2.3.0/go.mod h1:yzkgdLANANu/v56k0ptslGl++JJL4Op1V09HTavfoCo=
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0 h1:yTKngw4rBR3hdpoo/uCyBffYXfPfjNjlaDL8nTYUIds= github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0 h1:yTKngw4rBR3hdpoo/uCyBffYXfPfjNjlaDL8nTYUIds=
github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0/go.mod h1:HxQrwVKBx3/6bIwmdDcpqBpSQt2tpi/j4LfEhl+QFPk= github.com/alibabacloud-go/cdn-20180510/v5 v5.0.0/go.mod h1:HxQrwVKBx3/6bIwmdDcpqBpSQt2tpi/j4LfEhl+QFPk=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9 h1:fxMCrZatZfXq5nLcgkmWBXmU3FLC1OR+m/SqVtMqflk= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9 h1:fxMCrZatZfXq5nLcgkmWBXmU3FLC1OR+m/SqVtMqflk=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
@ -82,6 +79,8 @@ github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzY
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15 h1:r2uwBUQhLhcPzaWz9tRJqc8MjYwHb+oF2+Q6467BF14= github.com/aliyun/alibaba-cloud-sdk-go v1.63.15 h1:r2uwBUQhLhcPzaWz9tRJqc8MjYwHb+oF2+Q6467BF14=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/alibaba-cloud-sdk-go v1.63.15/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28= github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=

View File

@ -1,23 +1,16 @@
package deployer package deployer
import ( import (
"certimate/internal/applicant"
"certimate/internal/domain" "certimate/internal/domain"
"certimate/internal/utils/rand"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"strings"
cas20200407 "github.com/alibabacloud-go/cas-20200407/v2/client" "github.com/aliyun/aliyun-oss-go-sdk/oss"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
) )
type aliyun struct { type aliyun struct {
client *cas20200407.Client client *oss.Client
option *DeployerOption option *DeployerOption
infos []string infos []string
} }
@ -47,160 +40,31 @@ func (a *aliyun) GetInfo() []string {
} }
func (a *aliyun) Deploy(ctx context.Context) error { func (a *aliyun) Deploy(ctx context.Context) error {
err := a.client.PutBucketCnameWithCertificate(getDeployString(a.option.DeployConfig, "bucket"), oss.PutBucketCname{
// 查询有没有对应的资源 Cname: getDeployString(a.option.DeployConfig, "domain"),
resource, err := a.resource() CertificateConfiguration: &oss.CertificateConfiguration{
if err != nil { Certificate: a.option.Certificate.Certificate,
return err PrivateKey: a.option.Certificate.PrivateKey,
} Force: true,
},
a.infos = append(a.infos, toStr("查询对应的资源", resource))
// 查询有没有对应的联系人
contacts, err := a.contacts()
if err != nil {
return err
}
a.infos = append(a.infos, toStr("查询联系人", contacts))
// 上传证书
certId, err := a.uploadCert(&a.option.Certificate)
if err != nil {
return err
}
a.infos = append(a.infos, toStr("上传证书", certId))
// 部署证书
jobId, err := a.deploy(resource, certId, contacts)
if err != nil {
return err
}
a.infos = append(a.infos, toStr("创建部署证书任务", jobId))
// 等待部署成功
err = a.updateDeployStatus(*jobId)
if err != nil {
return err
}
// 部署成功后删除旧的证书
a.deleteCert(resource)
return nil
}
func (a *aliyun) updateDeployStatus(jobId int64) error {
// 查询部署状态
req := &cas20200407.UpdateDeploymentJobStatusRequest{
JobId: tea.Int64(jobId),
}
resp, err := a.client.UpdateDeploymentJobStatus(req)
if err != nil {
return err
}
a.infos = append(a.infos, toStr("查询对应的资源", resp))
return nil
}
func (a *aliyun) deleteCert(resource *cas20200407.ListCloudResourcesResponseBodyData) error {
// 查询有没有对应的资源
if resource.CertId == nil {
return nil
}
// 删除证书
_, err := a.client.DeleteUserCertificate(&cas20200407.DeleteUserCertificateRequest{
CertId: resource.CertId,
}) })
if err != nil { if err != nil {
return err return fmt.Errorf("deploy aliyun oss error: %w", err)
} }
return nil return nil
} }
func (a *aliyun) contacts() ([]*cas20200407.ListContactResponseBodyContactList, error) { func (a *aliyun) createClient(accessKeyId, accessKeySecret string) (*oss.Client, error) {
listContactRequest := &cas20200407.ListContactRequest{} client, err := oss.New(
runtime := &util.RuntimeOptions{} getDeployString(a.option.DeployConfig, "endpoint"),
accessKeyId,
resp, err := a.client.ListContactWithOptions(listContactRequest, runtime) accessKeySecret,
)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("create aliyun client error: %w", err)
} }
if resp.Body.TotalCount == nil { return client, nil
return nil, errors.New("no contact found")
}
return resp.Body.ContactList, nil
}
func (a *aliyun) deploy(resource *cas20200407.ListCloudResourcesResponseBodyData, certId int64, contacts []*cas20200407.ListContactResponseBodyContactList) (*int64, error) {
contactIds := make([]string, 0, len(contacts))
for _, contact := range contacts {
contactIds = append(contactIds, fmt.Sprintf("%d", *contact.ContactId))
}
// 部署证书
createCloudResourceRequest := &cas20200407.CreateDeploymentJobRequest{
CertIds: tea.String(fmt.Sprintf("%d", certId)),
Name: tea.String(a.option.Domain + rand.RandStr(6)),
JobType: tea.String("user"),
ResourceIds: tea.String(fmt.Sprintf("%d", *resource.Id)),
ContactIds: tea.String(strings.Join(contactIds, ",")),
}
runtime := &util.RuntimeOptions{}
resp, err := a.client.CreateDeploymentJobWithOptions(createCloudResourceRequest, runtime)
if err != nil {
return nil, err
}
return resp.Body.JobId, nil
}
func (a *aliyun) uploadCert(cert *applicant.Certificate) (int64, error) {
uploadUserCertificateRequest := &cas20200407.UploadUserCertificateRequest{
Cert: &cert.Certificate,
Key: &cert.PrivateKey,
Name: tea.String(a.option.Domain + rand.RandStr(6)),
}
runtime := &util.RuntimeOptions{}
resp, err := a.client.UploadUserCertificateWithOptions(uploadUserCertificateRequest, runtime)
if err != nil {
return 0, err
}
return *resp.Body.CertId, nil
}
func (a *aliyun) createClient(accessKeyId, accessKeySecret string) (_result *cas20200407.Client, _err error) {
config := &openapi.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
}
config.Endpoint = tea.String("cas.aliyuncs.com")
_result = &cas20200407.Client{}
_result, _err = cas20200407.NewClient(config)
return _result, _err
}
func (a *aliyun) resource() (*cas20200407.ListCloudResourcesResponseBodyData, error) {
listCloudResourcesRequest := &cas20200407.ListCloudResourcesRequest{
CloudProduct: tea.String(a.option.Product),
Keyword: tea.String(getDeployString(a.option.DeployConfig, "domain")),
}
resp, err := a.client.ListCloudResources(listCloudResourcesRequest)
if err != nil {
return nil, err
}
if *resp.Body.Total == 0 {
return nil, errors.New("no resource found")
}
return resp.Body.Data[0], nil
} }

File diff suppressed because one or more lines are too long

2
ui/dist/index.html vendored
View File

@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title> <title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-DbwFzZm1.js"></script> <script type="module" crossorigin src="/assets/index-DvxNVikK.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CWUb5Xuf.css"> <link rel="stylesheet" crossorigin href="/assets/index-CWUb5Xuf.css">
</head> </head>
<body class="bg-background"> <body class="bg-background">

View File

@ -479,7 +479,7 @@ const DeployEdit = ({ type }: DeployEditProps) => {
case "dcdn": case "dcdn":
return <DeployCDN />; return <DeployCDN />;
case "oss": case "oss":
return <DeployCDN />; return <DeployOSS />;
case "webhook": case "webhook":
return <DeployWebhook />; return <DeployWebhook />;
default: default:
@ -659,6 +659,158 @@ const DeployCDN = () => {
); );
}; };
const DeployOSS = () => {
const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
const { t } = useTranslation();
useEffect(() => {
setError({});
}, []);
useEffect(() => {
const resp = domainSchema.safeParse(data.config?.domain);
if (!resp.success) {
setError({
...error,
domain: JSON.parse(resp.error.message)[0].message,
});
} else {
setError({
...error,
domain: "",
});
}
}, [data]);
useEffect(() => {
const bucketResp = bucketSchema.safeParse(data.config?.domain);
if (!bucketResp.success) {
setError({
...error,
bucket: JSON.parse(bucketResp.error.message)[0].message,
});
} else {
setError({
...error,
bucket: "",
});
}
}, []);
useEffect(() => {
if (!data.id) {
setDeploy({
...data,
config: {
endpoint: "oss-cn-hangzhou.aliyuncs.com",
bucket: "",
domain: "",
},
});
}
}, []);
const domainSchema = z
.string()
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
message: t("domain.not.empty.verify.message"),
});
const bucketSchema = z.string().min(1, {
message: t("deployment.access.oss.bucket.not.empty"),
});
return (
<div className="flex flex-col space-y-2">
<div>
<Label>{t("deployment.access.oss.endpoint")}</Label>
<Input
className="w-full mt-1"
value={data?.config?.endpoint}
onChange={(e) => {
const temp = e.target.value;
const newData = produce(data, (draft) => {
if (!draft.config) {
draft.config = {};
}
draft.config.endpoint = temp;
});
setDeploy(newData);
}}
/>
<div className="text-red-600 text-sm mt-1">{error?.endpoint}</div>
<Label>{t("deployment.access.oss.bucket")}</Label>
<Input
placeholder={t("deployment.access.oss.bucket.not.empty")}
className="w-full mt-1"
value={data?.config?.bucket}
onChange={(e) => {
const temp = e.target.value;
const resp = bucketSchema.safeParse(temp);
if (!resp.success) {
setError({
...error,
bucket: JSON.parse(resp.error.message)[0].message,
});
} else {
setError({
...error,
bucket: "",
});
}
const newData = produce(data, (draft) => {
if (!draft.config) {
draft.config = {};
}
draft.config.bucket = temp;
});
setDeploy(newData);
}}
/>
<div className="text-red-600 text-sm mt-1">{error?.bucket}</div>
<Label>{t("deployment.access.cdn.deploy.to.domain")}</Label>
<Input
placeholder={t("deployment.access.cdn.deploy.to.domain")}
className="w-full mt-1"
value={data?.config?.domain}
onChange={(e) => {
const temp = e.target.value;
const resp = domainSchema.safeParse(temp);
if (!resp.success) {
setError({
...error,
domain: JSON.parse(resp.error.message)[0].message,
});
} else {
setError({
...error,
domain: "",
});
}
const newData = produce(data, (draft) => {
if (!draft.config) {
draft.config = {};
}
draft.config.domain = temp;
});
setDeploy(newData);
}}
/>
<div className="text-red-600 text-sm mt-1">{error?.domain}</div>
</div>
</div>
);
};
const DeployWebhook = () => { const DeployWebhook = () => {
const { deploy: data, setDeploy } = useDeployEditContext(); const { deploy: data, setDeploy } = useDeployEditContext();

View File

@ -239,5 +239,9 @@
"deployment.not.added": "Deployment not added yet", "deployment.not.added": "Deployment not added yet",
"deployment.access.type": "Access Type", "deployment.access.type": "Access Type",
"deployment.access.config": "Access Configuration", "deployment.access.config": "Access Configuration",
"deployment.access.cdn.deploy.to.domain": "Deploy to domain" "deployment.access.cdn.deploy.to.domain": "Deploy to domain",
"deployment.access.oss.bucket": "Bucket",
"deployment.access.oss.bucket.not.empty": "Please enter Bucket",
"deployment.access.oss.endpoint": "Endpoint"
} }

View File

@ -239,5 +239,9 @@
"deployment.not.added": "暂无部署配置,请添加后开始部署证书吧", "deployment.not.added": "暂无部署配置,请添加后开始部署证书吧",
"deployment.access.type": "授权类型", "deployment.access.type": "授权类型",
"deployment.access.config": "授权配置", "deployment.access.config": "授权配置",
"deployment.access.cdn.deploy.to.domain": "部署到域名" "deployment.access.cdn.deploy.to.domain": "部署到域名",
"deployment.access.oss.bucket": "Bucket",
"deployment.access.oss.bucket.not.empty": "请输入 Bucket",
"deployment.access.oss.endpoint": "Endpoint"
} }