diff --git a/logic/db_types/user.go b/logic/db_types/user.go index f29e62a..08838b4 100644 --- a/logic/db_types/user.go +++ b/logic/db_types/user.go @@ -7,15 +7,16 @@ import ( type UserType int -const ( - User_Normal UserType = iota + 1 - User_Admin +const ( + User_Not_Auth UserType = iota + User_Normal + User_Admin ) type User struct { - Id string - Username string - Email string + Id string + Username string + Email string UserType int } @@ -26,7 +27,7 @@ func UserFromToken(db *sql.DB, token string) (*User, error) { if err != nil { return nil, err } - defer rows.Close() + defer rows.Close() var id string var username string diff --git a/logic/utils/handler.go b/logic/utils/handler.go index 5cfe090..09b1c69 100644 --- a/logic/utils/handler.go +++ b/logic/utils/handler.go @@ -4,8 +4,6 @@ import ( "database/sql" "errors" "fmt" - "html/template" - "io" "net/http" "os" "path" @@ -47,15 +45,6 @@ type Handle struct { validate *validator.Validate } -func decodeBody(r *http.Request) (string, *Error) { - body, err := io.ReadAll(r.Body) - if err == nil { - return "", &Error{Code: http.StatusBadRequest} - } - - return string(body[:]), nil -} - func handleError(err *Error, c *Context) { if err != nil { c.Logger.Warn("Responded with error", "code", err.Code, "data", err.data) @@ -73,10 +62,20 @@ func handleError(err *Error, c *Context) { } } +// This group of functions defines some endpoints func (x *Handle) Get(path string, fn func(c *Context) *Error) { x.gets = append(x.gets, HandleFunc{path, fn}) } +func (x *Handle) GetAuth(path string, authLevel int, fn func(c *Context) *Error) { + inner_fn := func(c *Context) *Error { + if !c.CheckAuthLevel(authLevel) { + return nil + } + return fn(c) + } + x.gets = append(x.gets, HandleFunc{path, inner_fn}) +} func (x *Handle) Post(path string, fn func(c *Context) *Error) { x.posts = append(x.posts, HandleFunc{path, fn}) } @@ -91,9 +90,9 @@ func (x *Handle) PostAuth(path string, authLevel int, fn func(c *Context) *Error x.posts = append(x.posts, HandleFunc{path, inner_fn}) } -func PostAuthJson[T interface{}](x *Handle, path string, authLevel int, fn func(c *Context, obj *T) *Error) { +func PostAuthJson[T interface{}](x *Handle, path string, authLevel dbtypes.UserType, fn func(c *Context, obj *T) *Error) { inner_fn := func(c *Context) *Error { - if !c.CheckAuthLevel(authLevel) { + if !c.CheckAuthLevel(int(authLevel)) { return nil } @@ -141,7 +140,8 @@ func DeleteAuthJson[T interface{}](x *Handle, path string, authLevel int, fn fun x.deletes = append(x.deletes, HandleFunc{path, inner_fn}) } -func (x *Handle) handleGets(context *Context) { +// This function handles loop of a list of possible handler functions +func handleLoop(array []HandleFunc, context *Context) { defer func() { if r := recover(); r != nil { context.Logger.Error("Something went very wrong", "Error", r) @@ -149,60 +149,30 @@ func (x *Handle) handleGets(context *Context) { } }() - for _, s := range x.gets { + for _, s := range array { if s.path == context.R.URL.Path { handleError(s.fn(context), context) return } } - context.ShowMessage = false - handleError(&Error{404, "Endpoint not found"}, context) -} -func (x *Handle) handlePosts(context *Context) { - defer func() { - if r := recover(); r != nil { - context.Logger.Error("Something went very wrong", "Error", r) - handleError(&Error{500, "500"}, context) - } - }() - - for _, s := range x.posts { - if s.path == context.R.URL.Path { - handleError(s.fn(context), context) - return - } - } - context.ShowMessage = false - handleError(&Error{404, "Endpoint not found"}, context) -} - -func (x *Handle) handleDeletes(context *Context) { - defer func() { - if r := recover(); r != nil { - context.Logger.Error("Something went very wrong", "Error", r) - handleError(&Error{500, "500"}, context) - } - }() - - for _, s := range x.deletes { - if s.path == context.R.URL.Path { - handleError(s.fn(context), context) - return - } - } context.ShowMessage = false handleError(&Error{404, "Endpoint not found"}, context) } func (c *Context) CheckAuthLevel(authLevel int) bool { if authLevel > 0 { - if c.requireAuth() { - c.Logoff() + if c.User == nil { + contextlessLogoff(c.Writer) return false } if c.User.UserType < authLevel { - c.NotAuth() + c.Writer.WriteHeader(http.StatusUnauthorized) + e := c.SendJSON("Not Authorized") + if e != nil { + c.Writer.WriteHeader(http.StatusInternalServerError) + c.Writer.Write([]byte("You can not access this resource!")) + } return false } } @@ -221,6 +191,7 @@ type Context struct { Handle *Handle } +// This is required for this to integrate simealy with my orl func (c Context) GetDb() *sql.DB { return c.Db } @@ -237,7 +208,6 @@ func (c Context) Prepare(str string) (*sql.Stmt, error) { if c.Tx == nil { return c.Db.Prepare(str) } - return c.Tx.Prepare(str) } @@ -395,6 +365,7 @@ func (c Context) ErrorCode(err error, code int, data any) *Error { return &Error{code, data} } +// Deprecated: Use the E500M instead func (c Context) Error500(err error) *Error { return c.ErrorCode(err, http.StatusInternalServerError, nil) } @@ -403,13 +374,6 @@ func (c Context) E500M(msg string, err error) *Error { return c.ErrorCode(err, http.StatusInternalServerError, msg) } -func (c *Context) requireAuth() bool { - if c.User == nil { - return true - } - return false -} - var LogoffError = errors.New("Invalid token!") func (x Handle) createContext(handler *Handle, r *http.Request, w http.ResponseWriter) (*Context, error) { @@ -453,102 +417,6 @@ func contextlessLogoff(w http.ResponseWriter) { w.Write([]byte("\"Not Authorized\"")) } -func (c *Context) Logoff() { contextlessLogoff(c.Writer) } - -func (c *Context) NotAuth() { - c.Writer.WriteHeader(http.StatusUnauthorized) - e := c.SendJSON("Not Authorized") - if e != nil { - c.Writer.WriteHeader(http.StatusInternalServerError) - c.Writer.Write([]byte("You can not access this resource!")) - } -} - -func (x Handle) StaticFiles(pathTest string, fileType string, contentType string) { - http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) { - path := r.URL.Path[len(pathTest):] - - if !strings.HasSuffix(path, fileType) { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("File not found")) - return - } - - t, err := template.ParseFiles("./views" + pathTest + path) - - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("Failed to load template")) - return - } - - w.Header().Set("Content-Type", contentType+"; charset=utf-8") - t.Execute(w, nil) - }) -} - -func (x Handle) ReadFiles(pathTest string, baseFilePath string, fileType string, contentType string) { - http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) { - user_path := r.URL.Path[len(pathTest):] - - // fmt.Printf("Requested path: %s\n", user_path) - - if !strings.HasSuffix(user_path, fileType) { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("File not found")) - return - } - - bytes, err := os.ReadFile(path.Join(baseFilePath, pathTest, user_path)) - if err != nil { - fmt.Println(err) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("Failed to load file")) - return - } - - w.Header().Set("Content-Type", contentType) - w.Write(bytes) - }) -} - -// TODO remove this -func (x Handle) ReadTypesFiles(pathTest string, baseFilePath string, fileTypes []string, contentTypes []string) { - http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) { - user_path := r.URL.Path[len(pathTest):] - - // fmt.Printf("Requested path: %s\n", user_path) - - found := false - index := -1 - - for i, fileType := range fileTypes { - if strings.HasSuffix(user_path, fileType) { - found = true - index = i - break - } - } - - if !found { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("File not found")) - return - } - - bytes, err := os.ReadFile(path.Join(baseFilePath, pathTest, user_path)) - if err != nil { - fmt.Println(err) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("Failed to load file")) - return - } - - w.Header().Set("Content-Type", contentTypes[index]) - w.Write(bytes) - }) -} - func (x Handle) ReadTypesFilesApi(pathTest string, baseFilePath string, fileTypes []string, contentTypes []string) { http.HandleFunc("/api"+pathTest, func(w http.ResponseWriter, r *http.Request) { r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1) @@ -586,7 +454,6 @@ func (x Handle) ReadTypesFilesApi(pathTest string, baseFilePath string, fileType } func NewHandler(db *sql.DB, config Config) *Handle { - var gets []HandleFunc var posts []HandleFunc var deletes []HandleFunc @@ -624,11 +491,11 @@ func NewHandler(db *sql.DB, config Config) *Handle { // context.Logger.Info("Parsing", "path", r.URL.Path) if r.Method == "GET" { - x.handleGets(context) + handleLoop(x.gets, context) } else if r.Method == "POST" { - x.handlePosts(context) + handleLoop(x.posts, context) } else if r.Method == "DELETE" { - x.handleDeletes(context) + handleLoop(x.deletes, context) } else if r.Method == "OPTIONS" { // do nothing } else { diff --git a/main.go b/main.go index b2a908d..7c5490d 100644 --- a/main.go +++ b/main.go @@ -51,10 +51,6 @@ func main() { } // TODO Handle this in other way - handle.StaticFiles("/styles/", ".css", "text/css") - handle.StaticFiles("/js/", ".js", "text/javascript") - handle.ReadFiles("/imgs/", "views", ".png", "image/png;") - handle.ReadTypesFiles("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"}) handle.ReadTypesFilesApi("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"}) usersEndpints(db, handle) diff --git a/users.go b/users.go index 4861e4f..4feef5a 100644 --- a/users.go +++ b/users.go @@ -81,18 +81,12 @@ func generateToken(db *sql.DB, email string, password string, name string) (stri } func usersEndpints(db *sql.DB, handle *Handle) { - handle.Post("/login", func(c *Context) *Error { - type UserLogin struct { - Email string `json:"email"` - Password string `json:"password"` - } - - var dat UserLogin - - if err := c.ToJSON(&dat); err != nil { - return err - } + type UserLogin struct { + Email string `json:"email"` + Password string `json:"password"` + } + PostAuthJson(handle, "/login", dbtypes.User_Not_Auth, func(c *Context, dat *UserLogin) *Error { // TODO Give this to the generateToken function token, login := generateToken(db, dat.Email, dat.Password, "Logged in user") if !login { @@ -101,7 +95,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { user, err := dbtypes.UserFromToken(c.Db, token) if err != nil { - return c.Error500(err) + return c.E500M("Failed to get user from token", err) } type UserReturn struct { @@ -123,43 +117,29 @@ func usersEndpints(db *sql.DB, handle *Handle) { return c.SendJSON(userReturn) }) - handle.Post("/register", func(c *Context) *Error { - type UserLogin struct { - Username string `json:"username"` - Email string `json:"email"` - Password string `json:"password"` + type UserRegister struct { + Username string `json:"username" validate:"required"` + Email string `json:"email" validate:"required"` + Password string `json:"password" validate:"required"` + } + PostAuthJson(handle, "/register", dbtypes.User_Not_Auth, func(c *Context, dat *UserRegister) *Error { + + var prevUser struct { + Username string + Email string } - - var dat UserLogin - - if err := c.ToJSON(&dat); err != nil { - return err - } - - if len(dat.Username) == 0 || len(dat.Password) == 0 || len(dat.Email) == 0 { - return c.SendJSONStatus(http.StatusBadRequest, "Please provide a valid json") - } - - rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email) - if err != nil { - return c.Error500(err) - } - defer rows.Close() - - if rows.Next() { - var db_username string - var db_email string - err = rows.Scan(&db_username, &db_email) - if err != nil { - return c.Error500(err) - } - if db_email == dat.Email { + err := GetDBOnce(c, &prevUser, "users where username=$1 or email=$2;", dat.Username, dat.Email) + if err == NotFoundError { + // Do nothing the user does not exist and it's ok to create a new one + } else if err != nil { + return c.E500M("Falied to get user data", err) + } else { + if prevUser.Email == dat.Email { return c.SendJSONStatus(http.StatusBadRequest, "Email already in use!") } - if db_username == dat.Username { + if prevUser.Username == dat.Username { return c.SendJSONStatus(http.StatusBadRequest, "Username already in use!") } - panic("Unrechable") } if len([]byte(dat.Password)) > 68 { @@ -169,12 +149,12 @@ func usersEndpints(db *sql.DB, handle *Handle) { salt := generateSalt() hash_password, err := hashPassword(dat.Password, salt) if err != nil { - return c.Error500(err) + return c.E500M("Falied to store password", err) } _, err = db.Exec("insert into users (username, email, salt, password) values ($1, $2, $3, $4);", dat.Username, dat.Email, salt, hash_password) if err != nil { - return c.Error500(err) + return c.E500M("Falied to create user", err) } // TODO Give this to the generateToken function @@ -185,7 +165,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { user, err := dbtypes.UserFromToken(c.Db, token) if err != nil { - return c.Error500(err) + return c.E500M("Falied to create user", err) } type UserReturn struct { @@ -208,14 +188,10 @@ func usersEndpints(db *sql.DB, handle *Handle) { }) // TODO allow admin users to update this data - handle.Get("/user/info", func(c *Context) *Error { - if !c.CheckAuthLevel(1) { - return nil - } - + handle.GetAuth("/user/info", int(dbtypes.User_Normal), func(c *Context) *Error { user, err := dbtypes.UserFromToken(c.Db, *c.Token) if err != nil { - return c.Error500(err) + return c.E500M("Falied to get user data", err) } type UserReturn struct { @@ -236,22 +212,11 @@ func usersEndpints(db *sql.DB, handle *Handle) { }) // Handles updating users - handle.Post("/user/info", func(c *Context) *Error { - if !c.CheckAuthLevel(int(dbtypes.User_Normal)) { - return nil - } - - type UserData struct { - Id string `json:"id"` - Email string `json:"email"` - } - - var dat UserData - - if err := c.ToJSON(&dat); err != nil { - return err - } - + type UpdateUserData struct { + Id string `json:"id"` + Email string `json:"email"` + } + PostAuthJson(handle, "/user/info", dbtypes.User_Normal, func(c *Context, dat *UpdateUserData) *Error { if dat.Id != c.User.Id && c.User.UserType != int(dbtypes.User_Admin) { return c.SendJSONStatus(403, "You need to be an admin to update another users account") } @@ -265,17 +230,14 @@ func usersEndpints(db *sql.DB, handle *Handle) { if err == NotFoundError { return c.JsonBadRequest("User does not exist") } else if err != nil { - return c.Error500(err) + return c.E500M("Falied to get data for user", err) } } - var data struct { - Id string - } - + var data JustId err := utils.GetDBOnce(c, &data, "users where email=$1", dat.Email) if err != nil && err != NotFoundError { - return c.Error500(err) + return c.E500M("Falied to get data for user", err) } if err != NotFoundError { @@ -288,7 +250,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { _, err = c.Db.Exec("update users set email=$2 where id=$1", dat.Id, dat.Email) if err != nil { - return c.Error500(err) + return c.E500M("Failed to update data", err) } var user struct { @@ -300,7 +262,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { err = utils.GetDBOnce(c, &user, "users where id=$1", dat.Id) if err != nil { - return c.Error500(err) + return c.E500M("Failed to get user data", err) } toReturnUser := dbtypes.User{ @@ -313,25 +275,12 @@ func usersEndpints(db *sql.DB, handle *Handle) { return c.SendJSON(toReturnUser) }) - handle.Post("/user/info/password", func(c *Context) *Error { - if !c.CheckAuthLevel(1) { - return nil - } - - var dat struct { - Old_Password string `json:"old_password"` - Password string `json:"password"` - Password2 string `json:"password2"` - } - - if err := c.ToJSON(&dat); err != nil { - return err - } - - if dat.Password == "" { - return c.JsonBadRequest("Password can not be empty") - } - + type PasswordUpdate struct { + Old_Password string `json:"old_password" validate:"required"` + Password string `json:"password" validate:"required"` + Password2 string `json:"password2" validate:"required"` + } + PostAuthJson(handle, "/user/info/password", dbtypes.User_Normal, func(c *Context, dat *PasswordUpdate) *Error { if dat.Password != dat.Password2 { return c.JsonBadRequest("New passwords did not match") } @@ -345,12 +294,12 @@ func usersEndpints(db *sql.DB, handle *Handle) { salt := generateSalt() hash_password, err := hashPassword(dat.Password, salt) if err != nil { - return c.Error500(err) + return c.E500M("Failed to parse the password", err) } _, err = db.Exec("update users set salt=$1, password=$2 where id=$3", salt, hash_password, c.User.Id) if err != nil { - return c.Error500(err) + return c.E500M("Failed to update password", err) } return c.SendJSON(c.User.Id) @@ -405,6 +354,4 @@ func usersEndpints(db *sql.DB, handle *Handle) { return c.SendJSON("Ok") }) - - // TODO create function to remove token } diff --git a/webpage/src/routes/register/+page.svelte b/webpage/src/routes/register/+page.svelte index c720189..726bf6f 100644 --- a/webpage/src/routes/register/+page.svelte +++ b/webpage/src/routes/register/+page.svelte @@ -1,104 +1,96 @@ - - Register - + Register
-
-

- Register -

-
-
- - - -
-
- - - -
-
- - - -
- {#if errorMessage} -
- {errorMessage} -
- {/if} - -
- - Login - -
-
+ + {#if errorMessage} +
+ {errorMessage} +
+ {/if} + +
+ Login + +