refactor(template): 移除部署编排时自动放行端口选项

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
耗子
2026-09-06 04:50:59 +08:00
co-authored by Claude Fable 5.1
parent 29676d312f
commit b93581ef57
7 changed files with 11 additions and 172 deletions
+2 -13
View File
@@ -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)
}
+4 -85
View File
@@ -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")
+4 -5
View File
@@ -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"`
}
+1 -1
View File
@@ -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
-51
View File
@@ -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)
-1
View File
@@ -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`),
-16
View File
@@ -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>