2023-04-22 20:39:27 +08:00
package controller
import (
"encoding/json"
"fmt"
"net/http"
2023-04-22 21:14:09 +08:00
"one-api/common"
2025-04-03 18:57:15 +08:00
"one-api/constant"
2025-06-10 19:29:32 +08:00
"one-api/middleware"
2023-04-22 21:14:09 +08:00
"one-api/model"
2024-12-22 17:24:29 +08:00
"one-api/setting"
2025-06-16 22:15:12 +08:00
"one-api/setting/console_setting"
2025-03-04 18:52:08 +08:00
"one-api/setting/operation_setting"
2025-03-11 22:00:31 +08:00
"one-api/setting/system_setting"
2023-07-30 12:44:41 +08:00
"strings"
"github.com/gin-gonic/gin"
2023-04-22 20:39:27 +08:00
)
2024-03-04 19:32:59 +08:00
func TestStatus ( c * gin . Context ) {
err := model . PingDB ( )
if err != nil {
c . JSON ( http . StatusServiceUnavailable , gin . H {
"success" : false ,
"message" : "数据库连接失败" ,
} )
return
}
2025-06-10 19:29:32 +08:00
// 获取HTTP统计信息
httpStats := middleware . GetStats ( )
2024-03-04 19:32:59 +08:00
c . JSON ( http . StatusOK , gin . H {
2025-06-10 19:29:32 +08:00
"success" : true ,
"message" : "Server is running" ,
"http_stats" : httpStats ,
2024-03-04 19:32:59 +08:00
} )
return
}
2023-04-22 20:39:27 +08:00
func GetStatus ( c * gin . Context ) {
2025-06-10 19:29:32 +08:00
2025-06-14 01:39:23 +08:00
cs := console_setting . GetConsoleSetting ( )
data := gin . H {
2025-06-16 22:15:12 +08:00
"version" : common . Version ,
"start_time" : common . StartTime ,
"email_verification" : common . EmailVerificationEnabled ,
"github_oauth" : common . GitHubOAuthEnabled ,
"github_client_id" : common . GitHubClientId ,
"linuxdo_oauth" : common . LinuxDOOAuthEnabled ,
"linuxdo_client_id" : common . LinuxDOClientId ,
"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" : setting . ServerAddress ,
"price" : setting . Price ,
2025-07-10 16:29:38 +08:00
"stripe_unit_price" : setting . StripeUnitPrice ,
2025-06-16 22:15:12 +08:00
"min_topup" : setting . MinTopUp ,
2025-07-10 16:29:38 +08:00
"stripe_min_topup" : setting . StripeMinTopUp ,
2025-06-16 22:15:12 +08:00
"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" : common . DisplayInCurrencyEnabled ,
"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 ,
"enable_online_topup" : setting . PayAddress != "" && setting . EpayId != "" && setting . EpayKey != "" ,
2025-07-10 16:29:38 +08:00
"enable_stripe_topup" : setting . StripeApiSecret != "" && setting . StripeWebhookSecret != "" && setting . StripePriceId != "" ,
2025-06-16 22:15:12 +08:00
"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 ,
2025-06-18 21:23:06 +08:00
"pay_methods" : setting . PayMethods ,
2025-06-14 01:39:23 +08:00
// 面板启用开关
2025-06-16 22:15:12 +08:00
"api_info_enabled" : cs . ApiInfoEnabled ,
"uptime_kuma_enabled" : cs . UptimeKumaEnabled ,
"announcements_enabled" : cs . AnnouncementsEnabled ,
"faq_enabled" : cs . FAQEnabled ,
2025-06-14 01:39:23 +08:00
"oidc_enabled" : system_setting . GetOIDCSettings ( ) . Enabled ,
"oidc_client_id" : system_setting . GetOIDCSettings ( ) . ClientId ,
"oidc_authorization_endpoint" : system_setting . GetOIDCSettings ( ) . AuthorizationEndpoint ,
"setup" : constant . Setup ,
}
// 根据启用状态注入可选内容
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 ( )
}
2023-04-22 20:39:27 +08:00
c . JSON ( http . StatusOK , gin . H {
"success" : true ,
"message" : "" ,
2025-06-14 01:39:23 +08:00
"data" : data ,
2023-04-22 20:39:27 +08:00
} )
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
}
2023-08-14 22:16:32 +08:00
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
}
2023-05-13 21:27:49 +08:00
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
}
2023-04-22 20:39:27 +08:00
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
}
2024-04-06 17:50:47 +08:00
parts := strings . Split ( email , "@" )
if len ( parts ) != 2 {
c . JSON ( http . StatusOK , gin . H {
"success" : false ,
"message" : "无效的邮箱地址" ,
} )
return
}
localPart := parts [ 0 ]
domainPart := parts [ 1 ]
2024-04-03 23:57:49 +08:00
if common . EmailDomainRestrictionEnabled {
2023-07-30 12:44:41 +08:00
allowed := false
2024-04-03 23:57:49 +08:00
for _ , domain := range common . EmailDomainWhitelist {
2024-04-02 01:13:12 +08:00
if domainPart == domain {
2023-07-30 12:44:41 +08:00
allowed = true
break
}
}
2024-04-06 17:50:47 +08:00
if ! allowed {
2024-04-02 01:13:12 +08:00
c . JSON ( http . StatusOK , gin . H {
2024-04-04 12:33:11 +08:00
"success" : false ,
2024-04-06 17:50:47 +08:00
"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." ,
2024-04-02 01:13:12 +08:00
} )
2024-04-04 12:33:11 +08:00
return
2024-04-06 17:50:47 +08:00
}
}
if common . EmailAliasRestrictionEnabled {
2024-05-11 21:18:30 +08:00
containsSpecialSymbols := strings . Contains ( localPart , "+" ) || strings . Contains ( localPart , "." )
2024-04-06 17:50:47 +08:00
if containsSpecialSymbols {
2023-07-30 12:44:41 +08:00
c . JSON ( http . StatusOK , gin . H {
"success" : false ,
2024-04-06 17:50:47 +08:00
"message" : "管理员已启用邮箱地址别名限制,您的邮箱地址由于包含特殊符号而被拒绝。" ,
2023-07-30 12:44:41 +08:00
} )
return
}
}
2024-04-06 17:50:47 +08:00
2023-04-22 20:39:27 +08:00
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 ( "<p>您好,你正在进行%s邮箱验证。</p>" +
"<p>您的验证码为: <strong>%s</strong></p>" +
"<p>验证码 %d 分钟内有效,如果不是本人操作,请忽略。</p>" , common . SystemName , code , common . VerificationValidMinutes )
err := common . SendEmail ( subject , email , content )
if err != nil {
2025-07-14 21:59:42 +08:00
common . ApiError ( c , err )
2023-04-22 20:39:27 +08:00
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 )
2024-12-22 17:24:29 +08:00
link := fmt . Sprintf ( "%s/user/reset?email=%s&token=%s" , setting . ServerAddress , email , code )
2023-04-22 20:39:27 +08:00
subject := fmt . Sprintf ( "%s密码重置" , common . SystemName )
content := fmt . Sprintf ( "<p>您好,你正在进行%s密码重置。</p>" +
2023-07-23 13:25:28 +08:00
"<p>点击 <a href='%s'>此处</a> 进行密码重置。</p>" +
"<p>如果链接无法点击,请尝试点击下面的链接或将其复制到浏览器中打开:<br> %s </p>" +
"<p>重置链接 %d 分钟内有效,如果不是本人操作,请忽略。</p>" , common . SystemName , link , link , common . VerificationValidMinutes )
2023-04-22 20:39:27 +08:00
err := common . SendEmail ( subject , email , content )
if err != nil {
2025-07-14 21:59:42 +08:00
common . ApiError ( c , err )
2023-04-22 20:39:27 +08:00
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 {
2025-07-14 21:59:42 +08:00
common . ApiError ( c , err )
2023-04-22 20:39:27 +08:00
return
}
common . DeleteKey ( req . Email , common . PasswordResetPurpose )
c . JSON ( http . StatusOK , gin . H {
"success" : true ,
"message" : "" ,
"data" : password ,
} )
return
}