chore: did some clean up

This commit is contained in:
Andre Henriques 2024-04-13 23:55:01 +01:00
parent 4862e9a79e
commit fbf7eb9271
5 changed files with 148 additions and 345 deletions

View File

@ -8,7 +8,8 @@ import (
type UserType int type UserType int
const ( const (
User_Normal UserType = iota + 1 User_Not_Auth UserType = iota
User_Normal
User_Admin User_Admin
) )

View File

@ -4,8 +4,6 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"html/template"
"io"
"net/http" "net/http"
"os" "os"
"path" "path"
@ -47,15 +45,6 @@ type Handle struct {
validate *validator.Validate 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) { func handleError(err *Error, c *Context) {
if err != nil { if err != nil {
c.Logger.Warn("Responded with error", "code", err.Code, "data", err.data) 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) { func (x *Handle) Get(path string, fn func(c *Context) *Error) {
x.gets = append(x.gets, HandleFunc{path, fn}) 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) { func (x *Handle) Post(path string, fn func(c *Context) *Error) {
x.posts = append(x.posts, HandleFunc{path, fn}) 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}) 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 { inner_fn := func(c *Context) *Error {
if !c.CheckAuthLevel(authLevel) { if !c.CheckAuthLevel(int(authLevel)) {
return nil 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}) 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() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
context.Logger.Error("Something went very wrong", "Error", r) 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 { if s.path == context.R.URL.Path {
handleError(s.fn(context), context) handleError(s.fn(context), context)
return 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 context.ShowMessage = false
handleError(&Error{404, "Endpoint not found"}, context) handleError(&Error{404, "Endpoint not found"}, context)
} }
func (c *Context) CheckAuthLevel(authLevel int) bool { func (c *Context) CheckAuthLevel(authLevel int) bool {
if authLevel > 0 { if authLevel > 0 {
if c.requireAuth() { if c.User == nil {
c.Logoff() contextlessLogoff(c.Writer)
return false return false
} }
if c.User.UserType < authLevel { 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 return false
} }
} }
@ -221,6 +191,7 @@ type Context struct {
Handle *Handle Handle *Handle
} }
// This is required for this to integrate simealy with my orl
func (c Context) GetDb() *sql.DB { func (c Context) GetDb() *sql.DB {
return c.Db return c.Db
} }
@ -237,7 +208,6 @@ func (c Context) Prepare(str string) (*sql.Stmt, error) {
if c.Tx == nil { if c.Tx == nil {
return c.Db.Prepare(str) return c.Db.Prepare(str)
} }
return c.Tx.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} return &Error{code, data}
} }
// Deprecated: Use the E500M instead
func (c Context) Error500(err error) *Error { func (c Context) Error500(err error) *Error {
return c.ErrorCode(err, http.StatusInternalServerError, nil) 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) 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!") var LogoffError = errors.New("Invalid token!")
func (x Handle) createContext(handler *Handle, r *http.Request, w http.ResponseWriter) (*Context, error) { 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\"")) 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) { func (x Handle) ReadTypesFilesApi(pathTest string, baseFilePath string, fileTypes []string, contentTypes []string) {
http.HandleFunc("/api"+pathTest, func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/api"+pathTest, func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1) 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 { func NewHandler(db *sql.DB, config Config) *Handle {
var gets []HandleFunc var gets []HandleFunc
var posts []HandleFunc var posts []HandleFunc
var deletes []HandleFunc var deletes []HandleFunc
@ -624,11 +491,11 @@ func NewHandler(db *sql.DB, config Config) *Handle {
// context.Logger.Info("Parsing", "path", r.URL.Path) // context.Logger.Info("Parsing", "path", r.URL.Path)
if r.Method == "GET" { if r.Method == "GET" {
x.handleGets(context) handleLoop(x.gets, context)
} else if r.Method == "POST" { } else if r.Method == "POST" {
x.handlePosts(context) handleLoop(x.posts, context)
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
x.handleDeletes(context) handleLoop(x.deletes, context)
} else if r.Method == "OPTIONS" { } else if r.Method == "OPTIONS" {
// do nothing // do nothing
} else { } else {

View File

@ -51,10 +51,6 @@ func main() {
} }
// TODO Handle this in other way // 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"}) handle.ReadTypesFilesApi("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"})
usersEndpints(db, handle) usersEndpints(db, handle)

129
users.go
View File

@ -81,18 +81,12 @@ func generateToken(db *sql.DB, email string, password string, name string) (stri
} }
func usersEndpints(db *sql.DB, handle *Handle) { func usersEndpints(db *sql.DB, handle *Handle) {
handle.Post("/login", func(c *Context) *Error {
type UserLogin struct { type UserLogin struct {
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"` Password string `json:"password"`
} }
PostAuthJson(handle, "/login", dbtypes.User_Not_Auth, func(c *Context, dat *UserLogin) *Error {
var dat UserLogin
if err := c.ToJSON(&dat); err != nil {
return err
}
// TODO Give this to the generateToken function // TODO Give this to the generateToken function
token, login := generateToken(db, dat.Email, dat.Password, "Logged in user") token, login := generateToken(db, dat.Email, dat.Password, "Logged in user")
if !login { if !login {
@ -101,7 +95,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
user, err := dbtypes.UserFromToken(c.Db, token) user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil { if err != nil {
return c.Error500(err) return c.E500M("Failed to get user from token", err)
} }
type UserReturn struct { type UserReturn struct {
@ -123,43 +117,29 @@ func usersEndpints(db *sql.DB, handle *Handle) {
return c.SendJSON(userReturn) return c.SendJSON(userReturn)
}) })
handle.Post("/register", func(c *Context) *Error { type UserRegister struct {
type UserLogin struct { Username string `json:"username" validate:"required"`
Username string `json:"username"` Email string `json:"email" validate:"required"`
Email string `json:"email"` Password string `json:"password" validate:"required"`
Password string `json:"password"`
} }
PostAuthJson(handle, "/register", dbtypes.User_Not_Auth, func(c *Context, dat *UserRegister) *Error {
var dat UserLogin var prevUser struct {
Username string
if err := c.ToJSON(&dat); err != nil { Email string
return err
} }
err := GetDBOnce(c, &prevUser, "users where username=$1 or email=$2;", dat.Username, dat.Email)
if len(dat.Username) == 0 || len(dat.Password) == 0 || len(dat.Email) == 0 { if err == NotFoundError {
return c.SendJSONStatus(http.StatusBadRequest, "Please provide a valid json") // 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)
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email) } else {
if err != nil { if prevUser.Email == dat.Email {
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 {
return c.SendJSONStatus(http.StatusBadRequest, "Email already in use!") 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!") return c.SendJSONStatus(http.StatusBadRequest, "Username already in use!")
} }
panic("Unrechable")
} }
if len([]byte(dat.Password)) > 68 { if len([]byte(dat.Password)) > 68 {
@ -169,12 +149,12 @@ func usersEndpints(db *sql.DB, handle *Handle) {
salt := generateSalt() salt := generateSalt()
hash_password, err := hashPassword(dat.Password, salt) hash_password, err := hashPassword(dat.Password, salt)
if err != nil { 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) _, 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 { if err != nil {
return c.Error500(err) return c.E500M("Falied to create user", err)
} }
// TODO Give this to the generateToken function // 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) user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil { if err != nil {
return c.Error500(err) return c.E500M("Falied to create user", err)
} }
type UserReturn struct { type UserReturn struct {
@ -208,14 +188,10 @@ func usersEndpints(db *sql.DB, handle *Handle) {
}) })
// TODO allow admin users to update this data // TODO allow admin users to update this data
handle.Get("/user/info", func(c *Context) *Error { handle.GetAuth("/user/info", int(dbtypes.User_Normal), func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
user, err := dbtypes.UserFromToken(c.Db, *c.Token) user, err := dbtypes.UserFromToken(c.Db, *c.Token)
if err != nil { if err != nil {
return c.Error500(err) return c.E500M("Falied to get user data", err)
} }
type UserReturn struct { type UserReturn struct {
@ -236,22 +212,11 @@ func usersEndpints(db *sql.DB, handle *Handle) {
}) })
// Handles updating users // Handles updating users
handle.Post("/user/info", func(c *Context) *Error { type UpdateUserData struct {
if !c.CheckAuthLevel(int(dbtypes.User_Normal)) {
return nil
}
type UserData struct {
Id string `json:"id"` Id string `json:"id"`
Email string `json:"email"` Email string `json:"email"`
} }
PostAuthJson(handle, "/user/info", dbtypes.User_Normal, func(c *Context, dat *UpdateUserData) *Error {
var dat UserData
if err := c.ToJSON(&dat); err != nil {
return err
}
if dat.Id != c.User.Id && c.User.UserType != int(dbtypes.User_Admin) { 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") 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 { if err == NotFoundError {
return c.JsonBadRequest("User does not exist") return c.JsonBadRequest("User does not exist")
} else if err != nil { } else if err != nil {
return c.Error500(err) return c.E500M("Falied to get data for user", err)
} }
} }
var data struct { var data JustId
Id string
}
err := utils.GetDBOnce(c, &data, "users where email=$1", dat.Email) err := utils.GetDBOnce(c, &data, "users where email=$1", dat.Email)
if err != nil && err != NotFoundError { if err != nil && err != NotFoundError {
return c.Error500(err) return c.E500M("Falied to get data for user", err)
} }
if err != NotFoundError { 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) _, err = c.Db.Exec("update users set email=$2 where id=$1", dat.Id, dat.Email)
if err != nil { if err != nil {
return c.Error500(err) return c.E500M("Failed to update data", err)
} }
var user struct { 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) err = utils.GetDBOnce(c, &user, "users where id=$1", dat.Id)
if err != nil { if err != nil {
return c.Error500(err) return c.E500M("Failed to get user data", err)
} }
toReturnUser := dbtypes.User{ toReturnUser := dbtypes.User{
@ -313,25 +275,12 @@ func usersEndpints(db *sql.DB, handle *Handle) {
return c.SendJSON(toReturnUser) return c.SendJSON(toReturnUser)
}) })
handle.Post("/user/info/password", func(c *Context) *Error { type PasswordUpdate struct {
if !c.CheckAuthLevel(1) { Old_Password string `json:"old_password" validate:"required"`
return nil 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 {
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")
}
if dat.Password != dat.Password2 { if dat.Password != dat.Password2 {
return c.JsonBadRequest("New passwords did not match") return c.JsonBadRequest("New passwords did not match")
} }
@ -345,12 +294,12 @@ func usersEndpints(db *sql.DB, handle *Handle) {
salt := generateSalt() salt := generateSalt()
hash_password, err := hashPassword(dat.Password, salt) hash_password, err := hashPassword(dat.Password, salt)
if err != nil { 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) _, err = db.Exec("update users set salt=$1, password=$2 where id=$3", salt, hash_password, c.User.Id)
if err != nil { if err != nil {
return c.Error500(err) return c.E500M("Failed to update password", err)
} }
return c.SendJSON(c.User.Id) return c.SendJSON(c.User.Id)
@ -405,6 +354,4 @@ func usersEndpints(db *sql.DB, handle *Handle) {
return c.SendJSON("Ok") return c.SendJSON("Ok")
}) })
// TODO create function to remove token
} }

View File

@ -9,40 +9,36 @@
let loginData = $state({ let loginData = $state({
username: '', username: '',
email: '', email: '',
password: '', password: ''
}); });
let errorMessage = $state(""); let errorMessage = $state('');
async function onSubmit() { async function onSubmit() {
submitted = true; submitted = true;
errorMessage = ""; errorMessage = '';
try { try {
const req = await post('register', loginData); const req = await post('register', loginData);
userStore.user = req; userStore.user = req;
goto("/"); goto('/');
} catch (e) { } catch (e) {
if (e instanceof Response) { if (e instanceof Response) {
errorMessage = await e.json(); errorMessage = await e.json();
} else { } else {
errorMessage = "Server could not perform login"; errorMessage = 'Server could not perform login';
} }
} }
} }
</script> </script>
<svelte:head> <svelte:head>
<title> <title>Register</title>
Register
</title>
</svelte:head> </svelte:head>
<div class="login-page"> <div class="login-page">
<div> <div>
<h1> <h1>Register</h1>
Register
</h1>
<form on:submit|preventDefault={onSubmit} class:submitted> <form on:submit|preventDefault={onSubmit} class:submitted>
<fieldset> <fieldset>
<label for="username">Username</label> <label for="username">Username</label>
@ -76,13 +72,9 @@
{errorMessage} {errorMessage}
</div> </div>
{/if} {/if}
<button> <button> Register </button>
Register
</button>
<div class="spacer"></div> <div class="spacer"></div>
<a class="simple-link text-center w100" href="/login"> <a class="simple-link text-center w100" href="/login"> Login </a>
Login
</a>
</form> </form>
</div> </div>
</div> </div>