package handler import ( "encoding/base64" "net/http" "strconv" "strings" "xboard-go/internal/model" "xboard-go/internal/protocol" "xboard-go/internal/service" "github.com/gin-gonic/gin" ) func ClientSubscribe(c *gin.Context) { user, ok := currentUser(c) if !ok { Fail(c, http.StatusForbidden, "invalid token") return } servers, err := service.AvailableServersForUser(user) if err != nil { Fail(c, 500, "failed to fetch servers") return } ua := c.GetHeader("User-Agent") flag := c.Query("flag") if strings.Contains(ua, "Clash") || flag == "clash" { config, _ := protocol.GenerateClash(servers, *user) c.Header("Content-Type", "application/octet-stream") c.String(http.StatusOK, config) return } if strings.Contains(strings.ToLower(ua), "sing-box") || flag == "sing-box" { config, _ := protocol.GenerateSingBox(servers, *user) c.Header("Content-Type", "application/json; charset=utf-8") c.String(http.StatusOK, config) return } links := make([]string, 0, len(servers)) for _, server := range servers { links = append(links, "vmess://"+server.Name) } c.Header("Content-Type", "text/plain; charset=utf-8") c.Header("Subscription-Userinfo", subscriptionUserInfo(*user)) c.String(http.StatusOK, base64.StdEncoding.EncodeToString([]byte(strings.Join(links, "\n")))) } func ClientAppConfigV1(c *gin.Context) { user, ok := currentUser(c) if !ok { Fail(c, http.StatusForbidden, "invalid token") return } servers, err := service.AvailableServersForUser(user) if err != nil { Fail(c, 500, "failed to fetch servers") return } config, _ := protocol.GenerateClash(servers, *user) c.Header("Content-Type", "text/yaml; charset=utf-8") c.String(http.StatusOK, config) } func ClientAppConfigV2(c *gin.Context) { config := gin.H{ "app_info": gin.H{ "app_name": service.MustGetString("app_name", "XBoard"), "app_description": service.MustGetString("app_description", ""), "app_url": service.GetAppURL(), "logo": service.MustGetString("logo", ""), "version": service.MustGetString("app_version", "1.0.0"), }, "features": gin.H{ "enable_register": service.MustGetBool("app_enable_register", true), "enable_invite_system": service.MustGetBool("app_enable_invite_system", true), "enable_telegram_bot": service.MustGetBool("telegram_bot_enable", false), "enable_ticket_system": service.MustGetBool("app_enable_ticket_system", true), "enable_commission_system": service.MustGetBool("app_enable_commission_system", true), "enable_traffic_log": service.MustGetBool("app_enable_traffic_log", true), "enable_knowledge_base": service.MustGetBool("app_enable_knowledge_base", true), "enable_announcements": service.MustGetBool("app_enable_announcements", true), }, "security_config": gin.H{ "tos_url": service.MustGetString("tos_url", ""), "privacy_policy_url": service.MustGetString("app_privacy_policy_url", ""), "is_email_verify": service.MustGetInt("email_verify", 0), "is_invite_force": service.MustGetInt("invite_force", 0), "email_whitelist_suffix": service.MustGetString("email_whitelist_suffix", ""), "is_captcha": service.MustGetInt("captcha_enable", 0), "captcha_type": service.MustGetString("captcha_type", "recaptcha"), "recaptcha_site_key": service.MustGetString("recaptcha_site_key", ""), "turnstile_site_key": service.MustGetString("turnstile_site_key", ""), }, } Success(c, config) } func ClientAppVersion(c *gin.Context) { ua := strings.ToLower(c.GetHeader("User-Agent")) if strings.Contains(ua, "tidalab/4.0.0") || strings.Contains(ua, "tunnelab/4.0.0") { if strings.Contains(ua, "win64") { Success(c, gin.H{ "version": service.MustGetString("windows_version", ""), "download_url": service.MustGetString("windows_download_url", ""), }) return } Success(c, gin.H{ "version": service.MustGetString("macos_version", ""), "download_url": service.MustGetString("macos_download_url", ""), }) return } Success(c, gin.H{ "windows_version": service.MustGetString("windows_version", ""), "windows_download_url": service.MustGetString("windows_download_url", ""), "macos_version": service.MustGetString("macos_version", ""), "macos_download_url": service.MustGetString("macos_download_url", ""), "android_version": service.MustGetString("android_version", ""), "android_download_url": service.MustGetString("android_download_url", ""), }) } func subscriptionUserInfo(user model.User) string { expire := int64(0) if user.ExpiredAt != nil { expire = *user.ExpiredAt } return "upload=" + strconv.FormatInt(int64(user.U), 10) + "; download=" + strconv.FormatInt(int64(user.D), 10) + "; total=" + strconv.FormatInt(int64(user.TransferEnable), 10) + "; expire=" + strconv.FormatInt(expire, 10) }