| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- package handler
- import (
- "html"
- "net/http"
- "strings"
- "github.com/Wei-Shaw/sub2api/internal/handler/dto"
- "github.com/Wei-Shaw/sub2api/internal/pkg/response"
- "github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
- "github.com/Wei-Shaw/sub2api/internal/service"
- "github.com/gin-gonic/gin"
- )
- // SettingHandler 公开设置处理器(无需认证)
- type SettingHandler struct {
- settingService *service.SettingService
- notificationEmailService *service.NotificationEmailService
- version string
- }
- // NewSettingHandler 创建公开设置处理器
- func NewSettingHandler(settingService *service.SettingService, version string) *SettingHandler {
- return &SettingHandler{
- settingService: settingService,
- version: version,
- }
- }
- // SetNotificationEmailService attaches the public notification email service without
- // changing the constructor signature used by existing tests.
- func (h *SettingHandler) SetNotificationEmailService(notificationEmailService *service.NotificationEmailService) {
- h.notificationEmailService = notificationEmailService
- }
- // GetPublicSettings 获取公开设置
- // GET /api/v1/settings/public
- func (h *SettingHandler) GetPublicSettings(c *gin.Context) {
- settings, err := h.settingService.GetPublicSettings(c.Request.Context())
- if err != nil {
- response.ErrorFrom(c, err)
- return
- }
- response.Success(c, dto.PublicSettings{
- RegistrationEnabled: settings.RegistrationEnabled,
- EmailVerifyEnabled: settings.EmailVerifyEnabled,
- ForceEmailOnThirdPartySignup: settings.ForceEmailOnThirdPartySignup,
- RegistrationEmailSuffixWhitelist: settings.RegistrationEmailSuffixWhitelist,
- RegistrationEmailDomainQuotaEnabled: settings.RegistrationEmailDomainQuotaEnabled,
- PromoCodeEnabled: settings.PromoCodeEnabled,
- PasswordResetEnabled: settings.PasswordResetEnabled,
- InvitationCodeEnabled: settings.InvitationCodeEnabled,
- TotpEnabled: settings.TotpEnabled,
- PasskeyEnabled: settings.PasskeyEnabled,
- LoginAgreementEnabled: settings.LoginAgreementEnabled,
- LoginAgreementMode: settings.LoginAgreementMode,
- LoginAgreementUpdatedAt: settings.LoginAgreementUpdatedAt,
- LoginAgreementRevision: settings.LoginAgreementRevision,
- LoginAgreementDocuments: publicLoginAgreementDocumentsToDTO(settings.LoginAgreementDocuments),
- TurnstileEnabled: settings.TurnstileEnabled,
- TurnstileSiteKey: settings.TurnstileSiteKey,
- TencentCaptchaEnabled: settings.TencentCaptchaEnabled,
- TencentCaptchaAppID: settings.TencentCaptchaAppID,
- TencentCaptchaRegion: settings.TencentCaptchaRegion,
- AliyunCaptchaEnabled: settings.AliyunCaptchaEnabled,
- AliyunCaptchaSceneID: settings.AliyunCaptchaSceneID,
- AliyunCaptchaPrefix: settings.AliyunCaptchaPrefix,
- AliyunCaptchaRegion: settings.AliyunCaptchaRegion,
- SiteName: settings.SiteName,
- SiteLogo: settings.SiteLogo,
- SiteSubtitle: settings.SiteSubtitle,
- APIBaseURL: settings.APIBaseURL,
- ContactInfo: settings.ContactInfo,
- DocURL: settings.DocURL,
- HomeContent: settings.HomeContent,
- CompactHomeEnabled: settings.CompactHomeEnabled,
- HideCcsImportButton: settings.HideCcsImportButton,
- PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled,
- PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL,
- TableDefaultPageSize: settings.TableDefaultPageSize,
- TablePageSizeOptions: settings.TablePageSizeOptions,
- CustomMenuItems: dto.ParseUserVisibleMenuItems(settings.CustomMenuItems),
- CustomEndpoints: dto.ParseCustomEndpoints(settings.CustomEndpoints),
- DingTalkOAuthEnabled: settings.DingTalkOAuthEnabled,
- LinuxDoOAuthEnabled: settings.LinuxDoOAuthEnabled,
- WeChatOAuthEnabled: settings.WeChatOAuthEnabled,
- WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled,
- WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled,
- WeChatOAuthMobileEnabled: settings.WeChatOAuthMobileEnabled,
- OIDCOAuthEnabled: settings.OIDCOAuthEnabled,
- OIDCOAuthProviderName: settings.OIDCOAuthProviderName,
- GitHubOAuthEnabled: settings.GitHubOAuthEnabled,
- GoogleOAuthEnabled: settings.GoogleOAuthEnabled,
- BackendModeEnabled: settings.BackendModeEnabled,
- PaymentEnabled: settings.PaymentEnabled,
- Version: h.version,
- ServerTimezone: timezone.Name(),
- ServerUTCOffset: timezone.UTCOffset(),
- BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
- AccountQuotaNotifyEnabled: settings.AccountQuotaNotifyEnabled,
- BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
- BalanceLowNotifyRechargeURL: settings.BalanceLowNotifyRechargeURL,
- ChannelMonitorEnabled: settings.ChannelMonitorEnabled,
- ChannelMonitorMode: settings.ChannelMonitorMode,
- ChannelMonitorDefaultIntervalSeconds: settings.ChannelMonitorDefaultIntervalSeconds,
- ChannelMonitorHideThroughput: settings.ChannelMonitorHideThroughput,
- ChannelMonitorShowQuota: settings.ChannelMonitorShowQuota,
- ChannelMonitorHideUserRanking: settings.ChannelMonitorHideUserRanking,
- AvailableChannelsEnabled: settings.AvailableChannelsEnabled,
- ModelPlazaEnabled: settings.ModelPlazaEnabled,
- ModelPlazaRequireAuth: settings.ModelPlazaRequireAuth,
- PluginManagementEnabled: settings.PluginManagementEnabled,
- AffiliateEnabled: settings.AffiliateEnabled,
- RiskControlEnabled: settings.RiskControlEnabled,
- AllowUserViewErrorRequests: settings.AllowUserViewErrorRequests,
- })
- }
- // UnsubscribeNotificationEmail handles optional notification email opt-outs.
- // GET /api/v1/settings/email-unsubscribe?token=...
- func (h *SettingHandler) UnsubscribeNotificationEmail(c *gin.Context) {
- if h.notificationEmailService == nil {
- response.InternalError(c, "notification email service is not configured")
- return
- }
- token := strings.TrimSpace(c.Query("token"))
- if token == "" {
- response.BadRequest(c, "token is required")
- return
- }
- result, err := h.notificationEmailService.Unsubscribe(c.Request.Context(), token)
- if err != nil {
- response.BadRequest(c, err.Error())
- return
- }
- body := "<!doctype html><html><head><meta charset=\"utf-8\"><title>Unsubscribed</title></head><body style=\"font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;padding:32px;\"><h1>Unsubscribed</h1><p>You have unsubscribed <strong>" + html.EscapeString(result.Email) + "</strong> from <strong>" + html.EscapeString(result.Event) + "</strong> emails.</p></body></html>"
- c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(body))
- }
- func publicLoginAgreementDocumentsToDTO(items []service.LoginAgreementDocument) []dto.LoginAgreementDocument {
- result := make([]dto.LoginAgreementDocument, 0, len(items))
- for _, item := range items {
- result = append(result, dto.LoginAgreementDocument{
- ID: item.ID,
- Title: item.Title,
- ContentMD: item.ContentMD,
- })
- }
- return result
- }
|