mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-21 13:18:58 +08:00
【调整】暗色主题样式
This commit is contained in:
102
frontend/allinssl/backend/internal/cert/deploy/plugin/deploy.go
Normal file
102
frontend/allinssl/backend/internal/cert/deploy/plugin/deploy.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/internal/access"
|
||||
"ALLinSSL/backend/public"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type CertDeployPlugin struct {
|
||||
Config map[string]any
|
||||
Key string
|
||||
Cert string
|
||||
}
|
||||
|
||||
func Deploy(cfg map[string]any, logger *public.Logger) error {
|
||||
cert, ok := cfg["certificate"].(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("证书不存在")
|
||||
}
|
||||
action, ok := cfg["action"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("操作类型错误:action")
|
||||
}
|
||||
var providerID string
|
||||
switch v := cfg["provider_id"].(type) {
|
||||
case float64:
|
||||
providerID = strconv.Itoa(int(v))
|
||||
case string:
|
||||
providerID = v
|
||||
default:
|
||||
return fmt.Errorf("参数错误:provider_id")
|
||||
}
|
||||
providerData, err := access.GetAccess(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
providerConfigStr, ok := providerData["config"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("api配置错误")
|
||||
}
|
||||
var providerConfig map[string]any
|
||||
err = json.Unmarshal([]byte(providerConfigStr), &providerConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api配置解析错误:%v", err)
|
||||
}
|
||||
pluginName, ok := providerConfig["name"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("插件名称错误")
|
||||
}
|
||||
var pluginConfig map[string]any
|
||||
switch v := providerConfig["config"].(type) {
|
||||
case map[string]any:
|
||||
pluginConfig = v
|
||||
case string:
|
||||
err = json.Unmarshal([]byte(v), &pluginConfig)
|
||||
if err != nil {
|
||||
fmt.Println(v)
|
||||
return fmt.Errorf("插件配置解析错误:%v", err)
|
||||
}
|
||||
default:
|
||||
fmt.Println(v)
|
||||
return fmt.Errorf("插件配置格式错误")
|
||||
}
|
||||
pluginParams, ok := cfg["params"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("插件参数错误:params")
|
||||
}
|
||||
var paramsMap map[string]any
|
||||
err = json.Unmarshal([]byte(pluginParams), ¶msMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("插件参数解析错误:%v", err)
|
||||
}
|
||||
// 合并插件配置和参数
|
||||
for k, v := range paramsMap {
|
||||
pluginConfig[k] = v
|
||||
}
|
||||
|
||||
// 设置证书
|
||||
keyPem, ok := cert["key"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("证书错误:key")
|
||||
}
|
||||
certPem, ok := cert["cert"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("证书错误:cert")
|
||||
}
|
||||
|
||||
pluginConfig["key"] = keyPem
|
||||
pluginConfig["cert"] = certPem
|
||||
|
||||
// 调用插件
|
||||
logger.Debug(fmt.Sprintf("调用插件%s:%s", pluginName, action))
|
||||
|
||||
_, err = CallPlugin(pluginName, action, pluginConfig, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("调用插件失败:%v", err)
|
||||
}
|
||||
//fmt.Println(rep)
|
||||
return err
|
||||
}
|
||||
204
frontend/allinssl/backend/internal/cert/deploy/plugin/plugin.go
Normal file
204
frontend/allinssl/backend/internal/cert/deploy/plugin/plugin.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPluginNotFound = errors.New("插件未找到")
|
||||
ErrActionNotFound = errors.New("插件不支持该 action")
|
||||
pluginRegistry = map[string]PluginMetadata{}
|
||||
)
|
||||
|
||||
type ActionInfo struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Params map[string]any `json:"params,omitempty"` // 可选参数
|
||||
}
|
||||
|
||||
type PluginMetadata struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Version string `json:"version"`
|
||||
Author string `json:"author"`
|
||||
Actions []ActionInfo `json:"actions"`
|
||||
Config map[string]any `json:"config,omitempty"` // 可选配置
|
||||
Path string // 插件路径
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
Action string `json:"action"`
|
||||
Params map[string]interface{} `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Result map[string]interface{} `json:"result"`
|
||||
}
|
||||
|
||||
func scanPlugins(dir string) ([]PluginMetadata, error) {
|
||||
pluginRegistry = map[string]PluginMetadata{} // 清空旧的
|
||||
var plugins []PluginMetadata
|
||||
_ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil || d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
meta, err := getMetadata(path)
|
||||
if err != nil {
|
||||
fmt.Println("插件无效:", path, "错误:", err)
|
||||
return nil
|
||||
}
|
||||
meta.Path = path
|
||||
plugins = append(plugins, meta)
|
||||
pluginRegistry[meta.Name] = meta
|
||||
return nil
|
||||
})
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
func getMetadata(path string) (PluginMetadata, error) {
|
||||
req := Request{Action: "get_metadata"}
|
||||
data, _ := json.Marshal(req)
|
||||
|
||||
cmd := exec.Command(path)
|
||||
cmd.Stdin = bytes.NewReader(data)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return PluginMetadata{}, fmt.Errorf("运行失败: %w", err)
|
||||
}
|
||||
|
||||
var resp Response
|
||||
if err := json.Unmarshal(out.Bytes(), &resp); err != nil {
|
||||
return PluginMetadata{}, fmt.Errorf("输出无效: %w", err)
|
||||
}
|
||||
if resp.Status != "success" {
|
||||
return PluginMetadata{}, fmt.Errorf("插件响应错误: %s", resp.Message)
|
||||
}
|
||||
|
||||
var meta PluginMetadata
|
||||
raw, _ := json.Marshal(resp.Result)
|
||||
if err := json.Unmarshal(raw, &meta); err != nil {
|
||||
return PluginMetadata{}, fmt.Errorf("元数据解析失败: %w", err)
|
||||
}
|
||||
|
||||
if meta.Name == "" || len(meta.Actions) == 0 {
|
||||
return PluginMetadata{}, fmt.Errorf("元数据缺失")
|
||||
}
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func CallPlugin(name, action string, params map[string]interface{}, logger *public.Logger) (*Response, error) {
|
||||
// 第一次尝试
|
||||
resp, err := tryCallPlugin(name, action, params, logger)
|
||||
if err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 如果是插件或 action 不存在,则刷新插件列表并再试一次
|
||||
if errors.Is(err, ErrPluginNotFound) || errors.Is(err, ErrActionNotFound) {
|
||||
logger.Debug("插件或插件内方法不存在,尝试刷新插件列表...")
|
||||
_, scanErr := GetPlugins()
|
||||
if scanErr != nil {
|
||||
logger.Error("插件刷新失败", scanErr)
|
||||
return nil, fmt.Errorf("插件刷新失败: %v", scanErr)
|
||||
}
|
||||
return tryCallPlugin(name, action, params, logger)
|
||||
}
|
||||
|
||||
// 其他错误直接返回
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func tryCallPlugin(name, action string, params map[string]interface{}, logger *public.Logger) (*Response, error) {
|
||||
plugin, ok := pluginRegistry[name]
|
||||
if !ok {
|
||||
return nil, ErrPluginNotFound
|
||||
}
|
||||
|
||||
// 检查 action 是否存在
|
||||
found := false
|
||||
for _, a := range plugin.Actions {
|
||||
if a.Name == action {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logger.Debug("插件不支持该 action", "plugin", name, "action", action)
|
||||
return nil, ErrActionNotFound
|
||||
}
|
||||
|
||||
// 构造请求
|
||||
req := Request{
|
||||
Action: action,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
// 启动插件进程
|
||||
cmd := exec.Command(plugin.Path)
|
||||
cmd.Stderr = io.Discard // ❌ 忽略所有插件错误/日志输出,避免污染
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
logger.Error("开启标准输入管道失败", err)
|
||||
return nil, err
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
logger.Error("开启标准输出管道失败", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
logger.Error("启动插件失败", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(stdin).Encode(req); err != nil {
|
||||
logger.Error("发送插件请求失败", err)
|
||||
return nil, err
|
||||
}
|
||||
stdin.Close()
|
||||
|
||||
respBytes, err := io.ReadAll(stdout)
|
||||
if err != nil {
|
||||
logger.Error("读取插件响应失败", err)
|
||||
return nil, err
|
||||
}
|
||||
var resp Response
|
||||
if err := json.Unmarshal(respBytes, &resp); err != nil {
|
||||
logger.Error("解析插件响应失败", err, "内容", string(respBytes))
|
||||
return nil, fmt.Errorf("解析插件响应失败: %v\n内容: %s", err, respBytes)
|
||||
}
|
||||
cmd.Wait()
|
||||
logger.Debug("插件响应", "plugin", name, "action", action, "response", resp)
|
||||
if resp.Status != "success" {
|
||||
return nil, fmt.Errorf("插件响应错误: %s", resp.Message)
|
||||
}
|
||||
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func GetPlugins() ([]PluginMetadata, error) {
|
||||
pluginDir := public.GetSettingIgnoreError("plugin_dir")
|
||||
return scanPlugins(pluginDir)
|
||||
}
|
||||
|
||||
func GetActions(pluginName string) ([]ActionInfo, error) {
|
||||
_, err := GetPlugins()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取插件列表失败: %v", err)
|
||||
}
|
||||
return pluginRegistry[pluginName].Actions, err
|
||||
}
|
||||
Reference in New Issue
Block a user