package handlers import ( "html/template" "log" "net/http" "stocksearch/config" "stocksearch/middleware" "stocksearch/services" ) // AuthHandler 로그인/로그아웃 핸들러 type AuthHandler struct { sessionSvc *services.SessionService loginTmpl *template.Template } // NewAuthHandler 인증 핸들러 초기화 func NewAuthHandler(sessionSvc *services.SessionService) *AuthHandler { tmpl, err := template.ParseFiles("templates/pages/login.html") if err != nil { log.Fatalf("로그인 템플릿 파싱 실패: %v", err) } return &AuthHandler{ sessionSvc: sessionSvc, loginTmpl: tmpl, } } // LoginPage GET /login — 로그인 폼 렌더링 func (h *AuthHandler) LoginPage(w http.ResponseWriter, r *http.Request) { // 이미 로그인된 경우 메인으로 리다이렉트 if cookie, err := r.Cookie(middleware.SessionCookieName); err == nil { if h.sessionSvc.Validate(cookie.Value) { http.Redirect(w, r, "/", http.StatusFound) return } } // next 파라미터가 없으면 로그인 후 메인 페이지로 next := r.URL.Query().Get("next") if next == "" { next = "/" } data := map[string]string{ "Next": next, "Error": "", } h.renderLogin(w, data) } // Login POST /login — ID/PW 검증 후 세션 발급 func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, "잘못된 요청입니다.", http.StatusBadRequest) return } id := r.FormValue("id") password := r.FormValue("password") next := r.FormValue("next") if next == "" { next = "/" } // ID/PW 검증 if id != config.App.AdminID || password != config.App.AdminPassword { data := map[string]string{ "Next": next, "Error": "아이디 또는 비밀번호가 올바르지 않습니다.", } w.WriteHeader(http.StatusUnauthorized) h.renderLogin(w, data) return } // 세션 생성 및 쿠키 설정 sessionID := h.sessionSvc.Create() http.SetCookie(w, &http.Cookie{ Name: middleware.SessionCookieName, Value: sessionID, Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, MaxAge: 86400, // 24시간 }) http.Redirect(w, r, next, http.StatusFound) } // Logout POST /logout — 세션 삭제 후 /login 리다이렉트 func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { if cookie, err := r.Cookie(middleware.SessionCookieName); err == nil { h.sessionSvc.Delete(cookie.Value) } // 쿠키 만료 처리 http.SetCookie(w, &http.Cookie{ Name: middleware.SessionCookieName, Value: "", Path: "/", HttpOnly: true, MaxAge: -1, }) http.Redirect(w, r, "/", http.StatusFound) } // renderLogin 로그인 템플릿 렌더링 헬퍼 func (h *AuthHandler) renderLogin(w http.ResponseWriter, data interface{}) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := h.loginTmpl.ExecuteTemplate(w, "login.html", data); err != nil { log.Printf("로그인 템플릿 렌더링 실패: %v", err) http.Error(w, "페이지를 표시할 수 없습니다.", http.StatusInternalServerError) } }