initial commit
This commit is contained in:
commit
d40b69f1f9
58 changed files with 7919 additions and 0 deletions
204
backend/router/route.go
Normal file
204
backend/router/route.go
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"git.red-panda.pet/pandaware/house/backend/db"
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
type IRequestContext interface {
|
||||
Search(key string) string
|
||||
Parameter(key string) string
|
||||
Cookie(name string) (*http.Cookie, error)
|
||||
SetCookie(cookie *http.Cookie)
|
||||
JSON(statusCode int, body any) error
|
||||
Redirect(statusCode int, url string) error
|
||||
Text(statusCode int, body string) error
|
||||
Bytes(statusCode int, body []byte) error
|
||||
With(key any, value any)
|
||||
}
|
||||
|
||||
type RequestError struct {
|
||||
Inner error `json:"-"`
|
||||
RequestID string `json:"requestID"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Error implements error.
|
||||
func (r *RequestError) Error() string {
|
||||
return r.Inner.Error()
|
||||
}
|
||||
|
||||
var _ error = new(RequestError)
|
||||
|
||||
type Context struct {
|
||||
id string
|
||||
statusCode int
|
||||
start time.Time
|
||||
log *log.Logger
|
||||
context context.Context
|
||||
search url.Values
|
||||
|
||||
resp http.ResponseWriter
|
||||
Request *http.Request
|
||||
DB *sql.Conn
|
||||
Query *db.Queries
|
||||
}
|
||||
|
||||
// Redirect implements IRequestContext.
|
||||
func (r *Context) Redirect(statusCode int, url string) error {
|
||||
http.Redirect(r, r.Request, url, statusCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// With implements IRequestContext.
|
||||
func (r *Context) With(key, value any) {
|
||||
r.context = context.WithValue(r.context, key, value)
|
||||
}
|
||||
|
||||
// Bytes implements IRequestContext.
|
||||
func (r *Context) Bytes(statusCode int, body []byte) error {
|
||||
r.WriteHeader(statusCode)
|
||||
_, err := r.Write(body)
|
||||
return err
|
||||
}
|
||||
|
||||
// Text implements IRequestContext.
|
||||
func (r *Context) Text(statusCode int, body string) error {
|
||||
r.Header().Set("Content-Type", "text/plain")
|
||||
return r.Bytes(statusCode, []byte(body))
|
||||
}
|
||||
|
||||
// JSON implements IRequestContext.
|
||||
func (r *Context) JSON(statusCode int, body any) error {
|
||||
enc := json.NewEncoder(r)
|
||||
r.Header().Set("Content-Type", "application/json")
|
||||
r.WriteHeader(statusCode)
|
||||
return enc.Encode(body)
|
||||
}
|
||||
|
||||
func (r *Context) Error(err error, statusCode int, message string) error {
|
||||
return &RequestError{
|
||||
Inner: err,
|
||||
RequestID: r.id,
|
||||
StatusCode: statusCode,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
func (r *Context) GenericError(err error, statusCode int) error {
|
||||
message := "Error"
|
||||
|
||||
switch statusCode {
|
||||
case http.StatusBadRequest:
|
||||
message = "Bad request"
|
||||
case http.StatusUnauthorized:
|
||||
message = "Unauthorized"
|
||||
case http.StatusNotFound:
|
||||
message = "Not found"
|
||||
case http.StatusInternalServerError:
|
||||
message = "Internal server error"
|
||||
}
|
||||
|
||||
return r.Error(err, statusCode, message)
|
||||
}
|
||||
|
||||
// Cookie implements IRequestContext.
|
||||
func (r *Context) Cookie(name string) (*http.Cookie, error) {
|
||||
return r.Request.Cookie(name)
|
||||
}
|
||||
|
||||
// Parameter implements IRequestContext.
|
||||
func (r *Context) Parameter(key string) string {
|
||||
return r.Request.PathValue(key)
|
||||
}
|
||||
|
||||
// Search implements IRequestContext.
|
||||
func (r *Context) Search(key string) string {
|
||||
if r.search == nil {
|
||||
r.search = r.Request.URL.Query()
|
||||
}
|
||||
return r.search.Get(key)
|
||||
}
|
||||
|
||||
// SetCookie implements IRequestContext.
|
||||
func (r *Context) SetCookie(cookie *http.Cookie) {
|
||||
http.SetCookie(r, cookie)
|
||||
}
|
||||
|
||||
// Deadline implements context.Context.
|
||||
func (r *Context) Deadline() (deadline time.Time, ok bool) {
|
||||
return r.context.Deadline()
|
||||
}
|
||||
|
||||
// Done implements context.Context.
|
||||
func (r *Context) Done() <-chan struct{} {
|
||||
return r.context.Done()
|
||||
}
|
||||
|
||||
// Err implements context.Context.
|
||||
func (r *Context) Err() error {
|
||||
return r.context.Err()
|
||||
}
|
||||
|
||||
// Value implements context.Context.
|
||||
func (r *Context) Value(key any) any {
|
||||
return r.context.Value(key)
|
||||
}
|
||||
|
||||
// Header implements http.ResponseWriter.
|
||||
func (r *Context) Header() http.Header {
|
||||
return r.resp.Header()
|
||||
}
|
||||
|
||||
// Write implements http.ResponseWriter.
|
||||
func (r *Context) Write(bs []byte) (int, error) {
|
||||
if r.statusCode == 0 {
|
||||
r.statusCode = 200
|
||||
}
|
||||
|
||||
r.log.Helper()
|
||||
r.log.Info("",
|
||||
"id", r.id,
|
||||
"duration", time.Since(r.start),
|
||||
"status", r.statusCode,
|
||||
)
|
||||
|
||||
return r.resp.Write(bs)
|
||||
}
|
||||
|
||||
// WriteHeader implements http.ResponseWriter.
|
||||
func (r *Context) WriteHeader(statusCode int) {
|
||||
r.statusCode = statusCode
|
||||
r.resp.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
var _ context.Context = new(Context)
|
||||
var _ http.ResponseWriter = new(Context)
|
||||
var _ IRequestContext = new(Context)
|
||||
|
||||
type Route interface {
|
||||
Handle(ctx *Context) error
|
||||
}
|
||||
|
||||
type AuthorizedRoute interface {
|
||||
Authorize(ctx *Context) error
|
||||
}
|
||||
|
||||
type ValidatedSearchRoute interface {
|
||||
ValidateSearch(ctx *Context) error
|
||||
}
|
||||
|
||||
type ValidatedBodyRoute interface {
|
||||
ValidateBody(ctx *Context) error
|
||||
}
|
||||
|
||||
type ValidatedParamRoute interface {
|
||||
ValidateParams(ctx *Context) error
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue