Fixed a lot of bad queries

This commit is contained in:
Marco Realacci 2022-11-22 23:41:52 +01:00
parent a3cf4f17f8
commit 233217beb9
15 changed files with 48 additions and 30 deletions

BIN
cmd/webapi/__debug_bin Executable file

Binary file not shown.

Binary file not shown.

View file

@ -22,6 +22,7 @@ func (rt *_router) Handler() http.Handler {
rt.router.PUT("/users/:user_id/bans/:ban_uid", rt.wrap(rt.PutBan)) rt.router.PUT("/users/:user_id/bans/:ban_uid", rt.wrap(rt.PutBan))
rt.router.DELETE("/users/:user_id/bans/:ban_uid", rt.wrap(rt.DeleteBan)) rt.router.DELETE("/users/:user_id/bans/:ban_uid", rt.wrap(rt.DeleteBan))
rt.router.GET("/users/:user_id/photos", rt.wrap(rt.GetUserPhotos))
rt.router.POST("/users/:user_id/photos", rt.wrap(rt.PostPhoto)) rt.router.POST("/users/:user_id/photos", rt.wrap(rt.PostPhoto))
rt.router.GET("/users/:user_id/photos/:photo_id", rt.wrap(rt.GetPhoto)) rt.router.GET("/users/:user_id/photos/:photo_id", rt.wrap(rt.GetPhoto))
rt.router.DELETE("/users/:user_id/photos/:photo_id", rt.wrap(rt.DeletePhoto)) rt.router.DELETE("/users/:user_id/photos/:photo_id", rt.wrap(rt.DeletePhoto))
@ -36,7 +37,7 @@ func (rt *_router) Handler() http.Handler {
rt.router.GET("/users/:user_id", rt.wrap(rt.GetUserProfile)) rt.router.GET("/users/:user_id", rt.wrap(rt.GetUserProfile))
rt.router.GET("/stream", rt.wrap(rt.GetUserStream)) //todo: why not "/users/:user_id/stream"? rt.router.GET("/stream", rt.wrap(rt.GetUserStream))
rt.router.GET("/", rt.getHelloWorld) rt.router.GET("/", rt.getHelloWorld)
rt.router.GET("/context", rt.wrap(rt.getContextReply)) rt.router.GET("/context", rt.wrap(rt.getContextReply))

View file

@ -37,7 +37,7 @@ func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcont
} }
// requested user is not found -> 404 as the resource is not found // requested user is not found -> 404 as the resource is not found
if auth == reqcontext.USER_NOT_FOUND { if auth == reqcontext.USER_NOT_FOUND {
helpers.SendStatus(notFoundStatus, w, "Resource not found", l) helpers.SendStatus(notFoundStatus, w, "User not found", l)
return false return false
} }
return true return true

View file

@ -15,6 +15,10 @@ import (
func (rt *_router) GetFollowersFollowing(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { func (rt *_router) GetFollowersFollowing(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) {
return
}
uid := ps.ByName("user_id") uid := ps.ByName("user_id")
if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) { if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) {

View file

@ -64,7 +64,7 @@ func (rt *_router) GetUserPhotos(w http.ResponseWriter, r *http.Request, ps http
} }
// Get user photos // Get user photos
photos, err := rt.db.GetUserPhotos(uid, start_index, limit) photos, err := rt.db.GetUserPhotos(uid, ctx.Auth.GetUserID(), start_index, limit)
if err != nil { if err != nil {
helpers.SendInternalError(err, "Database error: GetUserPhotos", w, rt.baseLogger) helpers.SendInternalError(err, "Database error: GetUserPhotos", w, rt.baseLogger)

View file

@ -41,7 +41,6 @@ func SendStatus(httpStatus int, w http.ResponseWriter, description string, l log
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
if err != nil { if err != nil {
l.WithError(err).Error("Error encoding json") l.WithError(err).Error("Error encoding json")
//todo: empty response?
} }
} }
@ -50,7 +49,6 @@ func SendNotFound(w http.ResponseWriter, description string, l logrus.FieldLogge
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
if err != nil { if err != nil {
l.WithError(err).Error("Error encoding json") l.WithError(err).Error("Error encoding json")
//todo: empty response?
} }
} }
@ -59,7 +57,6 @@ func SendBadRequest(w http.ResponseWriter, description string, l logrus.FieldLog
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
if err != nil { if err != nil {
l.WithError(err).Error("Error encoding json") l.WithError(err).Error("Error encoding json")
//todo: empty response?
} }
} }
@ -69,7 +66,6 @@ func SendBadRequestError(err error, description string, w http.ResponseWriter, l
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
if err != nil { if err != nil {
l.WithError(err).Error("Error encoding json") l.WithError(err).Error("Error encoding json")
//todo: empty response?
} }
} }
@ -79,7 +75,6 @@ func SendInternalError(err error, description string, w http.ResponseWriter, l l
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
if err != nil { if err != nil {
l.WithError(err).Error("Error encoding json") l.WithError(err).Error("Error encoding json")
//todo: empty response?
} }
} }

