基本功能复刻完成
All checks were successful
build / build (api, amd64, linux) (push) Successful in -47s
build / build (api, arm64, linux) (push) Successful in -47s
build / build (api.exe, amd64, windows) (push) Successful in -48s

This commit is contained in:
CN-JS-HuiBai
2026-04-17 12:24:00 +08:00
parent 06da23fbbc
commit 981ee4f406
37 changed files with 11737 additions and 770 deletions

View File

@@ -4,8 +4,6 @@ package main
import (
"log"
"net/http"
"strings"
"xboard-go/internal/config"
"xboard-go/internal/database"
"xboard-go/internal/handler"
@@ -24,6 +22,12 @@ func main() {
// 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())
@@ -37,41 +41,16 @@ func main() {
passport.POST("/register", handler.Register)
}
// Subscription (Client) Routes
v1.GET("/s/:token", handler.Subscribe)
// Authenticated Routes
auth := v1.Group("")
auth.Use(middleware.Auth())
{
// User module
user := auth.Group("/user")
{
user.GET("/info", handler.UserInfo)
}
}
// Admin Routes
admin := auth.Group("/admin")
admin.Use(middleware.AdminAuth())
{
admin.GET("/stats", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Admin Stats"})
})
}
// Admin Portal (Secure Path Entry)
v1.GET("/:path", handler.AdminPortal)
v1.GET("/:path/realname", handler.RealNameIndex)
// Plugin API (Secure Path)
realname := v1.Group("/api/v1/:path/realname")
realname.Use(middleware.AdminAuth()) // Ensure only admins can access plugin APIs
{
realname.GET("/records", handler.RealNameRecords)
realname.POST("/review/:id", handler.RealNameReview)
}
// Node (UniProxy) Routes
server := v1.Group("/server")
server.Use(middleware.NodeAuth())
@@ -82,8 +61,100 @@ func main() {
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")
{
// 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("/getOverride", handler.AdminDashboardSummary)
statGrp.GET("/getStats", handler.AdminDashboardSummary) // Fallback
statGrp.GET("/getTrafficRank", handler.AdminGetTrafficRank)
statGrp.GET("/getOrder", handler.AdminGetOrderStats)
}
// Essential Resources
admin.GET("/plan/fetch", handler.AdminPlansFetch)
admin.POST("/plan/save", handler.AdminPlanSave)
admin.POST("/plan/drop", handler.AdminPlanDrop)
admin.POST("/plan/sort", handler.AdminPlanSort)
admin.GET("/user/fetch", handler.AdminUsersFetch)
admin.POST("/user/update", handler.AdminUserUpdate)
admin.POST("/user/ban", handler.AdminUserBan)
admin.POST("/user/delete", handler.AdminUserDelete)
admin.GET("/order/fetch", handler.AdminOrdersFetch)
admin.POST("/order/paid", handler.AdminOrderPaid)
admin.POST("/order/cancel", handler.AdminOrderCancel)
admin.GET("/coupon/fetch", handler.AdminCouponsFetch)
admin.POST("/coupon/save", handler.AdminCouponSave)
admin.POST("/coupon/drop", handler.AdminCouponDrop)
admin.GET("/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)
}
// Gift Card
giftCardGrp := admin.Group("/gift-card")
{
giftCardGrp.GET("/fetch", handler.AdminGiftCardFetch)
giftCardGrp.POST("/save", handler.AdminGiftCardSave)
giftCardGrp.POST("/generate", handler.AdminGiftCardGenerate)
}
// Traffic Reset
trafficResetGrp := admin.Group("/traffic-reset")
{
trafficResetGrp.GET("/fetch", handler.AdminTrafficResetFetch)
}
// 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/drop", handler.AdminServerManageDrop)
admin.POST("/server/manage/sort", handler.AdminServerManageSort)
}
}
// SPA Fallback for Admin
r.NoRoute(func(c *gin.Context) {
// If path starts with secure admin path, serve the admin app
// This allows React Router to handle sub-paths like /admin/nodes
handler.AdminAppPage(c)
})
// Start server
log.Printf("Server starting on port %s", config.AppConfig.AppPort)
if err := r.Run(":" + config.AppConfig.AppPort); err != nil {