mirror of
https://github.com/notherealmarco/WASAPhoto.git
synced 2025-03-14 14:16:15 +01:00
Improve comments and code readability
This commit is contained in:
parent
f6ad6db2f7
commit
3de158e5a5
19 changed files with 84 additions and 43 deletions
|
@ -210,7 +210,7 @@ paths:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
status: "Resource not found"
|
status: "Resource not found"
|
||||||
'400': # todo: not sure if this is the right error code
|
'400':
|
||||||
description: Trying to follow a user that does not exist.
|
description: Trying to follow a user that does not exist.
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
|
|
|
@ -7,9 +7,11 @@ import (
|
||||||
"github.com/notherealmarco/WASAPhoto/service/database"
|
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AnonymousAuth is the authentication provider for non logged-in users
|
||||||
type AnonymousAuth struct {
|
type AnonymousAuth struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a newly created AnonymousAuth instance
|
||||||
func BuildAnonymous() *AnonymousAuth {
|
func BuildAnonymous() *AnonymousAuth {
|
||||||
return &AnonymousAuth{}
|
return &AnonymousAuth{}
|
||||||
}
|
}
|
||||||
|
@ -18,14 +20,17 @@ func (u *AnonymousAuth) GetType() string {
|
||||||
return "Anonymous"
|
return "Anonymous"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns UNAUTHORIZED, as anonymous users are logged in
|
||||||
func (u *AnonymousAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus, error) {
|
func (u *AnonymousAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus, error) {
|
||||||
return reqcontext.UNAUTHORIZED, nil
|
return reqcontext.UNAUTHORIZED, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns UNAUTHORIZED, as anonymous users are not logged in
|
||||||
func (u *AnonymousAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error) {
|
func (u *AnonymousAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error) {
|
||||||
return reqcontext.UNAUTHORIZED, nil
|
return reqcontext.UNAUTHORIZED, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns an empty string, as anonymous users have no user ID
|
||||||
func (u *AnonymousAuth) GetUserID() string {
|
func (u *AnonymousAuth) GetUserID() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"github.com/notherealmarco/WASAPhoto/service/database"
|
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BearerAuth is the authentication provider that authorizes users by Bearer tokens
|
||||||
|
// In this case, a token is the unique identifier for a user.
|
||||||
type BearerAuth struct {
|
type BearerAuth struct {
|
||||||
token string
|
token string
|
||||||
}
|
}
|
||||||
|
@ -16,6 +18,8 @@ func (b *BearerAuth) GetType() string {
|
||||||
return "Bearer"
|
return "Bearer"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given the content of the Authorization header, returns a BearerAuth instance for the user
|
||||||
|
// Returns an error if the header is not valid
|
||||||
func BuildBearer(header string) (*BearerAuth, error) {
|
func BuildBearer(header string) (*BearerAuth, error) {
|
||||||
if header == "" {
|
if header == "" {
|
||||||
return nil, errors.New("missing authorization header")
|
return nil, errors.New("missing authorization header")
|
||||||
|
@ -29,10 +33,12 @@ func BuildBearer(header string) (*BearerAuth, error) {
|
||||||
return &BearerAuth{token: header[7:]}, nil
|
return &BearerAuth{token: header[7:]}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the user ID of the user that is currently logged in
|
||||||
func (b *BearerAuth) GetUserID() string {
|
func (b *BearerAuth) GetUserID() string {
|
||||||
return b.token
|
return b.token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the token is valid
|
||||||
func (b *BearerAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus, error) {
|
func (b *BearerAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus, error) {
|
||||||
// this is the way we manage authorization, the bearer token is the user id
|
// this is the way we manage authorization, the bearer token is the user id
|
||||||
state, err := db.UserExists(b.token)
|
state, err := db.UserExists(b.token)
|
||||||
|
@ -47,6 +53,7 @@ func (b *BearerAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus,
|
||||||
return reqcontext.UNAUTHORIZED, nil
|
return reqcontext.UNAUTHORIZED, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the given user and the currently logged in user are the same user
|
||||||
func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error) {
|
func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error) {
|
||||||
|
|
||||||
// If uid is not a valid user, return USER_NOT_FOUND
|
// If uid is not a valid user, return USER_NOT_FOUND
|
||||||
|
@ -60,6 +67,7 @@ func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcon
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.token == uid {
|
if b.token == uid {
|
||||||
|
// If the user is the same as the one in the token, check if the user does actually exist in the database
|
||||||
auth, err := b.Authorized(db)
|
auth, err := b.Authorized(db)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -68,5 +76,6 @@ func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcon
|
||||||
|
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
// If the user is not the same as the one in the token, return FORBIDDEN
|
||||||
return reqcontext.FORBIDDEN, nil
|
return reqcontext.FORBIDDEN, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BuildAuth returns an Authorization implementation for the currently logged in user
|
||||||
func BuildAuth(header string) (reqcontext.Authorization, error) {
|
func BuildAuth(header string) (reqcontext.Authorization, error) {
|
||||||
auth, err := BuildBearer(header)
|
auth, err := BuildBearer(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -21,6 +22,8 @@ func BuildAuth(header string) (reqcontext.Authorization, error) {
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given a user authorization function, if the function returns some error, it sends the error to the client and return false
|
||||||
|
// Otherwise it returns true without sending anything to the client
|
||||||
func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error), uid string, db database.AppDatabase, w http.ResponseWriter, l logrus.FieldLogger, notFoundStatus int) bool {
|
func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error), uid string, db database.AppDatabase, w http.ResponseWriter, l logrus.FieldLogger, notFoundStatus int) bool {
|
||||||
auth, err := f(db, uid)
|
auth, err := f(db, uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -28,21 +31,25 @@ func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcont
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if auth == reqcontext.UNAUTHORIZED {
|
if auth == reqcontext.UNAUTHORIZED {
|
||||||
|
// The token is not valid
|
||||||
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", l)
|
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", l)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if auth == reqcontext.FORBIDDEN {
|
if auth == reqcontext.FORBIDDEN {
|
||||||
|
// The user is not authorized for this action
|
||||||
helpers.SendStatus(http.StatusForbidden, w, "Forbidden", l)
|
helpers.SendStatus(http.StatusForbidden, w, "Forbidden", l)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// requested user is not found -> 404 as the resource is not found
|
|
||||||
if auth == reqcontext.USER_NOT_FOUND {
|
if auth == reqcontext.USER_NOT_FOUND {
|
||||||
|
// Attempting to perform an action on a non-existent user
|
||||||
helpers.SendStatus(notFoundStatus, w, "User not found", l)
|
helpers.SendStatus(notFoundStatus, w, "User not found", l)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given a function that validates a token, if the function returns some error, it sends the error to the client and return false
|
||||||
|
// Otherwise it returns true without sending anything to the client
|
||||||
func SendErrorIfNotLoggedIn(f func(db database.AppDatabase) (reqcontext.AuthStatus, error), db database.AppDatabase, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
func SendErrorIfNotLoggedIn(f func(db database.AppDatabase) (reqcontext.AuthStatus, error), db database.AppDatabase, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
|
|
||||||
auth, err := f(db)
|
auth, err := f(db)
|
||||||
|
@ -53,6 +60,7 @@ func SendErrorIfNotLoggedIn(f func(db database.AppDatabase) (reqcontext.AuthStat
|
||||||
}
|
}
|
||||||
|
|
||||||
if auth == reqcontext.UNAUTHORIZED {
|
if auth == reqcontext.UNAUTHORIZED {
|
||||||
|
// The token is not valid
|
||||||
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", l)
|
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", l)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ func (rt *_router) PutBan(w http.ResponseWriter, r *http.Request, ps httprouter.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
status, err := rt.db.BanUser(uid, banned)
|
status, err := rt.db.BanUser(uid, banned)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -95,6 +96,7 @@ func (rt *_router) DeleteBan(w http.ResponseWriter, r *http.Request, ps httprout
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
status, err := rt.db.UnbanUser(uid, banned)
|
status, err := rt.db.UnbanUser(uid, banned)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -55,6 +55,7 @@ func (rt *_router) GetComments(w http.ResponseWriter, r *http.Request, ps httpro
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the response
|
// send the response
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
err = json.NewEncoder(w).Encode(comments)
|
err = json.NewEncoder(w).Encode(comments)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -79,6 +79,7 @@ func (rt *_router) PutFollow(w http.ResponseWriter, r *http.Request, ps httprout
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
status, err := rt.db.FollowUser(follower, uid)
|
status, err := rt.db.FollowUser(follower, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -109,6 +110,7 @@ func (rt *_router) DeleteFollow(w http.ResponseWriter, r *http.Request, ps httpr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
status, err := rt.db.UnfollowUser(follower, uid)
|
status, err := rt.db.UnfollowUser(follower, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Tries to decode a json, if it fails, it returns Bad Request to the client and the function returns false
|
||||||
|
// Otherwise it returns true without sending anything to the client
|
||||||
func DecodeJsonOrBadRequest(r io.Reader, w http.ResponseWriter, v interface{}, l logrus.FieldLogger) bool {
|
func DecodeJsonOrBadRequest(r io.Reader, w http.ResponseWriter, v interface{}, l logrus.FieldLogger) bool {
|
||||||
|
|
||||||
err := json.NewDecoder(r).Decode(v)
|
err := json.NewDecoder(r).Decode(v)
|
||||||
|
@ -20,6 +22,8 @@ func DecodeJsonOrBadRequest(r io.Reader, w http.ResponseWriter, v interface{}, l
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verifies if a user exists, if it doesn't, it returns Not Found to the client and the function returns false
|
||||||
|
// Otherwise it returns true without sending anything to the client
|
||||||
func VerifyUserOrNotFound(db database.AppDatabase, uid string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
func VerifyUserOrNotFound(db database.AppDatabase, uid string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
|
|
||||||
user_exists, err := db.UserExists(uid)
|
user_exists, err := db.UserExists(uid)
|
||||||
|
@ -36,6 +40,8 @@ func VerifyUserOrNotFound(db database.AppDatabase, uid string, w http.ResponseWr
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends a generic status response
|
||||||
|
// The response is a json object with a "status" field desribing the status of a request
|
||||||
func SendStatus(httpStatus int, w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
func SendStatus(httpStatus int, w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
||||||
w.WriteHeader(httpStatus)
|
w.WriteHeader(httpStatus)
|
||||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||||
|
@ -44,40 +50,29 @@ func SendStatus(httpStatus int, w http.ResponseWriter, description string, l log
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends a Not Found error to the client
|
||||||
func SendNotFound(w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
func SendNotFound(w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
SendStatus(http.StatusNotFound, w, description, l)
|
||||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
|
||||||
if err != nil {
|
|
||||||
l.WithError(err).Error("Error encoding json")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends a Bad Request error to the client
|
||||||
func SendBadRequest(w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
func SendBadRequest(w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
SendStatus(http.StatusBadRequest, w, description, l)
|
||||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
|
||||||
if err != nil {
|
|
||||||
l.WithError(err).Error("Error encoding json")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends a Bad Request error to the client and logs the given error
|
||||||
func SendBadRequestError(err error, description string, w http.ResponseWriter, l logrus.FieldLogger) {
|
func SendBadRequestError(err error, description string, w http.ResponseWriter, l logrus.FieldLogger) {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
l.WithError(err).Error(description)
|
l.WithError(err).Error(description)
|
||||||
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
SendBadRequest(w, description, l)
|
||||||
if err != nil {
|
|
||||||
l.WithError(err).Error("Error encoding json")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends an Internal Server Error to the client and logs the given error
|
||||||
func SendInternalError(err error, description string, w http.ResponseWriter, l logrus.FieldLogger) {
|
func SendInternalError(err error, description string, w http.ResponseWriter, l logrus.FieldLogger) {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
l.WithError(err).Error(description)
|
l.WithError(err).Error(description)
|
||||||
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
SendStatus(http.StatusInternalServerError, w, description, l)
|
||||||
if err != nil {
|
|
||||||
l.WithError(err).Error("Error encoding json")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tries to roll back a transaction, if it fails it logs the error
|
||||||
func RollbackOrLogError(tx database.DBTransaction, l logrus.FieldLogger) {
|
func RollbackOrLogError(tx database.DBTransaction, l logrus.FieldLogger) {
|
||||||
err := tx.Rollback()
|
err := tx.Rollback()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -85,6 +80,8 @@ func RollbackOrLogError(tx database.DBTransaction, l logrus.FieldLogger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if a user is banned by another user, then it returns Not Found to the client and the function returns false
|
||||||
|
// Otherwise it returns true whithout sending anything to the client
|
||||||
func SendNotFoundIfBanned(db database.AppDatabase, uid string, banner string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
func SendNotFoundIfBanned(db database.AppDatabase, uid string, banner string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
banned, err := db.IsBanned(uid, banner)
|
banned, err := db.IsBanned(uid, banner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,10 +6,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DEFAULT_LIMIT = 15
|
DEFAULT_LIMIT = 30
|
||||||
DEFAULT_OFFSET = 0
|
DEFAULT_OFFSET = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Get the start index and limit from the query.
|
||||||
|
// If they are not present, use the default values.
|
||||||
func GetLimits(query url.Values) (int, int, error) {
|
func GetLimits(query url.Values) (int, int, error) {
|
||||||
|
|
||||||
limit := DEFAULT_LIMIT
|
limit := DEFAULT_LIMIT
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Given a string, a regex and an error description, if the string doesn't match the regex, it sends a bad request error to the client and return false
|
||||||
|
// Otherwise it returns true without sending anything to the client
|
||||||
func MatchRegexOrBadRequest(str string, regex string, error_description string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
func MatchRegexOrBadRequest(str string, regex string, error_description string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
|
|
||||||
stat, err := regexp.Match(regex, []byte(str))
|
stat, err := regexp.Match(regex, []byte(str))
|
||||||
|
@ -25,6 +27,7 @@ func MatchRegexOrBadRequest(str string, regex string, error_description string,
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validates a username (must be between 3 and 16 characters long and can only contain letters, numbers and underscores)
|
||||||
func MatchUsernameOrBadRequest(username string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
func MatchUsernameOrBadRequest(username string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
return MatchRegexOrBadRequest(username,
|
return MatchRegexOrBadRequest(username,
|
||||||
`^[a-zA-Z0-9_]{3,16}$`, "Username must be between 3 and 16 characters long and can only contain letters, numbers and underscores",
|
`^[a-zA-Z0-9_]{3,16}$`, "Username must be between 3 and 16 characters long and can only contain letters, numbers and underscores",
|
||||||
|
@ -32,6 +35,7 @@ func MatchUsernameOrBadRequest(username string, w http.ResponseWriter, l logrus.
|
||||||
l)
|
l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validates a comment (must be between 1 and 255 characters long)
|
||||||
func MatchCommentOrBadRequest(comment string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
func MatchCommentOrBadRequest(comment string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
return MatchRegexOrBadRequest(comment,
|
return MatchRegexOrBadRequest(comment,
|
||||||
`^(.){1,255}$`, "Comment must be between 1 and 255 characters long",
|
`^(.){1,255}$`, "Comment must be between 1 and 255 characters long",
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/julienschmidt/httprouter"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// liveness is an HTTP handler that checks the API server status. If the server cannot serve requests (e.g., some
|
// liveness is an HTTP handler that checks the API server status. If the server cannot serve requests (e.g., some
|
||||||
// resources are not ready), this should reply with HTTP Status 500. Otherwise, with HTTP Status 200
|
// resources are not ready), this should reply with HTTP Status 500. Otherwise, with HTTP Status 200
|
||||||
func (rt *_router) liveness(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (rt *_router) liveness(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
/* Example of liveness check:
|
if err := rt.db.Ping(); err != nil {
|
||||||
if err := rt.DB.Ping(); err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}*/
|
}
|
||||||
|
helpers.SendStatus(200, w, "Server is live!", rt.baseLogger)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ func (rt *_router) PostPhoto(w http.ResponseWriter, r *http.Request, ps httprout
|
||||||
}
|
}
|
||||||
|
|
||||||
path := rt.dataPath + "/photos/" + uid + "/" + strconv.FormatInt(photo_id, 10) + ".jpg"
|
path := rt.dataPath + "/photos/" + uid + "/" + strconv.FormatInt(photo_id, 10) + ".jpg"
|
||||||
// todo: we should check if the body is a valid jpg image
|
|
||||||
|
|
||||||
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { // perms = 511
|
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { // perms = 511
|
||||||
helpers.SendInternalError(err, "Error creating directory", w, rt.baseLogger)
|
helpers.SendInternalError(err, "Error creating directory", w, rt.baseLogger)
|
||||||
|
@ -78,7 +77,6 @@ func (rt *_router) PostPhoto(w http.ResponseWriter, r *http.Request, ps httprout
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SendInternalError(err, "Error committing transaction", w, rt.baseLogger)
|
helpers.SendInternalError(err, "Error committing transaction", w, rt.baseLogger)
|
||||||
//todo: should I roll back?
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +154,7 @@ func (rt *_router) DeletePhoto(w http.ResponseWriter, r *http.Request, ps httpro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SendInternalError(err, "Error deleting photo from database", w, rt.baseLogger)
|
helpers.SendInternalError(err, "Error deleting photo from database", w, rt.baseLogger)
|
||||||
return
|
return
|
||||||
} // todo: maybe let's use a transaction also here
|
}
|
||||||
|
|
||||||
if !deleted {
|
if !deleted {
|
||||||
helpers.SendNotFound(w, "Photo not found", rt.baseLogger)
|
helpers.SendNotFound(w, "Photo not found", rt.baseLogger)
|
||||||
|
|
|
@ -11,9 +11,17 @@ const (
|
||||||
USER_NOT_FOUND = 3
|
USER_NOT_FOUND = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Authorization is the interface for an authorization provider
|
||||||
type Authorization interface {
|
type Authorization interface {
|
||||||
|
// Returns the type of the authorization provider
|
||||||
GetType() string
|
GetType() string
|
||||||
|
|
||||||
|
// Returns the ID of the currently logged in user
|
||||||
GetUserID() string
|
GetUserID() string
|
||||||
|
|
||||||
|
// Checks if the token is valid
|
||||||
Authorized(db database.AppDatabase) (AuthStatus, error)
|
Authorized(db database.AppDatabase) (AuthStatus, error)
|
||||||
|
|
||||||
|
// Checks if the given user and the currently logged in user are the same user
|
||||||
UserAuthorized(db database.AppDatabase, uid string) (AuthStatus, error)
|
UserAuthorized(db database.AppDatabase, uid string) (AuthStatus, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,8 +79,11 @@ type AppDatabase interface {
|
||||||
Ping() error
|
Ping() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DBTransaction is the interface for a generic database transaction
|
||||||
type DBTransaction interface {
|
type DBTransaction interface {
|
||||||
|
// Commit commits the transaction
|
||||||
Commit() error
|
Commit() error
|
||||||
|
// Rollback rolls back the transaction
|
||||||
Rollback() error
|
Rollback() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,16 +98,17 @@ func New(db *sql.DB) (AppDatabase, error) {
|
||||||
return nil, errors.New("database is required when building a AppDatabase")
|
return nil, errors.New("database is required when building a AppDatabase")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if tables exist. If not, the database is empty, and we need to create the structure
|
// Check if some table exists. If not, the database is empty, and we need to create the structure
|
||||||
var tableName string
|
var tableName string
|
||||||
//todo: check for all the tables, not just users
|
|
||||||
err := db.QueryRow(`SELECT name FROM sqlite_master WHERE type='table' AND name='users';`).Scan(&tableName)
|
err := db.QueryRow(`SELECT name FROM sqlite_master WHERE type='table' AND name='users';`).Scan(&tableName)
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
// Database is empty, let's create the structure
|
||||||
sqlStmt := `CREATE TABLE "users" (
|
sqlStmt := `CREATE TABLE "users" (
|
||||||
"uid" TEXT NOT NULL,
|
"uid" TEXT NOT NULL,
|
||||||
"name" TEXT NOT NULL UNIQUE,
|
"name" TEXT NOT NULL UNIQUE,
|
||||||
PRIMARY KEY("uid")
|
PRIMARY KEY("uid")
|
||||||
)` // todo: one query is enough! We are we doing a query per table?
|
)`
|
||||||
_, err = db.Exec(sqlStmt)
|
_, err = db.Exec(sqlStmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating database structure: %w", err)
|
return nil, fmt.Errorf("error creating database structure: %w", err)
|
||||||
|
|
|
@ -96,6 +96,7 @@ func (db *appdbimpl) UnlikePhoto(uid string, photo int64, liker_uid string) (Que
|
||||||
// But our DB implementation only requires the photo id.
|
// But our DB implementation only requires the photo id.
|
||||||
exists, err := db.photoExists(uid, photo)
|
exists, err := db.photoExists(uid, photo)
|
||||||
if err != nil || !exists {
|
if err != nil || !exists {
|
||||||
|
// The photo does not exist, or the user has been banned
|
||||||
return ERR_NOT_FOUND, err
|
return ERR_NOT_FOUND, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +112,7 @@ func (db *appdbimpl) UnlikePhoto(uid string, photo int64, liker_uid string) (Que
|
||||||
return ERR_INTERNAL, err
|
return ERR_INTERNAL, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The user was not liking the photo
|
||||||
if rows == 0 {
|
if rows == 0 {
|
||||||
return ERR_NOT_FOUND, nil
|
return ERR_NOT_FOUND, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ func (db *appdbimpl) PostPhoto(uid string) (DBTransaction, int64, error) {
|
||||||
err_rb := tx.Rollback()
|
err_rb := tx.Rollback()
|
||||||
// If rollback fails, we return the original error plus the rollback error
|
// If rollback fails, we return the original error plus the rollback error
|
||||||
if err_rb != nil {
|
if err_rb != nil {
|
||||||
// todo: we are losing track of err_rb here
|
|
||||||
err = fmt.Errorf("Rollback error. Rollback cause: %w", err)
|
err = fmt.Errorf("Rollback error. Rollback cause: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +29,6 @@ func (db *appdbimpl) PostPhoto(uid string) (DBTransaction, int64, error) {
|
||||||
err_rb := tx.Rollback()
|
err_rb := tx.Rollback()
|
||||||
// If rollback fails, we return the original error plus the rollback error
|
// If rollback fails, we return the original error plus the rollback error
|
||||||
if err_rb != nil {
|
if err_rb != nil {
|
||||||
// todo: we are losing track of err_rb here
|
|
||||||
err = fmt.Errorf("Rollback error. Rollback cause: %w", err)
|
err = fmt.Errorf("Rollback error. Rollback cause: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +64,7 @@ func (db *appdbimpl) photoExists(uid string, photo int64) (bool, error) {
|
||||||
return cnt > 0, nil
|
return cnt > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if a given photo owned by a given user exists, and the requesting user is not banned by the author
|
||||||
func (db *appdbimpl) PhotoExists(uid string, photo int64, requesting_uid string) (bool, error) {
|
func (db *appdbimpl) PhotoExists(uid string, photo int64, requesting_uid string) (bool, error) {
|
||||||
|
|
||||||
var cnt int64
|
var cnt int64
|
||||||
|
|
|
@ -2,14 +2,17 @@ package database
|
||||||
|
|
||||||
import "database/sql"
|
import "database/sql"
|
||||||
|
|
||||||
|
// dbtransaction is a struct to represent an SQL transaction, it implements the DBTransaction interface
|
||||||
type dbtransaction struct {
|
type dbtransaction struct {
|
||||||
c *sql.Tx
|
c *sql.Tx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *dbtransaction) Commit() error {
|
func (tx *dbtransaction) Commit() error {
|
||||||
|
// Commit the SQL transaction
|
||||||
return tx.c.Commit()
|
return tx.c.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *dbtransaction) Rollback() error {
|
func (tx *dbtransaction) Rollback() error {
|
||||||
|
// Rollback the SQL transaction
|
||||||
return tx.c.Rollback()
|
return tx.c.Rollback()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package db_errors
|
||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
// Returns true if the query result has no rows
|
// Returns true if the error is a "no rows in result set" error
|
||||||
func EmptySet(err error) bool {
|
func EmptySet(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
|
@ -10,6 +10,7 @@ func EmptySet(err error) bool {
|
||||||
return strings.Contains(err.Error(), "no rows in result set")
|
return strings.Contains(err.Error(), "no rows in result set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if the error is a Unique constraint violation error
|
||||||
func UniqueViolation(err error) bool {
|
func UniqueViolation(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
|
@ -17,6 +18,7 @@ func UniqueViolation(err error) bool {
|
||||||
return strings.Contains(err.Error(), "UNIQUE constraint failed")
|
return strings.Contains(err.Error(), "UNIQUE constraint failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if the error is a Foreign Key constraint violation error
|
||||||
func ForeignKeyViolation(err error) bool {
|
func ForeignKeyViolation(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package database
|
|
||||||
|
|
||||||
// SetName is an example that shows you how to execute insert/update
|
|
||||||
func (db *appdbimpl) SetName(name string) error {
|
|
||||||
_, err := db.c.Exec("INSERT INTO example_table (id, name) VALUES (1, ?)", name)
|
|
||||||
return err
|
|
||||||
}
|
|
Loading…
Reference in a new issue