diff --git a/internal/app/handlers/interface.go b/internal/app/handlers/interface.go index 634087e..6f80925 100644 --- a/internal/app/handlers/interface.go +++ b/internal/app/handlers/interface.go @@ -12,7 +12,6 @@ type Handlers struct { DB *sql.DB } -// ServerInterfaceWrapper wraps Handlers to conform to the generated interface type ServerInterfaceWrapper struct { handlers *Handlers } @@ -21,6 +20,16 @@ func NewServerInterfaceWrapper(handlers *Handlers) *ServerInterfaceWrapper { return &ServerInterfaceWrapper{handlers: handlers} } +type SessionServerInterfaceWrapper struct { + sessionHandler *SessionHandler +} + +func NewSessionServerInterfaceWrapper(handlers *Handlers) *SessionServerInterfaceWrapper { + return &SessionServerInterfaceWrapper{ + sessionHandler: NewSessionHandler(handlers), + } +} + func (siw *ServerInterfaceWrapper) ListGames(w http.ResponseWriter, r *http.Request) { siw.handlers.ListGames(w, r) } @@ -41,16 +50,16 @@ func (siw *ServerInterfaceWrapper) UpdateGame(w http.ResponseWriter, r *http.Req siw.handlers.UpdateGame(w, r, id) } -func (siw *ServerInterfaceWrapper) PostApiV1AuthSignIn(w http.ResponseWriter, r *http.Request) { - siw.handlers.PostApiV1AuthSignIn(w, r) +func (siw *SessionServerInterfaceWrapper) PostApiV1AuthSignIn(w http.ResponseWriter, r *http.Request) { + siw.sessionHandler.PostApiV1AuthSignIn(w, r) } -func (siw *ServerInterfaceWrapper) PostApiV1AuthSignOut(w http.ResponseWriter, r *http.Request) { - siw.handlers.PostApiV1AuthSignOut(w, r) +func (siw *SessionServerInterfaceWrapper) PostApiV1AuthSignOut(w http.ResponseWriter, r *http.Request) { + siw.sessionHandler.PostApiV1AuthSignOut(w, r) } -func (siw *ServerInterfaceWrapper) ValidateSession(w http.ResponseWriter, r *http.Request) { - siw.handlers.ValidateSession(w, r) +func (siw *SessionServerInterfaceWrapper) ValidateSession(w http.ResponseWriter, r *http.Request) { + siw.sessionHandler.ValidateSession(w, r) } func (siw *ServerInterfaceWrapper) ListResults(w http.ResponseWriter, r *http.Request) { diff --git a/internal/app/handlers/sessions.go b/internal/app/handlers/sessions.go index 642c7d6..3e64b79 100644 --- a/internal/app/handlers/sessions.go +++ b/internal/app/handlers/sessions.go @@ -4,16 +4,53 @@ import ( "encoding/json" "log/slog" "net/http" + "sync" "ctf01d/internal/app/repository" "ctf01d/internal/app/server" api_helpers "ctf01d/internal/app/utils" - "ctf01d/internal/app/view" + "github.com/google/uuid" openapi_types "github.com/oapi-codegen/runtime/types" ) -func (h *Handlers) PostApiV1AuthSignIn(w http.ResponseWriter, r *http.Request) { +type SessionCache struct { + cache sync.Map +} + +func NewSessionCache() *SessionCache { + return &SessionCache{} +} + +func (sc *SessionCache) GetSession(sessionID string) (openapi_types.UUID, bool) { + val, ok := sc.cache.Load(sessionID) + if !ok { + return uuid.Nil, false + } + return val.(openapi_types.UUID), true +} + +func (sc *SessionCache) SetSession(sessionID string, userID uuid.UUID) { + sc.cache.Store(sessionID, userID) +} + +func (sc *SessionCache) DeleteSession(sessionID string) { + sc.cache.Delete(sessionID) +} + +type SessionHandler struct { + *Handlers + SessionCache *SessionCache +} + +func NewSessionHandler(handlers *Handlers) *SessionHandler { + return &SessionHandler{ + Handlers: handlers, + SessionCache: NewSessionCache(), + } +} + +func (h *SessionHandler) PostApiV1AuthSignIn(w http.ResponseWriter, r *http.Request) { var req server.PostApiV1AuthSignInJSONBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { slog.Warn(err.Error(), "handler", "PostApiV1AuthSignIn") @@ -38,6 +75,9 @@ func (h *Handlers) PostApiV1AuthSignIn(w http.ResponseWriter, r *http.Request) { return } + // Добавляем сессию в кэш + h.SessionCache.SetSession(sessionId, user.Id) + http.SetCookie(w, &http.Cookie{ Name: "session_id", HttpOnly: true, @@ -49,7 +89,7 @@ func (h *Handlers) PostApiV1AuthSignIn(w http.ResponseWriter, r *http.Request) { api_helpers.RespondWithJSON(w, http.StatusOK, map[string]string{"data": "User logged in"}) } -func (h *Handlers) PostApiV1AuthSignOut(w http.ResponseWriter, r *http.Request) { +func (h *Handler) PostApiV1AuthSignOut(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_id") if err != nil { slog.Warn(err.Error(), "handler", "PostApiV1AuthSignOut") @@ -63,6 +103,10 @@ func (h *Handlers) PostApiV1AuthSignOut(w http.ResponseWriter, r *http.Request) api_helpers.RespondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to delete session"}) return } + + // Удаляем сессию из кэша + h.SessionCache.DeleteSession(cookie.Value) + http.SetCookie(w, &http.Cookie{ Name: "session_id", Value: "", @@ -72,13 +116,20 @@ func (h *Handlers) PostApiV1AuthSignOut(w http.ResponseWriter, r *http.Request) api_helpers.RespondWithJSON(w, http.StatusOK, map[string]string{"data": "User logout successful"}) } -func (h *Handlers) ValidateSession(w http.ResponseWriter, r *http.Request) { +func (h *SessionHandler) ValidateSession(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_id") if err != nil { slog.Warn(err.Error(), "handler", "ValidateSession") api_helpers.RespondWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "No session found"}) return } + + if userId, ok := h.SessionCache.GetSession(cookie.Value); ok { + slog.Debug("ValidateSession user.Id " + openapi_types.UUID(userId).String()) + h.respondWithUserDetails(w, r, userId) + return + } + slog.Debug("cookie.Value, " + cookie.Value) repo := repository.NewSessionRepository(h.DB) var userId openapi_types.UUID @@ -88,14 +139,23 @@ func (h *Handlers) ValidateSession(w http.ResponseWriter, r *http.Request) { api_helpers.RespondWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "No user or session found"}) return } + + h.SessionCache.SetSession(cookie.Value, userId) slog.Debug("ValidateSession user.Id " + openapi_types.UUID(userId).String()) + h.respondWithUserDetails(w, r, userId) +} +func (h *SessionHandler) respondWithUserDetails(w http.ResponseWriter, r *http.Request, userId openapi_types.UUID) { userRepo := repository.NewUserRepository(h.DB) user, err := userRepo.GetById(r.Context(), userId) if err != nil { - slog.Warn(err.Error(), "handler", "ValidateSession") + slog.Warn(err.Error(), "handler", "respondWithUserDetails") api_helpers.RespondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Could not find user by user id"}) return } - api_helpers.RespondWithJSON(w, http.StatusOK, view.NewSessionFromModel(user)) + res := make(map[string]string) + res["name"] = user.DisplayName + res["role"] = api_helpers.ConvertUserRequestRoleToString(user.Role) + + api_helpers.RespondWithJSON(w, http.StatusOK, res) }