Something went wrong. Try again.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071package api
import ( "net/http" "strings" "time"
"github.com/isabelroses/quoteit/internal/store" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "golang.org/x/time/rate")
const adminCtxKey = "admin"
func rateLimiter() echo.MiddlewareFunc { return ipRateLimiter(rate.Limit(10), 30, 3*time.Minute)}
// addRateLimiter is a per-route limiter applied to POST /api/add. It's much// stricter than the global one because that endpoint is the spam vector.func addRateLimiter() echo.MiddlewareFunc { return ipRateLimiter(rate.Every(time.Minute), 3, 10*time.Minute)}
func ipRateLimiter(r rate.Limit, burst int, expires time.Duration) echo.MiddlewareFunc { return middleware.RateLimiterWithConfig(middleware.RateLimiterConfig{ Store: middleware.NewRateLimiterMemoryStoreWithConfig( middleware.RateLimiterMemoryStoreConfig{ Rate: r, Burst: burst, ExpiresIn: expires, }, ), IdentifierExtractor: func(c echo.Context) (string, error) { return c.RealIP(), nil }, ErrorHandler: func(c echo.Context, _ error) error { return httpError(http.StatusInternalServerError, "rate limiter error") }, DenyHandler: func(c echo.Context, _ string, _ error) error { return httpError(http.StatusTooManyRequests, "rate limit exceeded") }, })}
func (s *Server) bearerAuth(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { auth := c.Request().Header.Get("Authorization") raw, ok := strings.CutPrefix(auth, "Bearer ") token := strings.TrimSpace(raw) if !ok || token == "" { return httpError(http.StatusUnauthorized, "missing bearer token") } admin, err := s.store.AdminByToken(c.Request().Context(), token) if store.IsNotFound(err) { return httpError(http.StatusUnauthorized, "invalid token") } if err != nil { c.Logger().Errorf("auth lookup: %v", err) return httpError(http.StatusInternalServerError, "auth failed") } c.Set(adminCtxKey, admin) return next(c) }}
func adminFrom(c echo.Context) store.Admin { return c.Get(adminCtxKey).(store.Admin)}