feat(tls): add optional nginx setup and gateway preview
This commit is contained in:
1 parent
9f743e19ad
commit
d2f1f5040e
15 files changed
+753
-16
No files matched your search
@@ -39,6 +39,7 @@ import (
|
||||
type Server struct {
|
||||
mgr *core.Manager
|
||||
taskMgr *taskmgr.Manager
|
||||
router *gin.Engine
|
||||
}
|
||||
|
||||
func NewServer(mgr *core.Manager) *Server { return &Server{mgr: mgr, taskMgr: taskmgr.NewManager()} }
|
||||
@@ -210,6 +211,7 @@ func requirePasswordChanged() gin.HandlerFunc {
|
||||
|
||||
// RegisterRoutes 权限:viewer 只读;operator 专线运维;admin 用户/LDAP/插件/审计
|
||||
func (s *Server) RegisterRoutes(r *gin.Engine) {
|
||||
s.router = r
|
||||
r.Use(securityHeaders())
|
||||
api := r.Group("/api")
|
||||
api.POST("/login", s.login)
|
||||
@@ -286,6 +288,10 @@ func (s *Server) RegisterRoutes(r *gin.Engine) {
|
||||
admin.POST("/system/fix", s.systemFix)
|
||||
admin.GET("/system/tls", s.tlsStatus)
|
||||
admin.PUT("/system/tls/domain", s.setTLSDomain)
|
||||
admin.PUT("/system/tls/mode", s.setTLSMode)
|
||||
admin.GET("/system/tls/nginx-preview", s.nginxPreview)
|
||||
admin.GET("/system/tls/nginx-setup", s.nginxSetupStatus)
|
||||
admin.POST("/system/tls/nginx-setup", s.initializeNginx)
|
||||
admin.GET("/users", s.listUsers)
|
||||
admin.POST("/users", s.createUser)
|
||||
admin.PUT("/users/:id", s.updateUser)
|
||||
|
||||
@@ -1,19 +1,92 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"vpn-platform/backend/internal/nginxconfig"
|
||||
"vpn-platform/backend/internal/nginxsetup"
|
||||
"vpn-platform/backend/internal/service"
|
||||
"vpn-platform/backend/internal/store"
|
||||
"vpn-platform/backend/internal/tlsservice"
|
||||
)
|
||||
|
||||
func (s *Server) nginxSetupStatus(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, nginxsetup.Inspect())
|
||||
}
|
||||
|
||||
func (s *Server) initializeNginx(c *gin.Context) {
|
||||
status := nginxsetup.Inspect()
|
||||
if !status.Supported || !status.Root {
|
||||
c.JSON(http.StatusPreconditionFailed, gin.H{"error": "仅支持 root 运行的 Ubuntu / TencentOS Server 4"})
|
||||
return
|
||||
}
|
||||
if !nginxsetup.Begin() {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "初始化正在运行,请等待完成"})
|
||||
return
|
||||
}
|
||||
defer nginxsetup.End()
|
||||
c.Header("Content-Type", "application/x-ndjson; charset=utf-8")
|
||||
c.Header("Cache-Control", "no-store")
|
||||
c.Header("X-Accel-Buffering", "no")
|
||||
c.Status(http.StatusOK)
|
||||
write := func(kind, message string) {
|
||||
payload, _ := json.Marshal(gin.H{"type": kind, "message": message})
|
||||
fmt.Fprintf(c.Writer, "%s\n", payload)
|
||||
c.Writer.Flush()
|
||||
}
|
||||
write("info", "开始初始化 Nginx / Certbot;此步骤不会启用 80/443 或签发证书。\n")
|
||||
err := nginxsetup.Run(func(message string) { write("output", message) })
|
||||
if err != nil {
|
||||
store.Audit(c.GetString("username"), "nginx_setup", "failed", err.Error())
|
||||
write("error", err.Error())
|
||||
return
|
||||
}
|
||||
store.Audit(c.GetString("username"), "nginx_setup", "completed", "software and webroot ready")
|
||||
write("done", "初始化完成;继续配置 HTTP-01 站点与证书。")
|
||||
}
|
||||
|
||||
func (s *Server) tlsStatus(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, tlsservice.Status())
|
||||
}
|
||||
|
||||
func (s *Server) nginxPreview(c *gin.Context) {
|
||||
status := tlsservice.Status()
|
||||
if status.ConfiguredDomain == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请先在页面保存域名"})
|
||||
return
|
||||
}
|
||||
httpSite, httpsSite, err := nginxconfig.Render(status.ConfiguredDomain, s.router.Routes())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"http_site": httpSite, "https_site": httpsSite,
|
||||
"warning": "仅预览:先安装 Nginx/Certbot,停用 Go 的 80/443 监听并签出证书;认证域名须单独配置。",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) setTLSMode(c *gin.Context) {
|
||||
var req struct {
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请输入入口模式"})
|
||||
return
|
||||
}
|
||||
if err := tlsservice.ConfigureMode(service.BaseDir(), req.Mode); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
store.Audit(c.GetString("username"), "tls_mode_configured", req.Mode, "需检查外部入口后重启")
|
||||
status := tlsservice.Status()
|
||||
c.JSON(http.StatusOK, gin.H{"status": status, "restart_required": status.RestartRequired})
|
||||
}
|
||||
|
||||
func (s *Server) setTLSDomain(c *gin.Context) {
|
||||
var req struct {
|
||||
Domain string `json:"domain"`
|
||||
|
||||
Reference in new issue
Block a user