f1909da1ad
Go CI / build (push) Failing after 2m42s
feat: create basic server to manage google oauth, account, sessions, places, attributes and ratings.
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
package api
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.pengzhan.dev/noteplace-server/internal/auth"
|
|
"git.pengzhan.dev/noteplace-server/internal/models"
|
|
"git.pengzhan.dev/noteplace-server/internal/store"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// AuthHandler handles the authentication API endpoints.
|
|
type AuthHandler struct {
|
|
Store *store.Store
|
|
Authenticator *auth.Authenticator
|
|
}
|
|
|
|
// HandleCliLogin provides the user with the URL to start the auth process.
|
|
func (h *AuthHandler) HandleCliLogin(c *gin.Context) {
|
|
authURL := h.Authenticator.GetAuthURL()
|
|
|
|
response := gin.H{
|
|
"message": `Please open the following URL in your browser to authorize the application.\nAfter authorizing, Google will provide a code. Paste it here to complete the login.\nExample: curl -X POST -d '{\"code\":\"YOUR_CODE_HERE\"}' http://localhost:3001/api/auth/google/cli/callback`,
|
|
"url": authURL,
|
|
}
|
|
c.JSON(http.StatusOK, response)
|
|
}
|
|
|
|
// HandleCliCallback exchanges the code for a token and creates a session.
|
|
func (h *AuthHandler) HandleCliCallback(c *gin.Context) {
|
|
var reqBody struct {
|
|
Code string `json:"code"`
|
|
}
|
|
if err := c.ShouldBindJSON(&reqBody); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
|
return
|
|
}
|
|
|
|
token, err := h.Authenticator.ExchangeCodeForToken(reqBody.Code)
|
|
if err != nil {
|
|
log.Printf("Failed to exchange code for token: %v", err)
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Failed to validate code"})
|
|
return
|
|
}
|
|
|
|
userInfo, err := h.Authenticator.GetUserInfo(token)
|
|
if err != nil {
|
|
log.Printf("Failed to get user info: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user info from Google"})
|
|
return
|
|
}
|
|
|
|
// --- User and Session Logic ---
|
|
user, found := h.Store.GetUserByGoogleID(userInfo.GoogleID)
|
|
if !found {
|
|
userInfo.ID = "user-" + uuid.New().String()
|
|
userInfo.CreatedAt = time.Now().UTC().Format(time.RFC3339)
|
|
if err := h.Store.CreateUser(userInfo); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"})
|
|
return
|
|
}
|
|
user = userInfo
|
|
}
|
|
|
|
// Create a new session and save it to the store
|
|
sessionID := "session-" + uuid.New().String()
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
newSession := models.Session{
|
|
ID: sessionID,
|
|
UserID: user.ID,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
if err := h.Store.CreateSession(newSession); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create session"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "Login successful!", "session_token": newSession.ID})
|
|
}
|
|
|
|
// HandleGetUser returns the current user based on the session token.
|
|
func (h *AuthHandler) HandleGetUser(c *gin.Context) {
|
|
user := c.MustGet("user").(models.User)
|
|
c.JSON(http.StatusOK, user)
|
|
}
|