package handlers import ( "encoding/json" "os" "strings" "gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/auth" "gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/logger" "gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/repositories" "gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/storage" "github.com/gin-gonic/gin" "golang.org/x/oauth2" "golang.org/x/oauth2/endpoints" ) type AuthHandlers struct { repo repositories.AuthRepository logger *logger.Logger config *oauth2.Config frontendURL string } func NewAuthHandlers(repo repositories.AuthRepository) *AuthHandlers { clientID := os.Getenv("GITHUB_CLIENT_ID") clientSecret := os.Getenv("GITHUB_CLIENT_SECRET") redirectURL := os.Getenv("REDIRECT_URL") frontendURL := os.Getenv("FRONTEND_URL") if clientID == "" || clientSecret == "" { panic("GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET must be set") } if redirectURL == "" { redirectURL = "http://localhost:8080/api/v1/callback/github" } if frontendURL == "" { frontendURL = "https://d3m0k1d.ru" } return &AuthHandlers{ repo: repo, logger: logger.New(false), frontendURL: frontendURL, config: &oauth2.Config{ ClientID: clientID, ClientSecret: clientSecret, RedirectURL: redirectURL, Scopes: []string{"user:email"}, Endpoint: endpoints.GitHub, }, } } // LoginGithub godoc // @Summary Start GitHub OAuth login // @Description Redirects to GitHub authorization // @Tags auth // @Success 302 // @Router /auth/github [get] func (h *AuthHandlers) LoginGithub(c *gin.Context) { url := h.config.AuthCodeURL("state", oauth2.AccessTypeOnline) h.logger.Info("Redirect to GitHub: " + url) c.Redirect(302, url) } // CallbackGithub godoc // @Summary GitHub OAuth callback // @Description Exchanges authorization code for access token // @Tags auth // @Param code query string true "Authorization code" // @Produce json // @Success 200 {object} map[string]interface{} "Access token" // @Failure 400 {object} map[string]string "Missing code" // @Failure 500 {object} map[string]string "Exchange failed" // @Router /callback/github [get] func (h *AuthHandlers) CallbackGithub(c *gin.Context) { var id int h.logger.Info("CallbackGithub called") code := c.Query("code") if code == "" { h.logger.Error("missing code") c.Redirect(302, h.frontendURL+"/login?error=missing_code") return } h.logger.Info("Processing code: " + code[:10] + "...") token, err := h.config.Exchange(c.Request.Context(), code) if err != nil { h.logger.Error("Exchange failed: " + err.Error()) c.Redirect(302, h.frontendURL+"/login?error=auth_failed") return } client := h.config.Client(c.Request.Context(), token) resp, err := client.Get("https://api.github.com/user") if err != nil { h.logger.Error("Get failed: " + err.Error()) c.Redirect(302, h.frontendURL+"/login?error=github_api_failed") return } var ghUser storage.UserReg err = json.NewDecoder(resp.Body).Decode(&ghUser) if err != nil { h.logger.Error("Decode failed: " + err.Error()) c.Redirect(302, h.frontendURL+"/login?error=decode_failed") return } isreg, err := h.repo.IsRegistered(c.Request.Context(), ghUser.GithubID) if err != nil { h.logger.Error("Database check failed: " + err.Error()) c.Redirect(302, h.frontendURL+"/login?error=database_error") return } if !isreg { h.logger.Info("New user, registering: " + ghUser.GithubLogin) id, err = h.repo.Register(c.Request.Context(), ghUser) if err != nil { h.logger.Error("Registration failed: " + err.Error()) c.Redirect(302, h.frontendURL+"/login?error=registration_failed") return } } else { h.logger.Info("Existing user, fetching data: " + ghUser.GithubLogin) user, err := h.repo.GetUserByGithubID(c.Request.Context(), ghUser.GithubID) if err != nil { h.logger.Error("Failed to fetch user: " + err.Error()) c.Redirect(302, h.frontendURL+"/login?error=user_fetch_failed") return } id = user.ID ghUser.GithubLogin = user.GithubLogin ghUser.Email = user.Email ghUser.AvatarURL = user.AvatarURL } user := storage.User{ ID: id, GithubID: ghUser.GithubID, GithubLogin: ghUser.GithubLogin, Email: ghUser.Email, AvatarURL: ghUser.AvatarURL, } jwtToken, err := auth.GenerateJWT(user) if err != nil { h.logger.Error("JWT generation failed: " + err.Error()) c.Redirect(302, h.frontendURL+"/login?error=token_failed") return } h.logger.Info("Authentication successful for user: " + ghUser.GithubLogin) c.Redirect(302, h.frontendURL+"/auth/callback#token="+jwtToken) } // GetSession godoc // @Summary Get user session // @Description Returns user session data // @Tags auth // @Produce json // @Success 200 {object} map[string]interface{} "Session data" // @Failure 401 {object} map[string]string "Unauthorized" // @Router /session [get] func (h *AuthHandlers) GetSession(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(401, gin.H{"error": "unauthorized"}) return } tokenString := strings.TrimPrefix(authHeader, "Bearer ") if tokenString == authHeader { c.JSON(401, gin.H{"error": "invalid authorization header"}) return } user, err := auth.ValidateJWT(tokenString) if err != nil { c.JSON(401, gin.H{"error": "invalid token"}) return } c.JSON(200, gin.H{ "user": gin.H{ "name": user.GithubLogin, "email": user.Email, "avatar": user.AvatarURL, }, }) }