mirror of
https://github.com/tnb-labs/panel.git
synced 2026-09-19 10:03:36 +08:00
refactor(template): 移除部署编排时自动放行端口选项
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,6 @@ type TemplateRepo interface {
|
||||
LoadLocalTemplates() api.Templates
|
||||
Callback(slug string) error
|
||||
WriteCompose(name, compose string, envs []types.KV) (string, error)
|
||||
OpenComposePorts(compose string) error
|
||||
}
|
||||
|
||||
type TemplateUsecase struct {
|
||||
@@ -80,7 +79,7 @@ func (uc *TemplateUsecase) Callback(slug string) error {
|
||||
}
|
||||
|
||||
// CreateCompose 创建编排
|
||||
func (uc *TemplateUsecase) CreateCompose(name, compose string, envs []types.KV, autoFirewall bool) (string, error) {
|
||||
func (uc *TemplateUsecase) CreateCompose(name, compose string, envs []types.KV) (string, error) {
|
||||
dir := filepath.Join(app.Root, "compose", name)
|
||||
|
||||
// 检查编排是否已存在
|
||||
@@ -88,15 +87,5 @@ func (uc *TemplateUsecase) CreateCompose(name, compose string, envs []types.KV,
|
||||
return "", errors.New(uc.t.Get("compose %s already exists", name))
|
||||
}
|
||||
|
||||
dir, err := uc.repo.WriteCompose(name, compose, envs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 自动放行端口
|
||||
if autoFirewall {
|
||||
_ = uc.repo.OpenComposePorts(compose)
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
return uc.repo.WriteCompose(name, compose, envs)
|
||||
}
|
||||
|
||||
@@ -6,31 +6,26 @@ import (
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"go.yaml.in/yaml/v4"
|
||||
|
||||
"github.com/acepanel/panel/v3/internal/app"
|
||||
"github.com/acepanel/panel/v3/internal/biz"
|
||||
"github.com/acepanel/panel/v3/pkg/api"
|
||||
"github.com/acepanel/panel/v3/pkg/firewall"
|
||||
"github.com/acepanel/panel/v3/pkg/types"
|
||||
)
|
||||
|
||||
type templateRepo struct {
|
||||
log *slog.Logger
|
||||
api *api.API
|
||||
firewall firewall.Firewall
|
||||
log *slog.Logger
|
||||
api *api.API
|
||||
}
|
||||
|
||||
func NewTemplateRepo(log *slog.Logger) biz.TemplateRepo {
|
||||
return &templateRepo{
|
||||
log: log,
|
||||
api: api.NewAPI(app.Version, app.Locale),
|
||||
firewall: firewall.NewFirewall(),
|
||||
log: log,
|
||||
api: api.NewAPI(app.Version, app.Locale),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,82 +59,6 @@ func (r *templateRepo) WriteCompose(name, compose string, envs []types.KV) (stri
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// OpenComposePorts 自动放行编排端口
|
||||
func (r *templateRepo) OpenComposePorts(compose string) error {
|
||||
ports := r.parsePortsFromCompose(compose)
|
||||
for _, port := range ports {
|
||||
_ = r.firewall.Port(firewall.FireInfo{
|
||||
Family: "ipv4",
|
||||
PortStart: port.Port,
|
||||
PortEnd: port.Port,
|
||||
Protocol: port.Protocol,
|
||||
Strategy: firewall.StrategyAccept,
|
||||
Direction: "in",
|
||||
}, firewall.OperationAdd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type composePort struct {
|
||||
Port uint
|
||||
Protocol firewall.Protocol
|
||||
}
|
||||
|
||||
// parsePortsFromCompose 从 compose 文件中解析端口
|
||||
func (r *templateRepo) parsePortsFromCompose(compose string) []composePort {
|
||||
var ports []composePort
|
||||
seen := make(map[string]bool)
|
||||
|
||||
// 匹配 ports 部分的端口映射
|
||||
// 支持格式: "8080:80", "8080:80/tcp", "8080:80/udp", "80", "80/tcp"
|
||||
portRegex := regexp.MustCompile(`(?m)^\s*-\s*["']?(\d+)(?::\d+)?(?:/(\w+))?["']?\s*$`)
|
||||
matches := portRegex.FindAllStringSubmatch(compose, -1)
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
portStr := match[1]
|
||||
protocol := firewall.ProtocolTCP
|
||||
if len(match) > 2 && match[2] != "" {
|
||||
switch strings.ToLower(match[2]) {
|
||||
case "udp":
|
||||
protocol = firewall.ProtocolUDP
|
||||
case "tcp":
|
||||
protocol = firewall.ProtocolTCP
|
||||
}
|
||||
}
|
||||
|
||||
// 去重
|
||||
key := portStr + "/" + string(protocol)
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
|
||||
var port uint
|
||||
if _, _, found := strings.Cut(portStr, ":"); found {
|
||||
// 格式: host:container
|
||||
parts := strings.Split(portStr, ":")
|
||||
if len(parts) > 0 {
|
||||
port = cast.ToUint(parts[0])
|
||||
}
|
||||
} else {
|
||||
port = cast.ToUint(portStr)
|
||||
}
|
||||
|
||||
if port > 0 && port <= 65535 {
|
||||
ports = append(ports, composePort{
|
||||
Port: port,
|
||||
Protocol: protocol,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ports
|
||||
}
|
||||
|
||||
// LoadLocalTemplates 从本地目录加载模板
|
||||
func (r *templateRepo) LoadLocalTemplates() api.Templates {
|
||||
dir := filepath.Join(app.Root, "panel/storage/templates")
|
||||
|
||||
@@ -7,9 +7,8 @@ type TemplateSlug struct {
|
||||
}
|
||||
|
||||
type TemplateCreate struct {
|
||||
Slug string `json:"slug" validate:"required"`
|
||||
Name string `json:"name" validate:"required && regex:\"^[a-zA-Z0-9_-]+$\""`
|
||||
Compose string `json:"compose"`
|
||||
Envs []types.KV `json:"envs"`
|
||||
AutoFirewall bool `json:"auto_firewall"`
|
||||
Slug string `json:"slug" validate:"required"`
|
||||
Name string `json:"name" validate:"required && regex:\"^[a-zA-Z0-9_-]+$\""`
|
||||
Compose string `json:"compose"`
|
||||
Envs []types.KV `json:"envs"`
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func (s *TemplateService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// 创建编排
|
||||
dir, err := s.templateRepo.CreateCompose(req.Name, compose, req.Envs, req.AutoFirewall)
|
||||
dir, err := s.templateRepo.CreateCompose(req.Name, compose, req.Envs)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
|
||||
@@ -134,57 +134,6 @@ func (_c *TemplateRepo_LoadLocalTemplates_Call) RunAndReturn(run func() api.Temp
|
||||
return _c
|
||||
}
|
||||
|
||||
// OpenComposePorts provides a mock function for the type TemplateRepo
|
||||
func (_mock *TemplateRepo) OpenComposePorts(compose string) error {
|
||||
ret := _mock.Called(compose)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for OpenComposePorts")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = returnFunc(compose)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// TemplateRepo_OpenComposePorts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OpenComposePorts'
|
||||
type TemplateRepo_OpenComposePorts_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// OpenComposePorts is a helper method to define mock.On call
|
||||
// - compose string
|
||||
func (_e *TemplateRepo_Expecter) OpenComposePorts(compose any) *TemplateRepo_OpenComposePorts_Call {
|
||||
return &TemplateRepo_OpenComposePorts_Call{Call: _e.mock.On("OpenComposePorts", compose)}
|
||||
}
|
||||
|
||||
func (_c *TemplateRepo_OpenComposePorts_Call) Run(run func(compose string)) *TemplateRepo_OpenComposePorts_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *TemplateRepo_OpenComposePorts_Call) Return(err error) *TemplateRepo_OpenComposePorts_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *TemplateRepo_OpenComposePorts_Call) RunAndReturn(run func(compose string) error) *TemplateRepo_OpenComposePorts_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// WriteCompose provides a mock function for the type TemplateRepo
|
||||
func (_mock *TemplateRepo) WriteCompose(name string, compose string, envs []types.KV) (string, error) {
|
||||
ret := _mock.Called(name, compose, envs)
|
||||
|
||||
@@ -12,7 +12,6 @@ export default {
|
||||
name: string
|
||||
compose: string
|
||||
envs: { key: string; value: string }[]
|
||||
auto_firewall: boolean
|
||||
}): any => http.Post('/template', data),
|
||||
// 模版下载回调
|
||||
callback: (slug: string): any => http.Post(`/template/${slug}/callback`),
|
||||
|
||||
@@ -43,7 +43,6 @@ const upCommand = ref('')
|
||||
const deployModel = reactive({
|
||||
name: '',
|
||||
autoStart: true,
|
||||
autoFirewall: false,
|
||||
envs: {} as Record<string, any>,
|
||||
})
|
||||
|
||||
@@ -230,7 +229,6 @@ const handleSubmit = async () => {
|
||||
name: deployModel.name,
|
||||
compose: finalCompose.value,
|
||||
envs: finalEnvs.value,
|
||||
auto_firewall: deployModel.autoFirewall,
|
||||
}),
|
||||
)
|
||||
.onSuccess(({ data }) => {
|
||||
@@ -287,7 +285,6 @@ const handlePrev = () => {
|
||||
const resetForm = () => {
|
||||
deployModel.name = props.template?.slug || ''
|
||||
deployModel.autoStart = true
|
||||
deployModel.autoFirewall = false
|
||||
deployModel.envs = {}
|
||||
currentStep.value = 1
|
||||
deployMode.value = 'create'
|
||||
@@ -407,14 +404,6 @@ const composeOptions = computed(() => {
|
||||
<n-switch v-model:value="deployModel.autoStart" />
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="8">
|
||||
<n-form-item path="autoFirewall" :label="$gettext('Auto Firewall')">
|
||||
<n-switch v-model:value="deployModel.autoFirewall" />
|
||||
<template #feedback>
|
||||
<span>{{ $gettext('Automatically allow ports defined in compose') }}</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
|
||||
<!-- 环境变量 -->
|
||||
@@ -606,11 +595,6 @@ const composeOptions = computed(() => {
|
||||
{{ deployModel.autoStart ? $gettext('Yes') : $gettext('No') }}
|
||||
</n-tag>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item v-if="deployMode === 'create'" :label="$gettext('Auto Firewall')">
|
||||
<n-tag :type="deployModel.autoFirewall ? 'success' : 'default'">
|
||||
{{ deployModel.autoFirewall ? $gettext('Yes') : $gettext('No') }}
|
||||
</n-tag>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item :label="$gettext('Environment Variables')">
|
||||
{{ finalEnvs.length }} {{ $gettext('variables') }}
|
||||
</n-descriptions-item>
|
||||
|
||||
Reference in New Issue
Block a user