View file

@ -14,6 +14,10 @@ import (
func (rt *_router) GetLikes(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { func (rt *_router) GetLikes(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) {
return
}
// get the user id from the url // get the user id from the url
uid := ps.ByName("user_id") uid := ps.ByName("user_id")
photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64) photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64)

View file

@ -23,7 +23,7 @@ type _respbody struct {
func (rt *_router) PostSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { func (rt *_router) PostSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
var request _reqbody var request _reqbody
err := json.NewDecoder(r.Body).Decode(&request) //todo: capire se serve close err := json.NewDecoder(r.Body).Decode(&request)
var uid string var uid string
if err == nil { // test if user exists if err == nil { // test if user exists

View file

@ -21,19 +21,12 @@ func (rt *_router) UpdateUsername(w http.ResponseWriter, r *http.Request, ps htt
return return
} }
//err := json.NewDecoder(r.Body).Decode(&req) //todo: capire se serve close
//if err != nil {
// w.WriteHeader(http.StatusBadRequest) // todo: move to DecodeOrBadRequest helper
// return
//}
err := rt.db.UpdateUsername(uid, req.Name) err := rt.db.UpdateUsername(uid, req.Name)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok, maybe let's use a helper helpers.SendInternalError(err, "Database error: UpdateUsername", w, rt.baseLogger)
return return
} }
w.WriteHeader(http.StatusNoContent) // todo: change to 204 also in API spec w.WriteHeader(http.StatusNoContent)
} }

View file

