initial commit

This commit is contained in:
basil 2025-06-14 23:47:44 -04:00
commit d40b69f1f9
Signed by: basil
SSH key fingerprint: SHA256:y04xIFL/yqNaG9ae9Vl95vELtHfApGAIoOGLeVLP/fE
58 changed files with 7919 additions and 0 deletions

View file

@ -0,0 +1,56 @@
package middleware
import (
"crypto/sha256"
"encoding/base64"
"errors"
"net/http"
"git.red-panda.pet/pandaware/house/backend/db"
"git.red-panda.pet/pandaware/house/backend/router"
)
func Authenticated(ctx *router.Context) error {
sessionCookie, err := ctx.Cookie("session")
if err != nil {
return ctx.Error(err, http.StatusUnauthorized, "login required")
}
sessionBs, err := base64.StdEncoding.DecodeString(sessionCookie.Value)
if err != nil {
return ctx.Error(err, http.StatusUnauthorized, "login required")
}
hashedKey := sha256.Sum256(sessionBs)
session, err := ctx.Query.GetSession(ctx, hashedKey[:])
if err != nil {
return ctx.Error(err, http.StatusUnauthorized, "login required")
}
user, err := ctx.Query.GetUser(ctx, session.UserID)
if err != nil {
return ctx.Error(err, http.StatusUnauthorized, "login required")
}
ctx.With(sessionKey, session)
ctx.With(userKey, user)
return nil
}
func Session(route router.AuthorizedRoute, ctx *router.Context) db.Session {
user, ok := ctx.Value(sessionKey).(db.Session)
if !ok {
panic(errors.New("middleware.Session cannot be used from an unauthenticated route"))
}
return user
}
func User(route router.AuthorizedRoute, ctx *router.Context) db.GetUserRow {
user, ok := ctx.Value(userKey).(db.GetUserRow)
if !ok {
panic(errors.New("middleware.User cannot be used from an unauthenticated route"))
}
return user
}

View file

@ -0,0 +1,10 @@
package middleware
type middlewareContextKey int
const (
sessionKey middlewareContextKey = iota
userKey
jsonBodyKey
paramsKey
)

View file

@ -0,0 +1,49 @@
package middleware
import (
"encoding/json"
"errors"
"net/http"
"git.red-panda.pet/pandaware/house/backend/router"
)
func ParseJSONBody[T any](ctx *router.Context) error {
contentType := ctx.Request.Header.Get("Content-Type")
if contentType != "application/json" {
return ctx.GenericError(nil, http.StatusBadRequest)
}
var body T
dec := json.NewDecoder(ctx.Request.Body)
err := dec.Decode(&body)
if err != nil {
return ctx.GenericError(err, http.StatusBadRequest)
}
ctx.With(jsonBodyKey, body)
return nil
}
func ParseJSONBodyWithValidator[T any](
ctx *router.Context,
r router.ValidatedBodyRoute,
validator func(value T) error,
) error {
err := ParseJSONBody[T](ctx)
if err != nil {
return err
}
body := JSONBody[T](r, ctx)
return validator(body)
}
func JSONBody[T any](r router.ValidatedBodyRoute, ctx *router.Context) T {
body, ok := ctx.Value(jsonBodyKey).(T)
if !ok {
panic(errors.New("middleware.JSONBody cannot be used with a route that doesn't implement router.ValidatedBodyRoute"))
}
return body
}

View file

@ -0,0 +1,33 @@
package middleware
import (
"strconv"
"git.red-panda.pet/pandaware/house/backend/router"
"github.com/google/uuid"
)
func ValidateUUIDParam(ctx *router.Context, key string) error {
param := ctx.Parameter(key)
if err := uuid.Validate(param); err != nil {
return ctx.Error(err, 400, "Bad path parameter "+key)
}
return nil
}
func ValidateIDParam(ctx *router.Context, key string) error {
param := ctx.Parameter(key)
v, err := strconv.ParseInt(param, 10, 64)
if err != nil {
return ctx.Error(err, 400, "Bad path parameter "+key)
}
ctx.With(paramsKey, v)
return nil
}
// TODO: expand this kinda api for more than just one param, better validation, etc.
func ParamID(r router.ValidatedParamRoute, ctx *router.Context) int64 {
return ctx.Value(paramsKey).(int64)
}