mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 06:16:37 +08:00
943818f7d4
将 OpenFlare 与平台业务的数据访问从 model 与 apps 直连迁入 repository, model 仅保留实体与无 IO 规则;补充 code-check 架构守卫与开发规范。
102 lines
3.0 KiB
Go
102 lines
3.0 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package tls
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/repository"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/infra/task"
|
|
)
|
|
|
|
const (
|
|
// SSLSingleRenewTask renews a single ACME TLS certificate.
|
|
SSLSingleRenewTask = "openflare:ssl_single_renew"
|
|
// TaskTypeSSLSingleRenew is the admin task type for single SSL renewal.
|
|
TaskTypeSSLSingleRenew = "of_ssl_single_renew"
|
|
)
|
|
|
|
// SSLSingleRenewMeta describes the single SSL renewal task.
|
|
var SSLSingleRenewMeta = task.TaskMeta{
|
|
Type: TaskTypeSSLSingleRenew,
|
|
AsynqTask: SSLSingleRenewTask,
|
|
Name: "OpenFlare 单证书 SSL 续期",
|
|
Description: "对单个指定的 ACME 证书执行续期",
|
|
SupportsTime: false,
|
|
MaxRetry: task.DefaultMaxRetry,
|
|
Queue: task.QueueDefault,
|
|
Retryable: true,
|
|
Params: []task.TaskParam{
|
|
{
|
|
Name: "id",
|
|
Label: "证书 ID",
|
|
Type: "number",
|
|
Required: true,
|
|
Placeholder: "请输入证书 ID",
|
|
Description: "待续期的 TLS 证书 ID",
|
|
},
|
|
},
|
|
}
|
|
|
|
// SSLSingleRenewPayload is the payload structure for SSLSingleRenewTask.
|
|
type SSLSingleRenewPayload struct {
|
|
ID uint `json:"id"`
|
|
}
|
|
|
|
// SSLSingleRenewHandler renews a specific TLS certificate.
|
|
type SSLSingleRenewHandler struct{}
|
|
|
|
// ValidatePayload validates and normalizes the task payload.
|
|
func (h *SSLSingleRenewHandler) ValidatePayload(payload []byte) ([]byte, error) {
|
|
if len(payload) == 0 {
|
|
return nil, errors.New("任务参数不能为空")
|
|
}
|
|
|
|
var req SSLSingleRenewPayload
|
|
if err := json.Unmarshal(payload, &req); err != nil {
|
|
return nil, fmt.Errorf("无效的 JSON 格式: %w", err)
|
|
}
|
|
|
|
if req.ID == 0 {
|
|
return nil, errors.New("证书 ID 不能为空或零")
|
|
}
|
|
|
|
return json.Marshal(req)
|
|
}
|
|
|
|
// Execute runs the certificate renewal for the specified ID.
|
|
func (h *SSLSingleRenewHandler) Execute(ctx context.Context, payload []byte) (*task.TaskResult, error) {
|
|
var req SSLSingleRenewPayload
|
|
if err := json.Unmarshal(payload, &req); err != nil {
|
|
return nil, fmt.Errorf("解析任务参数: %w", err)
|
|
}
|
|
|
|
task.AppendLog(ctx, "开始续期证书,ID: %d", req.ID)
|
|
|
|
cert, err := repository.GetTLSCertificateByID(ctx, req.ID)
|
|
if err != nil {
|
|
task.AppendLog(ctx, "获取证书记录失败 ID=%d: %v", req.ID, err)
|
|
return nil, fmt.Errorf("获取证书记录失败: %w", err)
|
|
}
|
|
|
|
if cert.Provider != tlsProviderACME {
|
|
task.AppendLog(ctx, "证书 %s (ID=%d) 不是 ACME 托管证书,无法自动续期 (Provider: %s)", cert.PrimaryDomain, req.ID, cert.Provider)
|
|
return nil, fmt.Errorf("证书 %s 不是 ACME 托管证书,无法自动续期", cert.PrimaryDomain)
|
|
}
|
|
|
|
task.AppendLog(ctx, "准备为域名 [%s] 申请/续期证书", cert.PrimaryDomain)
|
|
if err := obtainTLSCertificate(ctx, cert); err != nil {
|
|
task.AppendLog(ctx, "申请证书失败: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
msg := fmt.Sprintf("证书 %s 续签成功", cert.PrimaryDomain)
|
|
task.AppendLog(ctx, "%s", msg)
|
|
return &task.TaskResult{Message: msg}, nil
|
|
}
|