@ -42,6 +42,7 @@ import (
type AppDatabase interface { type AppDatabase interface {
CreateUser(name string) (string, error) CreateUser(name string) (string, error)
UserExists(uid string) (bool, error) UserExists(uid string) (bool, error)
UserExistsNotBanned(uid string, requesting_uid string) (bool, error)
GetUserID(name string) (string, error) GetUserID(name string) (string, error)
SearchByName(name string, requesting_uid string, start_index int, limit int) (*[]structures.UIDName, error) SearchByName(name string, requesting_uid string, start_index int, limit int) (*[]structures.UIDName, error)
@ -67,7 +68,7 @@ type AppDatabase interface {
UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error)
GetUserProfile(uid string, requesting_uid string) (QueryResult, *structures.UserProfile, error) GetUserProfile(uid string, requesting_uid string) (QueryResult, *structures.UserProfile, error)
GetUserPhotos(uid string, start_index int, limit int) (*[]structures.UserPhoto, error) GetUserPhotos(uid string, requesting_uid string, start_index int, limit int) (*[]structures.UserPhoto, error)
GetUserStream(uid string, start_index int, limit int) (*[]structures.Photo, error) GetUserStream(uid string, start_index int, limit int) (*[]structures.Photo, error)
GetComments(uid string, photo_id int64, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.Comment, error) GetComments(uid string, photo_id int64, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.Comment, error)

View file

@ -23,8 +23,9 @@ func (db *appdbimpl) PostComment(uid string, photo_id int64, comment_user string
_, err = db.c.Exec(`PRAGMA foreign_keys = ON; _, err = db.c.Exec(`PRAGMA foreign_keys = ON;
INSERT INTO "comments" ("user", "photo", "comment", "date") VALUES (?, ?, ?, ?)`, comment_user, photo_id, comment, time.Now().Format(time.RFC3339)) INSERT INTO "comments" ("user", "photo", "comment", "date") VALUES (?, ?, ?, ?)`, comment_user, photo_id, comment, time.Now().Format(time.RFC3339))
// todo: we don't actually need it, it's already done before
if db_errors.ForeignKeyViolation(err) { if db_errors.ForeignKeyViolation(err) {
// trying to post a comment on a photo that does not exist
// (actually this should never happen, as we checked if the photo exists before)
return ERR_NOT_FOUND, nil return ERR_NOT_FOUND, nil
} }

View file

@ -64,7 +64,7 @@ func (db *appdbimpl) GetUserProfile(uid string, requesting_uid string) (QueryRes
}, nil }, nil
} }
func (db *appdbimpl) GetUserPhotos(uid string, start_index int, limit int) (*[]structures.UserPhoto, error) { func (db *appdbimpl) GetUserPhotos(uid string, requesting_uid string, start_index int, limit int) (*[]structures.UserPhoto, error) {
// Get photos // Get photos
rows, err := db.c.Query(`SELECT "p"."id", "p"."date", rows, err := db.c.Query(`SELECT "p"."id", "p"."date",
@ -83,8 +83,9 @@ func (db *appdbimpl) GetUserPhotos(uid string, start_index int, limit int) (*[]s
) )
FROM "photos" AS "p" FROM "photos" AS "p"
WHERE "p"."user" = ? WHERE "p"."user" = ?
OFFSET ?
LIMIT ?`, uid, uid, start_index, limit) LIMIT ?
OFFSET ?`, requesting_uid, uid, limit, start_index)
if err != nil { if err != nil {
// Return the error // Return the error
return nil, err return nil, err

View file

@ -20,6 +20,24 @@ func (db *appdbimpl) UserExists(uid string) (bool, error) {
return cnt > 0, nil return cnt > 0, nil
} }
// User exists and is not banned
func (db *appdbimpl) UserExistsNotBanned(uid string, requesting_uid string) (bool, error) {
var cnt int
err := db.c.QueryRow(`SELECT COUNT(*) FROM "users"
WHERE "uid" = ?
AND NOT EXISTS (
SELECT "bans"."user" FROM "bans"
WHERE "bans"."user" = "users"."uid"
AND "bans"."ban" = ?
)`, uid, requesting_uid).Scan(&cnt)
if err != nil {
return false, err
}
return cnt > 0, nil
}
// Get user id by username // Get user id by username
func (db *appdbimpl) GetUserID(name string) (string, error) { func (db *appdbimpl) GetUserID(name string) (string, error) {
var uid string var uid string
@ -47,7 +65,7 @@ func (db *appdbimpl) UpdateUsername(uid string, name string) error {
func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_index int, limit int) (QueryResult, *[]structures.UIDName, error) { func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_index int, limit int) (QueryResult, *[]structures.UIDName, error) {
// user may exist but have no followers // user may exist but have no followers
exists, err := db.UserExists(uid) exists, err := db.UserExistsNotBanned(uid, requesting_uid)
if err != nil { if err != nil {
return ERR_INTERNAL, nil, err return ERR_INTERNAL, nil, err
@ -57,7 +75,7 @@ func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_i
return ERR_NOT_FOUND, nil, nil return ERR_NOT_FOUND, nil, nil
} }
rows, err := db.c.Query(`SELECT "follower", "user"."name" FROM "follows", "users" rows, err := db.c.Query(`SELECT "follower", "users"."name" FROM "follows", "users"
WHERE "follows"."follower" = "users"."uid" WHERE "follows"."follower" = "users"."uid"
AND "follows"."follower" NOT IN ( AND "follows"."follower" NOT IN (
@ -67,8 +85,8 @@ func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_i
) )
AND "followed" = ? AND "followed" = ?
OFFSET ? LIMIT ?
LIMIT ?`, uid, requesting_uid, start_index, limit) OFFSET ?`, uid, requesting_uid, limit, start_index)
followers, err := db.uidNameQuery(rows, err) followers, err := db.uidNameQuery(rows, err)
@ -83,7 +101,7 @@ func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_i
func (db *appdbimpl) GetUserFollowing(uid string, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.UIDName, error) { func (db *appdbimpl) GetUserFollowing(uid string, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.UIDName, error) {
// user may exist but have no followers // user may exist but have no followers
exists, err := db.UserExists(uid) exists, err := db.UserExistsNotBanned(uid, requesting_uid)
if err != nil { if err != nil {
return ERR_INTERNAL, nil, err return ERR_INTERNAL, nil, err

View file

@ -21,7 +21,7 @@ type Comment struct {
Date string `json:"date"` Date string `json:"date"`
} }
type Photo struct { //todo: move to structures type Photo struct {
UID string `json:"user_id"` UID string `json:"user_id"`
ID int64 `json:"photo_id"` ID int64 `json:"photo_id"`
Likes int64 `json:"likes"` Likes int64 `json:"likes"`
@ -44,5 +44,5 @@ type UserProfile struct {
Following int64 `json:"following"` Following int64 `json:"following"`
Followers int64 `json:"followers"` Followers int64 `json:"followers"`
Followed bool `json:"followed"` Followed bool `json:"followed"`
Photos int64 `json:"photos"` //todo: check json names Photos int64 `json:"photos"`
} }