package admin import ( "encoding/json" "net/http" "os" "time" "git.huntm.net/wedding/server/errors" "git.huntm.net/wedding/server/guest" "github.com/golang-jwt/jwt/v5" ) type AdminHandler struct { adminStore adminStore guestStore guest.GuestStore } type adminStore interface { Find(admin Admin) (Admin, error) } func NewAdminHandler(a adminStore, g guest.GuestStore) *AdminHandler { return &AdminHandler{a, g} } func (a *AdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch { case r.Method == http.MethodOptions: w.WriteHeader(http.StatusOK) case r.Method == http.MethodPost && r.URL.Path == "/api/admin/login": a.handleLogIn(w, r) default: w.WriteHeader(http.StatusNotFound) } } func (a *AdminHandler) handleLogIn(w http.ResponseWriter, r *http.Request) { token, err := a.logIn(r) if err != nil { http.Error(w, string(err.Message), err.Status) } else { w.Write(token) } } func (a *AdminHandler) logIn(r *http.Request) ([]byte, *errors.AppError) { requestAdmin, err := a.decodeCredentials(r) if err != nil { return nil, errors.NewAppError(http.StatusBadRequest, err.Error()) } _, err = a.adminStore.Find(requestAdmin) if err != nil { return nil, errors.NewAppError(http.StatusUnauthorized, err.Error()) } expirationTime := a.setExpirationTime() claims := a.createClaims(requestAdmin, expirationTime) key, err := a.readKey() if err != nil { return nil, errors.NewAppError(http.StatusInternalServerError, err.Error()) } token, err := a.createToken(claims, key) if err != nil { return nil, errors.NewAppError(http.StatusInternalServerError, err.Error()) } guests, err := a.guestStore.Get() if err != nil { return nil, errors.NewAppError(http.StatusInternalServerError, err.Error()) } jsonBytes, err := a.marshalResponse(guests, token) if err != nil { return nil, errors.NewAppError(http.StatusInternalServerError, err.Error()) } return jsonBytes, nil } func (a *AdminHandler) decodeCredentials(r *http.Request) (Admin, error) { var admin Admin err := json.NewDecoder(r.Body).Decode(&admin) defer r.Body.Close() return admin, err } func (a *AdminHandler) setExpirationTime() time.Time { return time.Now().Add(15 * time.Minute) } func (a *AdminHandler) createClaims(admin Admin, expirationTime time.Time) *Claims { return &Claims{ admin, jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expirationTime), }, } } func (a *AdminHandler) readKey() ([]byte, error) { return os.ReadFile(os.Getenv("ADMIN_KEY")) } func (a *AdminHandler) createToken(claims *Claims, key []byte) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(key) } func (a *AdminHandler) marshalResponse(guests []guest.Guest, token string) ([]byte, error) { loginResponse := a.createLoginResponse(guests, token) return json.Marshal(loginResponse) } func (a *AdminHandler) createLoginResponse(guests []guest.Guest, token string) *Login { return &Login{guests, token} }