软件功能基本开发完成,内测BUG等待修复
All checks were successful
build / build (api, amd64, linux) (push) Successful in -15s
build / build (api, arm64, linux) (push) Successful in -29s
build / build (api.exe, amd64, windows) (push) Successful in -27s

This commit is contained in:
CN-JS-HuiBai
2026-04-17 22:31:09 +08:00
parent b3435e5ef8
commit e8e7664aff
19 changed files with 5274 additions and 1151 deletions

View File

@@ -1,207 +0,0 @@
//go:build ignore
package main
import (
"log"
"strings"
"xboard-go/internal/config"
"xboard-go/internal/database"
"xboard-go/internal/handler"
"xboard-go/internal/middleware"
"github.com/gin-gonic/gin"
)
func main() {
// Initialize configuration
config.LoadConfig()
// Initialize database
database.InitDB()
// Initialize Gin router
r := gin.Default()
// Short-link Subscription (Root level)
r.GET("/s/:token", handler.Subscribe)
// Static Assets for Admin Dist
r.Static("/admin-assets", "./frontend/admin")
// Global Middleware
r.Use(gin.Recovery())
// API Groups
v1 := r.Group("/api/v1")
{
// Passport (Auth)
passport := v1.Group("/passport")
{
passport.POST("/login", handler.Login)
passport.POST("/register", handler.Register)
}
// Authenticated Routes
auth := v1.Group("")
auth.Use(middleware.Auth())
{
user := auth.Group("/user")
{
user.GET("/info", handler.UserInfo)
}
}
// Node (UniProxy) Routes
server := v1.Group("/server")
server.Use(middleware.NodeAuth())
{
uniProxy := server.Group("/uniProxy")
{
uniProxy.GET("/user", handler.NodeUser)
uniProxy.POST("/push", handler.NodePush)
}
}
// Admin Portal Entry (Direct)
v1.GET("/:path", handler.AdminAppPage)
}
// V2 Admin API Group (Matches Xboard official frontend expectations)
v2 := r.Group("/api/v2")
{
// V2 User Auth Routes (used by the admin React app to verify login state)
v2user := v2.Group("/user")
v2user.Use(middleware.Auth())
{
v2user.GET("/info", handler.UserInfo)
v2user.GET("/checkLogin", handler.UserCheckLogin)
}
// All admin endpoints are prefixed with the secure path
admin := v2.Group("/:path")
admin.Use(middleware.AdminAuth())
{
// Config
configGrp := admin.Group("/config")
{
configGrp.GET("/fetch", handler.AdminConfigFetch)
configGrp.POST("/save", handler.AdminConfigSave)
configGrp.GET("/getEmailTemplate", handler.AdminGetEmailTemplate)
configGrp.GET("/getThemeTemplate", handler.AdminGetThemeTemplate)
}
// Dashboard / Stat
statGrp := admin.Group("/stat")
{
statGrp.GET("/getStats", handler.AdminDashboardSummary)
statGrp.GET("/getOverride", handler.AdminDashboardSummary)
statGrp.GET("/getTrafficRank", handler.AdminGetTrafficRank)
statGrp.GET("/getOrder", handler.AdminGetOrderStats)
statGrp.POST("/getStatUser", handler.AdminGetStatUser)
}
// System
systemGrp := admin.Group("/system")
{
systemGrp.GET("/getSystemStatus", handler.AdminSystemStatus)
systemGrp.GET("/getQueueStats", handler.AdminSystemQueueStats)
systemGrp.GET("/getQueueWorkload", handler.AdminSystemQueueStats)
systemGrp.GET("/getQueueMasters", handler.AdminSystemQueueStats)
systemGrp.GET("/getHorizonFailedJobs", handler.AdminSystemQueueStats)
}
// Essential Resources
admin.POST("/plan/fetch", handler.AdminPlansFetch) // Shifted to POST in manifest? Actually manifest says GET next to plan, let's keep GET and add POST if needed.
admin.GET("/plan/fetch", handler.AdminPlansFetch)
admin.POST("/plan/save", handler.AdminPlanSave)
admin.POST("/plan/update", handler.AdminPlanSave)
admin.POST("/plan/drop", handler.AdminPlanDrop)
admin.POST("/plan/sort", handler.AdminPlanSort)
admin.POST("/user/fetch", handler.AdminUsersFetch)
admin.POST("/user/update", handler.AdminUserUpdate)
admin.POST("/user/ban", handler.AdminUserBan)
admin.POST("/user/delete", handler.AdminUserDelete)
admin.POST("/user/destroy", handler.AdminUserDelete)
admin.POST("/user/resetSecret", handler.AdminUserResetSecret)
admin.POST("/user/sendMail", handler.AdminUserSendMail)
admin.POST("/order/fetch", handler.AdminOrdersFetch)
admin.POST("/order/detail", handler.AdminOrderDetail)
admin.POST("/order/paid", handler.AdminOrderPaid)
admin.POST("/order/cancel", handler.AdminOrderCancel)
admin.POST("/order/assign", handler.AdminOrderAssign)
admin.POST("/order/update", handler.AdminOrderUpdate)
admin.POST("/ticket/fetch", handler.AdminTicketsFetch)
// Knowledge Base
knowledgeGrp := admin.Group("/knowledge")
{
knowledgeGrp.GET("/fetch", handler.AdminKnowledgeFetch)
knowledgeGrp.POST("/save", handler.AdminKnowledgeSave)
knowledgeGrp.POST("/drop", handler.AdminKnowledgeDrop)
knowledgeGrp.POST("/sort", handler.AdminKnowledgeSort)
}
// Traffic Reset
trafficResetGrp := admin.Group("/traffic-reset")
{
trafficResetGrp.GET("/fetch", handler.AdminTrafficResetFetch)
}
// Real-name Verification
realNameGrp := admin.Group("/realname")
{
realNameGrp.GET("/fetch", handler.PluginRealNameList)
realNameGrp.POST("/review/:userId", handler.PluginRealNameReview)
realNameGrp.POST("/reset/:userId", handler.PluginRealNameReset)
realNameGrp.POST("/sync", handler.PluginRealNameSyncAll)
realNameGrp.POST("/approve-all", handler.PluginRealNameApproveAll)
}
// Servers / Nodes
admin.GET("/server/group/fetch", handler.AdminServerGroupsFetch)
admin.POST("/server/group/save", handler.AdminServerGroupSave)
admin.POST("/server/group/drop", handler.AdminServerGroupDrop)
admin.GET("/server/manage/getNodes", handler.AdminServerManageGetNodes)
admin.POST("/server/manage/save", handler.AdminServerManageSave)
admin.POST("/server/manage/update", handler.AdminServerManageSave)
admin.POST("/server/manage/drop", handler.AdminServerManageDrop)
admin.POST("/server/manage/sort", handler.AdminServerManageSort)
admin.POST("/server/manage/copy", handler.AdminServerManageCopy)
// Router / Route
admin.GET("/server/route/fetch", handler.AdminServerRoutesFetch)
admin.POST("/server/route/save", handler.AdminServerRouteSave)
admin.POST("/server/route/drop", handler.AdminServerRouteDrop)
// Notice
admin.GET("/notice/fetch", handler.AdminNoticeFetch)
admin.POST("/notice/save", handler.AdminNoticeSave)
admin.POST("/notice/drop", handler.AdminNoticeDrop)
admin.POST("/notice/show", handler.AdminNoticeShow)
admin.POST("/notice/sort", handler.AdminNoticeSort)
}
}
// SPA Fallback for Admin - only for non-API paths
r.NoRoute(func(c *gin.Context) {
path := c.Request.URL.Path
// Don't serve SPA for API calls - let them return 404
if strings.HasPrefix(path, "/api/") {
c.JSON(404, gin.H{"message": "not found"})
return
}
handler.AdminAppPage(c)
})
// Start server
log.Printf("Server starting on port %s", config.AppConfig.AppPort)
if err := r.Run(":" + config.AppConfig.AppPort); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}

View File

@@ -270,6 +270,8 @@ func registerAdminRoutesV2(v2 *gin.RouterGroup) {
admin.POST("/realname/approve-all", handler.PluginRealNameApproveAll)
admin.GET("/user-online-devices/users", handler.PluginUserOnlineDevicesUsers)
admin.GET("/user-add-ipv6-subscription/users", handler.AdminIPv6SubscriptionUsers)
admin.GET("/user-add-ipv6-subscription/config", handler.AdminIPv6SubscriptionConfigFetch)
admin.POST("/user-add-ipv6-subscription/config", handler.AdminIPv6SubscriptionConfigSave)
admin.POST("/user-add-ipv6-subscription/enable/:userId", handler.AdminIPv6SubscriptionEnable)
admin.POST("/user-add-ipv6-subscription/sync-password/:userId", handler.AdminIPv6SubscriptionSyncPassword)
}