package auth import ( "net/url" "nixcn-cms/internal/exception" "nixcn-cms/pkgs/authcode" "nixcn-cms/pkgs/email" "nixcn-cms/pkgs/turnstile" "nixcn-cms/utils" "github.com/gin-gonic/gin" "github.com/spf13/viper" ) type MagicRequest struct { ClientId string `json:"client_id"` RedirectUri string `json:"redirect_uri"` State string `json:"state"` Email string `json:"email"` TurnstileToken string `json:"turnstile_token"` } func Magic(c *gin.Context) { // Parse request var req MagicRequest if err := c.ShouldBindJSON(&req); err != nil { errorCode := new(exception.Builder). SetStatus(exception.StatusUser). SetService(exception.ServiceAuth). SetEndpoint(exception.EndpointAuthServiceMagic). SetType(exception.TypeCommon). SetOriginal(exception.CommonErrorInvalidInput). SetError(err). Build(c) utils.HttpResponse(c, 400, errorCode) return } // Cloudflare turnstile ok, err := turnstile.VerifyTurnstile(req.TurnstileToken, c.ClientIP()) if err != nil || !ok { errorCode := new(exception.Builder). SetStatus(exception.StatusUser). SetService(exception.ServiceAuth). SetEndpoint(exception.EndpointAuthServiceMagic). SetType(exception.TypeSpecific). SetOriginal(exception.AuthMagicTurnstileFailed). SetError(err). Build(c) utils.HttpResponse(c, 403, errorCode) return } code, err := authcode.NewAuthCode(c, req.ClientId, req.Email) if err != nil { errorCode := new(exception.Builder). SetStatus(exception.StatusServer). SetService(exception.ServiceAuth). SetEndpoint(exception.EndpointAuthServiceMagic). SetType(exception.TypeSpecific). SetOriginal(exception.AuthMagicCodeGenFailed). SetError(err). Build(c) utils.HttpResponse(c, 500, errorCode) return } externalUrl := viper.GetString("server.external_url") url, err := url.Parse(externalUrl) if err != nil { errorCode := new(exception.Builder). SetStatus(exception.StatusServer). SetService(exception.ServiceAuth). SetEndpoint(exception.EndpointAuthServiceMagic). SetType(exception.TypeSpecific). SetOriginal(exception.AuthMagicInvalidExternalUrl). SetError(err). Build(c) utils.HttpResponse(c, 500, errorCode) return } url.Path = "/api/v1/auth/redirect" query := url.Query() query.Set("code", code) query.Set("redirect_uri", req.RedirectUri) query.Set("state", req.State) query.Set("client_id", req.ClientId) url.RawQuery = query.Encode() debugMode := viper.GetBool("server.debug_mode") if debugMode { uriData := struct { Uri string `json:"uri"` }{url.String()} utils.HttpResponse(c, 200, "", "magiclink sent", uriData) return } else { // Send email using resend emailClient, err := new(email.Client).NewSMTPClient() if err != nil { errorCode := new(exception.Builder). SetStatus(exception.StatusServer). SetService(exception.ServiceAuth). SetEndpoint(exception.EndpointAuthServiceMagic). SetType(exception.TypeSpecific). SetOriginal(exception.AuthMagicInvalidEmailConfig). SetError(err). Build(c) utils.HttpResponse(c, 500, errorCode) return } emailClient.Send( "NixCN CMS ", req.Email, "NixCN CMS Email Verify", "

Click the link below to verify your email. This link will expire in 10 minutes.

"+url.String()+"", ) } errorCode := new(exception.Builder). SetStatus(exception.StatusServer). SetService(exception.ServiceAuth). SetEndpoint(exception.EndpointAuthServiceMagic). SetType(exception.TypeCommon). SetOriginal(exception.CommonSuccess). Build(c) utils.HttpResponse(c, 200, errorCode) }