diff --git a/go.mod b/go.mod index 5bf1b8f..e50c8c5 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module andr3h3nriqu3s.com/m +module git.andr3h3nriqu3s.com/andr3/fyp go 1.20 diff --git a/logic/db_types/user.go b/logic/db_types/user.go new file mode 100644 index 0000000..44ed861 --- /dev/null +++ b/logic/db_types/user.go @@ -0,0 +1,39 @@ +package dbtypes + +import ( + "database/sql" + "errors" +) + +type User struct { + Id string + Username string + Email string + UserType int +} + +var ErrUserNotFound = errors.New("User Not found") + +func UserFromToken(db *sql.DB, token string) (*User, error) { + row, err := db.Query("select users.id, users.username, users.email, users.user_type from users inner join tokens on tokens.user_id = users.id where tokens.token = $1;", token) + + if err != nil { + return nil, err + } + + var id string + var username string + var email string + var user_type int + + if !row.Next() { + return nil, ErrUserNotFound + } + + err = row.Scan(&id, &username, &email, &user_type) + if err != nil { + return nil, err + } + + return &User{id, username, email, user_type}, nil +} diff --git a/logic/models/add.go b/logic/models/add.go new file mode 100644 index 0000000..852a2d1 --- /dev/null +++ b/logic/models/add.go @@ -0,0 +1,183 @@ +package models + +import ( + "bytes" + "fmt" + "image" + _ "image/png" + "image/color" + "io" + "net/http" + "os" + "path" + + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" +) + +func loadBaseImage(handle *Handle, id string) { + // TODO handle more types than png + infile, err := os.Open(path.Join("savedData", id, "baseimage.png")) + if err != nil { + // TODO better logging + fmt.Println(err) + fmt.Printf("Failed to read image for model with id %s\n", id) + modelUpdateStatus(handle, id, -1) + return + } + defer infile.Close() + + src, format, err := image.Decode(infile) + if err != nil { + // TODO better logging + fmt.Println(err) + fmt.Printf("Failed to load image for model with id %s\n", id) + modelUpdateStatus(handle, id, -1) + return + } + if format != "png" { + // TODO better logging + fmt.Printf("Found unkown format '%s'\n", format) + panic("Handle diferent files than .png") + } + + var model_color string + + bounds := src.Bounds() + width, height := bounds.Max.X, bounds.Max.Y + + switch src.ColorModel() { + case color.Gray16Model: + fallthrough + case color.GrayModel: + model_color = "greyscale" + default: + fmt.Println("Do not know how to handle this color model") + + if src.ColorModel() == color.RGBA64Model { + fmt.Println("Color is rgb") + } else if src.ColorModel() == color.NRGBAModel { + fmt.Println("Color is nrgb") + } else if src.ColorModel() == color.YCbCrModel { + fmt.Println("Color is ycbcr") + } else if src.ColorModel() == color.AlphaModel { + fmt.Println("Color is alpha") + } else if src.ColorModel() == color.CMYKModel { + fmt.Println("Color is cmyk") + } else { + fmt.Println("Other so assuming color") + } + + modelUpdateStatus(handle, id, -1) + return + } + + // Note: this also updates the status to 2 + _, err = handle.Db.Exec("update models set width=$1, height=$2, color_mode=$3, status=$4 where id=$5", width, height, model_color, CONFIRM_PRE_TRAINING, id) + if err != nil { + // TODO better logging + fmt.Println(err) + fmt.Printf("Could not update model\n") + modelUpdateStatus(handle, id, -1) + return + } +} + +func handleAdd(handle *Handle) { +handle.GetHTML("/models/add", AnswerTemplate("models/add.html", nil, 1)) + // TODO json + handle.Post("/models/add", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { + if c.Mode == JSON { + panic("TODO JSON") + } + if !CheckAuthLevel(1, w, r, c) { + return nil + } + + read_form, err := r.MultipartReader() + if err != nil { + LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil)) + return nil + } + + var name string + var file []byte + + for { + part, err_part := read_form.NextPart() + if err_part == io.EOF { + break + } else if err_part != nil { + return &Error{Code: http.StatusBadRequest} + } + if part.FormName() == "name" { + buf := new(bytes.Buffer) + buf.ReadFrom(part) + name = buf.String() + } + if part.FormName() == "file" { + buf := new(bytes.Buffer) + buf.ReadFrom(part) + file = buf.Bytes() + } + } + + if name == "" || len(file) == 0 { + LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil)) + return nil + } + + row, err := handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id) + if err != nil { + return Error500(err) + } + + if row.Next() { + LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(AnyMap{ + "NameFoundError": true, + "Name": name, + })) + return nil + } + + _, err = handle.Db.Exec("insert into models (user_id, name) values ($1, $2)", c.User.Id, name) + if err != nil { + return Error500(err) + } + + row, err = handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id) + if err != nil { + return Error500(err) + } + + if !row.Next() { + return &Error{Code: http.StatusInternalServerError} + } + + var id string + err = row.Scan(&id) + if err != nil { + return Error500(err) + } + + // TODO mk this path configurable + dir_path := path.Join("savedData", id) + + err = os.Mkdir(dir_path, os.ModePerm) + if err != nil { + return Error500(err) + } + f, err := os.Create(path.Join(dir_path, "baseimage.png")) + if err != nil { + return Error500(err) + } + defer f.Close() + + f.Write(file) + + fmt.Printf("Created model with id %s! Started to proccess image!\n", id) + go loadBaseImage(handle, id) + + Redirect("/models/edit?id="+id, c.Mode, w, r) + return nil + }) +} diff --git a/logic/models/delete.go b/logic/models/delete.go new file mode 100644 index 0000000..d861212 --- /dev/null +++ b/logic/models/delete.go @@ -0,0 +1,107 @@ +package models + +import ( + "fmt" + "net/http" + "os" + "path" + "strconv" + + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" +) + +func deleteModel(handle *Handle, id string, w http.ResponseWriter, c *Context, model BaseModel) { + fmt.Printf("Removing model with id: %s\n", id) + _, err := handle.Db.Exec("delete from models where id=$1;", id) + if err != nil { + fmt.Println(err) + panic("TODO handle better deleteModel failed delete database query") + } + + model_path := path.Join("./savedData", id) + fmt.Printf("Removing folder of model with id: %s at %s\n", id, model_path) + err = os.RemoveAll(model_path) + if err != nil { + fmt.Println(err) + panic("TODO handle better deleteModel failed to delete folder") + } + + if c.Mode == HTML { + // TODO move this to a constant so i don't forget + w.WriteHeader(309) + c.Mode = HTMLFULL + } + + LoadBasedOnAnswer(c.Mode, w, "/models/delete.html", c.AddMap(AnyMap{ + "Model": model, + })) +} + +func handleDelete(handle *Handle) { + handle.Delete("/models/delete", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { + if c.Mode == JSON { + panic("TODO handle json on models/delete") + } + + f, err := MyParseForm(r) + if err != nil { + return ErrorCode(err, 400, nil) + } + + if !CheckId(f, "id") { + return ErrorCode(nil, http.StatusNotFound, AnyMap{ + "NotFoundMessage": "Model not found", + "GoBackLink": "/models", + }) + } + + id := f.Get("id") + + // TODO handle admin users + rows, err := handle.Db.Query("select name, status from models where id=$1 and user_id=$2;", id, c.User.Id) + if err != nil { + return Error500(err) + } + + if !rows.Next() { + return ErrorCode(nil, http.StatusNotFound, AnyMap{ + "NotFoundMessage": "Model not found", + "GoBackLink": "/models", + }) + } + + var model BaseModel = BaseModel{} + model.Id = id + + err = rows.Scan(&model.Name, &model.Status) + if err != nil { + return Error500(err) + } + + switch model.Status { + case FAILED_PREPARING: + deleteModel(handle, id, w, c, model) + return nil + case CONFIRM_PRE_TRAINING: + + if CheckEmpty(f, "name") { + // TODO improve result + return ErrorCode(nil, http.StatusBadRequest, nil) + } + + name := f.Get("name") + if name != model.Name { + LoadError(w, "/models/edit.html", "delete-model-card", c.AddMap(AnyMap{ + "NameDoesNotMatch": true, + "Model": model, + })) + return nil + } + + deleteModel(handle, id, w, c, model) + return nil + default: + panic("Do not know how to handle model in status:" + strconv.Itoa(model.Status)) + } + }) +} diff --git a/logic/models/edit.go b/logic/models/edit.go new file mode 100644 index 0000000..83179ed --- /dev/null +++ b/logic/models/edit.go @@ -0,0 +1,80 @@ +package models + +import ( + "fmt" + "net/http" + + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" +) + +func handleEdit(handle *Handle) { + handle.GetHTML("/models/edit", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { + if !CheckAuthLevel(1, w, r, c) { + return nil + } + + id, err := GetIdFromUrl(r, "id") + if err != nil { + return ErrorCode(nil, http.StatusNotFound, AnyMap{ + "NotFoundMessage": "Model not found", + "GoBackLink": "/models", + }) + } + + // TODO handle admin users + rows, err := handle.Db.Query("select name, status, width, height, color_mode from models where id=$1 and user_id=$2;", id, c.User.Id) + if err != nil { + return Error500(err) + } + + if !rows.Next() { + return ErrorCode(nil, http.StatusNotFound, AnyMap{ + "NotFoundMessage": "Model not found", + "GoBackLink": "/models", + }) + } + + type rowmodel struct { + Name string + Status int + Id string + Width *int + Height *int + Color_mode *string + } + + var model rowmodel = rowmodel{} + model.Id = id + + err = rows.Scan(&model.Name, &model.Status, &model.Width, &model.Height, &model.Color_mode) + if err != nil { + return Error500(err) + } + + // Handle errors + // All errors will be negative + if model.Status < 0 { + LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{ + "Model": model, + })) + return nil + } + + switch model.Status { + case PREPARING: + LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{ + "Model": model, + })) + case CONFIRM_PRE_TRAINING: + LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{ + "Model": model, + })) + + default: + fmt.Printf("Unkown Status: %d\n", model.Status) + return Error500(nil) + } + + return nil + }) +} diff --git a/logic/models/index.go b/logic/models/index.go new file mode 100644 index 0000000..014c863 --- /dev/null +++ b/logic/models/index.go @@ -0,0 +1,12 @@ +package models + +import ( + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" +) + +func HandleModels (handle *Handle) { + handleAdd(handle) + handleEdit(handle) + handleDelete(handle) + handleList(handle) +} diff --git a/logic/models/list.go b/logic/models/list.go new file mode 100644 index 0000000..d5a6cda --- /dev/null +++ b/logic/models/list.go @@ -0,0 +1,47 @@ +package models + +import ( + "net/http" + + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" +) + + +func handleList(handle *Handle) { + // TODO json + handle.GetHTML("/models", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { + if c.Mode == JSON { + panic("TODO JSON") + } + if !CheckAuthLevel(1, w, r, c) { + return nil + } + + rows, err := handle.Db.Query("select id, name from models where user_id=$1;", c.User.Id) + if err != nil { + return Error500(err) + } + + type row struct { + Name string + Id string + } + + got := []row{} + + for rows.Next() { + var r row + err = rows.Scan(&r.Id, &r.Name) + if err != nil { + return Error500(err) + } + got = append(got, r) + } + + LoadBasedOnAnswer(c.Mode, w, "/models/list.html", c.AddMap(AnyMap{ + "List": got, + })) + return nil + }) + +} diff --git a/logic/models/types.go b/logic/models/types.go new file mode 100644 index 0000000..4b1c9b3 --- /dev/null +++ b/logic/models/types.go @@ -0,0 +1,14 @@ +package models + +type BaseModel struct { + Name string + Status int + Id string +} + +const ( + FAILED_PREPARING = -1 + + PREPARING = iota + CONFIRM_PRE_TRAINING +) diff --git a/logic/models/utils.go b/logic/models/utils.go new file mode 100644 index 0000000..ff1667e --- /dev/null +++ b/logic/models/utils.go @@ -0,0 +1,16 @@ +package models + +import ( + "fmt" + + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" +) + +func modelUpdateStatus(handle *Handle, id string, status int) { + _, err := handle.Db.Exec("update models set status = $1 where id = $2", status, id) + if err != nil { + fmt.Println("Failed to update model status") + fmt.Println(err) + panic("TODO handle better") + } +} diff --git a/handler.go b/logic/utils/handler.go similarity index 89% rename from handler.go rename to logic/utils/handler.go index 71e8c2a..6bfe694 100644 --- a/handler.go +++ b/logic/utils/handler.go @@ -1,4 +1,4 @@ -package main +package utils import ( "database/sql" @@ -12,6 +12,8 @@ import ( "path" "strings" "time" + + dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types" ) func baseLoadTemplate(base string, path string) (*template.Template, any) { @@ -127,8 +129,8 @@ func LoadError(writer http.ResponseWriter, path string, base string, data AnyMap type AnyMap = map[string]interface{} type Error struct { - code int - msg *string + Code int + Msg *string data AnyMap } @@ -180,7 +182,7 @@ type Handler interface { } type Handle struct { - db *sql.DB + Db *sql.DB gets []HandleFunc posts []HandleFunc deletes []HandleFunc @@ -189,7 +191,7 @@ type Handle struct { func decodeBody(r *http.Request) (string, *Error) { body, err := io.ReadAll(r.Body) if err == nil { - return "", &Error{code: http.StatusBadRequest} + return "", &Error{Code: http.StatusBadRequest} } return string(body[:]), nil @@ -198,18 +200,18 @@ func decodeBody(r *http.Request) (string, *Error) { func handleError(err *Error, w http.ResponseWriter, context *Context) { if err != nil { - data := context.addMap(err.data) - w.WriteHeader(err.code) - if err.code == http.StatusNotFound { + data := context.AddMap(err.data) + w.WriteHeader(err.Code) + if err.Code == http.StatusNotFound { LoadBasedOnAnswer(context.Mode, w, "404.html", data) return } - if err.code == http.StatusBadRequest { + if err.Code == http.StatusBadRequest { LoadBasedOnAnswer(context.Mode, w, "400.html", data) return } - if err.msg != nil { - w.Write([]byte(*err.msg)) + if err.Msg != nil { + w.Write([]byte(*err.Msg)) } } } @@ -260,7 +262,7 @@ func (x *Handle) handleGets(w http.ResponseWriter, r *http.Request, context *Con if context.Mode != HTMLFULL { w.WriteHeader(http.StatusNotFound) } - LoadBasedOnAnswer(context.Mode, w, "404.html", context.addMap(nil)) + LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil)) } func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Context) { @@ -273,7 +275,7 @@ func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Co if context.Mode != HTMLFULL { w.WriteHeader(http.StatusNotFound) } - LoadBasedOnAnswer(context.Mode, w, "404.html", context.addMap(nil)) + LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil)) } func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context *Context) { @@ -286,16 +288,16 @@ func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context * if context.Mode != HTMLFULL { w.WriteHeader(http.StatusNotFound) } - LoadBasedOnAnswer(context.Mode, w, "404.html", context.addMap(nil)) + LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil)) } -func checkAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Context) bool { +func CheckAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Context) bool { if authLevel > 0 { if c.requireAuth(w, r) { - logoff(c.Mode, w, r) + Logoff(c.Mode, w, r) return false } - if c.User.user_type < authLevel { + if c.User.UserType < authLevel { notAuth(c.Mode, w, r) return false } @@ -305,21 +307,21 @@ func checkAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Co func AnswerTemplate(path string, data AnyMap, authLevel int) func(w http.ResponseWriter, r *http.Request, c *Context) *Error { return func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !checkAuthLevel(authLevel, w, r, c) { + if !CheckAuthLevel(authLevel, w, r, c) { return nil } - LoadBasedOnAnswer(c.Mode, w, path, c.addMap(data)) + LoadBasedOnAnswer(c.Mode, w, path, c.AddMap(data)) return nil } } type Context struct { Token *string - User *User + User *dbtypes.User Mode AnswerType } -func (c Context) addMap(m AnyMap) AnyMap { +func (c Context) AddMap(m AnyMap) AnyMap { if m == nil { return map[string]interface{}{ "Context": c, @@ -356,7 +358,7 @@ func (x Handle) createContext(mode AnswerType, r *http.Request) (*Context, error }, nil } - user, err := userFromToken(x.db, *token) + user, err := dbtypes.UserFromToken(x.Db, *token) if err != nil { return nil, errors.Join(err, LogoffError) } @@ -365,7 +367,7 @@ func (x Handle) createContext(mode AnswerType, r *http.Request) (*Context, error } // TODO check if I can use http.Redirect -func redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Request) { +func Redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", path) if mode == JSON { w.WriteHeader(http.StatusSeeOther) @@ -380,7 +382,7 @@ func redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Reque } } -func logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) { +func Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) { // Delete cookie cookie := &http.Cookie{ Name: "auth", @@ -388,7 +390,7 @@ func logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) { Expires: time.Unix(0, 0), } http.SetCookie(w, cookie) - redirect("/login", mode, w, r) + Redirect("/login", mode, w, r) } func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) { @@ -405,7 +407,7 @@ func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) { } } -func (x Handle) staticFiles(pathTest string, fileType string, contentType string) { +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):] @@ -428,7 +430,7 @@ func (x Handle) staticFiles(pathTest string, fileType string, contentType string }) } -func errorCode(err error, code int, data AnyMap) *Error { +func ErrorCode(err error, code int, data AnyMap) *Error { // TODO Improve Logging if err != nil { fmt.Printf("Something went wrong returning with: %d\n.Err:\n", code) @@ -437,11 +439,11 @@ func errorCode(err error, code int, data AnyMap) *Error { return &Error{code, nil, data} } -func error500(err error) *Error { - return errorCode(err, http.StatusInternalServerError, nil) +func Error500(err error) *Error { + return ErrorCode(err, http.StatusInternalServerError, nil) } -func (x Handle) readFiles(pathTest string, baseFilePath string, fileType string, contentType string) { +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):] @@ -487,7 +489,7 @@ func NewHandler(db *sql.DB) *Handle { //Login state context, err := x.createContext(ans, r) if err != nil { - logoff(ans, w, r) + Logoff(ans, w, r) return } diff --git a/utils.go b/logic/utils/utils.go similarity index 91% rename from utils.go rename to logic/utils/utils.go index f649e8f..44c4f63 100644 --- a/utils.go +++ b/logic/utils/utils.go @@ -1,4 +1,4 @@ -package main +package utils import ( "errors" @@ -10,20 +10,20 @@ import ( "github.com/google/uuid" ) -func checkEmpty(f url.Values, path string) bool { +func CheckEmpty(f url.Values, path string) bool { return !f.Has(path) || f.Get(path) == "" } -func checkId(f url.Values, path string) bool { - return !checkEmpty(f, path) && isValidUUID(f.Get(path)) +func CheckId(f url.Values, path string) bool { + return !CheckEmpty(f, path) && IsValidUUID(f.Get(path)) } -func isValidUUID(u string) bool { +func IsValidUUID(u string) bool { _, err := uuid.Parse(u) return err == nil } -func getIdFromUrl(r *http.Request, target string) (string, error) { +func GetIdFromUrl(r *http.Request, target string) (string, error) { if !r.URL.Query().Has(target) { return "", errors.New("Query does not have " + target) } @@ -33,7 +33,7 @@ func getIdFromUrl(r *http.Request, target string) (string, error) { return "", errors.New("Query is empty for " + target) } - if !isValidUUID(id) { + if !IsValidUUID(id) { return "", errors.New("Value of query is not a valid uuid for " + target) } diff --git a/main.go b/main.go index 99996a5..057e1d6 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,9 @@ import ( "fmt" _ "github.com/lib/pq" + + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/models" ) const ( @@ -33,15 +36,15 @@ 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.readFiles("/savedData/", ".", ".png", "image/png;"); + handle.StaticFiles("/styles/", ".css", "text/css"); + handle.StaticFiles("/js/", ".js", "text/javascript"); + handle.ReadFiles("/imgs/", "views", ".png", "image/png;"); + handle.ReadFiles("/savedData/", ".", ".png", "image/png;"); handle.GetHTML("/", AnswerTemplate("index.html", nil, 0)) usersEndpints(db, handle) - handleModelsEndpoints(handle) + HandleModels(handle) handle.Startup() } diff --git a/models.go b/models.go deleted file mode 100644 index 1f88221..0000000 --- a/models.go +++ /dev/null @@ -1,405 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "image" - "image/color" - _ "image/png" - "io" - "net/http" - "os" - "path" - "strconv" -) - -const ( - FAILED_PREPARING = -1 - - PREPARING = iota - CONFIRM_PRE_TRAINING -) - -type BaseModel struct { - Name string - Status int - Id string -} - -func modelUpdateStatus(handle *Handle, id string, status int) { - _, err := handle.db.Exec("update models set status = $1 where id = $2", status, id) - if err != nil { - fmt.Println("Failed to update model status") - fmt.Println(err) - panic("TODO handle better") - } -} - -func loadBaseImage(handle *Handle, id string) { - // TODO handle more types than png - infile, err := os.Open(path.Join("savedData", id, "baseimage.png")) - if err != nil { - // TODO better logging - fmt.Println(err) - fmt.Printf("Failed to read image for model with id %s\n", id) - modelUpdateStatus(handle, id, -1) - return - } - defer infile.Close() - - src, format, err := image.Decode(infile) - if err != nil { - // TODO better logging - fmt.Println(err) - fmt.Printf("Failed to load image for model with id %s\n", id) - modelUpdateStatus(handle, id, -1) - return - } - if format != "png" { - // TODO better logging - fmt.Printf("Found unkown format '%s'\n", format) - panic("Handle diferent files than .png") - } - - var model_color string - - bounds := src.Bounds() - width, height := bounds.Max.X, bounds.Max.Y - - switch src.ColorModel() { - case color.Gray16Model: - fallthrough - case color.GrayModel: - model_color = "greyscale" - default: - fmt.Println("Do not know how to handle this color model") - - if src.ColorModel() == color.RGBA64Model { - fmt.Println("Color is rgb") - } else if src.ColorModel() == color.NRGBAModel { - fmt.Println("Color is nrgb") - } else if src.ColorModel() == color.YCbCrModel { - fmt.Println("Color is ycbcr") - } else if src.ColorModel() == color.AlphaModel { - fmt.Println("Color is alpha") - } else if src.ColorModel() == color.CMYKModel { - fmt.Println("Color is cmyk") - } else { - fmt.Println("Other so assuming color") - } - - modelUpdateStatus(handle, id, -1) - return - } - - // Note: this also updates the status to 2 - _, err = handle.db.Exec("update models set width=$1, height=$2, color_mode=$3, status=$4 where id=$5", width, height, model_color, CONFIRM_PRE_TRAINING, id) - if err != nil { - // TODO better logging - fmt.Println(err) - fmt.Printf("Could not update model\n") - modelUpdateStatus(handle, id, -1) - return - } -} - -func deleteModel(handle *Handle, id string, w http.ResponseWriter, c *Context, model BaseModel) { - fmt.Printf("Removing model with id: %s\n", id) - _, err := handle.db.Exec("delete from models where id=$1;", id) - if err != nil { - fmt.Println(err) - panic("TODO handle better deleteModel failed delete database query") - } - - model_path := path.Join("./savedData", id) - fmt.Printf("Removing folder of model with id: %s at %s\n", id, model_path) - err = os.RemoveAll(model_path) - if err != nil { - fmt.Println(err) - panic("TODO handle better deleteModel failed to delete folder") - } - - if c.Mode == HTML { - // TODO move this to a constant so i don't forget - w.WriteHeader(309) - c.Mode = HTMLFULL - } - - LoadBasedOnAnswer(c.Mode, w, "/models/delete.html", c.addMap(AnyMap{ - "Model": model, - })) -} - -func handleModelsEndpoints(handle *Handle) { - - // TODO json - handle.GetHTML("/models", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if c.Mode == JSON { - panic("TODO JSON") - } - if !checkAuthLevel(1, w, r, c) { - return nil - } - - rows, err := handle.db.Query("select id, name from models where user_id=$1;", c.User.id) - if err != nil { - return error500(err) - } - - type row struct { - Name string - Id string - } - - got := []row{} - - for rows.Next() { - var r row - err = rows.Scan(&r.Id, &r.Name) - if err != nil { - return error500(err) - } - got = append(got, r) - } - - LoadBasedOnAnswer(c.Mode, w, "/models/list.html", c.addMap(AnyMap{ - "List": got, - })) - return nil - }) - - handle.GetHTML("/models/add", AnswerTemplate("models/add.html", nil, 1)) - // TODO json - handle.Post("/models/add", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if c.Mode == JSON { - panic("TODO JSON") - } - if !checkAuthLevel(1, w, r, c) { - return nil - } - - read_form, err := r.MultipartReader() - if err != nil { - LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.addMap(nil)) - return nil - } - - var name string - var file []byte - - for { - part, err_part := read_form.NextPart() - if err_part == io.EOF { - break - } else if err_part != nil { - return &Error{code: http.StatusBadRequest} - } - if part.FormName() == "name" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - name = buf.String() - } - if part.FormName() == "file" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - file = buf.Bytes() - } - } - - if name == "" || len(file) == 0 { - LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.addMap(nil)) - return nil - } - - row, err := handle.db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.id) - if err != nil { - return error500(err) - } - - if row.Next() { - LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.addMap(AnyMap{ - "NameFoundError": true, - "Name": name, - })) - return nil - } - - _, err = handle.db.Exec("insert into models (user_id, name) values ($1, $2)", c.User.id, name) - if err != nil { - return error500(err) - } - - row, err = handle.db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.id) - if err != nil { - return error500(err) - } - - if !row.Next() { - return &Error{code: http.StatusInternalServerError} - } - - var id string - err = row.Scan(&id) - if err != nil { - return error500(err) - } - - // TODO mk this path configurable - dir_path := path.Join("savedData", id) - - err = os.Mkdir(dir_path, os.ModePerm) - if err != nil { - return error500(err) - } - f, err := os.Create(path.Join(dir_path, "baseimage.png")) - if err != nil { - return error500(err) - } - defer f.Close() - - f.Write(file) - - fmt.Printf("Created model with id %s! Started to proccess image!\n", id) - go loadBaseImage(handle, id) - - redirect("/models/edit?id="+id, c.Mode, w, r) - return nil - }) - - handle.GetHTML("/models/edit", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !checkAuthLevel(1, w, r, c) { - return nil - } - - id, err := getIdFromUrl(r, "id") - if err != nil { - return errorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) - } - - // TODO handle admin users - rows, err := handle.db.Query("select name, status, width, height, color_mode from models where id=$1 and user_id=$2;", id, c.User.id) - if err != nil { - return error500(err) - } - - if !rows.Next() { - return errorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) - } - - type rowmodel struct { - Name string - Status int - Id string - Width *int - Height *int - Color_mode *string - } - - var model rowmodel = rowmodel{} - model.Id = id - - err = rows.Scan(&model.Name, &model.Status, &model.Width, &model.Height, &model.Color_mode) - if err != nil { - return error500(err) - } - - // Handle errors - // All errors will be negative - if model.Status < 0 { - LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.addMap(AnyMap{ - "Model": model, - })) - return nil - } - - switch model.Status { - case PREPARING: - LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.addMap(AnyMap{ - "Model": model, - })) - case CONFIRM_PRE_TRAINING: - LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.addMap(AnyMap{ - "Model": model, - })) - - default: - fmt.Printf("Unkown Status: %d\n", model.Status) - return error500(nil) - } - - return nil - }) - - handle.Delete("/models/delete", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if c.Mode == JSON { - panic("TODO handle json on models/delete") - } - - f, err := MyParseForm(r) - if err != nil { - return errorCode(err, 400, nil) - } - - if !checkId(f, "id") { - return errorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) - } - - id := f.Get("id") - - // TODO handle admin users - rows, err := handle.db.Query("select name, status from models where id=$1 and user_id=$2;", id, c.User.id) - if err != nil { - return error500(err) - } - - if !rows.Next() { - return errorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) - } - - var model BaseModel = BaseModel{} - model.Id = id - - err = rows.Scan(&model.Name, &model.Status) - if err != nil { - return error500(err) - } - - switch model.Status { - case FAILED_PREPARING: - deleteModel(handle, id, w, c, model) - return nil - case CONFIRM_PRE_TRAINING: - - if checkEmpty(f, "name") { - // TODO improve result - return errorCode(nil, http.StatusBadRequest, nil) - } - - name := f.Get("name") - if name != model.Name { - LoadError(w, "/models/edit.html", "delete-model-card", c.addMap(AnyMap{ - "NameDoesNotMatch": true, - "Model": model, - })) - return nil - } - - deleteModel(handle, id, w, c, model) - return nil - default: - panic("Do not know how to handle model in status:" + strconv.Itoa(model.Status)) - } - }) -} diff --git a/sql/models.sql b/sql/models.sql index 6de6cc5..4d8fbd2 100644 --- a/sql/models.sql +++ b/sql/models.sql @@ -1,4 +1,4 @@ --- drop table if exists model_defenitions +-- drop table if exists model_defenitions; -- drop table if exists models; create table if not exists models ( id uuid primary key default gen_random_uuid(), @@ -14,7 +14,9 @@ create table if not exists models ( color_mode varchar (20) ); --- create table model_defenitions ( --- id uuid primary key default gen_random_uuid(), --- model_id uuid references models (id) not null, --- ) +-- drop table if exists model_classes; +create table if not exists model_classes ( + id uuid primary key default gen_random_uuid(), + model_id uuid references models (id) not null, + name varchar (70) not null +); diff --git a/users.go b/users.go index 2e7bd55..536bf27 100644 --- a/users.go +++ b/users.go @@ -4,48 +4,16 @@ import ( "crypto/rand" "database/sql" "encoding/hex" - "errors" "fmt" "io" "net/http" "time" "golang.org/x/crypto/bcrypt" + + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" ) -type User struct { - id string - username string - email string - user_type int -} - -var ErrUserNotFound = errors.New("User Not found") - -func userFromToken(db *sql.DB, token string) (*User, error) { - row, err := db.Query("select users.id, users.username, users.email, users.user_type from users inner join tokens on tokens.user_id = users.id where tokens.token = $1;", token) - - if err != nil { - return nil, err - } - - var id string - var username string - var email string - var user_type int - - if !row.Next() { - return nil, ErrUserNotFound - } - - err = row.Scan(&id, &username, &email, &user_type) - if err != nil { - return nil, err - } - - return &User{id, username, email, user_type}, nil -} - func generateSalt() string { salt := make([]byte, 4) _, err := io.ReadFull(rand.Reader, salt) @@ -112,14 +80,14 @@ func usersEndpints(db *sql.DB, handle *Handle) { handle.Post("/login", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { if c.Mode == JSON { fmt.Println("Handle JSON") - return &Error{code: 404} + return &Error{Code: 404} } r.ParseForm() f := r.Form - if checkEmpty(f, "email") || checkEmpty(f, "password") { - LoadBasedOnAnswer(c.Mode, w, "login.html", c.addMap(AnyMap{ + if CheckEmpty(f, "email") || CheckEmpty(f, "password") { + LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{ "Submited": true, })) return nil @@ -132,7 +100,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { expiration := time.Now().Add(24 * time.Hour) token, login := generateToken(db, email, password) if !login { - LoadBasedOnAnswer(c.Mode, w, "login.html", c.addMap(AnyMap{ + LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{ "Submited": true, "NoUserOrPassword": true, "Email": email, @@ -151,13 +119,13 @@ func usersEndpints(db *sql.DB, handle *Handle) { handle.GetHTML("/register", AnswerTemplate("register.html", nil, 0)) handle.Post("/register", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { if c.Mode == JSON { - return &Error{code: http.StatusNotFound} + return &Error{Code: http.StatusNotFound} } r.ParseForm() f := r.Form - if checkEmpty(f, "email") || checkEmpty(f, "password") || checkEmpty(f, "username") { + if CheckEmpty(f, "email") || CheckEmpty(f, "password") || CheckEmpty(f, "username") { LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{ "Submited": true, }) @@ -205,7 +173,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { hash_password, err := hashPassword(password, salt) if err != nil { return &Error{ - code: http.StatusInternalServerError, + Code: http.StatusInternalServerError, } } @@ -213,7 +181,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { if err != nil { return &Error{ - code: http.StatusInternalServerError, + Code: http.StatusInternalServerError, } } @@ -224,8 +192,8 @@ func usersEndpints(db *sql.DB, handle *Handle) { if !login { msg := "Login failed" return &Error{ - code: http.StatusInternalServerError, - msg: &msg, + Code: http.StatusInternalServerError, + Msg: &msg, } } @@ -240,7 +208,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { if c.Mode == JSON { panic("TODO handle json") } - logoff(c.Mode, w, r) - return nil + Logoff(c.Mode, w, r) + return nil }) } diff --git a/views/models/edit.html b/views/models/edit.html index ec37702..7f63dbf 100644 --- a/views/models/edit.html +++ b/views/models/edit.html @@ -42,6 +42,18 @@ {{ end }} +{{ define "data-model-card" }} +
+{{ end }} + +{{ define "train-model-card" }} + +{{ end }} + {{ define "mainbody" }}