From a5519c2f6c5c50a3c8090a50348b8cba1dec776e Mon Sep 17 00:00:00 2001 From: cnbugs Date: Mon, 10 Aug 2026 00:35:30 +0800 Subject: [PATCH] Feature: per-instance public_host for .ovpn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating/editing an instance, add '公网地址' (public_host) field. This is the domain/IP clients will use to reach the OpenVPN server. When downloading a .ovpn, this field is used first, falling back to: 1. ?host= query parameter (one-time override) 2. Request Host header 3. 'vpn.example.com' default This eliminates the need to manually edit .ovpn files after download when the server's public address differs from its internal IP. Backend changes: model.Instance: add PublicHost string field service.GenerateOVPN: priority PublicHost > remoteHost > default service.UpdateInstance: PublicHost persisted (already via JSON tag) Frontend changes: Instances.vue: new '公网地址' input in create/edit dialog Instances.vue: new column in list table (shows '未配置' when empty) Users.vue: hint text updated to '可选: 覆盖实例公网地址' E2E verified: public_host='vpn.yunwei.blog' → .ovpn has 'remote vpn.yunwei.blog 1194' public_host='' + host=X → .ovpn has 'remote X 1194' public_host set + no host → public_host wins --- backend/internal/model/model.go | 1 + backend/internal/service/service.go | 9 +++++++-- frontend/src/views/Instances.vue | 14 +++++++++++++- frontend/src/views/Users.vue | 2 +- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/backend/internal/model/model.go b/backend/internal/model/model.go index faf5b92..0be5ac7 100644 --- a/backend/internal/model/model.go +++ b/backend/internal/model/model.go @@ -41,6 +41,7 @@ type Instance struct { AccessMode AccessMode `json:"access_mode"` // open | whitelist AllowNetworks []string `json:"allow_networks"` // 实例级白名单 CIDR 列表 AuthMode AuthMode `json:"auth_mode"` // cert | cert+password + PublicHost string `json:"public_host"` // 公网域名/IP,生成 .ovpn 时优先用它;空则用下载时前端传入的 host Status string `json:"status"` // running/stopped/error PID int `json:"pid"` CreatedAt time.Time `json:"created_at"` diff --git a/backend/internal/service/service.go b/backend/internal/service/service.go index 8f0f40c..52fcd3c 100644 --- a/backend/internal/service/service.go +++ b/backend/internal/service/service.go @@ -400,7 +400,8 @@ func (s *Service) CreateUser(u model.VPNUser) (*model.VPNUser, error) { return &u, nil } -// GenerateOVPN 下载/重新生成 .ovpn,remoteHost 由前端传入。 +// GenerateOVPN 下载/重新生成 .ovpn。 +// remoteHost 优先级:Instance.PublicHost > 传入的 remoteHost > "vpn.example.com"。 func (s *Service) GenerateOVPN(userID, remoteHost string) (string, error) { u, err := s.Store.GetUser(userID) if err != nil { @@ -410,7 +411,11 @@ func (s *Service) GenerateOVPN(userID, remoteHost string) (string, error) { if err != nil { return "", err } - return s.Ovm.GenerateClientOVPNFor(u, in, remoteHost) + host := in.PublicHost + if host == "" { + host = remoteHost + } + return s.Ovm.GenerateClientOVPNFor(u, in, host) } // RevokeUser 吊销用户:禁用 + 标记。 diff --git a/frontend/src/views/Instances.vue b/frontend/src/views/Instances.vue index 8d9e79c..7129cd5 100644 --- a/frontend/src/views/Instances.vue +++ b/frontend/src/views/Instances.vue @@ -12,6 +12,12 @@ + + +