软件功能基本开发完成,内测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,6 +1,8 @@
package handler
import (
"encoding/json"
"net/http"
"strings"
"time"
"xboard-go/internal/database"
@@ -11,6 +13,31 @@ import (
"github.com/gin-gonic/gin"
)
func ipv6StatusPresentation(status string, allowed bool) (string, string, string) {
status = strings.TrimSpace(strings.ToLower(status))
switch status {
case "active":
return "active", "IPv6 enabled", ""
case "eligible":
return "eligible", "Ready to enable", ""
case "", "not_allowed", "not_eligible", "disallowed":
if allowed {
return "eligible", "Ready to enable", ""
}
return "not_allowed", "Not eligible", "Current plan or group is not allowed to enable IPv6"
default:
label := strings.ReplaceAll(strings.Title(strings.ReplaceAll(status, "_", " ")), "Ipv6", "IPv6")
if strings.EqualFold(label, "Not Allowed") {
label = "Not eligible"
}
reason := ""
if !allowed && (status == "not_allowed" || status == "not_eligible") {
reason = "Current plan or group is not allowed to enable IPv6"
}
return status, label, reason
}
}
func PluginUserOnlineDevicesUsers(c *gin.Context) {
page := parsePositiveInt(c.DefaultQuery("page", "1"), 1)
@@ -165,7 +192,7 @@ func AdminIPv6SubscriptionUsers(c *gin.Context) {
shadowByParentID := make(map[int]model.User, len(userIDs))
if len(userIDs) > 0 {
var shadowUsers []model.User
if err := database.DB.Where("parent_id IN ?", userIDs).Find(&shadowUsers).Error; err == nil {
if err := database.DB.Preload("Plan").Where("parent_id IN ?", userIDs).Find(&shadowUsers).Error; err == nil {
for _, shadow := range shadowUsers {
if shadow.ParentID != nil {
shadowByParentID[*shadow.ParentID] = shadow
@@ -173,6 +200,17 @@ func AdminIPv6SubscriptionUsers(c *gin.Context) {
}
}
}
shadowPlanID := parsePositiveInt(
service.GetPluginConfigString(service.PluginUserAddIPv6, "ipv6_plan_id", "0"),
0,
)
planNames := make(map[int]string)
var plans []model.Plan
if err := database.DB.Select("id", "name").Find(&plans).Error; err == nil {
for _, plan := range plans {
planNames[plan.ID] = plan.Name
}
}
list := make([]gin.H, 0, len(users))
for _, user := range users {
@@ -191,32 +229,30 @@ func AdminIPv6SubscriptionUsers(c *gin.Context) {
}
allowed := service.PluginUserAllowed(&user, user.Plan)
status := "not_allowed"
statusLabel := "Not eligible"
if allowed {
status = "eligible"
statusLabel = "Ready to enable"
planID := shadowPlanID
planNameValue := planNames[shadowPlanID]
if hasShadowUser {
planID = intFromPointer(shadowUser.PlanID)
planNameValue = planName(shadowUser.Plan)
}
shadowUserID := 0
shadowUpdatedAt := int64(0)
if hasSubscription {
status = firstString(subscription.Status, status)
statusLabel = "IPv6 enabled"
if subscription.Status != "active" && subscription.Status != "" {
statusLabel = strings.ReplaceAll(strings.Title(strings.ReplaceAll(subscription.Status, "_", " ")), "Ipv6", "IPv6")
}
if subscription.ShadowUserID != nil {
shadowUserID = *subscription.ShadowUserID
}
shadowUpdatedAt = subscription.UpdatedAt
} else if allowed {
statusLabel = "Ready to enable"
}
effectiveAllowed := allowed || hasSubscription && subscription.Allowed
status, statusLabel, _ := ipv6StatusPresentation(status, effectiveAllowed)
list = append(list, gin.H{
"id": user.ID,
"email": user.Email,
"plan_name": planName(user.Plan),
"allowed": allowed || hasSubscription && subscription.Allowed,
"plan_id": planID,
"plan_name": firstString(planNameValue, "-"),
"allowed": effectiveAllowed,
"is_active": hasSubscription && subscription.Status == "active",
"status": status,
"status_label": statusLabel,
@@ -238,6 +274,71 @@ func AdminIPv6SubscriptionUsers(c *gin.Context) {
})
}
func AdminIPv6SubscriptionConfigFetch(c *gin.Context) {
cfg := service.GetPluginConfig(service.PluginUserAddIPv6)
Success(c, gin.H{
"ipv6_plan_id": parsePositiveInt(service.GetPluginConfigString(service.PluginUserAddIPv6, "ipv6_plan_id", "0"), 0),
"allowed_plans": service.GetPluginConfigIntList(service.PluginUserAddIPv6, "allowed_plans"),
"allowed_groups": service.GetPluginConfigIntList(service.PluginUserAddIPv6, "allowed_groups"),
"email_suffix": firstString(stringFromAny(cfg["email_suffix"]), "-ipv6"),
"reference_flag": firstString(stringFromAny(cfg["reference_flag"]), "ipv6"),
})
}
func AdminIPv6SubscriptionConfigSave(c *gin.Context) {
var payload struct {
IPv6PlanID int `json:"ipv6_plan_id"`
}
if err := c.ShouldBindJSON(&payload); err != nil {
Fail(c, http.StatusBadRequest, "invalid request body")
return
}
var plugin model.Plugin
err := database.DB.Where("code = ?", service.PluginUserAddIPv6).First(&plugin).Error
if err != nil {
plugin = model.Plugin{
Code: service.PluginUserAddIPv6,
Name: "IPv6 Subscription",
IsEnabled: true,
}
}
cfg := service.GetPluginConfig(service.PluginUserAddIPv6)
if payload.IPv6PlanID > 0 {
cfg["ipv6_plan_id"] = payload.IPv6PlanID
} else {
delete(cfg, "ipv6_plan_id")
}
raw, marshalErr := json.Marshal(cfg)
if marshalErr != nil {
Fail(c, http.StatusInternalServerError, "failed to encode plugin config")
return
}
configText := string(raw)
plugin.Config = &configText
if plugin.ID > 0 {
if updateErr := database.DB.Model(&model.Plugin{}).Where("id = ?", plugin.ID).Updates(map[string]any{
"config": plugin.Config,
"is_enabled": plugin.IsEnabled,
}).Error; updateErr != nil {
Fail(c, http.StatusInternalServerError, "failed to save plugin config")
return
}
} else {
if createErr := database.DB.Create(&plugin).Error; createErr != nil {
Fail(c, http.StatusInternalServerError, "failed to create plugin config")
return
}
}
Success(c, gin.H{
"ipv6_plan_id": payload.IPv6PlanID,
})
}
func AdminIPv6SubscriptionEnable(c *gin.Context) {
userID := parsePositiveInt(c.Param("userId"), 0)
if userID == 0 {
@@ -346,24 +447,14 @@ func PluginUserAddIPv6Check(c *gin.Context) {
hasSubscription := database.DB.Where("user_id = ?", user.ID).First(&subscription).Error == nil
allowed := service.PluginUserAllowed(user, plan)
status := "not_allowed"
statusLabel := "Not eligible"
reason := "Current plan or group is not allowed to enable IPv6"
if allowed {
status = "eligible"
statusLabel = "Ready to enable"
reason = ""
}
if hasSubscription {
status = firstString(subscription.Status, "active")
statusLabel = "IPv6 enabled"
reason = ""
if subscription.Status != "active" && subscription.Status != "" {
statusLabel = strings.ReplaceAll(strings.Title(strings.ReplaceAll(subscription.Status, "_", " ")), "Ipv6", "IPv6")
}
}
effectiveAllowed := allowed || hasSubscription && subscription.Allowed
status, statusLabel, reason := ipv6StatusPresentation(status, effectiveAllowed)
Success(c, gin.H{
"allowed": allowed || hasSubscription && subscription.Allowed,
"allowed": effectiveAllowed,
"is_active": hasSubscription && subscription.Status == "active",
"status": status,
"status_label": statusLabel,