chore: optimize runtime environment container configuration (#13071)

* chore: optimize runtime environment container configuration

* chore: optimize runtime environment container configuration
This commit is contained in:
CityFun
2026-06-17 19:09:31 +08:00
committed by GitHub
parent 5e0a598799
commit 5591fe50f0
40 changed files with 1116 additions and 312 deletions

View File

@@ -42,12 +42,14 @@ type Environment struct {
type Volume struct {
Source string `json:"source"`
Target string `json:"target"`
Mode string `json:"mode"`
}
type ExposedPort struct {
HostPort int `json:"hostPort"`
ContainerPort int `json:"containerPort"`
HostIP string `json:"hostIP"`
Protocol string `json:"protocol"`
}
type ExtraHost struct {

View File

@@ -89,7 +89,15 @@ func checkPort(key string, params map[string]interface{}) (int, error) {
return 0, nil
}
func isPortInUse(port int, protocol string) bool {
return common.ScanPortWithProto(port, normalizeComposeProtocol(protocol))
}
func checkPortExist(port int) error {
return checkPortExistWithProtocol(port, "")
}
func checkPortExistWithProtocol(port int, protocol string) error {
errMap := make(map[string]interface{})
errMap["port"] = port
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
@@ -110,7 +118,7 @@ func checkPortExist(port int) error {
errMap["name"] = domain.Domain
return buserr.WithMap("ErrPortExist", errMap, nil)
}
if common.ScanPort(port) {
if isPortInUse(port, protocol) {
return buserr.WithDetail("ErrPortInUsed", port, nil)
}
return nil

View File

@@ -0,0 +1,84 @@
package service
import (
"fmt"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
)
func composePortEnvKeys(index int) (containerPort, hostPort, hostIP, protocol string) {
return fmt.Sprintf("CONTAINER_PORT_%d", index),
fmt.Sprintf("HOST_PORT_%d", index),
fmt.Sprintf("HOST_IP_%d", index),
fmt.Sprintf("PORT_PROTOCOL_%d", index)
}
func isComposePortEnvKey(key string) bool {
return strings.HasPrefix(key, "CONTAINER_PORT_") ||
strings.HasPrefix(key, "HOST_PORT_") ||
strings.HasPrefix(key, "HOST_IP_") ||
strings.HasPrefix(key, "PORT_PROTOCOL_")
}
func formatComposePortMapping(hostIP, hostPort, containerPort, protocol string) string {
return fmt.Sprintf("${%s}:${%s}:${%s}/%s", hostIP, hostPort, containerPort, normalizeComposeProtocol(protocol))
}
func normalizeComposeProtocol(protocol string) string {
switch strings.ToLower(strings.TrimSpace(protocol)) {
case "udp":
return "udp"
default:
return "tcp"
}
}
func formatComposeVolume(source, target, mode string) string {
return fmt.Sprintf("%s:%s:%s", source, target, normalizeComposeVolumeMode(mode))
}
func normalizeComposeVolumeMode(mode string) string {
switch strings.ToLower(strings.TrimSpace(mode)) {
case "ro":
return "ro"
default:
return "rw"
}
}
func loadComposeExposedPortsFromEnv(envs map[string]string, defaultHostIP string, strict bool) ([]request.ExposedPort, error) {
var ports []request.ExposedPort
for key, value := range envs {
if !strings.HasPrefix(key, "CONTAINER_PORT_") {
continue
}
index := strings.TrimPrefix(key, "CONTAINER_PORT_")
containerPort, err := strconv.Atoi(value)
if err != nil {
if strict {
return nil, err
}
continue
}
hostPort, err := strconv.Atoi(envs["HOST_PORT_"+index])
if err != nil {
if strict {
return nil, err
}
continue
}
hostIP := envs["HOST_IP_"+index]
if hostIP == "" {
hostIP = defaultHostIP
}
ports = append(ports, request.ExposedPort{
ContainerPort: containerPort,
HostPort: hostPort,
HostIP: hostIP,
Protocol: normalizeComposeProtocol(envs["PORT_PROTOCOL_"+index]),
})
}
return ports, nil
}

View File

@@ -792,9 +792,14 @@ func loadMcpServerComposeConfig(serverDTO *response.McpServerDTO) error {
}
if service.Volumes != nil {
for _, volume := range service.Volumes {
mode := "rw"
if volume.ReadOnly {
mode = "ro"
}
serverDTO.Volumes = append(serverDTO.Volumes, request.Volume{
Source: volume.Source,
Target: volume.Target,
Mode: mode,
})
}
}
@@ -850,6 +855,9 @@ func handleCreateParams(mcpServer *model.McpServer, environments []request.Envir
normalizeMcpServerGateway(mcpServer)
serviceValue["command"] = buildSupergatewayCommand(mcpServer)
serviceValue["image"] = mcpServer.GatewayImage
serviceValue["ports"] = []string{
formatComposePortMapping("HOST_IP", "PANEL_APP_PORT_HTTP", "PANEL_APP_PORT_HTTP", ""),
}
oldEnv, hasOldEnv := serviceValue["environment"]
delete(serviceValue, "environment")
if environments != nil && len(environments) > 0 {
@@ -869,7 +877,7 @@ func handleCreateParams(mcpServer *model.McpServer, environments []request.Envir
if volumes != nil && len(volumes) > 0 {
volumeList := make([]string, 0)
for _, volume := range volumes {
volumeList = append(volumeList, fmt.Sprintf("%s:%s", volume.Source, volume.Target))
volumeList = append(volumeList, formatComposeVolume(volume.Source, volume.Target, volume.Mode))
}
serviceValue["volumes"] = volumeList
} else if volumes == nil {

View File

@@ -137,7 +137,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
create.Install = true
for _, export := range create.ExposedPorts {
hostPorts = append(hostPorts, strconv.Itoa(export.HostPort))
if err := checkPortExist(export.HostPort); err != nil {
if err := checkPortExistWithProtocol(export.HostPort, export.Protocol); err != nil {
return nil, err
}
}
@@ -241,30 +241,11 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
runtimeDTO.AppID = detail.AppId
}
for k, v := range envs {
runtimeDTO.Params[k] = v
if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") {
if strings.Contains(k, "CONTAINER_PORT") {
matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k)
if len(matches) < 2 {
continue
}
containerPort, err := strconv.Atoi(v)
if err != nil {
continue
}
hostPort, err := strconv.Atoi(envs[fmt.Sprintf("HOST_PORT_%s", matches[1])])
if err != nil {
continue
}
hostIP := envs[fmt.Sprintf("HOST_IP_%s", matches[1])]
runtimeDTO.ExposedPorts = append(runtimeDTO.ExposedPorts, request.ExposedPort{
ContainerPort: containerPort,
HostPort: hostPort,
HostIP: hostIP,
})
}
if !isComposePortEnvKey(k) {
runtimeDTO.Params[k] = v
}
}
runtimeDTO.ExposedPorts, _ = loadComposeExposedPortsFromEnv(envs, "", false)
res = append(res, runtimeDTO)
}
return total, res, nil
@@ -435,9 +416,11 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
return buserr.New("ErrImageExist")
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:
ownedPortKeys := runtimeOwnedPortKeys(runtime.Env)
for _, export := range req.ExposedPorts {
hostPorts = append(hostPorts, strconv.Itoa(export.HostPort))
if err = checkRuntimePortExist(export.HostPort, false, runtime.ID); err != nil {
_, owned := ownedPortKeys[composePortCheckKey(export.HostPort, export.Protocol)]
if err = checkRuntimePortExistWithProtocol(export.HostPort, export.Protocol, !owned, runtime.ID); err != nil {
return err
}
}
@@ -1058,6 +1041,7 @@ func (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) erro
var (
composeContent []byte
)
ownedPortKeys := runtimeOwnedPortKeys(runtime.Env)
for _, export := range req.ExposedPorts {
if strconv.Itoa(export.HostPort) == runtime.Port {
return buserr.WithName("ErrPHPRuntimePortFailed", strconv.Itoa(export.HostPort))
@@ -1065,7 +1049,8 @@ func (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) erro
if export.ContainerPort == 9000 {
return buserr.New("ErrPHPPortIsDefault")
}
if err = checkRuntimePortExist(export.HostPort, false, runtime.ID); err != nil {
_, owned := ownedPortKeys[composePortCheckKey(export.HostPort, export.Protocol)]
if err = checkRuntimePortExistWithProtocol(export.HostPort, export.Protocol, !owned, runtime.ID); err != nil {
return err
}
}
@@ -1090,7 +1075,7 @@ func (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) erro
return err
}
for k := range envs {
if strings.HasPrefix(k, "CONTAINER_PORT_") || strings.HasPrefix(k, "HOST_PORT_") || strings.HasPrefix(k, "HOST_IP_") || strings.Contains(k, "APP_PORT") {
if isComposePortEnvKey(k) || strings.Contains(k, "APP_PORT") {
delete(envs, k)
}
}
@@ -1102,6 +1087,7 @@ func (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) erro
ExposedPorts: req.ExposedPorts,
Environments: req.Environments,
Volumes: req.Volumes,
ExtraHosts: req.ExtraHosts,
},
}
composeContent, err = handleCompose(envs, composeContent, create, projectDir)

View File

@@ -19,7 +19,6 @@ import (
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
@@ -33,7 +32,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/pkg/errors"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
@@ -464,7 +462,7 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
return
}
for k := range env {
if strings.HasPrefix(k, "CONTAINER_PORT_") || strings.HasPrefix(k, "HOST_PORT_") || strings.HasPrefix(k, "HOST_IP_") || strings.Contains(k, "APP_PORT") {
if isComposePortEnvKey(k) || strings.Contains(k, "APP_PORT") {
delete(env, k)
}
}
@@ -621,15 +619,16 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
if len(create.ExposedPorts) > 0 {
var ports []interface{}
for i, port := range create.ExposedPorts {
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
hostPortStr := fmt.Sprintf("HOST_PORT_%d", i)
containerPortStr, hostPortStr, hostIPStr, protocolStr := composePortEnvKeys(i)
existMap[containerPortStr] = struct{}{}
existMap[hostPortStr] = struct{}{}
hostIPStr := fmt.Sprintf("HOST_IP_%d", i)
ports = append(ports, fmt.Sprintf("${%s}:${%s}:${%s}", hostIPStr, hostPortStr, containerPortStr))
existMap[hostIPStr] = struct{}{}
existMap[protocolStr] = struct{}{}
ports = append(ports, formatComposePortMapping(hostIPStr, hostPortStr, containerPortStr, port.Protocol))
create.Params[containerPortStr] = port.ContainerPort
create.Params[hostPortStr] = port.HostPort
create.Params[hostIPStr] = port.HostIP
create.Params[protocolStr] = normalizeComposeProtocol(port.Protocol)
}
if create.Type == constant.RuntimePHP {
ports = append(ports, "127.0.0.1:${PANEL_APP_PORT_HTTP}:9000")
@@ -662,7 +661,7 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
volumes = append(volumes, fmt.Sprintf("%s:%s", k, v))
}
for _, volume := range create.Volumes {
volumes = append(volumes, fmt.Sprintf("%s:%s", volume.Source, volume.Target))
volumes = append(volumes, formatComposeVolume(volume.Source, volume.Target, volume.Mode))
}
var extraHosts []interface{}
@@ -678,7 +677,7 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
break
}
for k := range env {
if strings.Contains(k, "CONTAINER_PORT_") || strings.Contains(k, "HOST_PORT_") {
if isComposePortEnvKey(k) {
if _, ok := existMap[k]; !ok {
delete(env, k)
}
@@ -872,9 +871,14 @@ func getDockerComposeVolumes(yml []byte) ([]request.Volume, error) {
if len(envArray) > 1 {
target = envArray[1]
}
mode := ""
if len(envArray) > 2 {
mode = envArray[2]
}
res = append(res, request.Volume{
Source: source,
Target: target,
Mode: normalizeComposeVolumeMode(mode),
})
}
}
@@ -908,7 +912,31 @@ func getDockerComposeExtraHosts(yml []byte) ([]request.ExtraHost, error) {
return res, nil
}
func composePortCheckKey(port int, protocol string) string {
return fmt.Sprintf("%d/%s", port, normalizeComposeProtocol(protocol))
}
func runtimeOwnedPortKeys(envStr string) map[string]struct{} {
portKeys := make(map[string]struct{})
envs, err := gotenv.Unmarshal(envStr)
if err != nil {
return portKeys
}
exposedPorts, err := loadComposeExposedPortsFromEnv(envs, "", false)
if err != nil {
return portKeys
}
for _, port := range exposedPorts {
portKeys[composePortCheckKey(port.HostPort, port.Protocol)] = struct{}{}
}
return portKeys
}
func checkRuntimePortExist(port int, scanPort bool, runtimeID uint) error {
return checkRuntimePortExistWithProtocol(port, "", scanPort, runtimeID)
}
func checkRuntimePortExistWithProtocol(port int, protocol string, scanPort bool, runtimeID uint) error {
errMap := make(map[string]interface{})
errMap["port"] = port
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
@@ -933,7 +961,7 @@ func checkRuntimePortExist(port int, scanPort bool, runtimeID uint) error {
errMap["name"] = domain.Domain
return buserr.WithMap("ErrPortExist", errMap, nil)
}
if scanPort && common.ScanPort(port) {
if scanPort && isPortInUse(port, protocol) {
return buserr.WithDetail("ErrPortInUsed", port, nil)
}
return nil
@@ -1006,31 +1034,16 @@ func handleRuntimeDTO(res *response.RuntimeDTO, runtime model.Runtime) error {
return err
}
for k, v := range envs {
if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") {
if strings.Contains(k, "CONTAINER_PORT") {
matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k)
if len(matches) < 2 {
return fmt.Errorf("invalid container port key: %s", k)
}
containerPort, err := strconv.Atoi(v)
if err != nil {
return err
}
hostPort, err := strconv.Atoi(envs[fmt.Sprintf("HOST_PORT_%s", matches[1])])
if err != nil {
return err
}
hostIP := envs[fmt.Sprintf("HOST_IP_%s", matches[1])]
res.ExposedPorts = append(res.ExposedPorts, request.ExposedPort{
ContainerPort: containerPort,
HostPort: hostPort,
HostIP: hostIP,
})
}
if isComposePortEnvKey(k) {
continue
} else {
res.Params[k] = v
}
}
res.ExposedPorts, err = loadComposeExposedPortsFromEnv(envs, "", true)
if err != nil {
return err
}
if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok {
res.Source = v
}

View File

@@ -23,7 +23,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/env"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/re"
)
type TensorRTLLMService struct{}
@@ -59,33 +58,7 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR
serverDTO.Image = envs["IMAGE"]
serverDTO.Command = getCommand(item.Env)
for k, v := range envs {
if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") {
if strings.Contains(k, "CONTAINER_PORT") {
matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k)
if len(matches) < 2 {
continue
}
containerPort, err := strconv.Atoi(v)
if err != nil {
continue
}
hostPort, err := strconv.Atoi(envs[fmt.Sprintf("HOST_PORT_%s", matches[1])])
if err != nil {
continue
}
hostIP := envs[fmt.Sprintf("HOST_IP_%s", matches[1])]
if hostIP == "" {
hostIP = "0.0.0.0"
}
serverDTO.ExposedPorts = append(serverDTO.ExposedPorts, request.ExposedPort{
ContainerPort: containerPort,
HostPort: hostPort,
HostIP: hostIP,
})
}
}
}
serverDTO.ExposedPorts, _ = loadComposeExposedPortsFromEnv(envs, "0.0.0.0", false)
composeByte, err := files.NewFileOp().GetContent(path.Join(global.Dir.TensorRTLLMDir, item.Name, "docker-compose.yml"))
if err == nil {
@@ -149,11 +122,9 @@ func handleLLMParams(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) e
delete(serviceValue, "ports")
if len(create.ExposedPorts) > 0 {
var ports []interface{}
for i := range create.ExposedPorts {
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
hostPortStr := fmt.Sprintf("HOST_PORT_%d", i)
hostIPStr := fmt.Sprintf("HOST_IP_%d", i)
ports = append(ports, fmt.Sprintf("${%s}:${%s}:${%s}", hostIPStr, hostPortStr, containerPortStr))
for i, port := range create.ExposedPorts {
containerPortStr, hostPortStr, hostIPStr, _ := composePortEnvKeys(i)
ports = append(ports, formatComposePortMapping(hostIPStr, hostPortStr, containerPortStr, port.Protocol))
}
serviceValue["ports"] = ports
}
@@ -174,7 +145,7 @@ func handleLLMParams(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) e
volumes = append(volumes, fmt.Sprintf("%s:%s", k, v))
}
for _, volume := range create.Volumes {
volumes = append(volumes, fmt.Sprintf("%s:%s", volume.Source, volume.Target))
volumes = append(volumes, formatComposeVolume(volume.Source, volume.Target, volume.Mode))
}
serviceValue["volumes"] = volumes
@@ -195,12 +166,11 @@ func handleLLMEnv(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) gote
envMap["IMAGE"] = create.Image
envMap["COMMAND"] = create.Command
for i, port := range create.ExposedPorts {
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
hostPortStr := fmt.Sprintf("HOST_PORT_%d", i)
hostIPStr := fmt.Sprintf("HOST_IP_%d", i)
containerPortStr, hostPortStr, hostIPStr, protocolStr := composePortEnvKeys(i)
envMap[containerPortStr] = strconv.Itoa(port.ContainerPort)
envMap[hostPortStr] = strconv.Itoa(port.HostPort)
envMap[hostIPStr] = port.HostIP
envMap[protocolStr] = normalizeComposeProtocol(port.Protocol)
}
orders := []string{"MODEL_PATH", "COMMAND"}
envStr, _ := env.MarshalWithOrder(envMap, orders)
@@ -219,7 +189,7 @@ func (t TensorRTLLMService) Create(create request.TensorRTLLMCreate) error {
}
}
for _, export := range create.ExposedPorts {
if err := checkPortExist(export.HostPort); err != nil {
if err := checkPortExistWithProtocol(export.HostPort, export.Protocol); err != nil {
return err
}
}

View File

@@ -1247,6 +1247,33 @@
"formatZH": "更新 AI 网关分组 [name]",
"formatEN": "update AI proxy group [name]"
},
"/core/enterprise/ai-proxy/model-groups/create": {
"bodyKeys": [
"name"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "创建 AI 网关模型组 [name]",
"formatEN": "create AI proxy model group [name]"
},
"/core/enterprise/ai-proxy/model-groups/delete": {
"bodyKeys": [
"id"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "删除 AI 网关模型组 [id]",
"formatEN": "delete AI proxy model group [id]"
},
"/core/enterprise/ai-proxy/model-groups/update": {
"bodyKeys": [
"name"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "更新 AI 网关模型组 [name]",
"formatEN": "update AI proxy model group [name]"
},
"/core/enterprise/ai-proxy/service/operate": {
"bodyKeys": [
"operate"
@@ -2166,6 +2193,15 @@
"formatZH": "更新自定义应用配置 [status]",
"formatEN": "update custom app config [status]"
},
"/core/xpack/settings/app/image-prefix": {
"bodyKeys": [
"imagePrefix"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "更新应用镜像前缀 [imagePrefix]",
"formatEN": "update app image prefix [imagePrefix]"
},
"/core/xpack/settings/reset": {
"bodyKeys": [],
"paramKeys": [],
@@ -2193,25 +2229,13 @@
"/core/xpack/sync/app": {
"bodyKeys": [
"sourceNode",
"targetNode",
"appInstallID",
"appBackupID"
"targetNodes",
"appInstallID"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "同步应用备份 [appInstallID] 到节点 [targetNode]",
"formatEN": "sync app backup [appInstallID] to node [targetNode]"
},
"/core/xpack/sync/app/install": {
"bodyKeys": [
"appKey",
"version",
"nodes"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "安装应用 [appKey] [version] 到节点 [nodes]",
"formatEN": "install app [appKey] [version] to nodes [nodes]"
"formatZH": "同步应用 [appInstallID] 到节点 [targetNodes]",
"formatEN": "sync app [appInstallID] to nodes [targetNodes]"
},
"/core/xpack/sync/ssl": {
"bodyKeys": [
@@ -2352,6 +2376,15 @@
"formatZH": "更新自定义应用配置 [status]",
"formatEN": "update custom app config [status]"
},
"/custom/app/image-prefix": {
"bodyKeys": [
"imagePrefix"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "更新应用镜像前缀 [imagePrefix]",
"formatEN": "update app image prefix [imagePrefix]"
},
"/custom/app/install": {
"bodyKeys": [
"name",
@@ -2379,6 +2412,15 @@
"formatZH": "同步自定义应用商店",
"formatEN": "sync custom app store"
},
"/custom/app/sync/backup": {
"bodyKeys": [
"appInstallID"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "创建应用同步备份 [appInstallID]",
"formatEN": "create app sync backup [appInstallID]"
},
"/dashboard/app/launcher/show": {
"bodyKeys": [
"key",
@@ -4431,6 +4473,24 @@
"formatZH": "申请证书 [domain]",
"formatEN": "apply ssl [domain]"
},
"/websites/ssl/push": {
"bodyKeys": [
"id"
],
"paramKeys": [],
"beforeFunctions": [
{
"input_column": "id",
"input_value": "id",
"isList": false,
"db": "website_ssls",
"output_column": "primary_domain",
"output_value": "domain"
}
],
"formatZH": "推送证书到节点 [domain]",
"formatEN": "Push ssl to nodes [domain]"
},
"/websites/ssl/update": {
"bodyKeys": [
"id"
@@ -5083,4 +5143,4 @@
"formatZH": "从主节点同步设置",
"formatEN": "sync settings from master"
}
}
}

View File

@@ -37,7 +37,7 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.10
github.com/qiniu/go-sdk/v7 v7.26.12
github.com/qiniu/go-sdk/v7 v7.26.13
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v4 v4.26.5
github.com/sirupsen/logrus v1.9.4
@@ -46,12 +46,12 @@ require (
github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0
github.com/subosito/gotenv v1.6.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.73
github.com/tencentyun/cos-go-sdk-v5 v0.7.74
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19
github.com/upyun/go-sdk v2.1.0+incompatible
go.mongodb.org/mongo-driver/v2 v2.6.0
go.mongodb.org/mongo-driver/v2 v2.6.1
golang.org/x/crypto v0.53.0
golang.org/x/net v0.55.0
golang.org/x/net v0.56.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.21.0
golang.org/x/sys v0.46.0

View File

@@ -746,8 +746,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/qiniu/go-sdk/v7 v7.26.12 h1:AnWiKjBY62XpULoB/MySKdNmlUo9S9pQB7/0s7QueCo=
github.com/qiniu/go-sdk/v7 v7.26.12/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
github.com/qiniu/go-sdk/v7 v7.26.13 h1:U4usDKoLldAqJntLN4wv4pkAjwvM9dueOUY/wbbO/yM=
github.com/qiniu/go-sdk/v7 v7.26.13/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
@@ -833,8 +833,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.24/go.mod h
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.106 h1:YjsxDQwoiB+C0ISjnVk22Wbks02Ir+7Z5wmujcz69Vg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.106/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.73 h1:uFfgp1A7cQaAGR6QP9DsIkoEQ67b8ewj5r1RV6XB540=
github.com/tencentyun/cos-go-sdk-v5 v0.7.73/go.mod h1:STbTNaNKq03u+gscPEGOahKzLcGSYOj6Dzc5zNay7Pg=
github.com/tencentyun/cos-go-sdk-v5 v0.7.74 h1:YCsGhIK+Q5vMcmAiQqPuMyhSQ3zN1p7U+dhlHQD++6c=
github.com/tencentyun/cos-go-sdk-v5 v0.7.74/go.mod h1:STbTNaNKq03u+gscPEGOahKzLcGSYOj6Dzc5zNay7Pg=
github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20250515025012-e0eec8a5d123/go.mod h1:b18KQa4IxHbxeseW1GcZox53d7J0z39VNONTxvvlkXw=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
@@ -898,8 +898,8 @@ go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lL
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8=
go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.mongodb.org/mongo-driver/v2 v2.6.1 h1:YyGZ2lt+4Nv+dWuiGMKoyWuxmBSlGLH5jllheKiQGu0=
go.mongodb.org/mongo-driver/v2 v2.6.1/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1073,8 +1073,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@@ -2883,6 +2883,44 @@ const docTemplate = `{
]
}
},
"/ai/mcp/server/connection/test": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.McpServerConnectionTest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.McpServerConnectionTestRes"
}
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Test mcp server connection",
"tags": [
"McpServer"
]
}
},
"/ai/mcp/server/del": {
"post": {
"consumes": [
@@ -2918,6 +2956,44 @@ const docTemplate = `{
]
}
},
"/ai/mcp/server/detail": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.McpServerDetail"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.McpServerDTO"
}
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Load mcp server detail",
"tags": [
"McpServer"
]
}
},
"/ai/mcp/server/op": {
"post": {
"consumes": [
@@ -2953,6 +3029,47 @@ const docTemplate = `{
]
}
},
"/ai/mcp/server/status/sync": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.McpServerStatusSync"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"items": {
"$ref": "#/definitions/response.McpServerStatusDTO"
},
"type": "array"
}
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Sync mcp server status",
"tags": [
"McpServer"
]
}
},
"/ai/mcp/server/update": {
"post": {
"consumes": [
@@ -27588,6 +27705,59 @@ const docTemplate = `{
}
}
},
"/websites/ssl/push": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.WebsiteSSLPush"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Push ssl to nodes",
"tags": [
"Website SSL"
],
"x-panel-log": {
"BeforeFunctions": [
{
"db": "website_ssls",
"input_column": "id",
"input_value": "id",
"isList": false,
"output_column": "primary_domain",
"output_value": "domain"
}
],
"bodyKeys": [
"id"
],
"formatEN": "Push ssl to nodes [domain]",
"formatZH": "推送证书到节点 [domain]",
"paramKeys": []
}
}
},
"/websites/ssl/resolve": {
"post": {
"consumes": [
@@ -31531,6 +31701,9 @@ const docTemplate = `{
"fileName": {
"type": "string"
},
"isImmediate": {
"type": "boolean"
},
"name": {
"type": "string"
},
@@ -39396,6 +39569,9 @@ const docTemplate = `{
},
"hostPort": {
"type": "integer"
},
"protocol": {
"type": "string"
}
},
"type": "object"
@@ -40282,6 +40458,17 @@ const docTemplate = `{
],
"type": "object"
},
"request.McpServerConnectionTest": {
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
],
"type": "object"
},
"request.McpServerCreate": {
"properties": {
"baseUrl": {
@@ -40299,6 +40486,9 @@ const docTemplate = `{
},
"type": "array"
},
"gatewayImage": {
"type": "string"
},
"hostIP": {
"type": "string"
},
@@ -40311,6 +40501,9 @@ const docTemplate = `{
"port": {
"type": "integer"
},
"protocolVersion": {
"type": "string"
},
"ssePath": {
"type": "string"
},
@@ -40347,6 +40540,17 @@ const docTemplate = `{
],
"type": "object"
},
"request.McpServerDetail": {
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
],
"type": "object"
},
"request.McpServerOperate": {
"properties": {
"id": {
@@ -40383,6 +40587,17 @@ const docTemplate = `{
],
"type": "object"
},
"request.McpServerStatusSync": {
"properties": {
"ids": {
"items": {
"type": "integer"
},
"type": "array"
}
},
"type": "object"
},
"request.McpServerUpdate": {
"properties": {
"baseUrl": {
@@ -40400,6 +40615,9 @@ const docTemplate = `{
},
"type": "array"
},
"gatewayImage": {
"type": "string"
},
"hostIP": {
"type": "string"
},
@@ -40415,6 +40633,9 @@ const docTemplate = `{
"port": {
"type": "integer"
},
"protocolVersion": {
"type": "string"
},
"ssePath": {
"type": "string"
},
@@ -41691,6 +41912,9 @@ const docTemplate = `{
},
"request.Volume": {
"properties": {
"mode": {
"type": "string"
},
"source": {
"type": "string"
},
@@ -41852,9 +42076,15 @@ const docTemplate = `{
],
"type": "string"
},
"nodes": {
"type": "string"
},
"pushDir": {
"type": "boolean"
},
"pushNode": {
"type": "boolean"
},
"renew": {
"type": "boolean"
},
@@ -42726,6 +42956,27 @@ const docTemplate = `{
},
"type": "object"
},
"request.WebsiteSSLPush": {
"properties": {
"id": {
"type": "integer"
},
"nodes": {
"type": "string"
},
"pushNode": {
"type": "boolean"
},
"taskID": {
"type": "string"
}
},
"required": [
"id",
"taskID"
],
"type": "object"
},
"request.WebsiteSSLSearch": {
"properties": {
"acmeAccountID": {
@@ -42745,6 +42996,7 @@ const docTemplate = `{
"orderBy": {
"enum": [
"created_at",
"updated_at",
"expire_date"
],
"type": "string"
@@ -42843,12 +43095,18 @@ const docTemplate = `{
"description": {
"type": "string"
},
"nodes": {
"type": "string"
},
"privateKey": {
"type": "string"
},
"privateKeyPath": {
"type": "string"
},
"pushNode": {
"type": "boolean"
},
"sslID": {
"type": "integer"
},
@@ -43976,6 +44234,26 @@ const docTemplate = `{
},
"type": "object"
},
"response.McpServerConnectionTestRes": {
"properties": {
"endpoint": {
"type": "string"
},
"message": {
"type": "string"
},
"outputTransport": {
"type": "string"
},
"protocolVersion": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"type": "object"
},
"response.McpServerDTO": {
"properties": {
"baseUrl": {
@@ -44005,6 +44283,9 @@ const docTemplate = `{
},
"type": "array"
},
"gatewayImage": {
"type": "string"
},
"hostIP": {
"type": "string"
},
@@ -44023,6 +44304,9 @@ const docTemplate = `{
"port": {
"type": "integer"
},
"protocolVersion": {
"type": "string"
},
"ssePath": {
"type": "string"
},
@@ -44050,6 +44334,20 @@ const docTemplate = `{
},
"type": "object"
},
"response.McpServerStatusDTO": {
"properties": {
"id": {
"type": "integer"
},
"message": {
"type": "string"
},
"status": {
"type": "string"
}
},
"type": "object"
},
"response.McpServersRes": {
"properties": {
"items": {

View File

@@ -2879,6 +2879,44 @@
]
}
},
"/ai/mcp/server/connection/test": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.McpServerConnectionTest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.McpServerConnectionTestRes"
}
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Test mcp server connection",
"tags": [
"McpServer"
]
}
},
"/ai/mcp/server/del": {
"post": {
"consumes": [
@@ -2914,6 +2952,44 @@
]
}
},
"/ai/mcp/server/detail": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.McpServerDetail"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.McpServerDTO"
}
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Load mcp server detail",
"tags": [
"McpServer"
]
}
},
"/ai/mcp/server/op": {
"post": {
"consumes": [
@@ -2949,6 +3025,47 @@
]
}
},
"/ai/mcp/server/status/sync": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.McpServerStatusSync"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"items": {
"$ref": "#/definitions/response.McpServerStatusDTO"
},
"type": "array"
}
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Sync mcp server status",
"tags": [
"McpServer"
]
}
},
"/ai/mcp/server/update": {
"post": {
"consumes": [
@@ -27584,6 +27701,59 @@
}
}
},
"/websites/ssl/push": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"description": "request",
"in": "body",
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/request.WebsiteSSLPush"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"summary": "Push ssl to nodes",
"tags": [
"Website SSL"
],
"x-panel-log": {
"BeforeFunctions": [
{
"db": "website_ssls",
"input_column": "id",
"input_value": "id",
"isList": false,
"output_column": "primary_domain",
"output_value": "domain"
}
],
"bodyKeys": [
"id"
],
"formatEN": "Push ssl to nodes [domain]",
"formatZH": "推送证书到节点 [domain]",
"paramKeys": []
}
}
},
"/websites/ssl/resolve": {
"post": {
"consumes": [
@@ -31527,6 +31697,9 @@
"fileName": {
"type": "string"
},
"isImmediate": {
"type": "boolean"
},
"name": {
"type": "string"
},
@@ -39392,6 +39565,9 @@
},
"hostPort": {
"type": "integer"
},
"protocol": {
"type": "string"
}
},
"type": "object"
@@ -40278,6 +40454,17 @@
],
"type": "object"
},
"request.McpServerConnectionTest": {
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
],
"type": "object"
},
"request.McpServerCreate": {
"properties": {
"baseUrl": {
@@ -40295,6 +40482,9 @@
},
"type": "array"
},
"gatewayImage": {
"type": "string"
},
"hostIP": {
"type": "string"
},
@@ -40307,6 +40497,9 @@
"port": {
"type": "integer"
},
"protocolVersion": {
"type": "string"
},
"ssePath": {
"type": "string"
},
@@ -40343,6 +40536,17 @@
],
"type": "object"
},
"request.McpServerDetail": {
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
],
"type": "object"
},
"request.McpServerOperate": {
"properties": {
"id": {
@@ -40379,6 +40583,17 @@
],
"type": "object"
},
"request.McpServerStatusSync": {
"properties": {
"ids": {
"items": {
"type": "integer"
},
"type": "array"
}
},
"type": "object"
},
"request.McpServerUpdate": {
"properties": {
"baseUrl": {
@@ -40396,6 +40611,9 @@
},
"type": "array"
},
"gatewayImage": {
"type": "string"
},
"hostIP": {
"type": "string"
},
@@ -40411,6 +40629,9 @@
"port": {
"type": "integer"
},
"protocolVersion": {
"type": "string"
},
"ssePath": {
"type": "string"
},
@@ -41687,6 +41908,9 @@
},
"request.Volume": {
"properties": {
"mode": {
"type": "string"
},
"source": {
"type": "string"
},
@@ -41848,9 +42072,15 @@
],
"type": "string"
},
"nodes": {
"type": "string"
},
"pushDir": {
"type": "boolean"
},
"pushNode": {
"type": "boolean"
},
"renew": {
"type": "boolean"
},
@@ -42722,6 +42952,27 @@
},
"type": "object"
},
"request.WebsiteSSLPush": {
"properties": {
"id": {
"type": "integer"
},
"nodes": {
"type": "string"
},
"pushNode": {
"type": "boolean"
},
"taskID": {
"type": "string"
}
},
"required": [
"id",
"taskID"
],
"type": "object"
},
"request.WebsiteSSLSearch": {
"properties": {
"acmeAccountID": {
@@ -42741,6 +42992,7 @@
"orderBy": {
"enum": [
"created_at",
"updated_at",
"expire_date"
],
"type": "string"
@@ -42839,12 +43091,18 @@
"description": {
"type": "string"
},
"nodes": {
"type": "string"
},
"privateKey": {
"type": "string"
},
"privateKeyPath": {
"type": "string"
},
"pushNode": {
"type": "boolean"
},
"sslID": {
"type": "integer"
},
@@ -43972,6 +44230,26 @@
},
"type": "object"
},
"response.McpServerConnectionTestRes": {
"properties": {
"endpoint": {
"type": "string"
},
"message": {
"type": "string"
},
"outputTransport": {
"type": "string"
},
"protocolVersion": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"type": "object"
},
"response.McpServerDTO": {
"properties": {
"baseUrl": {
@@ -44001,6 +44279,9 @@
},
"type": "array"
},
"gatewayImage": {
"type": "string"
},
"hostIP": {
"type": "string"
},
@@ -44019,6 +44300,9 @@
"port": {
"type": "integer"
},
"protocolVersion": {
"type": "string"
},
"ssePath": {
"type": "string"
},
@@ -44046,6 +44330,20 @@
},
"type": "object"
},
"response.McpServerStatusDTO": {
"properties": {
"id": {
"type": "integer"
},
"message": {
"type": "string"
},
"status": {
"type": "string"
}
},
"type": "object"
},
"response.McpServersRes": {
"properties": {
"items": {

View File

@@ -1247,6 +1247,33 @@
"formatZH": "更新 AI 网关分组 [name]",
"formatEN": "update AI proxy group [name]"
},
"/core/enterprise/ai-proxy/model-groups/create": {
"bodyKeys": [
"name"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "创建 AI 网关模型组 [name]",
"formatEN": "create AI proxy model group [name]"
},
"/core/enterprise/ai-proxy/model-groups/delete": {
"bodyKeys": [
"id"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "删除 AI 网关模型组 [id]",
"formatEN": "delete AI proxy model group [id]"
},
"/core/enterprise/ai-proxy/model-groups/update": {
"bodyKeys": [
"name"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "更新 AI 网关模型组 [name]",
"formatEN": "update AI proxy model group [name]"
},
"/core/enterprise/ai-proxy/service/operate": {
"bodyKeys": [
"operate"
@@ -2166,6 +2193,15 @@
"formatZH": "更新自定义应用配置 [status]",
"formatEN": "update custom app config [status]"
},
"/core/xpack/settings/app/image-prefix": {
"bodyKeys": [
"imagePrefix"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "更新应用镜像前缀 [imagePrefix]",
"formatEN": "update app image prefix [imagePrefix]"
},
"/core/xpack/settings/reset": {
"bodyKeys": [],
"paramKeys": [],
@@ -2193,25 +2229,13 @@
"/core/xpack/sync/app": {
"bodyKeys": [
"sourceNode",
"targetNode",
"appInstallID",
"appBackupID"
"targetNodes",
"appInstallID"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "同步应用备份 [appInstallID] 到节点 [targetNode]",
"formatEN": "sync app backup [appInstallID] to node [targetNode]"
},
"/core/xpack/sync/app/install": {
"bodyKeys": [
"appKey",
"version",
"nodes"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "安装应用 [appKey] [version] 到节点 [nodes]",
"formatEN": "install app [appKey] [version] to nodes [nodes]"
"formatZH": "同步应用 [appInstallID] 到节点 [targetNodes]",
"formatEN": "sync app [appInstallID] to nodes [targetNodes]"
},
"/core/xpack/sync/ssl": {
"bodyKeys": [
@@ -2352,6 +2376,15 @@
"formatZH": "更新自定义应用配置 [status]",
"formatEN": "update custom app config [status]"
},
"/custom/app/image-prefix": {
"bodyKeys": [
"imagePrefix"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "更新应用镜像前缀 [imagePrefix]",
"formatEN": "update app image prefix [imagePrefix]"
},
"/custom/app/install": {
"bodyKeys": [
"name",
@@ -2379,6 +2412,15 @@
"formatZH": "同步自定义应用商店",
"formatEN": "sync custom app store"
},
"/custom/app/sync/backup": {
"bodyKeys": [
"appInstallID"
],
"paramKeys": [],
"beforeFunctions": [],
"formatZH": "创建应用同步备份 [appInstallID]",
"formatEN": "create app sync backup [appInstallID]"
},
"/dashboard/app/launcher/show": {
"bodyKeys": [
"key",
@@ -4431,6 +4473,24 @@
"formatZH": "申请证书 [domain]",
"formatEN": "apply ssl [domain]"
},
"/websites/ssl/push": {
"bodyKeys": [
"id"
],
"paramKeys": [],
"beforeFunctions": [
{
"input_column": "id",
"input_value": "id",
"isList": false,
"db": "website_ssls",
"output_column": "primary_domain",
"output_value": "domain"
}
],
"formatZH": "推送证书到节点 [domain]",
"formatEN": "Push ssl to nodes [domain]"
},
"/websites/ssl/update": {
"bodyKeys": [
"id"
@@ -5083,4 +5143,4 @@
"formatZH": "从主节点同步设置",
"formatEN": "sync settings from master"
}
}
}

View File

@@ -32,7 +32,7 @@ require (
github.com/swaggo/swag v1.16.6
github.com/xlzd/gotp v0.1.0
golang.org/x/crypto v0.53.0
golang.org/x/net v0.55.0
golang.org/x/net v0.56.0
golang.org/x/sys v0.46.0
golang.org/x/term v0.44.0
golang.org/x/text v0.38.0

View File

@@ -256,8 +256,8 @@ golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -20,11 +20,11 @@
"@vueuse/core": "^14.3.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"anser": "^2.3.2",
"anser": "^2.3.5",
"axios": "^1.17.0",
"codemirror": "^6.0.2",
"crypto-js": "^4.2.0",
"dompurify": "^3.4.8",
"dompurify": "^3.4.9",
"echarts": "^5.5.0",
"element-plus": "2.14.0",
"js-base64": "^3.7.7",
@@ -51,8 +51,8 @@
"@tailwindcss/postcss": "^4.3.0",
"@types/node": "^25.9.1",
"@types/uuid": "^11.0.0",
"@typescript-eslint/eslint-plugin": "^8.58.1",
"@typescript-eslint/parser": "^8.58.1",
"@typescript-eslint/eslint-plugin": "^8.61.0",
"@typescript-eslint/parser": "^8.61.0",
"@vitejs/plugin-vue": "^6.0.7",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/compiler-sfc": "^3.5.35",
@@ -3048,17 +3048,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.1.tgz",
"integrity": "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz",
"integrity": "sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.58.1",
"@typescript-eslint/type-utils": "8.58.1",
"@typescript-eslint/utils": "8.58.1",
"@typescript-eslint/visitor-keys": "8.58.1",
"@typescript-eslint/scope-manager": "8.61.0",
"@typescript-eslint/type-utils": "8.61.0",
"@typescript-eslint/utils": "8.61.0",
"@typescript-eslint/visitor-keys": "8.61.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
@@ -3071,7 +3071,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.58.1",
"@typescript-eslint/parser": "^8.61.0",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
@@ -3087,17 +3087,17 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.1.tgz",
"integrity": "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.0.tgz",
"integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.58.1",
"@typescript-eslint/types": "8.58.1",
"@typescript-eslint/typescript-estree": "8.58.1",
"@typescript-eslint/visitor-keys": "8.58.1",
"@typescript-eslint/scope-manager": "8.61.0",
"@typescript-eslint/types": "8.61.0",
"@typescript-eslint/typescript-estree": "8.61.0",
"@typescript-eslint/visitor-keys": "8.61.0",
"debug": "^4.4.3"
},
"engines": {
@@ -3113,14 +3113,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz",
"integrity": "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.0.tgz",
"integrity": "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.58.1",
"@typescript-eslint/types": "^8.58.1",
"@typescript-eslint/tsconfig-utils": "^8.61.0",
"@typescript-eslint/types": "^8.61.0",
"debug": "^4.4.3"
},
"engines": {
@@ -3135,14 +3135,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz",
"integrity": "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz",
"integrity": "sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.58.1",
"@typescript-eslint/visitor-keys": "8.58.1"
"@typescript-eslint/types": "8.61.0",
"@typescript-eslint/visitor-keys": "8.61.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3153,9 +3153,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz",
"integrity": "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz",
"integrity": "sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3170,15 +3170,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.1.tgz",
"integrity": "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz",
"integrity": "sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.58.1",
"@typescript-eslint/typescript-estree": "8.58.1",
"@typescript-eslint/utils": "8.58.1",
"@typescript-eslint/types": "8.61.0",
"@typescript-eslint/typescript-estree": "8.61.0",
"@typescript-eslint/utils": "8.61.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
@@ -3195,9 +3195,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz",
"integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.0.tgz",
"integrity": "sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3209,16 +3209,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz",
"integrity": "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz",
"integrity": "sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.58.1",
"@typescript-eslint/tsconfig-utils": "8.58.1",
"@typescript-eslint/types": "8.58.1",
"@typescript-eslint/visitor-keys": "8.58.1",
"@typescript-eslint/project-service": "8.61.0",
"@typescript-eslint/tsconfig-utils": "8.61.0",
"@typescript-eslint/types": "8.61.0",
"@typescript-eslint/visitor-keys": "8.61.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -3247,9 +3247,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3276,16 +3276,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz",
"integrity": "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.0.tgz",
"integrity": "sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.58.1",
"@typescript-eslint/types": "8.58.1",
"@typescript-eslint/typescript-estree": "8.58.1"
"@typescript-eslint/scope-manager": "8.61.0",
"@typescript-eslint/types": "8.61.0",
"@typescript-eslint/typescript-estree": "8.61.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3300,13 +3300,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.58.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz",
"integrity": "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==",
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz",
"integrity": "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.58.1",
"@typescript-eslint/types": "8.61.0",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -3829,9 +3829,9 @@
"license": "MIT"
},
"node_modules/anser": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/anser/-/anser-2.3.3.tgz",
"integrity": "sha512-QGY1oxYE7/kkeNmbtY/2ZjQ07BCG3zYdz+k/+sf69kMzEIxb93guHkPnIXITQ+BYi61oQwG74twMOX1tD4aesg==",
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/anser/-/anser-2.3.5.tgz",
"integrity": "sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==",
"license": "MIT"
},
"node_modules/ansi-escapes": {
@@ -4998,9 +4998,9 @@
}
},
"node_modules/dompurify": {
"version": "3.4.8",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.8.tgz",
"integrity": "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==",
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.9.tgz",
"integrity": "sha512-4dPSRMRDqHvs0V4YDFCsaIZo4if5u0xM+llyxiM2fwuZFdKArUBAF3VtI2+n8NKg9P870WMdYk0UhqQNoWXbfQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"

View File

@@ -31,11 +31,11 @@
"@vueuse/core": "^14.3.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"anser": "^2.3.2",
"anser": "^2.3.5",
"axios": "^1.17.0",
"codemirror": "^6.0.2",
"crypto-js": "^4.2.0",
"dompurify": "^3.4.8",
"dompurify": "^3.4.9",
"echarts": "^5.5.0",
"element-plus": "2.14.0",
"js-base64": "^3.7.7",
@@ -62,8 +62,8 @@
"@tailwindcss/postcss": "^4.3.0",
"@types/node": "^25.9.1",
"@types/uuid": "^11.0.0",
"@typescript-eslint/eslint-plugin": "^8.58.1",
"@typescript-eslint/parser": "^8.58.1",
"@typescript-eslint/eslint-plugin": "^8.61.0",
"@typescript-eslint/parser": "^8.61.0",
"@vitejs/plugin-vue": "^6.0.7",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/compiler-sfc": "^3.5.35",

View File

@@ -256,6 +256,7 @@ export namespace AI {
hostPort: number;
containerPort: number;
hostIP: string;
protocol?: string;
}
export interface Environment {
@@ -265,6 +266,7 @@ export namespace AI {
export interface Volume {
source: string;
target: string;
mode: string;
}
export interface ExtraHosts {

View File

@@ -71,6 +71,7 @@ export namespace Runtime {
hostPort: number;
containerPort: number;
hostIP: string;
protocol: string;
}
export interface Environment {
key: string;
@@ -79,6 +80,7 @@ export namespace Runtime {
export interface Volume {
source: string;
target: string;
mode: string;
}
export interface ExtraHost {

View File

@@ -4958,7 +4958,7 @@ const message = {
theworld: 'TheWorld Browser',
edge: 'Microsoft Edge',
maxthon: 'Maxthon Browser',
monitorStatusHelper: 'Monitoring is not enabled, please enable it in settings',
monitorStatusHelper: 'Website monitoring is currently disabled. Enable it now',
excludeIp: 'Exclude IP Addresses',
excludeUa: 'Exclude User-Agent',
remotePort: 'Remote Port',

View File

@@ -5018,7 +5018,7 @@ const message = {
theworld: 'TheWorld Browser',
edge: 'Microsoft Edge',
maxthon: 'Maxthon Browser',
monitorStatusHelper: 'Monitorización no habilitada, actívala en Configuración',
monitorStatusHelper: 'La monitorización del sitio web está deshabilitada. Actívala ahora',
excludeIp: 'Excluir IPs',
excludeUa: 'Excluir User-Agent',
remotePort: 'Puerto remoto',

View File

@@ -4996,7 +4996,7 @@ const message = {
theworld: 'TheWorldブラウザ',
edge: 'Microsoft Edge',
maxthon: 'Maxthonブラウザ',
monitorStatusHelper: 'モニタリングは有効ではありません設定で有効にしてください',
monitorStatusHelper: '現在Webサイト監視は有効になっていません今すぐ有効にしてください',
excludeIp: 'IPアドレスを除外',
excludeUa: 'ユーザーエージェントを除外',
remotePort: 'リモートポート',

View File

@@ -4899,7 +4899,7 @@ const message = {
theworld: '더월드 브라우저',
edge: '마이크로소프트 엣지',
maxthon: '맥스톤 브라우저',
monitorStatusHelper: '모니터링이 활성화되지 않았습니다. 설정에서 활성화하세요',
monitorStatusHelper: '현재 웹사이트 모니터링이 활성화되어 있습니다. 지금 활성화하세요',
excludeIp: 'IP 주소 제외',
excludeUa: '사용자 에이전트 제외',
remotePort: '원격 포트',

View File

@@ -5058,7 +5058,7 @@ const message = {
theworld: 'Pelayar TheWorld',
edge: 'Microsoft Edge',
maxthon: 'Pelayar Maxthon',
monitorStatusHelper: 'Pemantauan tidak diaktifkan, sila aktifkan dalam tetapan',
monitorStatusHelper: 'Pemantauan laman web belum diaktifkan. Aktifkan sekarang',
excludeIp: 'Kecualikan Alamat IP',
excludeUa: 'Kecualikan User-Agent',
remotePort: 'Port Jauh',

View File

@@ -5202,7 +5202,7 @@ const message = {
theworld: 'Navegador TheWorld',
edge: 'Microsoft Edge',
maxthon: 'Navegador Maxthon',
monitorStatusHelper: 'O monitoramento não está habilitado, por favor habilite-o nas configurações',
monitorStatusHelper: 'O monitoramento do site está desabilitado. Habilite agora',
excludeIp: 'Excluir Endereços IP',
excludeUa: 'Excluir User-Agent',
remotePort: 'Porta Remota',

View File

@@ -5049,7 +5049,7 @@ const message = {
theworld: 'Браузер TheWorld',
edge: 'Microsoft Edge',
maxthon: 'Браузер Maxthon',
monitorStatusHelper: 'Мониторинг не включён, пожалуйста, включите его в настройках',
monitorStatusHelper: 'Мониторинг сайта сейчас отключён. Включите его сейчас',
excludeIp: 'Исключить IP-адреса',
excludeUa: 'Исключить User-Agent',
remotePort: 'Удаленный порт',

View File

@@ -5046,7 +5046,7 @@ const message = {
theworld: 'TheWorld Tarayıcı',
edge: 'Microsoft Edge',
maxthon: 'Maxthon Tarayıcı',
monitorStatusHelper: 'İzleme etkin değil, lütfen ayarlarda etkinleştirin',
monitorStatusHelper: 'Web sitesi izleme şu anda devre dışı. Şimdi etkinleştirin',
excludeIp: 'IP Adreslerini Hariç Tut',
excludeUa: 'Kullanıcı-Aracısını Hariç Tut',
remotePort: 'Uzak Port',

View File

@@ -4645,7 +4645,7 @@ const message = {
theworld: '世界之窗瀏覽器',
edge: 'Edge',
maxthon: '遨遊瀏覽器',
monitorStatusHelper: '監控未開啟,請在設定中開啟',
monitorStatusHelper: '目前網站監控未開啟,立即開啟',
excludeIp: '排除 IP',
excludeUa: '排除 User-Agent',
remotePort: '遠端埠',

View File

@@ -4083,7 +4083,7 @@ const message = {
theworld: '世界之窗浏览器',
edge: 'Edge',
maxthon: '遨游浏览器',
monitorStatusHelper: '监控未开启,请在设置中打开',
monitorStatusHelper: '当前网站监控未开启,立即开启',
excludeIp: '排除 IP',
excludeUa: '排除 User-Agent',
remotePort: '远程端口',

View File

@@ -117,34 +117,7 @@
</div>
</el-tab-pane>
<el-tab-pane :label="$t('runtime.environment')">
<div class="mt-1.5">
<el-row :gutter="20" v-for="(env, index) in mcpServer.environments" :key="index">
<el-col :span="8">
<el-form-item :prop="`environments.${index}.key`" :rules="rules.key">
<el-input v-model="env.key" :placeholder="$t('aiTools.mcp.envKey')" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :prop="`environments.${index}.value`" :rules="rules.value">
<el-input v-model="env.value" :placeholder="$t('aiTools.mcp.envValue')" />
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item>
<el-button v-permission type="primary" @click="removeEnv(index)" link class="mt-1">
{{ $t('commons.button.delete') }}
</el-button>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="4">
<el-button v-permission @click="addEnv">
{{ $t('commons.button.add') }}
</el-button>
</el-col>
</el-row>
</div>
<Environment :environments="mcpServer.environments" :show-helper="false" />
</el-tab-pane>
<el-tab-pane :label="$t('container.mount')">
<Volumes :volumes="mcpServer.volumes" />
@@ -171,7 +144,8 @@ import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus';
import { ref, watch } from 'vue';
import Volumes from '../volume/index.vue';
import Environment from '@/views/website/runtime/components/environment/index.vue';
import Volumes from '@/views/website/runtime/components/volume/index.vue';
import Import from '../import/index.vue';
const open = ref(false);
@@ -216,8 +190,6 @@ const rules = ref({
containerName: [Rules.requiredInput],
url: [Rules.requiredInput],
ssePath: [Rules.requiredInput],
key: [Rules.requiredInput],
value: [Rules.requiredInput],
outputTransport: [Rules.requiredSelect],
streamableHttpPath: [Rules.requiredInput],
type: [Rules.requiredSelect],
@@ -288,17 +260,6 @@ watch(
},
);
const addEnv = () => {
mcpServer.value.environments.push({
key: '',
value: '',
});
};
const removeEnv = (index: number) => {
mcpServer.value.environments.splice(index, 1);
};
const normalizeGatewayConfig = () => {
mcpServer.value.gatewayImage = mcpServer.value.gatewayImage || defaultGatewayImages[mcpServer.value.type];
mcpServer.value.protocolVersion = mcpServer.value.protocolVersion || defaultProtocolVersion;

View File

@@ -50,6 +50,7 @@ const addEnv = () => {
props.volumes.push({
source: '',
target: '',
mode: 'rw',
});
};

View File

@@ -1,5 +1,5 @@
<template>
<el-text type="info">{{ $t('php.dateTimezoneHelper') }}</el-text>
<el-text v-if="showHelper" type="info">{{ $t('php.dateTimezoneHelper') }}</el-text>
<div class="mt-1.5">
<el-row :gutter="20" v-for="(env, index) in environments" :key="index">
<el-col :span="7">
@@ -42,8 +42,14 @@ const props = defineProps({
type: Array<Runtime.Environment>,
required: true,
},
showHelper: {
type: Boolean,
default: true,
},
});
const { showHelper } = props;
const rules = reactive<FormRules>({
value: [Rules.requiredInput],
});

View File

@@ -1,16 +1,16 @@
<template>
<el-tabs type="border-card">
<el-tab-pane :label="$t('commons.table.port')">
<PortConfig :exposedPorts="runtime.exposedPorts" />
<PortConfig :exposedPorts="exposedPorts" />
</el-tab-pane>
<el-tab-pane :label="$t('runtime.environment')">
<Environment :environments="runtime.environments" />
<Environment :environments="environments" />
</el-tab-pane>
<el-tab-pane :label="$t('container.mount')">
<Volumes :volumes="runtime.volumes" />
<Volumes :volumes="volumes" />
</el-tab-pane>
<el-tab-pane :label="$t('runtime.extraHosts')">
<ExtraHosts :extraHosts="runtime.extraHosts" />
<ExtraHosts :extraHosts="extraHosts" />
</el-tab-pane>
</el-tabs>
</template>
@@ -21,13 +21,13 @@ import Environment from '@/views/website/runtime/components/environment/index.vu
import Volumes from '@/views/website/runtime/components/volume/index.vue';
import ExtraHosts from '@/views/website/runtime/components/extra-hosts/index.vue';
import { useVModel } from '@vueuse/core';
import type { PropType } from 'vue';
import { computed, type PropType } from 'vue';
interface RuntimeConfigFields {
exposedPorts: any[];
environments: any[];
volumes: any[];
extraHosts: any[];
exposedPorts?: any[];
environments?: any[];
volumes?: any[];
extraHosts?: any[];
}
const props = defineProps({
@@ -42,4 +42,16 @@ const emit = defineEmits<{
}>();
const runtime = useVModel(props, 'modelValue', emit);
const ensureConfigArray = (key: keyof RuntimeConfigFields): any[] => {
if (!runtime.value[key]) {
runtime.value[key] = [];
}
return runtime.value[key] as any[];
};
const exposedPorts = computed(() => ensureConfigArray('exposedPorts'));
const environments = computed(() => ensureConfigArray('environments'));
const volumes = computed(() => ensureConfigArray('volumes'));
const extraHosts = computed(() => ensureConfigArray('extraHosts'));
</script>

View File

@@ -1,17 +1,25 @@
<template>
<div class="mt-1.5">
<el-row :gutter="20" v-for="(port, index) in exposedPorts" :key="index">
<el-col :span="7">
<el-col :span="6">
<el-form-item :prop="`exposedPorts.${index}.hostPort`" :rules="rules.port">
<el-input v-model.number="port.hostPort" :placeholder="$t('runtime.externalPort')" />
</el-form-item>
</el-col>
<el-col :span="7">
<el-col :span="6">
<el-form-item :prop="`exposedPorts.${index}.containerPort`" :rules="rules.port">
<el-input v-model.number="port.containerPort" :placeholder="$t('runtime.appPort')" />
</el-form-item>
</el-col>
<el-col :span="7">
<el-col :span="4">
<el-form-item>
<el-select v-model="port.protocol" :placeholder="$t('commons.table.protocol')">
<el-option label="tcp" value="tcp" />
<el-option label="udp" value="udp" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-text>{{ $t('app.allowPort') }}</el-text>
<el-switch
class="ml-1"
@@ -60,6 +68,7 @@ const addPort = () => {
hostPort: undefined,
containerPort: undefined,
hostIP: '',
protocol: 'tcp',
});
};

View File

@@ -11,6 +11,14 @@
<el-input v-model="volume.target" :placeholder="$t('container.containerDir')" />
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item>
<el-select v-model="volume.mode" :placeholder="$t('container.mode')">
<el-option :label="$t('container.modeRW')" value="rw" />
<el-option :label="$t('container.modeR')" value="ro" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item>
<el-button v-permission type="primary" @click="removeEnv(index)" link class="mt-1">
@@ -48,6 +56,7 @@ const addEnv = () => {
props.volumes.push({
source: '',
target: '',
mode: 'rw',
});
};

View File

@@ -122,19 +122,22 @@ const submit = async (formEl: FormInstance | undefined) => {
return;
}
if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {
const containerPortMap = new Map();
const hostPortMap = new Map();
const containerPortMap = new Map<string, boolean>();
const hostPortMap = new Map<string, boolean>();
for (const port of runtime.exposedPorts) {
if (containerPortMap[port.containerPort]) {
const protocol = port.protocol || 'tcp';
const containerPortKey = `${port.containerPort}/${protocol}`;
const hostPortKey = `${port.hostPort}/${protocol}`;
if (containerPortMap.has(containerPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
if (hostPortMap[port.hostPort]) {
if (hostPortMap.has(hostPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
hostPortMap[port.hostPort] = true;
containerPortMap[port.containerPort] = true;
hostPortMap.set(hostPortKey, true);
containerPortMap.set(containerPortKey, true);
}
}

View File

@@ -147,19 +147,22 @@ const submit = async (formEl: FormInstance | undefined) => {
return;
}
if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {
const containerPortMap = new Map();
const hostPortMap = new Map();
const containerPortMap = new Map<string, boolean>();
const hostPortMap = new Map<string, boolean>();
for (const port of runtime.exposedPorts) {
if (containerPortMap[port.containerPort]) {
const protocol = port.protocol || 'tcp';
const containerPortKey = `${port.containerPort}/${protocol}`;
const hostPortKey = `${port.hostPort}/${protocol}`;
if (containerPortMap.has(containerPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
if (hostPortMap[port.hostPort]) {
if (hostPortMap.has(hostPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
hostPortMap[port.hostPort] = true;
containerPortMap[port.containerPort] = true;
hostPortMap.set(hostPortKey, true);
containerPortMap.set(containerPortKey, true);
}
}

View File

@@ -135,19 +135,22 @@ const submit = async (formEl: FormInstance | undefined) => {
return;
}
if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {
const containerPortMap = new Map();
const hostPortMap = new Map();
const containerPortMap = new Map<string, boolean>();
const hostPortMap = new Map<string, boolean>();
for (const port of runtime.exposedPorts) {
if (containerPortMap[port.containerPort]) {
const protocol = port.protocol || 'tcp';
const containerPortKey = `${port.containerPort}/${protocol}`;
const hostPortKey = `${port.hostPort}/${protocol}`;
if (containerPortMap.has(containerPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
if (hostPortMap[port.hostPort]) {
if (hostPortMap.has(hostPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
hostPortMap[port.hostPort] = true;
containerPortMap[port.containerPort] = true;
hostPortMap.set(hostPortKey, true);
containerPortMap.set(containerPortKey, true);
}
}

View File

@@ -167,19 +167,22 @@ const submit = async (formEl: FormInstance | undefined) => {
return;
}
if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {
const containerPortMap = new Map();
const hostPortMap = new Map();
const containerPortMap = new Map<string, boolean>();
const hostPortMap = new Map<string, boolean>();
for (const port of runtime.exposedPorts) {
if (containerPortMap[port.containerPort]) {
const protocol = port.protocol || 'tcp';
const containerPortKey = `${port.containerPort}/${protocol}`;
const hostPortKey = `${port.hostPort}/${protocol}`;
if (containerPortMap.has(containerPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
if (hostPortMap[port.hostPort]) {
if (hostPortMap.has(hostPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
hostPortMap[port.hostPort] = true;
containerPortMap[port.containerPort] = true;
hostPortMap.set(hostPortKey, true);
containerPortMap.set(containerPortKey, true);
}
}

View File

@@ -111,19 +111,22 @@ const submit = async (formEl: FormInstance | undefined) => {
return;
}
if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {
const containerPortMap = new Map();
const hostPortMap = new Map();
const containerPortMap = new Map<string, boolean>();
const hostPortMap = new Map<string, boolean>();
for (const port of runtime.exposedPorts) {
if (containerPortMap[port.containerPort]) {
const protocol = port.protocol || 'tcp';
const containerPortKey = `${port.containerPort}/${protocol}`;
const hostPortKey = `${port.hostPort}/${protocol}`;
if (containerPortMap.has(containerPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
if (hostPortMap[port.hostPort]) {
if (hostPortMap.has(hostPortKey)) {
MsgError(i18n.global.t('runtime.portError'));
return;
}
hostPortMap[port.hostPort] = true;
containerPortMap[port.containerPort] = true;
hostPortMap.set(hostPortKey, true);
containerPortMap.set(containerPortKey, true);
}
}