Implementing api handlers
Added some groups handlers and how we obtain the user for the handler Signed-off-by: Ethan Wellenreiter <ewellenreiter@gmail.com>
This commit is contained in:
parent
5e6d061330
commit
330d850790
@ -57,6 +57,9 @@ func (app *application) mount() http.Handler {
|
||||
r.Use(middleware.CleanPath)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
/// CSRF and SESSION TOKEN STUFF:
|
||||
// Store session token in https or whatever only so js can't access it, and the store csrf locally using react or whatever, remove the cookie, and then include it in the http header under 'X-CSRF-Token' or as a hidden input in an html form
|
||||
|
||||
r.Use(middleware.Throttle(100)) // temporary or removable. throttles the whole thing to 1000 concurrent requests
|
||||
// r.Use(cors.Handler(cors.Options{
|
||||
// AllowedOrigins: []string{env.GetString("CORS_ALLOWED_ORIGIN", "http://localhost:5174")},
|
||||
@ -96,7 +99,7 @@ func (app *application) mount() http.Handler {
|
||||
|
||||
r.Route("/user", func(r chi.Router) {
|
||||
r.Route("/{userID}", func(r chi.Router) {
|
||||
r.Use(app.AuthSessionMiddleware, app.CSRFCheckMiddleware, app.CheckUserMatchingMiddleware)
|
||||
r.Use(app.AuthSessionMiddleware, app.CSRFCheckMiddleware, app.CheckUserMatchingMiddleware) // consider the actual use of the user matching middleware and whether an admin should be used instead
|
||||
|
||||
r.Get("/", app.getUserHandler)
|
||||
|
||||
@ -104,16 +107,20 @@ func (app *application) mount() http.Handler {
|
||||
r.Get("/", app.getUsersGroupsHandler)
|
||||
|
||||
r.Route("/{groupID}", func(r chi.Router) {
|
||||
|
||||
r.Get("/", app.getUsersGroupHandler)
|
||||
r.Delete("/", app.removeUserGroupHandler) // maybe this should expect authentication headers to reverify the password when deleting a group you own.
|
||||
|
||||
r.Put("/moderator", app.addGroupModeratorHandler)
|
||||
r.Put("/owner", app.setGroupOwnerHandler)
|
||||
r.Get("/owner", app.getGroupOwnerHandler)
|
||||
|
||||
r.Post("/moderator", app.addGroupModeratorHandler)
|
||||
r.Delete("/moderator/{secondaryuserID}", app.removeModeratorPriviligesHandler)
|
||||
|
||||
r.Get("/users", app.getGroupUsersHandler)
|
||||
r.Delete("/users/{secondaryuserID}", app.removeUserFromGroupHandler)
|
||||
r.Post("/users", app.addGroupUserHandler) // needs to create a new user/do the whole invite thing if they don't already exist
|
||||
r.Delete("/users/{secondaryuserID}", app.removeUserFromGroupHandler) // needs to check if the user is an owner/moderator
|
||||
|
||||
r.Put("/owner", app.setGroupOwnerHandler)
|
||||
})
|
||||
})
|
||||
|
||||
@ -130,6 +137,7 @@ func (app *application) mount() http.Handler {
|
||||
r.Get("/", app.getReceiptImageHandler)
|
||||
r.Put("/", app.changeReceiptImageHandler)
|
||||
r.Delete("/", app.deleteReceiptImageHandler)
|
||||
r.Post("/confirm", app.confirmImageCreationHandler)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -140,7 +148,7 @@ func (app *application) mount() http.Handler {
|
||||
|
||||
})
|
||||
|
||||
r.Use(app.CSRFCheckMiddleware)
|
||||
// r.Use(app.CSRFCheckMiddleware)
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(app.AuthSessionMiddleware)
|
||||
|
||||
@ -89,3 +89,5 @@ func (app *application) registerUserHandler(w http.ResponseWriter, r *http.Reque
|
||||
func (app *application) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
// func (app *application)
|
||||
|
||||
106
backend/cmd/api/groups.go
Normal file
106
backend/cmd/api/groups.go
Normal file
@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.ewellenr.ca/receipt_indexer/backend/internal/storage"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func (app *application) getUserGroups(ctx context.Context, userID int64) (*storage.UserGroups, error) {
|
||||
if !app.config.redisCfg.enabled {
|
||||
return app.store.Groups.GetUserGroups(ctx, userID)
|
||||
}
|
||||
|
||||
usergroups, err := app.cacheStorage.UserGroups.Get(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if usergroups == nil {
|
||||
usergroups, err = app.store.Groups.GetUserGroups(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := app.cacheStorage.UserGroups.Set(ctx, usergroups); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return usergroups, nil
|
||||
}
|
||||
|
||||
func (app *application) getGroup(ctx context.Context, groupID int64) (*storage.Group, error) {
|
||||
if !app.config.redisCfg.enabled {
|
||||
return app.store.Groups.GetByID(ctx, groupID)
|
||||
|
||||
}
|
||||
|
||||
group, err := app.cacheStorage.Groups.Get(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if group == nil {
|
||||
group, err = app.store.Groups.GetByID(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := app.cacheStorage.Groups.Set(ctx, group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (app *application) getUsersGroupsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := app.getUserFromURL(w, r)
|
||||
if err != nil { // assume that the writing was already handled
|
||||
return
|
||||
}
|
||||
|
||||
usergroups, err := app.getUserGroups(r.Context(), user.ID)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case storage.ErrNotFound:
|
||||
app.notFoundResponse(w, r, err)
|
||||
return
|
||||
default:
|
||||
app.internalServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.jsonResponse(w, http.StatusOK, usergroups); err != nil {
|
||||
app.internalServerError(w, r, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *application) getUsersGroupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
groupID, err := strconv.ParseInt(chi.URLParam(r, "groupID"), 10, 64)
|
||||
if err != nil {
|
||||
app.badRequestResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
group, err := app.getGroup(r.Context(), groupID)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case storage.ErrNotFound:
|
||||
app.notFoundResponse(w, r, err)
|
||||
return
|
||||
default:
|
||||
app.internalServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.jsonResponse(w, http.StatusOK, group); err != nil {
|
||||
app.internalServerError(w, r, err)
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,8 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// copied from https://github.com/sikozonpc/GopherSocial/blob/main/cmd/api/json.go
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, data any) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
@ -18,3 +20,21 @@ func writeJSONError(w http.ResponseWriter, status int, message string) error {
|
||||
|
||||
return writeJSON(w, status, &envelope{Error: message})
|
||||
}
|
||||
|
||||
func readJSON(w http.ResponseWriter, r *http.Request, data any) error {
|
||||
maxBytes := 1_048_578 // 1mb
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
decoder.DisallowUnknownFields()
|
||||
|
||||
return decoder.Decode(data)
|
||||
}
|
||||
|
||||
func (app *application) jsonResponse(w http.ResponseWriter, status int, data any) error {
|
||||
type envelope struct {
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
return writeJSON(w, status, &envelope{Data: data})
|
||||
}
|
||||
|
||||
@ -3,16 +3,47 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
auth_storage "git.ewellenr.ca/receipt_indexer/backend/internal/storage/auth"
|
||||
"git.ewellenr.ca/receipt_indexer/backend/internal/storage"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type userKey string
|
||||
|
||||
const userCtx userKey = "user"
|
||||
|
||||
func (app *application) getUserFromURL(w http.ResponseWriter, r *http.Request) (*storage.User, error) {
|
||||
userID, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
|
||||
if err != nil {
|
||||
app.badRequestResponse(w, r, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := app.getUser(r.Context(), userID)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case storage.ErrNotFound:
|
||||
app.notFoundResponse(w, r, err)
|
||||
return nil, err
|
||||
default:
|
||||
app.internalServerError(w, r, err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (app *application) getUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
user, err := app.getUserFromURL(w, r)
|
||||
if err != nil { // assume that the writing was already handled
|
||||
return
|
||||
}
|
||||
|
||||
if err := app.jsonResponse(w, http.StatusOK, user); err != nil {
|
||||
app.internalServerError(w, r, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *application) getUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@ -20,7 +51,7 @@ func (app *application) getUsersHandler(w http.ResponseWriter, r *http.Request)
|
||||
func (app *application) deleteUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (app *application) getUser(ctx context.Context, userID int64) (*auth_storage.User, error) {
|
||||
func (app *application) getUser(ctx context.Context, userID int64) (*storage.User, error) {
|
||||
if !app.config.redisCfg.enabled {
|
||||
return app.auth.Users.GetByID(ctx, userID)
|
||||
}
|
||||
@ -44,7 +75,7 @@ func (app *application) getUser(ctx context.Context, userID int64) (*auth_storag
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func getUserFromContext(r *http.Request) *auth_storage.User {
|
||||
user, _ := r.Context().Value(userCtx).(*auth_storage.User)
|
||||
func getUserFromContext(r *http.Request) *storage.User {
|
||||
user, _ := r.Context().Value(userCtx).(*storage.User)
|
||||
return user
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user