package controller import ( "encoding/json" "fmt" "net/http" "strings" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/console_setting" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/setting/system_setting" "github.com/gin-gonic/gin" ) func TestStatus(c *gin.Context) { err := model.PingDB() if err != nil { c.JSON(http.StatusServiceUnavailable, gin.H{ "success": false, "message": "数据库连接失败", }) return } // 获取HTTP统计信息 httpStats := middleware.GetStats() c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Server is running", "http_stats": httpStats, }) return } func GetStatus(c *gin.Context) { cs := console_setting.GetConsoleSetting() common.OptionMapRWMutex.RLock() defer common.OptionMapRWMutex.RUnlock() passkeySetting := system_setting.GetPasskeySettings() legalSetting := system_setting.GetLegalSettings() data := gin.H{ "version": common.Version, "start_time": common.StartTime, "email_verification": common.EmailVerificationEnabled, "github_oauth": common.GitHubOAuthEnabled, "github_client_id": common.GitHubClientId, "discord_oauth": system_setting.GetDiscordSettings().Enabled, "discord_client_id": system_setting.GetDiscordSettings().ClientId, "linuxdo_oauth": common.LinuxDOOAuthEnabled, "linuxdo_client_id": common.LinuxDOClientId, "linuxdo_minimum_trust_level": common.LinuxDOMinimumTrustLevel, "telegram_oauth": common.TelegramOAuthEnabled, "telegram_bot_name": common.TelegramBotName, "system_name": common.SystemName, "logo": common.Logo, "footer_html": common.Footer, "wechat_qrcode": common.WeChatAccountQRCodeImageURL, "wechat_login": common.WeChatAuthEnabled, "server_address": system_setting.ServerAddress, "turnstile_check": common.TurnstileCheckEnabled, "turnstile_site_key": common.TurnstileSiteKey, "top_up_link": common.TopUpLink, "docs_link": operation_setting.GetGeneralSetting().DocsLink, "quota_per_unit": common.QuotaPerUnit, // 兼容旧前端:保留 display_in_currency,同时提供新的 quota_display_type "display_in_currency": operation_setting.IsCurrencyDisplay(), "quota_display_type": operation_setting.GetQuotaDisplayType(), "custom_currency_symbol": operation_setting.GetGeneralSetting().CustomCurrencySymbol, "custom_currency_exchange_rate": operation_setting.GetGeneralSetting().CustomCurrencyExchangeRate, "enable_batch_update": common.BatchUpdateEnabled, "enable_drawing": common.DrawingEnabled, "enable_task": common.TaskEnabled, "enable_data_export": common.DataExportEnabled, "data_export_default_time": common.DataExportDefaultTime, "default_collapse_sidebar": common.DefaultCollapseSidebar, "mj_notify_enabled": setting.MjNotifyEnabled, "chats": setting.Chats, "demo_site_enabled": operation_setting.DemoSiteEnabled, "self_use_mode_enabled": operation_setting.SelfUseModeEnabled, "default_use_auto_group": setting.DefaultUseAutoGroup, "usd_exchange_rate": operation_setting.USDExchangeRate, "price": operation_setting.Price, "stripe_unit_price": setting.StripeUnitPrice, // 面板启用开关 "api_info_enabled": cs.ApiInfoEnabled, "uptime_kuma_enabled": cs.UptimeKumaEnabled, "announcements_enabled": cs.AnnouncementsEnabled, "faq_enabled": cs.FAQEnabled, // 模块管理配置 "HeaderNavModules": common.OptionMap["HeaderNavModules"], "SidebarModulesAdmin": common.OptionMap["SidebarModulesAdmin"], "oidc_enabled": system_setting.GetOIDCSettings().Enabled, "oidc_client_id": system_setting.GetOIDCSettings().ClientId, "oidc_authorization_endpoint": system_setting.GetOIDCSettings().AuthorizationEndpoint, "passkey_login": passkeySetting.Enabled, "passkey_display_name": passkeySetting.RPDisplayName, "passkey_rp_id": passkeySetting.RPID, "passkey_origins": passkeySetting.Origins, "passkey_allow_insecure": passkeySetting.AllowInsecureOrigin, "passkey_user_verification": passkeySetting.UserVerification, "passkey_attachment": passkeySetting.AttachmentPreference, "setup": constant.Setup, "user_agreement_enabled": legalSetting.UserAgreement != "", "privacy_policy_enabled": legalSetting.PrivacyPolicy != "", } // 根据启用状态注入可选内容 if cs.ApiInfoEnabled { data["api_info"] = console_setting.GetApiInfo() } if cs.AnnouncementsEnabled { data["announcements"] = console_setting.GetAnnouncements() } if cs.FAQEnabled { data["faq"] = console_setting.GetFAQ() } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": data, }) return } func GetNotice(c *gin.Context) { common.OptionMapRWMutex.RLock() defer common.OptionMapRWMutex.RUnlock() c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": common.OptionMap["Notice"], }) return } func GetAbout(c *gin.Context) { common.OptionMapRWMutex.RLock() defer common.OptionMapRWMutex.RUnlock() c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": common.OptionMap["About"], }) return } func GetUserAgreement(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": system_setting.GetLegalSettings().UserAgreement, }) return } func GetPrivacyPolicy(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": system_setting.GetLegalSettings().PrivacyPolicy, }) return } func GetMidjourney(c *gin.Context) { common.OptionMapRWMutex.RLock() defer common.OptionMapRWMutex.RUnlock() c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": common.OptionMap["Midjourney"], }) return } func GetHomePageContent(c *gin.Context) { common.OptionMapRWMutex.RLock() defer common.OptionMapRWMutex.RUnlock() c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": common.OptionMap["HomePageContent"], }) return } func SendEmailVerification(c *gin.Context) { email := c.Query("email") if err := common.Validate.Var(email, "required,email"); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "无效的参数", }) return } parts := strings.Split(email, "@") if len(parts) != 2 { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "无效的邮箱地址", }) return } localPart := parts[0] domainPart := parts[1] if common.EmailDomainRestrictionEnabled { allowed := false for _, domain := range common.EmailDomainWhitelist { if domainPart == domain { allowed = true break } } if !allowed { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "The administrator has enabled the email domain name whitelist, and your email address is not allowed due to special symbols or it's not in the whitelist.", }) return } } if common.EmailAliasRestrictionEnabled { containsSpecialSymbols := strings.Contains(localPart, "+") || strings.Contains(localPart, ".") if containsSpecialSymbols { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "管理员已启用邮箱地址别名限制,您的邮箱地址由于包含特殊符号而被拒绝。", }) return } } if model.IsEmailAlreadyTaken(email) { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "邮箱地址已被占用", }) return } code := common.GenerateVerificationCode(6) common.RegisterVerificationCodeWithKey(email, code, common.EmailVerificationPurpose) subject := fmt.Sprintf("%s邮箱验证邮件", common.SystemName) content := fmt.Sprintf("
您好,你正在进行%s邮箱验证。
"+ "您的验证码为: %s
"+ "验证码 %d 分钟内有效,如果不是本人操作,请忽略。
", common.SystemName, code, common.VerificationValidMinutes) err := common.SendEmail(subject, email, content) if err != nil { common.ApiError(c, err) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", }) return } func SendPasswordResetEmail(c *gin.Context) { email := c.Query("email") if err := common.Validate.Var(email, "required,email"); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "无效的参数", }) return } if !model.IsEmailAlreadyTaken(email) { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "该邮箱地址未注册", }) return } code := common.GenerateVerificationCode(0) common.RegisterVerificationCodeWithKey(email, code, common.PasswordResetPurpose) link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", system_setting.ServerAddress, email, code) subject := fmt.Sprintf("%s密码重置", common.SystemName) content := fmt.Sprintf("您好,你正在进行%s密码重置。
"+ "点击 此处 进行密码重置。
"+ "如果链接无法点击,请尝试点击下面的链接或将其复制到浏览器中打开:
%s
重置链接 %d 分钟内有效,如果不是本人操作,请忽略。
", common.SystemName, link, link, common.VerificationValidMinutes) err := common.SendEmail(subject, email, content) if err != nil { common.ApiError(c, err) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", }) return } type PasswordResetRequest struct { Email string `json:"email"` Token string `json:"token"` } func ResetPassword(c *gin.Context) { var req PasswordResetRequest err := json.NewDecoder(c.Request.Body).Decode(&req) if req.Email == "" || req.Token == "" { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "无效的参数", }) return } if !common.VerifyCodeWithKey(req.Email, req.Token, common.PasswordResetPurpose) { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "重置链接非法或已过期", }) return } password := common.GenerateVerificationCode(12) err = model.ResetUserPasswordByEmail(req.Email, password) if err != nil { common.ApiError(c, err) return } common.DeleteKey(req.Email, common.PasswordResetPurpose) c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": password, }) return }