Go(Gin) + React(Vite + shadcn/ui + Tailwind) 前后端分离,全中文 UI - 集群管理:Kubeconfig / Token 双接入,kubectl + helm 部署 - 主机管理:SSH 密钥 / 密码双认证,CPU/内存/磁盘使用率监控,SSH 错误中文友好提示 - 存储配置、部署管理、系统设置 - Docker 镜像内置 kubectl + helm,开箱即用 - run.sh 一键启停(start/stop/restart/status) Co-Authored-By: Claude <noreply@anthropic.com>
128 lines
4.0 KiB
Go
128 lines
4.0 KiB
Go
package routers
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"lokideploy/controllers"
|
|
"lokideploy/middleware"
|
|
|
|
"github.com/gin-contrib/cors"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func SetupRouter() *gin.Engine {
|
|
r := gin.Default()
|
|
|
|
// CORS
|
|
cfg := cors.DefaultConfig()
|
|
cfg.AllowAllOrigins = true
|
|
cfg.AllowHeaders = []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With"}
|
|
cfg.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"}
|
|
r.Use(cors.New(cfg))
|
|
|
|
// Controllers
|
|
authCtrl := controllers.NewAuthController()
|
|
clusterCtrl := controllers.NewClusterController()
|
|
hostCtrl := controllers.NewHostController()
|
|
storageCtrl := controllers.NewStorageController()
|
|
deploymentCtrl := controllers.NewDeploymentController()
|
|
|
|
// API v1
|
|
api := r.Group("/api/v1")
|
|
|
|
// Public
|
|
api.POST("/auth/login", authCtrl.Login)
|
|
|
|
// Auth required
|
|
authorized := api.Group("")
|
|
authorized.Use(middleware.JWTAuth())
|
|
|
|
// Auth routes
|
|
authorized.GET("/auth/user", authCtrl.GetUser)
|
|
authorized.POST("/auth/password", authCtrl.ChangePassword)
|
|
authorized.POST("/auth/register", middleware.AdminAuth(), authCtrl.Register)
|
|
|
|
// Clusters
|
|
authorized.GET("/clusters", clusterCtrl.List)
|
|
authorized.GET("/clusters/:id", clusterCtrl.Get)
|
|
authorized.POST("/clusters", clusterCtrl.Create)
|
|
authorized.PUT("/clusters/:id", clusterCtrl.Update)
|
|
authorized.DELETE("/clusters/:id", clusterCtrl.Delete)
|
|
authorized.POST("/clusters/:id/validate", clusterCtrl.Validate)
|
|
authorized.GET("/clusters/:id/info", clusterCtrl.GetInfo)
|
|
|
|
// Hosts
|
|
authorized.GET("/hosts", hostCtrl.List)
|
|
authorized.GET("/hosts/:id", hostCtrl.Get)
|
|
authorized.POST("/hosts", hostCtrl.Create)
|
|
authorized.PUT("/hosts/:id", hostCtrl.Update)
|
|
authorized.DELETE("/hosts/:id", hostCtrl.Delete)
|
|
authorized.POST("/hosts/:id/validate", hostCtrl.Validate)
|
|
authorized.GET("/hosts/:id/info", hostCtrl.GetInfo)
|
|
authorized.POST("/hosts/:id/execute", hostCtrl.Execute)
|
|
|
|
// Storage
|
|
authorized.GET("/storages", storageCtrl.List)
|
|
authorized.GET("/storages/:id", storageCtrl.Get)
|
|
authorized.POST("/storages", storageCtrl.Create)
|
|
authorized.PUT("/storages/:id", storageCtrl.Update)
|
|
authorized.DELETE("/storages/:id", storageCtrl.Delete)
|
|
authorized.GET("/storages/default", storageCtrl.GetDefault)
|
|
|
|
// Deployments
|
|
authorized.GET("/deployments", deploymentCtrl.List)
|
|
authorized.GET("/deployments/:id", deploymentCtrl.Get)
|
|
authorized.POST("/deployments", deploymentCtrl.Create)
|
|
authorized.PUT("/deployments/:id", deploymentCtrl.Update)
|
|
authorized.DELETE("/deployments/:id", deploymentCtrl.Delete)
|
|
authorized.POST("/deployments/:id/deploy", deploymentCtrl.Deploy)
|
|
authorized.POST("/deployments/:id/stop", deploymentCtrl.Stop)
|
|
authorized.POST("/deployments/:id/restart", deploymentCtrl.Restart)
|
|
authorized.GET("/deployments/:id/logs", deploymentCtrl.GetLogs)
|
|
authorized.GET("/deployments/stats", deploymentCtrl.GetStats)
|
|
|
|
// Static files - use executable directory as base
|
|
distPath := findDistPath()
|
|
|
|
if distPath != "" {
|
|
r.Static("/assets", filepath.Join(distPath, "assets"))
|
|
r.NoRoute(func(c *gin.Context) {
|
|
if strings.HasPrefix(c.Request.URL.Path, "/api/") {
|
|
c.JSON(404, gin.H{"code": 404, "message": "API endpoint not found"})
|
|
return
|
|
}
|
|
c.File(filepath.Join(distPath, "index.html"))
|
|
})
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
// findDistPath finds the frontend dist directory
|
|
func findDistPath() string {
|
|
// Try relative to executable
|
|
if ex, err := os.Executable(); err == nil {
|
|
execDir := filepath.Dir(ex)
|
|
// If running via "go run", executable is in temp dir - fall through
|
|
if !strings.Contains(execDir, "/tmp/go-build") {
|
|
for _, rel := range []string{"dist", "../dist", "../../dist"} {
|
|
p := filepath.Join(execDir, rel)
|
|
if _, err := os.Stat(filepath.Join(p, "index.html")); err == nil {
|
|
return p
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Try working directory relative paths
|
|
for _, p := range []string{"./dist", "../dist", "/app/dist"} {
|
|
if abs, err := filepath.Abs(p); err == nil {
|
|
if _, err := os.Stat(filepath.Join(abs, "index.html")); err == nil {
|
|
return abs
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|