diff --git a/logic/models/add.go b/logic/models/add.go index 53c680b..5d65ea5 100644 --- a/logic/models/add.go +++ b/logic/models/add.go @@ -86,90 +86,14 @@ func loadBaseImage(c *Context, id string) { } func handleAdd(handle *Handle) { - handle.GetHTML("/models/add", AnswerTemplate("models/add.html", nil, 1)) - handle.Post("/models/add", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Post("/models/add", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - read_form, err := r.MultipartReader() - if err != nil { - return c.JsonErrorBadRequest(err, "Please provide a valid multipart Reader request") - } - - 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 { - return c.JsonBadRequest("Name is empty or file is empty") - } - - row, err := handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id) - if err != nil { - return c.Error500(err) - } - - if row.Next() { - return c.JsonBadRequest("Model with that name already exists!") - } - - row, err = handle.Db.Query("insert into models (user_id, name) values ($1, $2) returning id", c.User.Id, name) - if err != nil || !row.Next() { - return c.Error500(err) - } - - var id string - err = row.Scan(&id) - if err != nil { - return c.Error500(err) - } - - // TODO mk this path configurable - dir_path := path.Join("savedData", id) - - err = os.Mkdir(dir_path, os.ModePerm) - if err != nil { - return c.Error500(err) - } - - f, err := os.Create(path.Join(dir_path, "baseimage.png")) - if err != nil { - return c.Error500(err) - } - defer f.Close() - - f.Write(file) - - c.Logger.Warn("Created model with id %s! Started to proccess image!\n", "id", id) - go loadBaseImage(c, id) - - return c.SendJSON(id) - } - - read_form, err := r.MultipartReader() + read_form, err := c.R.MultipartReader() if err != nil { - LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil)) - return nil + return c.JsonErrorBadRequest(err, "Please provide a valid multipart Reader request") } var name string @@ -195,8 +119,7 @@ func handleAdd(handle *Handle) { } if name == "" || len(file) == 0 { - LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil)) - return nil + return c.JsonBadRequest("Name is empty or file is empty") } row, err := handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id) @@ -205,27 +128,14 @@ func handleAdd(handle *Handle) { } if row.Next() { - LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(AnyMap{ - "NameFoundError": true, - "Name": name, - })) - return nil + return c.JsonBadRequest("Model with that name already exists!") } - _, err = handle.Db.Exec("insert into models (user_id, name) values ($1, $2)", c.User.Id, name) - if err != nil { + row, err = handle.Db.Query("insert into models (user_id, name) values ($1, $2) returning id", c.User.Id, name) + if err != nil || !row.Next() { return c.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 c.Error500(err) - } - - if !row.Next() { - return &Error{Code: http.StatusInternalServerError} - } - var id string err = row.Scan(&id) if err != nil { @@ -239,6 +149,7 @@ func handleAdd(handle *Handle) { if err != nil { return c.Error500(err) } + f, err := os.Create(path.Join(dir_path, "baseimage.png")) if err != nil { return c.Error500(err) @@ -250,7 +161,6 @@ func handleAdd(handle *Handle) { c.Logger.Warn("Created model with id %s! Started to proccess image!\n", "id", id) go loadBaseImage(c, id) - Redirect("/models/edit?id="+id, c.Mode, w, r) - return nil + return c.SendJSON(id) }) } diff --git a/logic/models/classes/list.go b/logic/models/classes/list.go index 6762ad9..a40e012 100644 --- a/logic/models/classes/list.go +++ b/logic/models/classes/list.go @@ -1,148 +1,69 @@ package model_classes import ( - "net/http" "strconv" - . "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils" "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" ) -func models_data_list_json(w http.ResponseWriter, r *http.Request, c *Context) *Error { - id, err := GetIdFromUrl(r, "id") - if err != nil { - return c.JsonBadRequest("Model Class not found!") - } - - page := 0 - if r.URL.Query().Has("page") { - page_url := r.URL.Query().Get("page") - page_url_number, err := strconv.Atoi(page_url) - if err != nil { - return c.JsonBadRequest("Page is not a number") - } - page = page_url_number - } - - var class_row struct { - Name string - Model_id string - } - - err = utils.GetDBOnce(c, &class_row, "model_classes where id=$1", id) - if err == NotFoundError { - return c.JsonBadRequest("Model Class not found!") - } else if err != nil { - return c.Error500(err) - } - - type baserow struct { - Id string `json:"id"` - File_Path string `json:"file_path"` - Model_Mode int `json:"model_mode"` - Status int `json:"status"` - } - - rows, err := utils.GetDbMultitple[baserow](c, "model_data_point where class_id=$1 limit 11 offset $2", id, page*10) - if err != nil { - return c.Error500(err) - } - - type ReturnType struct { - ImageList []*baserow `json:"image_list"` - Page int `json:"page"` - ShowNext bool `json:"showNext"` - } - - max_len := min(11, len(rows)) - - return c.SendJSON(ReturnType{ - ImageList: rows[0:max_len], - Page: page, - ShowNext: len(rows) == 11, - }) -} - func HandleList(handle *Handle) { - handle.Get("/models/data/list", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Get("/models/data/list", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - return models_data_list_json(w, r, c) - } - id, err := GetIdFromUrl(r, "id") + id, err := GetIdFromUrl(c, "id") if err != nil { - return ErrorCode(err, 400, c.AddMap(nil)) + return c.JsonBadRequest("Model Class not found!") } page := 0 - if r.URL.Query().Has("page") { - page_url := r.URL.Query().Get("page") + if c.R.URL.Query().Has("page") { + page_url := c.R.URL.Query().Get("page") page_url_number, err := strconv.Atoi(page_url) if err != nil { - return ErrorCode(err, http.StatusBadRequest, c.AddMap(nil)) + return c.JsonBadRequest("Page is not a number") } page = page_url_number } - class_rows, err := handle.Db.Query("select name, model_id from model_classes where id=$1;", id) - if err != nil { - return Error500(err) - } - defer class_rows.Close() - - if !class_rows.Next() { - return ErrorCode(nil, 404, c.AddMap(nil)) + var class_row struct { + Name string + Model_id string } - name := "" - model_id := "" - if err = class_rows.Scan(&name, &model_id); err != nil { - return Error500(nil) + err = utils.GetDBOnce(c, &class_row, "model_classes where id=$1", id) + if err == NotFoundError { + return c.JsonBadRequest("Model Class not found!") + } else if err != nil { + return c.Error500(err) } - model, err := GetBaseModel(c.Db, model_id) - if err != nil { - return Error500(err) - } - - rows, err := handle.Db.Query("select id, file_path, model_mode, status from model_data_point where class_id=$1 limit 11 offset $2;", id, page*10) - if err != nil { - return Error500(err) - } - defer rows.Close() - type baserow struct { - Id string - FilePath string - Mode int - Status int + Id string `json:"id"` + File_Path string `json:"file_path"` + Model_Mode int `json:"model_mode"` + Status int `json:"status"` } - got := []baserow{} - - for rows.Next() { - nrow := baserow{} - err = rows.Scan(&nrow.Id, &nrow.FilePath, &nrow.Mode, &nrow.Status) - if err != nil { - return Error500(err) - } - got = append(got, nrow) + rows, err := utils.GetDbMultitple[baserow](c, "model_data_point where class_id=$1 limit 11 offset $2", id, page*10) + if err != nil { + return c.Error500(err) } - max_len := min(11, len(got)) + type ReturnType struct { + ImageList []*baserow `json:"image_list"` + Page int `json:"page"` + ShowNext bool `json:"showNext"` + } - LoadDefineTemplate(w, "/models/edit.html", "data-model-create-class-table-table", c.AddMap(AnyMap{ - "List": got[0:max_len], - "Page": page, - "Id": id, - "Name": name, - "Model": model, - "ShowNext": len(got) == 11, - })) - return nil + max_len := min(11, len(rows)) + + return c.SendJSON(ReturnType{ + ImageList: rows[0:max_len], + Page: page, + ShowNext: len(rows) == 11, + }) }) } diff --git a/logic/models/data.go b/logic/models/data.go index 9825fce..a04c888 100644 --- a/logic/models/data.go +++ b/logic/models/data.go @@ -152,8 +152,8 @@ func processZipFile(c *Context, model *BaseModel) { } func processZipFileExpand(c *Context, model *BaseModel) { - var err error - + var err error + failed := func(msg string) { c.Logger.Error(msg, "err", err) ModelUpdateStatus(c, model.Id, READY_FAILED) @@ -161,7 +161,7 @@ func processZipFileExpand(c *Context, model *BaseModel) { reader, err := zip.OpenReader(path.Join("savedData", model.Id, "expand_data.zip")) if err != nil { - failed("Faield to proccess zip file failed to open reader\n") + failed("Faield to proccess zip file failed to open reader\n") return } defer reader.Close() @@ -257,8 +257,8 @@ func processZipFileExpand(c *Context, model *BaseModel) { c.Logger.Warn("Not failling updating data point to status -1") message := "Image did not have valid format for the model" if err = model_classes.UpdateDataPointStatus(c.Db, data_point_id, -1, &message); err != nil { - failed(fmt.Sprintf("Failed to update data point status")) - return + failed(fmt.Sprintf("Failed to update data point status")) + return } } } @@ -268,130 +268,12 @@ func processZipFileExpand(c *Context, model *BaseModel) { } func handleDataUpload(handle *Handle) { - handle.Post("/models/data/upload", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Post("/models/data/upload", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - read_form, err := r.MultipartReader() - if err != nil { - return c.JsonBadRequest("Please provide a valid form data request!") - } - - var id string - var file []byte - - for { - part, err_part := read_form.NextPart() - if err_part == io.EOF { - break - } else if err_part != nil { - return c.JsonBadRequest("Please provide a valid form data request!") - } - if part.FormName() == "id" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - id = buf.String() - } - if part.FormName() == "file" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - file = buf.Bytes() - } - } - - model, err := GetBaseModel(handle.Db, id) - if err == ModelNotFoundError { - return c.SendJSONStatus(http.StatusNotFound, "Model not found") - } else if err != nil { - return Error500(err) - } - - // TODO mk this path configurable - dir_path := path.Join("savedData", id) - - f, err := os.Create(path.Join(dir_path, "base_data.zip")) - if err != nil { - return Error500(err) - } - defer f.Close() - - f.Write(file) - - ModelUpdateStatus(c, id, PREPARING_ZIP_FILE) - - go processZipFile(c, model) - - return c.SendJSON(model.Id) - } - - read_form, err := r.MultipartReader() - if err != nil { - LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil)) - return nil - } - - var id 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() == "id" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - id = buf.String() - } - if part.FormName() == "file" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - file = buf.Bytes() - } - } - - model, err := GetBaseModel(handle.Db, id) - if err == ModelNotFoundError { - return c.ErrorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) - } else if err != nil { - return Error500(err) - } - - // TODO mk this path configurable - dir_path := path.Join("savedData", id) - - f, err := os.Create(path.Join(dir_path, "base_data.zip")) - if err != nil { - return Error500(err) - } - defer f.Close() - - f.Write(file) - - ModelUpdateStatus(c, id, PREPARING_ZIP_FILE) - - go processZipFile(c, model) - - Redirect("/models/edit?id="+id, c.Mode, w, r) - return nil - }) - - // ------ - // ------ CLASS DATA UPLOAD - // ------ - handle.PostJSON("/models/data/class/upload", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { - return nil - } - - read_form, err := r.MultipartReader() + read_form, err := c.R.MultipartReader() if err != nil { return c.JsonBadRequest("Please provide a valid form data request!") } @@ -418,26 +300,86 @@ func handleDataUpload(handle *Handle) { } } - c.Logger.Info("Trying to expand model", "id", id) + model, err := GetBaseModel(handle.Db, id) + if err == ModelNotFoundError { + return c.SendJSONStatus(http.StatusNotFound, "Model not found") + } else if err != nil { + return c.Error500(err) + } + + // TODO mk this path configurable + dir_path := path.Join("savedData", id) + + f, err := os.Create(path.Join(dir_path, "base_data.zip")) + if err != nil { + return c.Error500(err) + } + defer f.Close() + + f.Write(file) + + ModelUpdateStatus(c, id, PREPARING_ZIP_FILE) + + go processZipFile(c, model) + + return c.SendJSON(model.Id) + }) + + // ------ + // ------ CLASS DATA UPLOAD + // ------ + handle.Post("/models/data/class/upload", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { + return nil + } + + read_form, err := c.R.MultipartReader() + if err != nil { + return c.JsonBadRequest("Please provide a valid form data request!") + } + + var id string + var file []byte + + for { + part, err_part := read_form.NextPart() + if err_part == io.EOF { + break + } else if err_part != nil { + return c.JsonBadRequest("Please provide a valid form data request!") + } + if part.FormName() == "id" { + buf := new(bytes.Buffer) + buf.ReadFrom(part) + id = buf.String() + } + if part.FormName() == "file" { + buf := new(bytes.Buffer) + buf.ReadFrom(part) + file = buf.Bytes() + } + } + + c.Logger.Info("Trying to expand model", "id", id) model, err := GetBaseModel(handle.Db, id) if err == ModelNotFoundError { return c.SendJSONStatus(http.StatusNotFound, "Model not found") } else if err != nil { - return Error500(err) + return c.Error500(err) + } + + // TODO work in allowing the model to add new in the pre ready moment + if model.Status != READY { + return c.JsonBadRequest("Model not in the correct state to add a more classes") } - - // TODO work in allowing the model to add new in the pre ready moment - if model.Status != READY { - return c.JsonBadRequest("Model not in the correct state to add a more classes") - } // TODO mk this path configurable dir_path := path.Join("savedData", id) f, err := os.Create(path.Join(dir_path, "expand_data.zip")) if err != nil { - return Error500(err) + return c.Error500(err) } defer f.Close() @@ -450,115 +392,68 @@ func handleDataUpload(handle *Handle) { return c.SendJSON(model.Id) }) - handle.Delete("/models/data/delete-zip-file", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Delete("/models/data/delete-zip-file", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - type ModelData struct { - Id string `json:"id"` - } - - var dat ModelData - - if err := c.ToJSON(r, &dat); err != nil { - return err - } - - model, err := GetBaseModel(handle.Db, dat.Id) - if err == ModelNotFoundError { - return c.SendJSONStatus(http.StatusNotFound, "Model not found") - } else if err != nil { - return Error500(err) - } - - delete_path := "base_data.zip" - - if model.Status == READY_FAILED { - delete_path = "expand_data.zip" - } else if model.Status != FAILED_PREPARING_ZIP_FILE { - return c.JsonBadRequest("Model not in the correct status") - } - - err = os.Remove(path.Join("savedData", model.Id, delete_path)) - if err != nil { - return Error500(err) - } - - if model.Status != READY_FAILED { - err = os.RemoveAll(path.Join("savedData", model.Id, "data")) - if err != nil { - return Error500(err) - } - } else { - c.Logger.Warn("Handle failed to remove the savedData when deleteing the zip file while expanding") - } - - if model.Status != READY_FAILED { - _, err = handle.Db.Exec("delete from model_classes where model_id=$1;", model.Id) - if err != nil { - return Error500(err) - } - } else { - _, err = handle.Db.Exec("delete from model_classes where model_id=$1 and status=$2;", model.Id, MODEL_CLASS_STATUS_TO_TRAIN) - if err != nil { - return Error500(err) - } - } - - if model.Status != READY_FAILED { - ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING) - } else { - ModelUpdateStatus(c, model.Id, READY) - } - - return c.SendJSON(model.Id) + type ModelData struct { + Id string `json:"id"` } - f, err := MyParseForm(r) - if err != nil { - return ErrorCode(err, 400, c.AddMap(nil)) + var dat ModelData + + if err := c.ToJSON(&dat); err != nil { + return err } - if !CheckId(f, "id") { - return ErrorCode(err, 400, c.AddMap(nil)) - } - - id := f.Get("id") - - model, err := GetBaseModel(handle.Db, id) + model, err := GetBaseModel(handle.Db, dat.Id) if err == ModelNotFoundError { - return ErrorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) + return c.SendJSONStatus(http.StatusNotFound, "Model not found") } else if err != nil { - return Error500(err) + return c.Error500(err) } - if model.Status != FAILED_PREPARING_ZIP_FILE { - // TODO add message - return ErrorCode(nil, 400, c.AddMap(nil)) + delete_path := "base_data.zip" + + if model.Status == READY_FAILED { + delete_path = "expand_data.zip" + } else if model.Status != FAILED_PREPARING_ZIP_FILE { + return c.JsonBadRequest("Model not in the correct status") } - err = os.Remove(path.Join("savedData", id, "base_data.zip")) + err = os.Remove(path.Join("savedData", model.Id, delete_path)) if err != nil { - return Error500(err) + return c.Error500(err) } - err = os.RemoveAll(path.Join("savedData", id, "data")) - if err != nil { - return Error500(err) + if model.Status != READY_FAILED { + err = os.RemoveAll(path.Join("savedData", model.Id, "data")) + if err != nil { + return c.Error500(err) + } + } else { + c.Logger.Warn("Handle failed to remove the savedData when deleteing the zip file while expanding") } - _, err = handle.Db.Exec("delete from model_classes where model_id=$1;", id) - if err != nil { - return Error500(err) + if model.Status != READY_FAILED { + _, err = handle.Db.Exec("delete from model_classes where model_id=$1;", model.Id) + if err != nil { + return c.Error500(err) + } + } else { + _, err = handle.Db.Exec("delete from model_classes where model_id=$1 and status=$2;", model.Id, MODEL_CLASS_STATUS_TO_TRAIN) + if err != nil { + return c.Error500(err) + } } - ModelUpdateStatus(c, id, CONFIRM_PRE_TRAINING) - Redirect("/models/edit?id="+id, c.Mode, w, r) - return nil + if model.Status != READY_FAILED { + ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING) + } else { + ModelUpdateStatus(c, model.Id, READY) + } + + return c.SendJSON(model.Id) }) } diff --git a/logic/models/delete.go b/logic/models/delete.go index 7d3b55b..4a25149 100644 --- a/logic/models/delete.go +++ b/logic/models/delete.go @@ -4,40 +4,12 @@ import ( "net/http" "os" "path" - "strconv" . "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils" . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" utils "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" ) -func deleteModel(handle *Handle, id string, w http.ResponseWriter, c *Context, model BaseModel) { - c.Logger.Warnf("Removing model with id: %s", id) - _, err := handle.Db.Exec("delete from models where id=$1;", id) - if err != nil { - c.Logger.Error(err) - panic("TODO handle better deleteModel failed delete database query") - } - - model_path := path.Join("./savedData", id) - c.Logger.Warnf("Removing folder of model with id: %s at %s", id, model_path) - err = os.RemoveAll(model_path) - if err != nil { - c.Logger.Error(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 deleteModelJSON(c *Context, id string) *Error { c.Logger.Warnf("Removing model with id: %s", id) _, err := c.Db.Exec("delete from models where id=$1;", id) @@ -56,94 +28,29 @@ func deleteModelJSON(c *Context, id string) *Error { } func handleDelete(handle *Handle) { - handle.Delete("/models/delete", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - - if c.Mode == JSON { - - var dat struct { - Id string `json:"id" validate:"required"` - Name *string `json:"name,omitempty"` - } - - if err_ := c.ToJSON(r, &dat); err_ != nil { - return err_ - } - - var model struct { - Id string - Name string - Status int - } - - err := utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2;", dat.Id, c.User.Id) - if err == NotFoundError { - return c.SendJSONStatus(http.StatusNotFound, "Model not found!") - } else if err != nil { - return c.Error500(err) - } - - switch model.Status { - case FAILED_TRAINING: - fallthrough - case FAILED_PREPARING_ZIP_FILE: - fallthrough - case FAILED_PREPARING_TRAINING: - fallthrough - case FAILED_PREPARING: - return deleteModelJSON(c, dat.Id) - - case READY: - fallthrough - case CONFIRM_PRE_TRAINING: - if dat.Name == nil { - return c.JsonBadRequest("Provided name does not match the model name") - } - - if *dat.Name != model.Name { - return c.JsonBadRequest("Provided name does not match the model name") - } - - return deleteModelJSON(c, dat.Id) - default: - c.Logger.Warn("Do not know how to handle model in status", "status", model.Status) - return c.JsonBadRequest("Model in invalid status") - } + handle.Delete("/models/delete", func(c *Context) *Error { + if c.CheckAuthLevel(1) { + return nil + } + var dat struct { + Id string `json:"id" validate:"required"` + Name *string `json:"name,omitempty"` } - // This is required to parse delete forms with bodies - f, err := MyParseForm(r) - if err != nil { - return c.ErrorCode(err, 400, nil) + if err_ := c.ToJSON(&dat); err_ != nil { + return err_ } - if !CheckId(f, "id") { - return c.ErrorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) + var model struct { + Id string + Name string + Status int } - 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 c.ErrorCode(err, http.StatusInternalServerError, nil) - } - defer rows.Close() - - if !rows.Next() { - c.Logger.Warn("Could not find model for", id, c.User.Id) - return c.ErrorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) - } - - model := BaseModel{} - model.Id = id - - if err = rows.Scan(&model.Name, &model.Status); err != nil { + err := utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2;", dat.Id, c.User.Id) + if err == NotFoundError { + return c.SendJSONStatus(http.StatusNotFound, "Model not found!") + } else if err != nil { return c.Error500(err) } @@ -155,32 +62,23 @@ func handleDelete(handle *Handle) { case FAILED_PREPARING_TRAINING: fallthrough case FAILED_PREPARING: - deleteModel(handle, id, w, c, model) - return nil + return deleteModelJSON(c, dat.Id) case READY: fallthrough case CONFIRM_PRE_TRAINING: - if CheckEmpty(f, "name") { - return c.Error400(nil, "Name is empty", w, "/models/edit.html", "delete-model-card", AnyMap{ - "NameDoesNotMatch": true, - "Model": model, - }) + if dat.Name == nil { + return c.JsonBadRequest("Provided name does not match the model name") } - name := f.Get("name") - if name != model.Name { - LoadDefineTemplate(w, "/models/edit.html", "delete-model-card", c.AddMap(AnyMap{ - "NameDoesNotMatch": true, - "Model": model, - })) - return nil + if *dat.Name != model.Name { + return c.JsonBadRequest("Provided name does not match the model name") } - deleteModel(handle, id, w, c, model) - return nil + return deleteModelJSON(c, dat.Id) default: - panic("Do not know how to handle model in status:" + strconv.Itoa(model.Status)) + c.Logger.Warn("Do not know how to handle model in status", "status", model.Status) + return c.JsonBadRequest("Model in invalid status") } }) } diff --git a/logic/models/edit.go b/logic/models/edit.go index 3312e57..f9a12e2 100644 --- a/logic/models/edit.go +++ b/logic/models/edit.go @@ -2,7 +2,6 @@ package models import ( "fmt" - "net/http" model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes" . "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils" @@ -10,79 +9,30 @@ import ( . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" ) -func handleJson(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 c.JsonBadRequest("Model not found") - } - - type rowmodel struct { - Name string `json:"name"` - Status int `json:"status"` - Id string `json:"id"` - Width *int `json:"width"` - Height *int `json:"height"` - Color_mode *string `json:"color_mode"` - Format string `json:"format"` - Model_type int `json:"model_type"` - } - - var model rowmodel = rowmodel{} - err = utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2", id, c.User.Id) - if err == NotFoundError { - return c.SendJSONStatus(404, "Model not found") - } else if err != nil { - return c.Error500(err) - } - - return c.SendJSON(model) - - /* - - switch model.Status { - case TRAINING: - } - */ -} - func handleEdit(handle *Handle) { - handle.Get("/models/edit/classes", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Get("/models/edit/classes", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode != JSON { - return c.ErrorCode(nil, 400, AnyMap{}) - } - id, err := GetIdFromUrl(r, "id") - if err != nil { - return c.SendJSONStatus(http.StatusNotFound, "Model not found") - } - - model, err := GetBaseModel(c.Db, id) - if err == ModelNotFoundError { - return c.SendJSONStatus(http.StatusNotFound, "Model not found") - } else if err != nil { - return c.Error500(err) - } + model, err_ := c.GetModelFromId("id") + if err_ != nil { + return err_ + } wrong_number, err := model_classes.GetNumberOfWrongDataPoints(c.Db, model.Id) if err != nil { return c.Error500(err) } - cls, err := model_classes.ListClasses(c, id) + cls, err := model_classes.ListClasses(c, model.Id) if err != nil { return c.Error500(err) } - has_data, err := model_classes.ModelHasDataPoints(handle.Db, id) + has_data, err := model_classes.ModelHasDataPoints(handle.Db, model.Id) if err != nil { - return Error500(err) + return c.Error500(err) } type ReturnType struct { @@ -98,25 +48,15 @@ func handleEdit(handle *Handle) { }) }) - handle.Get("/models/edit/definitions", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Get("/models/edit/definitions", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode != JSON { - return c.ErrorCode(nil, 400, AnyMap{}) - } - id, err := GetIdFromUrl(r, "id") - if err != nil { - return c.SendJSONStatus(http.StatusNotFound, "Model not found") - } - - model, err := GetBaseModel(c.Db, id) - if err == ModelNotFoundError { - return c.SendJSONStatus(http.StatusNotFound, "Model not found") - } else if err != nil { - return c.Error500(err) - } + model, err_ := c.GetModelFromId("id") + if err_ != nil { + return err_ + } type defrow struct { Id string @@ -243,217 +183,39 @@ func handleEdit(handle *Handle) { return c.SendJSON(defsToReturn) }) - handle.Get("/models/edit", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Get("/models/edit", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - return handleJson(w, r, c) + if !c.CheckAuthLevel(1) { + return nil } - id, err := GetIdFromUrl(r, "id") + id, err := GetIdFromUrl(c, "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, format, model_type from models where id=$1 and user_id=$2;", id, c.User.Id) - if err != nil { - return Error500(err) - } - defer rows.Close() - - if !rows.Next() { - return ErrorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) + return c.JsonBadRequest("Model not found") } type rowmodel struct { - Name string - Status int - Id string - Width *int - Height *int - Color_mode *string - Format string - Type int + Name string `json:"name"` + Status int `json:"status"` + Id string `json:"id"` + Width *int `json:"width"` + Height *int `json:"height"` + Color_mode *string `json:"color_mode"` + Format string `json:"format"` + Model_type int `json:"model_type"` } var model rowmodel = rowmodel{} - model.Id = id - - err = rows.Scan(&model.Name, &model.Status, &model.Width, &model.Height, &model.Color_mode, &model.Format, &model.Type) - if err != nil { - return Error500(err) + err = utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2", id, c.User.Id) + if err == NotFoundError { + return c.SendJSONStatus(404, "Model not found") + } else if err != nil { + return c.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: - - wrong_number, err := model_classes.GetNumberOfWrongDataPoints(c.Db, model.Id) - if err != nil { - return c.Error500(err) - } - - cls, err := model_classes.ListClasses(c, id) - if err != nil { - return c.Error500(err) - } - - has_data, err := model_classes.ModelHasDataPoints(handle.Db, id) - if err != nil { - return Error500(err) - } - - LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{ - "Model": model, - "Classes": cls, - "HasData": has_data, - "NumberOfInvalidImages": wrong_number, - })) - case READY: - LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{ - "Model": model, - })) - case TRAINING: - - type defrow struct { - Id string - Status int - EpochProgress int - Epoch int - Accuracy float64 - } - - defs := []defrow{} - - if model.Type == 2 { - def_rows, err := c.Db.Query("select md.id, md.status, md.epoch, h.epoch_progress, h.accuracy from model_definition as md inner join exp_model_head as h on h.def_id = md.id where md.model_id=$1 order by md.created_on asc", model.Id) - if err != nil { - return c.Error500(err) - } - defer def_rows.Close() - - for def_rows.Next() { - var def defrow - err = def_rows.Scan(&def.Id, &def.Status, &def.Epoch, &def.EpochProgress, &def.Accuracy) - if err != nil { - return c.Error500(err) - } - defs = append(defs, def) - } - } else { - def_rows, err := c.Db.Query("select id, status, epoch, epoch_progress, accuracy from model_definition where model_id=$1 order by created_on asc", model.Id) - if err != nil { - return c.Error500(err) - } - defer def_rows.Close() - - for def_rows.Next() { - var def defrow - err = def_rows.Scan(&def.Id, &def.Status, &def.Epoch, &def.EpochProgress, &def.Accuracy) - if err != nil { - return c.Error500(err) - } - defs = append(defs, def) - } - } - - type layerdef struct { - id string - LayerType int - Shape string - } - - layers := []layerdef{} - - for _, def := range defs { - if def.Status == MODEL_DEFINITION_STATUS_TRAINING { - rows, err := c.Db.Query("select id, layer_type, shape from model_definition_layer where def_id=$1 order by layer_order asc;", def.Id) - if err != nil { - return c.Error500(err) - } - defer rows.Close() - - for rows.Next() { - var layerdef layerdef - err = rows.Scan(&layerdef.id, &layerdef.LayerType, &layerdef.Shape) - if err != nil { - return c.Error500(err) - } - layers = append(layers, layerdef) - } - - if model.Type == 2 { - - type lastLayerType struct { - Id string - Range_start int - Range_end int - } - - var lastLayer lastLayerType - - err := GetDBOnce(c, &lastLayer, "exp_model_head where def_id=$1 and status=3;", def.Id) - if err != nil { - return c.Error500(err) - } - - layers = append(layers, layerdef{ - id: lastLayer.Id, - LayerType: LAYER_DENSE, - Shape: fmt.Sprintf("%d, 1", lastLayer.Range_end-lastLayer.Range_start+1), - }) - } - - break - } - } - - sep_mod := 100 - if len(layers) > 8 { - sep_mod = 100 - (len(layers)-8)*10 - } - - if sep_mod < 10 { - sep_mod = 10 - } - - LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{ - "Model": model, - "Defs": defs, - "Layers": layers, - "SepMod": sep_mod, - })) - case PREPARING_ZIP_FILE: - 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 + return c.SendJSON(model) }) - } diff --git a/logic/models/list.go b/logic/models/list.go index 016eda0..8094217 100644 --- a/logic/models/list.go +++ b/logic/models/list.go @@ -1,66 +1,26 @@ package models import ( - "net/http" - "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" ) func handleList(handle *Handle) { - handle.Get("/models", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Get("/models", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - // TODO handle admin - if c.Mode == JSON { - rows, err := handle.Db.Query("select id, name from models where user_id=$1;", c.User.Id) - if err != nil { - return Error500(err) - } - defer rows.Close() - - type Row struct { - Name string `json:"name"` - Id string `json:"id"` - } - - got, err := utils.GetDbMultitple[Row](c, "models where user_id=$1", c.User.Id); - if err != nil { - c.Logger.Warn("HERE 6") - return c.Error500(nil) - } - - return c.SendJSON(got) + type Row struct { + Name string `json:"name"` + Id string `json:"id"` } - rows, err := handle.Db.Query("select id, name from models where user_id=$1;", c.User.Id) + got, err := utils.GetDbMultitple[Row](c, "models where user_id=$1", c.User.Id) if err != nil { - return Error500(err) - } - defer rows.Close() - - type row struct { - Name string - Id string + return c.Error500(nil) } - 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 + return c.SendJSON(got) }) - } diff --git a/logic/models/run.go b/logic/models/run.go index 250ecb4..1255dd2 100644 --- a/logic/models/run.go +++ b/logic/models/run.go @@ -2,9 +2,7 @@ package models import ( "bytes" - "fmt" "io" - "net/http" "os" "path" @@ -118,143 +116,14 @@ func runModelExp(c *Context, model *BaseModel, def_id string, inputImage *tf.Ten } func handleRun(handle *Handle) { - handle.Post("/models/run", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Post("/models/run", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - read_form, err := r.MultipartReader() - if err != nil { - // TODO improve message - return ErrorCode(nil, 400, nil) - } - - var id string - var file []byte - - for { - part, err_part := read_form.NextPart() - if err_part == io.EOF { - break - } else if err_part != nil { - return c.JsonBadRequest("Invalid multipart data") - } - if part.FormName() == "id" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - id = buf.String() - } - if part.FormName() == "file" { - buf := new(bytes.Buffer) - buf.ReadFrom(part) - file = buf.Bytes() - } - } - - model, err := GetBaseModel(handle.Db, id) - if err == ModelNotFoundError { - return c.JsonBadRequest("Models not found") - } else if err != nil { - return c.Error500(err) - } - - if model.Status != READY { - return c.JsonBadRequest("Model not ready to run images") - } - - def := JustId{} - err = GetDBOnce(c, &def, "model_definition where model_id=$1", model.Id) - if err == NotFoundError { - return c.JsonBadRequest("Could not find definition") - } else if err != nil { - return c.Error500(err) - } - - def_id := def.Id - - // TODO create a database table with tasks - run_path := path.Join("/tmp", model.Id, "runs") - os.MkdirAll(run_path, os.ModePerm) - img_path := path.Join(run_path, "img."+model.Format) - - img_file, err := os.Create(img_path) - if err != nil { - return c.Error500(err) - } - defer img_file.Close() - img_file.Write(file) - - if !testImgForModel(c, model, img_path) { - return c.JsonBadRequest("Provided image does not match the model") - } - - root := tg.NewRoot() - - var tf_img *image.Image = nil - - switch model.Format { - case "png": - tf_img = ReadPNG(root, img_path, int64(model.ImageMode)) - case "jpeg": - tf_img = ReadJPG(root, img_path, int64(model.ImageMode)) - default: - panic("Not sure what to do with '" + model.Format + "'") - } - - exec_results := tg.Exec(root, []tf.Output{tf_img.Value()}, nil, &tf.SessionOptions{}) - inputImage, err := tf.NewTensor(exec_results[0].Value()) - if err != nil { - return c.Error500(err) - } - - vi := -1 - var confidence float32 = 0 - - if model.ModelType == 2 { - c.Logger.Info("Running model normal", "model", model.Id, "def", def_id) - vi, confidence, err = runModelExp(c, model, def_id, inputImage) - if err != nil { - return c.Error500(err) - } - } else { - c.Logger.Info("Running model normal", "model", model.Id, "def", def_id) - vi, confidence, err = runModelNormal(c, model, def_id, inputImage) - if err != nil { - return c.Error500(err) - } - } - - os.RemoveAll(run_path) - - rows, err := handle.Db.Query("select name from model_classes where model_id=$1 and class_order=$2;", model.Id, vi) - if err != nil { - return c.Error500(err) - } - if !rows.Next() { - return c.SendJSON(nil) - } - - var name string - if err = rows.Scan(&name); err != nil { - return c.Error500(err) - } - - returnValue := struct { - Class string `json:"class"` - Confidence float32 `json:"confidence"` - }{ - Class: name, - Confidence: confidence, - } - - return c.SendJSON(returnValue) - } - - read_form, err := r.MultipartReader() + read_form, err := c.R.MultipartReader() if err != nil { - // TODO improve message - return ErrorCode(nil, 400, nil) + return c.JsonBadRequest("Invalid muilpart body") } var id string @@ -265,7 +134,7 @@ func handleRun(handle *Handle) { if err_part == io.EOF { break } else if err_part != nil { - return &Error{Code: http.StatusBadRequest} + return c.JsonBadRequest("Invalid multipart data") } if part.FormName() == "id" { buf := new(bytes.Buffer) @@ -281,27 +150,21 @@ func handleRun(handle *Handle) { model, err := GetBaseModel(handle.Db, id) if err == ModelNotFoundError { - return ErrorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) + return c.JsonBadRequest("Models not found") } else if err != nil { - return Error500(err) + return c.Error500(err) } if model.Status != READY { - // TODO improve this - return ErrorCode(nil, 400, c.AddMap(nil)) + return c.JsonBadRequest("Model not ready to run images") } def := JustId{} err = GetDBOnce(c, &def, "model_definition where model_id=$1", model.Id) if err == NotFoundError { - // TODO improve this - fmt.Printf("Could not find definition\n") - return ErrorCode(nil, 400, c.AddMap(nil)) + return c.JsonBadRequest("Could not find definition") } else if err != nil { - return Error500(err) + return c.Error500(err) } def_id := def.Id @@ -313,19 +176,13 @@ func handleRun(handle *Handle) { img_file, err := os.Create(img_path) if err != nil { - return Error500(err) + return c.Error500(err) } defer img_file.Close() img_file.Write(file) if !testImgForModel(c, model, img_path) { - LoadDefineTemplate(w, "/models/edit.html", "run-model-card", c.AddMap(AnyMap{ - "Model": model, - "NotFound": false, - "Result": nil, - "ImageError": true, - })) - return nil + return c.JsonBadRequest("Provided image does not match the model") } root := tg.NewRoot() @@ -344,7 +201,7 @@ func handleRun(handle *Handle) { exec_results := tg.Exec(root, []tf.Output{tf_img.Value()}, nil, &tf.SessionOptions{}) inputImage, err := tf.NewTensor(exec_results[0].Value()) if err != nil { - return Error500(err) + return c.Error500(err) } vi := -1 @@ -368,27 +225,25 @@ func handleRun(handle *Handle) { rows, err := handle.Db.Query("select name from model_classes where model_id=$1 and class_order=$2;", model.Id, vi) if err != nil { - return Error500(err) + return c.Error500(err) } if !rows.Next() { - LoadDefineTemplate(w, "/models/edit.html", "run-model-card", c.AddMap(AnyMap{ - "Model": model, - "NotFound": true, - "Result": nil, - "Confidence": confidence, - })) - return nil + return c.SendJSON(nil) } var name string if err = rows.Scan(&name); err != nil { - return nil + return c.Error500(err) } - LoadDefineTemplate(w, "/models/edit.html", "run-model-card", c.AddMap(AnyMap{ - "Model": model, - "Result": name, - })) - return nil + returnValue := struct { + Class string `json:"class"` + Confidence float32 `json:"confidence"` + }{ + Class: name, + Confidence: confidence, + } + + return c.SendJSON(returnValue) }) } diff --git a/logic/models/train/main.go b/logic/models/train/main.go index befdb5b..2c9af45 100644 --- a/logic/models/train/main.go +++ b/logic/models/train/main.go @@ -7,7 +7,4 @@ import ( func HandleTrainEndpoints(handle *Handle) { handleTrain(handle) handleRest(handle) - - //TODO remove - handleTest(handle) } diff --git a/logic/models/train/reset.go b/logic/models/train/reset.go index 1fde63a..2bdd759 100644 --- a/logic/models/train/reset.go +++ b/logic/models/train/reset.go @@ -1,7 +1,6 @@ package models_train import ( - "net/http" "os" "path" @@ -10,82 +9,39 @@ import ( ) func handleRest(handle *Handle) { - handle.Delete("/models/train/reset", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Delete("/models/train/reset", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - var dat struct { - Id string `json:"id"` - } - - if err := c.ToJSON(r, &dat); err != nil { - return err; - } - - model, err := GetBaseModel(c.Db, dat.Id) - if err == ModelNotFoundError { - return c.JsonBadRequest("Model not found"); - } else if err != nil { - // TODO improve response - return c.Error500(err) - } - - if model.Status != FAILED_PREPARING_TRAINING && model.Status != FAILED_TRAINING { - return c.JsonBadRequest("Model is not in status that be reset") - } - - os.RemoveAll(path.Join("savedData", model.Id, "defs")) - - _, err = c.Db.Exec("delete from model_definition where model_id=$1", model.Id) - if err != nil { - // TODO improve response - return c.Error500(err) - } - - ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING) - return c.SendJSON(model.Id) + var dat struct { + Id string `json:"id"` } - f, err := MyParseForm(r) - if err != nil { - // TODO improve response - return c.ErrorCode(nil, 400, c.AddMap(nil)) + if err := c.ToJSON(&dat); err != nil { + return err } - if !CheckId(f, "id") { - // TODO improve response - return c.ErrorCode(nil, 400, c.AddMap(nil)) - } - - id := f.Get("id") - - model, err := GetBaseModel(handle.Db, id) + model, err := GetBaseModel(c.Db, dat.Id) if err == ModelNotFoundError { - return c.ErrorCode(nil, http.StatusNotFound, AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - }) + return c.JsonBadRequest("Model not found") } else if err != nil { // TODO improve response return c.Error500(err) } if model.Status != FAILED_PREPARING_TRAINING && model.Status != FAILED_TRAINING { - // TODO improve response - return c.ErrorCode(nil, 400, c.AddMap(nil)) + return c.JsonBadRequest("Model is not in status that be reset") } os.RemoveAll(path.Join("savedData", model.Id, "defs")) - _, err = handle.Db.Exec("delete from model_definition where model_id=$1", model.Id) + _, err = c.Db.Exec("delete from model_definition where model_id=$1", model.Id) if err != nil { // TODO improve response return c.Error500(err) } ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING) - Redirect("/models/edit?id="+model.Id, c.Mode, w, r) - return nil + return c.SendJSON(model.Id) }) } diff --git a/logic/models/train/tensorflow-test.go b/logic/models/train/tensorflow-test.go deleted file mode 100644 index e6d0a77..0000000 --- a/logic/models/train/tensorflow-test.go +++ /dev/null @@ -1,175 +0,0 @@ -package models_train - -import ( - "fmt" - "io" - "net/http" - "os" - "os/exec" - "path" - "strconv" - "strings" - "text/template" - - . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" -) - -/*import ( - tf "github.com/galeone/tensorflow/tensorflow/go" - tg "github.com/galeone/tfgo" - "github.com/galeone/tfgo/image" - "github.com/galeone/tfgo/image/filter" - "github.com/galeone/tfgo/image/padding" -)*/ - -func getDir() string { - dir, err := os.Getwd() - if err != nil { - panic(err) - } - return dir -} - -func shapeToSize(shape string) string { - split := strings.Split(shape, ",") - return strings.Join(split[:len(split) - 1], ",") -} - -func handleTest(handle *Handle) { - handle.Post("/models/train/test", 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(err, 400, c.AddMap(nil)) - } - - rows, err := handle.Db.Query("select mc.name, mdp.file_path from model_classes as mc join model_data_point as mdp on mdp.class_id = mc.id where mdp.model_mode = 1 and mc.model_id = $1 limit 1;", id) - if err != nil { - return Error500(err) - } - defer rows.Close() - - if !rows.Next() { - return Error500(err) - } - - var name string - var file_path string - err = rows.Scan(&name, &file_path) - if err != nil { - return Error500(err) - } - - file_path = strings.Replace(file_path, "file://", "", 1) - - img_path := path.Join("savedData", id, "data", "training", name, file_path) - - fmt.Printf("%s\n", img_path) - - definitions, err := handle.Db.Query("select id from model_definition where model_id=$1 and status=2 limit 1;", id) - if err != nil { - return Error500(err) - } - defer definitions.Close() - - if !definitions.Next() { - fmt.Println("Did not find definition") - return Error500(nil) - } - - var definition_id string - - if err = definitions.Scan(&definition_id); err != nil { - return Error500(err) - } - - layers, err := handle.Db.Query("select layer_type, shape from model_definition_layer where def_id=$1 order by layer_order asc;", definition_id) - if err != nil { - return Error500(err) - } - defer layers.Close() - - type layerrow struct { - LayerType int - Shape string - } - - got := []layerrow{} - - for layers.Next() { - var row = layerrow{} - if err = layers.Scan(&row.LayerType, &row.Shape); err != nil { - return Error500(err) - } - row.Shape = shapeToSize(row.Shape) - got = append(got, row) - } - - // Generate folder - - run_path := path.Join("/tmp", id, "defs", definition_id) - - err = os.MkdirAll(run_path, os.ModePerm) - if err != nil { - return Error500(err) - } - - f, err := os.Create(path.Join(run_path, "run.py")) - if err != nil { - return Error500(err) - } - defer f.Close() - - fmt.Printf("Using path: %s\n", run_path) - - tmpl, err := template.New("python_model_template.py").ParseFiles("views/py/python_model_template.py") - if err != nil { - return Error500(err) - } - - if err = tmpl.Execute(f, AnyMap{ - "Layers": got, - "Size": got[0].Shape, - "DataDir": path.Join(getDir(), "savedData", id, "data", "training"), - }); err != nil { - return Error500(err) - } - - cmd := exec.Command("bash", "-c", fmt.Sprintf("cd %s && python run.py", run_path)) - _, err = cmd.Output() - if err != nil { - return Error500(err) - } - - result_path := path.Join("savedData", id, "defs", definition_id) - - os.MkdirAll(result_path, os.ModePerm) - - err = exec.Command("cp", path.Join(run_path, "model.keras"), path.Join(result_path, "model.keras")).Run() - if err != nil { - return Error500(err) - } - - accuracy_file, err := os.Open(path.Join(run_path, "accuracy.val")) - if err != nil { - return Error500(err) - } - defer accuracy_file.Close() - - accuracy_file_bytes, err := io.ReadAll(accuracy_file) - if err != nil { - return Error500(err) - } - - accuracy, err := strconv.ParseFloat(string(accuracy_file_bytes), 64) - if err != nil { - return Error500(err) - } - - w.Write([]byte(strconv.FormatFloat(accuracy, 'f', -1, 64))) - return nil - }) -} diff --git a/logic/models/train/train.go b/logic/models/train/train.go index d91c0d7..d8be72d 100644 --- a/logic/models/train/train.go +++ b/logic/models/train/train.go @@ -6,12 +6,12 @@ import ( "fmt" "io" "math" - "net/http" "os" "os/exec" "path" "sort" "strconv" + "strings" "text/template" model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes" @@ -23,6 +23,19 @@ import ( const EPOCH_PER_RUN = 20 const MAX_EPOCH = 100 +func shapeToSize(shape string) string { + split := strings.Split(shape, ",") + return strings.Join(split[:len(split)-1], ",") +} + +func getDir() string { + dir, err := os.Getwd() + if err != nil { + panic(err) + } + return dir +} + func MakeDefenition(db *sql.DB, model_id string, target_accuracy int) (id string, err error) { id = "" rows, err := db.Query("insert into model_definition (model_id, target_accuracy) values ($1, $2) returning id;", model_id, target_accuracy) @@ -702,13 +715,13 @@ func trainModelExp(c *Context, model *BaseModel) { ModelUpdateStatus(c, model.Id, FAILED_TRAINING) } - var definitions TrainModelRowUsables + var definitions TrainModelRowUsables - definitions, err = GetDbMultitple[TrainModelRowUsable](c, "model_definition where status=$1 and model_id=$2", MODEL_DEFINITION_STATUS_INIT, model.Id) - if err != nil { - failed("Failed to get definitions"); - return - } + definitions, err = GetDbMultitple[TrainModelRowUsable](c, "model_definition where status=$1 and model_id=$2", MODEL_DEFINITION_STATUS_INIT, model.Id) + if err != nil { + failed("Failed to get definitions") + return + } if len(definitions) == 0 { failed("No Definitions defined!") return @@ -810,38 +823,38 @@ func trainModelExp(c *Context, model *BaseModel) { } } - // Set the class status to trained + // Set the class status to trained err = setModelClassStatus(c, MODEL_CLASS_STATUS_TRAINED, "model_id=$1 and status=$2;", model.Id, MODEL_CLASS_STATUS_TRAINING) if err != nil { failed("Failed to set class status") return } - - var dat JustId - err = GetDBOnce(c, &dat, "model_definition where model_id=$1 and status=$2 order by accuracy desc limit 1;", model.Id, MODEL_DEFINITION_STATUS_TRANIED) - if err == NotFoundError { + var dat JustId + + err = GetDBOnce(c, &dat, "model_definition where model_id=$1 and status=$2 order by accuracy desc limit 1;", model.Id, MODEL_DEFINITION_STATUS_TRANIED) + if err == NotFoundError { failed("All definitions failed to train!") return - } else if err != nil { + } else if err != nil { failed("DB: failed to read definition") return - } + } if _, err = c.Db.Exec("update model_definition set status=$1 where id=$2;", MODEL_DEFINITION_STATUS_READY, dat.Id); err != nil { failed("Failed to update model definition") return } - to_delete, err := GetDbMultitple[JustId](c, "model_definition where status!=$1 and model_id=$2", MODEL_DEFINITION_STATUS_READY, model.Id) - if err != nil { + to_delete, err := GetDbMultitple[JustId](c, "model_definition where status!=$1 and model_id=$2", MODEL_DEFINITION_STATUS_READY, model.Id) + if err != nil { failed("Failed to select model_definition to delete") return - } + } - for _, d := range(to_delete) { + for _, d := range to_delete { os.RemoveAll(path.Join("savedData", model.Id, "defs", d.Id)) - } + } // TODO Check if returning also works here if _, err = c.Db.Exec("delete from model_definition where status!=$1 and model_id=$2;", MODEL_DEFINITION_STATUS_READY, model.Id); err != nil { @@ -1293,163 +1306,81 @@ func generateExpandableDefinitions(c *Context, model *BaseModel, target_accuracy return nil } -func handle_models_train_json(w http.ResponseWriter, r *http.Request, c *Context) *Error { - var dat struct { - Id string `json:"id"` - ModelType string `json:"model_type"` - NumberOfModels int `json:"number_of_models"` - Accuracy int `json:"accuracy"` - } - - if err_ := c.ToJSON(r, &dat); err_ != nil { - return err_ - } - - if dat.Id == "" { - return c.JsonBadRequest("Please provide a id") - } - - modelTypeId := 1 - if dat.ModelType == "expandable" { - modelTypeId = 2 - } else if dat.ModelType != "simple" { - return c.JsonBadRequest("Invalid model type!") - } - - model, err := GetBaseModel(c.Db, dat.Id) - if err == ModelNotFoundError { - return c.JsonBadRequest("Model not found") - } else if err != nil { - return c.Error500(err) - } - - if model.Status != CONFIRM_PRE_TRAINING { - return c.JsonBadRequest("Model in invalid status for training") - } - - if modelTypeId == 2 { - full_error := generateExpandableDefinitions(c, model, dat.Accuracy, dat.NumberOfModels) - if full_error != nil { - return full_error - } - } else { - full_error := generateDefinitions(c, model, dat.Accuracy, dat.NumberOfModels) - if full_error != nil { - return full_error - } - } - - if modelTypeId == 2 { - go trainModelExp(c, model) - } else { - go trainModel(c, model) - } - - _, err = c.Db.Exec("update models set status = $1, model_type = $2 where id = $3", TRAINING, modelTypeId, model.Id) - if err != nil { - fmt.Println("Failed to update model status") - fmt.Println(err) - // TODO improve this response - return Error500(err) - } - - return c.SendJSON(model.Id) -} - func handleTrain(handle *Handle) { - handle.Post("/models/train", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Post("/models/train", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - return handle_models_train_json(w, r, c) + var dat struct { + Id string `json:"id"` + ModelType string `json:"model_type"` + NumberOfModels int `json:"number_of_models"` + Accuracy int `json:"accuracy"` } - r.ParseForm() - f := r.Form - - number_of_models := 0 - accuracy := 0 - - if !CheckId(f, "id") || CheckEmpty(f, "model_type") || !CheckNumber(f, "number_of_models", &number_of_models) || !CheckNumber(f, "accuracy", &accuracy) { - // TODO improve this response - return ErrorCode(nil, 400, c.AddMap(nil)) + if err_ := c.ToJSON(&dat); err_ != nil { + return err_ } - id := f.Get("id") - model_type_id := 1 - model_type_form := f.Get("model_type") - - if model_type_form == "expandable" { - model_type_id = 2 - } else if model_type_form != "simple" { - return c.Error400(nil, "Invalid model type!", w, "/models/edit.html", "train-model-card", AnyMap{ - "HasData": true, - "ErrorMessage": "Invalid model type!", - }) + if dat.Id == "" { + return c.JsonBadRequest("Please provide a id") } - model, err := GetBaseModel(handle.Db, id) + modelTypeId := 1 + if dat.ModelType == "expandable" { + modelTypeId = 2 + } else if dat.ModelType != "simple" { + return c.JsonBadRequest("Invalid model type!") + } + + model, err := GetBaseModel(c.Db, dat.Id) if err == ModelNotFoundError { - return ErrorCode(nil, http.StatusNotFound, c.AddMap(AnyMap{ - "NotFoundMessage": "Model not found", - "GoBackLink": "/models", - })) + return c.JsonBadRequest("Model not found") } else if err != nil { - // TODO improve this response - return Error500(err) + return c.Error500(err) } if model.Status != CONFIRM_PRE_TRAINING { - // TODO improve this response - return ErrorCode(nil, 400, c.AddMap(nil)) + return c.JsonBadRequest("Model in invalid status for training") } - if model_type_id == 2 { - full_error := generateExpandableDefinitions(c, model, accuracy, number_of_models) + if modelTypeId == 2 { + full_error := generateExpandableDefinitions(c, model, dat.Accuracy, dat.NumberOfModels) if full_error != nil { return full_error } } else { - full_error := generateDefinitions(c, model, accuracy, number_of_models) + full_error := generateDefinitions(c, model, dat.Accuracy, dat.NumberOfModels) if full_error != nil { return full_error } } - if model_type_id == 2 { + if modelTypeId == 2 { go trainModelExp(c, model) } else { go trainModel(c, model) } - _, err = c.Db.Exec("update models set status = $1, model_type = $2 where id = $3", TRAINING, model_type_id, model.Id) + _, err = c.Db.Exec("update models set status = $1, model_type = $2 where id = $3", TRAINING, modelTypeId, model.Id) if err != nil { fmt.Println("Failed to update model status") fmt.Println(err) // TODO improve this response - return Error500(err) + return c.Error500(err) } - Redirect("/models/edit?id="+model.Id, c.Mode, w, r) - return nil + + return c.SendJSON(model.Id) }) - handle.Get("/model/epoch/update", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - // TODO check auth level - if c.Mode != NORMAL { - // This should only handle normal requests - c.Logger.Warn("This function only works with normal") - return c.UnsafeErrorCode(nil, 400, nil) - } - - f := r.URL.Query() + handle.Get("/model/epoch/update", func(c *Context) *Error { + f := c.R.URL.Query() accuracy := 0.0 if !CheckId(f, "model_id") || !CheckId(f, "definition") || CheckEmpty(f, "epoch") || !CheckFloat64(f, "accuracy", &accuracy) { - c.Logger.Warn("Invalid: model_id or definition or epoch or accuracy") - return c.UnsafeErrorCode(nil, 400, nil) + return c.JsonBadRequest("Invalid: model_id or definition or epoch or accuracy") } accuracy = accuracy * 100 @@ -1458,9 +1389,7 @@ func handleTrain(handle *Handle) { def_id := f.Get("definition") epoch, err := strconv.Atoi(f.Get("epoch")) if err != nil { - c.Logger.Warn("Epoch is not a number") - // No need to improve message because this function is only called internaly - return c.UnsafeErrorCode(nil, 400, nil) + return c.JsonBadRequest("Epoch is not a number") } rows, err := c.Db.Query("select md.status from model_definition as md where md.model_id=$1 and md.id=$2", model_id, def_id) @@ -1482,8 +1411,7 @@ func handleTrain(handle *Handle) { if status != 3 { c.Logger.Warn("Definition not on status 3(training)", "status", status) - // No need to improve message because this function is only called internaly - return c.UnsafeErrorCode(nil, 400, nil) + return c.JsonBadRequest("Definition not on status 3(training)") } c.Logger.Info("Updated model_definition!", "model", model_id, "progress", epoch, "accuracy", accuracy) @@ -1495,21 +1423,13 @@ func handleTrain(handle *Handle) { return nil }) - handle.Get("/model/head/epoch/update", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - // TODO check auth level - if c.Mode != NORMAL { - // This should only handle normal requests - c.Logger.Warn("This function only works with normal") - return c.UnsafeErrorCode(nil, 400, nil) - } - - f := r.URL.Query() + handle.Get("/model/head/epoch/update", func(c *Context) *Error { + f := c.R.URL.Query() accuracy := 0.0 if !CheckId(f, "head_id") || CheckEmpty(f, "epoch") || !CheckFloat64(f, "accuracy", &accuracy) { - c.Logger.Warn("Invalid: model_id or head_id or epoch or accuracy") - return c.UnsafeErrorCode(nil, 400, nil) + return c.JsonBadRequest("Invalid: model_id or definition or epoch or accuracy") } accuracy = accuracy * 100 @@ -1517,9 +1437,7 @@ func handleTrain(handle *Handle) { head_id := f.Get("head_id") epoch, err := strconv.Atoi(f.Get("epoch")) if err != nil { - c.Logger.Warn("Epoch is not a number") - // No need to improve message because this function is only called internaly - return c.UnsafeErrorCode(nil, 400, nil) + return c.JsonBadRequest("Epoch is not a number") } rows, err := c.Db.Query("select hd.status from exp_model_head as hd where hd.id=$1;", head_id) @@ -1541,8 +1459,7 @@ func handleTrain(handle *Handle) { if status != 3 { c.Logger.Warn("Head not on status 3(training)", "status", status) - // No need to improve message because this function is only called internaly - return c.UnsafeErrorCode(nil, 400, nil) + return c.JsonBadRequest("Head not on status 3(training)") } c.Logger.Info("Updated model_head!", "head", head_id, "progress", epoch, "accuracy", accuracy) diff --git a/logic/models/utils/utils.go b/logic/models/utils/utils.go deleted file mode 100644 index 25c5755..0000000 --- a/logic/models/utils/utils.go +++ /dev/null @@ -1,14 +0,0 @@ -package models_utils - -import ( - . "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils" -) - -// TODO make this return and caller handle error -func ModelUpdateStatus(c *Context, id string, status int) { - _, err := c.Db.Exec("update models set status=$1 where id=$2;", status, id) - if err != nil { - c.Logger.Error("Failed to update model status", "err", err) - c.Logger.Warn("TODO Maybe handle better") - } -} diff --git a/logic/utils/handler.go b/logic/utils/handler.go index 8f91479..055238f 100644 --- a/logic/utils/handler.go +++ b/logic/utils/handler.go @@ -13,194 +13,28 @@ import ( "time" dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types" + . "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils" "github.com/charmbracelet/log" "github.com/goccy/go-json" ) -func Mul(n1 int, n2 int) int { - return n1 * n2 -} - -func Add(n1 int, n2 int) int { - return n1 + n2 -} - -func baseLoadTemplate(base string, path string) (*template.Template, any) { - funcs := map[string]any{ - "startsWith": strings.HasPrefix, - "replace": strings.Replace, - "mul": Mul, - "add": Add, - } - return template.New(base).Funcs(funcs).ParseFiles( - "./views/"+base, - "./views/"+path, - "./views/partials/header.html", - ) -} - -func loadTemplate(path string) (*template.Template, any) { - return baseLoadTemplate("layout.html", path) -} - -func LoadView(writer http.ResponseWriter, path string, data interface{}) { - tmpl, err := loadTemplate(path) - if err != nil { - fmt.Printf("Failed to load view %s\n", path) - fmt.Println(err) - if path == "500.html" { - writer.Write([]byte("

Failed to load 500.html check console for more info

")) - } else { - LoadView(writer, "500.html", nil) - } - return - } - - if err := tmpl.Execute(writer, data); err != nil { - fmt.Printf("Failed to load view %s\n", path) - fmt.Println(err) - writer.WriteHeader(http.StatusInternalServerError) - if path == "500.html" { - writer.Write([]byte("

Failed to load 500.html check console for more info

")) - } else { - LoadView(writer, "500.html", nil) - } - return - } -} - -/** Only returns the html without template */ -func LoadHtml(writer http.ResponseWriter, path string, data interface{}) { - tmpl, err := baseLoadTemplate("html.html", path) - if err != nil { - fmt.Printf("Failed to load template %s\n", path) - fmt.Println(err) - writer.WriteHeader(http.StatusInternalServerError) - if path == "500.html" { - writer.Write([]byte("

Failed to load 500.html check console for more info

")) - } else { - LoadHtml(writer, "500.html", nil) - } - return - } - - if err := tmpl.Execute(writer, data); err != nil { - fmt.Printf("Failed to execute template %s\n", path) - fmt.Println(err) - writer.WriteHeader(http.StatusInternalServerError) - if path == "500.html" { - writer.Write([]byte("

Failed to load 500.html check console for more info

")) - } else { - LoadHtml(writer, "500.html", nil) - } - return - } -} - -func LoadDefineTemplate(writer http.ResponseWriter, path string, base string, data AnyMap) { - if data == nil { - data = map[string]interface{}{ - "Error": true, - } - } else { - data["Error"] = true - } - - funcs := map[string]any{ - "startsWith": strings.HasPrefix, - "mul": Mul, - "replace": strings.Replace, - "add": Add, - } - - tmpl, err := template.New("").Funcs(funcs).Parse("{{template \"" + base + "\" . }}") - if err != nil { - panic("Lol") - } - - tmpl, err = tmpl.ParseFiles( - "./views/"+path, - "./views/partials/header.html", - ) - - if err != nil { - fmt.Printf("Failed to load template %s\n", path) - fmt.Println(err) - writer.WriteHeader(http.StatusInternalServerError) - if path == "500.html" { - writer.Write([]byte("

Failed to load 500.html check console for more info

")) - } else { - LoadHtml(writer, "500.html", nil) - } - return - } - - if err := tmpl.Execute(writer, data); err != nil { - fmt.Printf("Failed to execute template %s\n", path) - fmt.Println(err) - writer.WriteHeader(http.StatusInternalServerError) - if path == "500.html" { - writer.Write([]byte("

Failed to load 500.html check console for more info

")) - } else { - LoadHtml(writer, "500.html", nil) - } - return - } -} - type AnyMap = map[string]interface{} type Error struct { Code int - Msg *string - data AnyMap -} - -type AnswerType int - -const ( - NORMAL AnswerType = 1 << iota - HTML - JSON - HTMLFULL -) - -func LoadBasedOnAnswer(ans AnswerType, w http.ResponseWriter, path string, data map[string]interface{}) { - if ans == NORMAL { - LoadView(w, path, data) - return - } else if ans == HTML { - LoadHtml(w, path, data) - return - } else if ans == HTMLFULL { - if data == nil { - LoadHtml(w, path, map[string]interface{}{ - "App": true, - }) - } else { - data["App"] = true - LoadHtml(w, path, data) - } - return - } else if ans == JSON { - panic("TODO JSON!") - } else { - panic("unreachable") - } - + data any } type HandleFunc struct { path string - mode AnswerType - fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error + fn func(c *Context) *Error } type Handler interface { New() Startup() - Get(fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) - Post(fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) + Get(fn func(c *Context) *Error) + Post(fn func(c *Context) *Error) } type Handle struct { @@ -219,202 +53,162 @@ func decodeBody(r *http.Request) (string, *Error) { return string(body[:]), nil } -func handleError(err *Error, w http.ResponseWriter, context *Context) { +func handleError(err *Error, c *Context) { if err != nil { - data := context.AddMap(err.data) - if err.Code == http.StatusNotFound { - if context.Mode == HTML { - w.WriteHeader(309) - context.Mode = HTMLFULL - } - LoadBasedOnAnswer(context.Mode, w, "404.html", data) - return + c.Logger.Warn("Responded with error", "code", err.Code, "data", err.data) + c.Writer.WriteHeader(err.Code) + var e *Error + if err.data != nil { + e = c.SendJSON(err.data) + } else { + e = c.SendJSON(500) } - w.WriteHeader(err.Code) - if err.Code == http.StatusBadRequest { - LoadBasedOnAnswer(context.Mode, w, "400.html", data) - return - } - if err.Msg != nil { - w.Write([]byte(*err.Msg)) + if e != nil { + c.Logger.Error("Something went very wront while trying to send and error message") + c.Writer.Write([]byte("505")) } } } -func (x *Handle) Get(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.gets = append(x.gets, HandleFunc{path, NORMAL | HTML | HTMLFULL | JSON, fn}) +func (x *Handle) Get(path string, fn func(c *Context) *Error) { + x.gets = append(x.gets, HandleFunc{path, fn}) } -func (x *Handle) GetHTML(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.gets = append(x.gets, HandleFunc{path, NORMAL | HTML | HTMLFULL, fn}) +func (x *Handle) Post(path string, fn func(c *Context) *Error) { + x.posts = append(x.posts, HandleFunc{path, fn}) } -func (x *Handle) GetJSON(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.gets = append(x.gets, HandleFunc{path, JSON, fn}) +func (x *Handle) Delete(path string, fn func(c *Context) *Error) { + x.deletes = append(x.deletes, HandleFunc{path, fn}) } -func (x *Handle) Post(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.posts = append(x.posts, HandleFunc{path, NORMAL | HTML | HTMLFULL | JSON, fn}) -} - -func (x *Handle) PostHTML(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.posts = append(x.posts, HandleFunc{path, NORMAL | HTML | HTMLFULL, fn}) -} - -func (x *Handle) PostJSON(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.posts = append(x.posts, HandleFunc{path, JSON, fn}) -} - -func (x *Handle) Delete(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.deletes = append(x.deletes, HandleFunc{path, NORMAL | HTML | HTMLFULL | JSON, fn}) -} - -func (x *Handle) DeleteHTML(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.deletes = append(x.deletes, HandleFunc{path, NORMAL | HTML | HTMLFULL, fn}) -} - -func (x *Handle) DeleteJSON(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) { - x.deletes = append(x.deletes, HandleFunc{path, JSON, fn}) -} - -func (x *Handle) handleGets(w http.ResponseWriter, r *http.Request, context *Context) { +func (x *Handle) handleGets(context *Context) { for _, s := range x.gets { - if s.path == r.URL.Path && context.Mode&s.mode != 0 { - handleError(s.fn(w, r, context), w, context) + if s.path == context.R.URL.Path { + handleError(s.fn(context), context) return } } - if context.Mode != HTMLFULL { - w.WriteHeader(http.StatusNotFound) - } - LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil)) + handleError(&Error{404, "Endpoint not found"}, context) } -func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Context) { +func (x *Handle) handlePosts(context *Context) { for _, s := range x.posts { - if s.path == r.URL.Path && context.Mode&s.mode != 0 { - handleError(s.fn(w, r, context), w, context) + if s.path == context.R.URL.Path { + handleError(s.fn(context), context) return } } - if context.Mode != HTMLFULL { - w.WriteHeader(http.StatusNotFound) - } - LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil)) + handleError(&Error{404, "Endpoint not found"}, context) } -func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context *Context) { +func (x *Handle) handleDeletes(context *Context) { for _, s := range x.deletes { - if s.path == r.URL.Path && context.Mode&s.mode != 0 { - handleError(s.fn(w, r, context), w, context) + if s.path == context.R.URL.Path { + handleError(s.fn(context), context) return } } - if context.Mode != HTMLFULL { - w.WriteHeader(http.StatusNotFound) - } - LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil)) + handleError(&Error{404, "Endpoint not found"}, context) } -func CheckAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Context) bool { +func (c *Context) CheckAuthLevel(authLevel int) bool { if authLevel > 0 { - if c.requireAuth(w, r) { - Logoff(c.Mode, w, r) + if c.requireAuth() { + c.Logoff() return false } if c.User.UserType < authLevel { - notAuth(c.Mode, w, r) + c.NotAuth() return false } } return true } -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) { - return nil - } - LoadBasedOnAnswer(c.Mode, w, path, c.AddMap(data)) - return nil - } -} - type Context struct { Token *string User *dbtypes.User - Mode AnswerType Logger *log.Logger Db *sql.DB - Writer http.ResponseWriter + Writer http.ResponseWriter + R *http.Request } -func (c Context) ToJSON(r *http.Request, dat any) *Error { - - decoder := json.NewDecoder(r.Body) +func (c Context) ToJSON(dat any) *Error { - err := decoder.Decode(dat) - if err != nil { - return c.Error500(err) - } + decoder := json.NewDecoder(c.R.Body) - return nil + err := decoder.Decode(dat) + if err != nil { + return c.Error500(err) + } + + return nil } func (c Context) SendJSON(dat any) *Error { - c.Writer.Header().Add("content-type", "application/json") - text, err := json.Marshal(dat) - if err != nil { - return c.Error500(err) - } - if _, err = c.Writer.Write(text); err != nil { - return c.Error500(err) - } - return nil + c.Writer.Header().Add("content-type", "application/json") + text, err := json.Marshal(dat) + if err != nil { + return c.Error500(err) + } + if _, err = c.Writer.Write(text); err != nil { + return c.Error500(err) + } + return nil } func (c Context) SendJSONStatus(status int, dat any) *Error { - c.Writer.Header().Add("content-type", "application/json") - c.Writer.WriteHeader(status) - text, err := json.Marshal(dat) - if err != nil { - return c.Error500(err) - } - if _, err = c.Writer.Write(text); err != nil { - return c.Error500(err) - } - return nil + c.Writer.Header().Add("content-type", "application/json") + c.Writer.WriteHeader(status) + text, err := json.Marshal(dat) + if err != nil { + return c.Error500(err) + } + if _, err = c.Writer.Write(text); err != nil { + return c.Error500(err) + } + return nil } func (c Context) JsonBadRequest(dat any) *Error { - c.SetReportCaller(true) - c.Logger.Warn("Request failed with a bad request", "dat", dat) - c.SetReportCaller(false) - return c.SendJSONStatus(http.StatusBadRequest, dat) + c.SetReportCaller(true) + c.Logger.Warn("Request failed with a bad request", "dat", dat) + c.SetReportCaller(false) + return c.SendJSONStatus(http.StatusBadRequest, dat) } func (c Context) JsonErrorBadRequest(err error, dat any) *Error { - c.SetReportCaller(true) - c.Logger.Error("Error while processing request", "err", err, "dat", dat) - c.SetReportCaller(false) - return c.SendJSONStatus(http.StatusBadRequest, dat) + c.SetReportCaller(true) + c.Logger.Error("Error while processing request", "err", err, "dat", dat) + c.SetReportCaller(false) + return c.SendJSONStatus(http.StatusBadRequest, dat) } -func (c Context) Error400(err error, message string, w http.ResponseWriter, path string, base string, data AnyMap) *Error { - c.SetReportCaller(true) - c.Logger.Error(message) - c.SetReportCaller(false) +func (c *Context) GetModelFromId(id_path string) (*BaseModel, *Error) { + + id, err := GetIdFromUrl(c, id_path) if err != nil { - c.Logger.Errorf("Something went wrong returning with: %d\n.Err:\n", http.StatusBadRequest) - c.Logger.Error(err) + return nil, c.SendJSONStatus(http.StatusNotFound, "Model not found") } - if c.Mode == JSON { - return &Error{http.StatusBadRequest, nil, c.AddMap(data)} + model, err := GetBaseModel(c.Db, id) + if err == ModelNotFoundError { + return nil, c.SendJSONStatus(http.StatusNotFound, "Model not found") + } else if err != nil { + return nil, c.Error500(err) } - LoadDefineTemplate(w, path, base, c.AddMap(data)) - return nil + return model, nil +} + +func ModelUpdateStatus(c *Context, id string, status int) { + _, err := c.Db.Exec("update models set status=$1 where id=$2;", status, id) + if err != nil { + c.Logger.Error("Failed to update model status", "err", err) + c.Logger.Warn("TODO Maybe handle better") + } } func (c Context) SetReportCaller(report bool) { @@ -427,7 +221,7 @@ func (c Context) SetReportCaller(report bool) { } } -func (c Context) ErrorCode(err error, code int, data AnyMap) *Error { +func (c Context) ErrorCode(err error, code int, data any) *Error { if code == 400 { c.SetReportCaller(true) c.Logger.Warn("When returning BadRequest(400) please use context.Error400\n") @@ -437,32 +231,14 @@ func (c Context) ErrorCode(err error, code int, data AnyMap) *Error { c.Logger.Errorf("Something went wrong returning with: %d\n.Err:\n", code) c.Logger.Error(err) } - return &Error{code, nil, c.AddMap(data)} -} - -func (c Context) UnsafeErrorCode(err error, code int, data AnyMap) *Error { - if err != nil { - c.Logger.Errorf("Something went wrong returning with: %d\n.Err:\n", code) - c.Logger.Error(err) - } - return &Error{code, nil, c.AddMap(data)} + return &Error{code, data} } func (c Context) Error500(err error) *Error { return c.ErrorCode(err, http.StatusInternalServerError, nil) } -func (c Context) AddMap(m AnyMap) AnyMap { - if m == nil { - return map[string]interface{}{ - "Context": c, - } - } - m["Context"] = c - return m -} - -func (c *Context) requireAuth(w http.ResponseWriter, r *http.Request) bool { +func (c *Context) requireAuth() bool { if c.User == nil { return true } @@ -471,7 +247,7 @@ func (c *Context) requireAuth(w http.ResponseWriter, r *http.Request) bool { var LogoffError = errors.New("Invalid token!") -func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request, w http.ResponseWriter) (*Context, error) { +func (x Handle) createContext(handler *Handle, r *http.Request, w http.ResponseWriter) (*Context, error) { var token *string @@ -482,27 +258,18 @@ func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request, Prefix: r.URL.Path, }) - if mode != JSON { - for _, r := range r.Cookies() { - if r.Name == "auth" { - token = &r.Value - } - } - } else { - t := r.Header.Get("token") - if t != "" { - token = &t - } - } - + t := r.Header.Get("token") + if t != "" { + token = &t + } // TODO check that the token is still valid if token == nil { return &Context{ - Mode: mode, Logger: logger, Db: handler.Db, - Writer: w, + Writer: w, + R: r, }, nil } @@ -511,52 +278,22 @@ func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request, return nil, errors.Join(err, LogoffError) } - return &Context{token, user, mode, logger, handler.Db, w}, nil + return &Context{token, user, logger, handler.Db, w, r}, nil } -// TODO check if I can use http.Redirect -func Redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Request) { - w.Header().Set("Location", path) - if mode == JSON { - w.WriteHeader(http.StatusSeeOther) - w.Write([]byte(path)) - return - } - if mode&(HTMLFULL|HTML) != 0 { - w.Header().Add("HX-Redirect", path) - w.WriteHeader(204) - } else { - w.WriteHeader(http.StatusSeeOther) - } +func contextlessLogoff(w http.ResponseWriter) { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("\"Not Authorized\"")) } -func Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) { - if (mode == JSON) { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("\"Not Authorized\"")) - } else { - // Delete cookie - cookie := &http.Cookie{ - Name: "auth", - Value: "", - Expires: time.Unix(0, 0), - } - http.SetCookie(w, cookie) - Redirect("/login", mode, w, r) - } -} +func (c *Context) Logoff() { contextlessLogoff(c.Writer) } -func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) { - if mode == JSON { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("\"You can not access this resource!\"")) - return - } - if mode&(HTMLFULL|HTML) != 0 { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("You can not access this resource!")) - } else { - w.WriteHeader(http.StatusForbidden) +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!")) } } @@ -583,21 +320,6 @@ func (x Handle) StaticFiles(pathTest string, fileType string, contentType string }) } -func ErrorCode(err error, code int, data AnyMap) *Error { - log.Warn("This function is deprecated please use the one provided by context") - // TODO Improve Logging - if err != nil { - fmt.Printf("Something went wrong returning with: %d\n.Err:\n", code) - fmt.Println(err) - } - return &Error{code, nil, data} -} - -func Error500(err error) *Error { - log.Warn("This function is deprecated please use the one provided by context") - return ErrorCode(err, http.StatusInternalServerError, 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):] @@ -661,9 +383,9 @@ func (x Handle) ReadTypesFiles(pathTest string, baseFilePath string, fileTypes [ } 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) - + http.HandleFunc("/api"+pathTest, func(w http.ResponseWriter, r *http.Request) { + r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1) + user_path := r.URL.Path[len(pathTest):] found := false @@ -704,52 +426,50 @@ func NewHandler(db *sql.DB) *Handle { x := &Handle{db, gets, posts, deletes} http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + + w.Header().Add("Access-Control-Allow-Origin", "*") + w.Header().Add("Access-Control-Allow-Headers", "*") + w.Header().Add("Access-Control-Allow-Methods", "*") + // Decide answertype - ans := NORMAL - if r.Header.Get("HX-Request") == "true" || r.Header.Get("Request-Type") == "html" { - ans = HTML - } - if r.Header.Get("Request-Type") == "htmlfull" { - ans = HTMLFULL - } - if r.Header.Get("content-type") == "application/json" || r.Header.Get("response-type") == "application/json" { - ans = JSON - } - - if !strings.HasPrefix(r.URL.Path, "/api") { - return - } - - r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1) - - w.Header().Add("Access-Control-Allow-Origin", "*") - w.Header().Add("Access-Control-Allow-Headers", "*") - w.Header().Add("Access-Control-Allow-Methods", "*") - - //Login state - context, err := x.createContext(x, ans, r, w) - if err != nil { - Logoff(ans, w, r) + if !(r.Header.Get("content-type") == "application/json" || r.Header.Get("response-type") == "application/json") { + w.WriteHeader(500) + w.Write([]byte("Please set content-type to application/json or set response-type to application/json\n")) return } - // context.Logger.Info("Parsing", "path", r.URL.Path) + if !strings.HasPrefix(r.URL.Path, "/api") { + w.WriteHeader(404) + w.Write([]byte("Path not found")) + return + } + + r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1) + + //Login state + context, err := x.createContext(x, r, w) + if err != nil { + contextlessLogoff(w) + return + } + + // context.Logger.Info("Parsing", "path", r.URL.Path) if r.Method == "GET" { - x.handleGets(w, r, context) + x.handleGets(context) return } if r.Method == "POST" { - x.handlePosts(w, r, context) + x.handlePosts(context) return } if r.Method == "DELETE" { - x.handleDeletes(w, r, context) + x.handleDeletes(context) + return + } + if r.Method == "OPTIONS" { return } - if r.Method == "OPTIONS" { - return - } panic("TODO handle method: " + r.Method) }) diff --git a/logic/utils/utils.go b/logic/utils/utils.go index 592ba0c..4309b3a 100644 --- a/logic/utils/utils.go +++ b/logic/utils/utils.go @@ -58,12 +58,12 @@ func IsValidUUID(u string) bool { return err == nil } -func GetIdFromUrl(r *http.Request, target string) (string, error) { - if !r.URL.Query().Has(target) { +func GetIdFromUrl(c *Context, target string) (string, error) { + if !c.R.URL.Query().Has(target) { return "", errors.New("Query does not have " + target) } - id := r.URL.Query().Get("id") + id := c.R.URL.Query().Get("id") if len(id) == 0 { return "", errors.New("Query is empty for " + target) } diff --git a/main.go b/main.go index 3aa7a6f..0f0fce4 100644 --- a/main.go +++ b/main.go @@ -50,8 +50,6 @@ func main() { handle.ReadTypesFiles("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"}) handle.ReadTypesFilesApi("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"}) - handle.GetHTML("/", AnswerTemplate("index.html", nil, 0)) - usersEndpints(db, handle) HandleModels(handle) diff --git a/users.go b/users.go index 81b0c98..25c8716 100644 --- a/users.go +++ b/users.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "io" "net/http" - "time" "golang.org/x/crypto/bcrypt" @@ -76,187 +75,68 @@ func generateToken(db *sql.DB, email string, password string) (string, bool) { } func usersEndpints(db *sql.DB, handle *Handle) { - handle.GetHTML("/login", AnswerTemplate("login.html", nil, 0)) - handle.Post("/login", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if c.Mode == JSON { - - type UserLogin struct { - Email string `json:"email"` - Password string `json:"password"` - } - - var dat UserLogin - - if err := c.ToJSON(r, &dat); err != nil { - return err - } - - // TODO Give this to the generateToken function - token, login := generateToken(db, dat.Email, dat.Password) - if !login { - return c.SendJSONStatus(http.StatusUnauthorized, "Email or password are incorrect") - } - - user, err := dbtypes.UserFromToken(c.Db, token) - if err != nil { - return c.Error500(err) - } - - type UserReturn struct { - Token string `json:"token"` - Id string `json:"id"` - UserType int `json:"user_type"` - Username string `json:"username"` - Email string `json:"email"` - } - - userReturn := UserReturn{ - Token: token, - Id: user.Id, - UserType: user.UserType, - Username: user.Username, - Email: user.Email, - } - - return c.SendJSON(userReturn) + handle.Post("/login", func(c *Context) *Error { + type UserLogin struct { + Email string `json:"email"` + Password string `json:"password"` } - r.ParseForm() - f := r.Form + var dat UserLogin - if CheckEmpty(f, "email") || CheckEmpty(f, "password") { - LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{ - "Submited": true, - })) - return nil + if err := c.ToJSON(&dat); err != nil { + return err } - email := f.Get("email") - password := f.Get("password") - // TODO Give this to the generateToken function - expiration := time.Now().Add(24 * time.Hour) - token, login := generateToken(db, email, password) + token, login := generateToken(db, dat.Email, dat.Password) if !login { - LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{ - "Submited": true, - "NoUserOrPassword": true, - "Email": email, - })) - return nil + return c.SendJSONStatus(http.StatusUnauthorized, "Email or password are incorrect") } - cookie := &http.Cookie{Name: "auth", Value: token, HttpOnly: false, Expires: expiration} - http.SetCookie(w, cookie) + user, err := dbtypes.UserFromToken(c.Db, token) + if err != nil { + return c.Error500(err) + } - w.Header().Set("Location", "/") - w.WriteHeader(http.StatusSeeOther) - return nil + type UserReturn struct { + Token string `json:"token"` + Id string `json:"id"` + UserType int `json:"user_type"` + Username string `json:"username"` + Email string `json:"email"` + } + + userReturn := UserReturn{ + Token: token, + Id: user.Id, + UserType: user.UserType, + Username: user.Username, + Email: user.Email, + } + + return c.SendJSON(userReturn) }) - 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 { - type UserLogin struct { - Username string `json:"username"` - Email string `json:"email"` - Password string `json:"password"` - } - - var dat UserLogin - - if err := c.ToJSON(r, &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 { - return c.SendJSONStatus(http.StatusBadRequest, "Email already in use!") - } - if db_username == dat.Username { - return c.SendJSONStatus(http.StatusBadRequest, "Username already in use!") - } - panic("Unrechable") - } - - if len([]byte(dat.Password)) > 68 { - return c.JsonBadRequest("Password is to long!") - } - - salt := generateSalt() - hash_password, err := hashPassword(dat.Password, salt) - if err != nil { - return c.Error500(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) - } - - // TODO Give this to the generateToken function - token, login := generateToken(db, dat.Email, dat.Password) - if !login { - return c.SendJSONStatus(500, "Could not login after creatting account please try again later") - } - - user, err := dbtypes.UserFromToken(c.Db, token) - if err != nil { - return c.Error500(err) - } - - type UserReturn struct { - Token string `json:"token"` - Id string `json:"id"` - UserType int `json:"user_type"` - Username string `json:"username"` - Email string `json:"email"` - } - - userReturn := UserReturn{ - Token: token, - Id: user.Id, - UserType: user.UserType, - Username: user.Username, - Email: user.Email, - } - - 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"` } - r.ParseForm() - f := r.Form + var dat UserLogin - if CheckEmpty(f, "email") || CheckEmpty(f, "password") || CheckEmpty(f, "username") { - LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{ - "Submited": true, - }) - return nil + if err := c.ToJSON(&dat); err != nil { + return err } - email := f.Get("email") - username := f.Get("username") - password := f.Get("password") + 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;", username, email) + rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email) if err != nil { - panic("TODO handle this") + return c.Error500(err) } defer rows.Close() @@ -265,85 +145,95 @@ func usersEndpints(db *sql.DB, handle *Handle) { var db_email string err = rows.Scan(&db_username, &db_email) if err != nil { - panic("TODO handle this better") + return c.Error500(err) } - LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{ - "Submited": true, - "Email": email, - "Username": username, - "EmailError": db_email == email, - "UserError": db_username == username, - }) - return nil + if db_email == dat.Email { + return c.SendJSONStatus(http.StatusBadRequest, "Email already in use!") + } + if db_username == dat.Username { + return c.SendJSONStatus(http.StatusBadRequest, "Username already in use!") + } + panic("Unrechable") } - if len([]byte(password)) > 68 { - LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{ - "Submited": true, - "Email": email, - "Username": username, - "PasswordToLong": true, - }) - return nil + if len([]byte(dat.Password)) > 68 { + return c.JsonBadRequest("Password is to long!") } salt := generateSalt() - hash_password, err := hashPassword(password, salt) + hash_password, err := hashPassword(dat.Password, salt) if err != nil { - return &Error{ - Code: http.StatusInternalServerError, - } + return c.Error500(err) } - _, err = db.Exec("insert into users (username, email, salt, password) values ($1, $2, $3, $4);", username, 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 { - return &Error{ - Code: http.StatusInternalServerError, - } + return c.Error500(err) } // TODO Give this to the generateToken function - expiration := time.Now().Add(24 * time.Hour) - token, login := generateToken(db, email, password) - + token, login := generateToken(db, dat.Email, dat.Password) if !login { - msg := "Login failed" - return &Error{ - Code: http.StatusInternalServerError, - Msg: &msg, - } + return c.SendJSONStatus(500, "Could not login after creatting account please try again later") } - cookie := &http.Cookie{Name: "auth", Value: token, HttpOnly: false, Expires: expiration} - http.SetCookie(w, cookie) - w.Header().Set("Location", "/") - w.WriteHeader(http.StatusSeeOther) - return nil + user, err := dbtypes.UserFromToken(c.Db, token) + if err != nil { + return c.Error500(err) + } + + type UserReturn struct { + Token string `json:"token"` + Id string `json:"id"` + UserType int `json:"user_type"` + Username string `json:"username"` + Email string `json:"email"` + } + + userReturn := UserReturn{ + Token: token, + Id: user.Id, + UserType: user.UserType, + Username: user.Username, + Email: user.Email, + } + + return c.SendJSON(userReturn) }) - handle.Get("/user/info", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + // TODO allow admin users to update this data + handle.Get("/user/info", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - return c.Error500(nil) + + user, err := dbtypes.UserFromToken(c.Db, *c.Token) + if err != nil { + return c.Error500(err) } - LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{ - "Email": c.User.Email, - })) - return nil + type UserReturn struct { + Id string `json:"id"` + UserType int `json:"user_type"` + Username string `json:"username"` + Email string `json:"email"` + } + + userReturn := UserReturn{ + Id: user.Id, + UserType: user.UserType, + Username: user.Username, + Email: user.Email, + } + + return c.SendJSON(userReturn) }) // Handles updating users - handle.Post("/user/info", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(int(dbtypes.User_Normal), w, r, c) { + handle.Post("/user/info", func(c *Context) *Error { + if !c.CheckAuthLevel(int(dbtypes.User_Normal)) { return nil } - if c.Mode != JSON { - return c.Error500(nil) - } type UserData struct { Id string `json:"id"` @@ -352,7 +242,7 @@ func usersEndpints(db *sql.DB, handle *Handle) { var dat UserData - if err := c.ToJSON(r, &dat); err != nil { + if err := c.ToJSON(&dat); err != nil { return err } @@ -417,108 +307,38 @@ func usersEndpints(db *sql.DB, handle *Handle) { return c.SendJSON(toReturnUser) }) - handle.Post("/user/info/email", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { + handle.Post("/user/info/password", func(c *Context) *Error { + if !c.CheckAuthLevel(1) { return nil } - if c.Mode == JSON { - return c.Error500(nil) + + var dat struct { + Old_Password string `json:"old_password"` + Password string `json:"password"` + Password2 string `json:"password2"` } - r.ParseForm() - - if CheckEmpty(r.Form, "email") { - return c.Error400(nil, "Email Not provided", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{ - "Email": c.User.Email, - })) + if err := c.ToJSON(&dat); err != nil { + return err } - _, err := c.Db.Exec("update users set email=$1 where id=$2", r.Form.Get("email"), c.User.Id) - if err != nil { - return c.Error500(err) + if dat.Password == "" { + return c.JsonBadRequest("Password can not be empty") } - LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{ - "Email": r.Form.Get("email"), - })) - return nil - }) - - handle.Post("/user/info/password", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if !CheckAuthLevel(1, w, r, c) { - return nil - } - if c.Mode == JSON { - - var dat struct { - Old_Password string `json:"old_password"` - Password string `json:"password"` - Password2 string `json:"password2"` - } - - if err := c.ToJSON(r, &dat); err != nil { - return err - } - - if dat.Password == "" { - return c.JsonBadRequest("Password can not be empty") - } - - if dat.Password != dat.Password2 { - return c.JsonBadRequest("New passwords did not match") - } - - c.Logger.Warn("test", "dat", dat) - - _, login := generateToken(db, c.User.Email, dat.Old_Password) - if !login { - return c.JsonBadRequest("Password is incorrect"); - } - - salt := generateSalt() - hash_password, err := hashPassword(dat.Password, salt) - if err != nil { - return c.Error500(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.SendJSON(c.User.Id) + if dat.Password != dat.Password2 { + return c.JsonBadRequest("New passwords did not match") } - r.ParseForm() - f := r.Form + c.Logger.Warn("test", "dat", dat) - if CheckEmpty(f, "old_password") || CheckEmpty(f, "password") || CheckEmpty(f, "password2") { - return c.Error400(nil, "OldPassword, Password or Password2 not provided!", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{ - "Email": c.User.Email, - "NoUserOrPassword": true, - })) - } - - password := f.Get("password") - password2 := f.Get("password2") - - if password != password2 { - return c.Error400(nil, "New passwords did not match", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{ - "Email": c.User.Email, - "PasswordNotTheSame": true, - })) - } - - _, login := generateToken(db, c.User.Email, f.Get("old_password")) + _, login := generateToken(db, c.User.Email, dat.Old_Password) if !login { - return c.Error400(nil, "Password was incorrect", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{ - "Email": c.User.Email, - "NoUserOrPassword": true, - })) + return c.JsonBadRequest("Password is incorrect") } salt := generateSalt() - hash_password, err := hashPassword(password, salt) + hash_password, err := hashPassword(dat.Password, salt) if err != nil { return c.Error500(err) } @@ -528,17 +348,8 @@ func usersEndpints(db *sql.DB, handle *Handle) { return c.Error500(err) } - LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{ - "email": c.User.Email, - })) - return nil + return c.SendJSON(c.User.Id) }) - handle.Get("/logout", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { - if c.Mode == JSON { - panic("TODO handle json") - } - Logoff(c.Mode, w, r) - return nil - }) + // TODO create function to remove token } diff --git a/views/404.html b/views/404.html deleted file mode 100644 index 126b007..0000000 --- a/views/404.html +++ /dev/null @@ -1,28 +0,0 @@ -{{ define "mainbody" }} -
-
-

- 404 -

- {{ if .NotFoundMessage }} -

- {{ .NotFoundMessage }} -

- {{ if .GoBackLink }} - - {{ end }} - {{ else }} -

- Page Not found -

-
- The page you were looking for does not exist -
- {{ end }} -
-
-{{ end }} diff --git a/views/500.html b/views/500.html deleted file mode 100644 index fec8cfa..0000000 --- a/views/500.html +++ /dev/null @@ -1,6 +0,0 @@ -{{ define "head" }} - - Error Page - -{{ end }} -{{ define "body" }}Heyyyyyy Err {{ end }} diff --git a/views/html.html b/views/html.html deleted file mode 100644 index 97328b6..0000000 --- a/views/html.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ if .Full }} - - {{ block "body" . }} - {{ block "header.html" . }} {{end}} -
- {{ block "mainbody" . }} {{end}} -
- {{end}} - -{{ else }} - {{if .App }} -
- {{ block "mainbody" . }} {{end}} -
- {{ else }} - {{ block "mainbody" . }} {{end}} - {{end}} -{{end}} - diff --git a/views/imgs/upload-icon.png b/views/imgs/upload-icon.png deleted file mode 100644 index 88e26b8..0000000 Binary files a/views/imgs/upload-icon.png and /dev/null differ diff --git a/views/index.html b/views/index.html deleted file mode 100644 index 553fcdc..0000000 --- a/views/index.html +++ /dev/null @@ -1,6 +0,0 @@ -{{ define "title"}} Home : AI Stuff {{ end }} - -{{ define "mainbody" }} - Main Page TODO -{{ end }} - diff --git a/views/js/main.js b/views/js/main.js deleted file mode 100644 index 6c83ca4..0000000 --- a/views/js/main.js +++ /dev/null @@ -1,150 +0,0 @@ -function tabs() { - for (const elm of document.querySelectorAll(".tabs")) { - let count = 0; - let selected = 0; - for (const child of elm.children) { - if (child.tagName == "BUTTON") { - count++; - if (child.classList.contains("selected")) { - selected++; - } - if (!child.getAttribute("data-armed")) { - child.addEventListener("click", () => { - if (!child.classList.contains("selected")) { - for (const elm of child.parentElement.children) { - elm.classList.remove("selected"); - } - child.classList.add("selected"); - - for (const childElm of child.parentElement.children) { - if (childElm.classList.contains("selected")) { - childElm.classList.remove("selected") - } - } - - document.querySelector(`.tabs .content[data-tab="${ - child.getAttribute("data-tab") - }"]`)?.classList.add("selected"); - } - }); - child.setAttribute("data-armed", "true") - } - } - } - - if (selected > 1 || selected == 0) { - for (const child of elm.children) { - child.classList.remove("selected"); - } - for (const child of elm.children) { - if (child.tagName == "BUTTON") { - child.classList.add("selected"); - document.querySelector(`.tabs .content[data-tab="${ - child.getAttribute("data-tab") - }"]`)?.classList.add("selected"); - break; - } - } - - } - } - for (const elm of document.querySelectorAll(".tabs-header")) { - let count = 0; - let selected = 0; - for (const child of elm.children[0].children) { - if (child.tagName == "BUTTON") { - count++; - if (child.classList.contains("selected")) { - selected++; - } - if (!child.getAttribute("data-armed")) { - child.addEventListener("click", () => { - if (!child.classList.contains("selected")) { - for (const elm of child.parentElement.children) { - elm.classList.remove("selected"); - } - child.classList.add("selected"); - - for (const childElm of child.parentElement.parentElement.children) { - if (childElm.classList.contains("selected")) { - childElm.classList.remove("selected") - } - } - - document.querySelector(`.tabs-header .content[data-tab="${ - child.getAttribute("data-tab") - }"]`)?.classList.add("selected"); - } - }); - child.setAttribute("data-armed", "true") - } - } - } - - if (selected > 1 || selected == 0) { - for (const child of elm.children) { - child.classList.remove("selected"); - } - for (const child of elm.children[0].children) { - if (child.tagName == "BUTTON") { - child.classList.add("selected"); - document.querySelector(`.tabs .content[data-tab="${ - child.getAttribute("data-tab") - }"]`)?.classList.add("selected"); - break; - } - } - - } - } -} -function load() { - for (const elm of document.querySelectorAll("form > button")) { - elm.addEventListener('click', (e) => { - e.target.parentElement.classList.add("submitted"); - }); - } - for (const elm of document.querySelectorAll("button.icon")) { - elm.addEventListener('click', (e) => { - e.preventDefault() - const input = document.querySelectorAll('form .file-upload input[type="file"]')[0]; - if (input) { - input.click(); - input.addEventListener('change', (e) => { - const file = input.files[0]; - if (!file) return; - elm.setAttribute("disabled", "true"); - - const spanToReplace = document.querySelector('.file-upload .icon span'); - const imgToReplace = document.querySelector('.file-upload .icon img'); - if (!imgToReplace || !spanToReplace) return; - - if (imgToReplace.getAttribute("replace")) { - const fileReader = new FileReader(); - fileReader.onloadend = () => { - imgToReplace.setAttribute("src", fileReader.result) - elm.classList.add("adapt"); - } - fileReader.readAsDataURL(file) - } - - if (spanToReplace.getAttribute("replace")) { - spanToReplace.innerHTML = spanToReplace.getAttribute("replace") - } - }) - } - }); - } - tabs(); -} -window.onload = load; -htmx.on('htmx:afterSwap', load); -htmx.on('htmx:beforeSwap', (env) => { - if (env.detail.xhr.status === 401) { - window.location = "/login" - } else - // 309 is the code I selected for html to htmlfull change - if (env.detail.xhr.status === 309) { - env.detail.target = htmx.find(".app") - } -}); diff --git a/views/layout.html b/views/layout.html deleted file mode 100644 index 3bca6e3..0000000 --- a/views/layout.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - {{ block "header" . }} - - {{ block "title" . }} - Ai stuff - {{ end }} - - {{ end }} - - - - - - {{ block "other_imports" . }} {{end}} - - - - - {{ block "js_imports" . }} {{end}} - {{ block "body" . }} - {{ block "header.html" . }} {{end}} -
- {{ block "mainbody" . }} {{end}} -
- {{end}} - - diff --git a/views/login.html b/views/login.html deleted file mode 100644 index 2d7e615..0000000 --- a/views/login.html +++ /dev/null @@ -1,34 +0,0 @@ -{{ define "title"}} Home : AI Stuff {{ end }} - -{{ define "mainbody" }} -
-
-

- Login -

-
-
- - -
-
- - - {{if .NoUserOrPassword}} - - Either the password or the email are incorrect - - {{end}} -
- -
- - Register - -
-
-
-{{ end }} - diff --git a/views/models/add.html b/views/models/add.html deleted file mode 100644 index 273a561..0000000 --- a/views/models/add.html +++ /dev/null @@ -1,44 +0,0 @@ -{{define "title"}} - Create New Model : AI Stuff -{{end}} - -{{define "mainbody"}} -
-

- Create new Model -

-
-
- - - {{if .NameFoundError}} - - You already have a model with that name. - - {{end}} -
-
- -
- Please provide a base image.
- This image is a sample of the images that you are going to classfiy. -
-
- - -
-
- - -
-
-{{end}} diff --git a/views/models/delete.html b/views/models/delete.html deleted file mode 100644 index 15fb1f0..0000000 --- a/views/models/delete.html +++ /dev/null @@ -1,12 +0,0 @@ -{{ define "mainbody" }} -
-

- Model {{ .Model.Name }} was deleted! -

-
- - 👈 Go back - -
-
-{{ end }} diff --git a/views/models/edit.html b/views/models/edit.html deleted file mode 100644 index 4eb09b9..0000000 --- a/views/models/edit.html +++ /dev/null @@ -1,556 +0,0 @@ -{{ define "title" }} - Model: {{ .Model.Name }} -{{ end }} - -{{ define "base-model-card" }} -
-

- {{ .Model.Name }} -

-
- -
-
- Image Type: {{ .Model.Color_mode }} -
-
- Image Size: {{ .Model.Width }}x{{ .Model.Height }} -
-
-
-
-{{ end }} - -{{ define "delete-model-card" }} -
-
- - - {{ if .NameDoesNotMatch }} - - Name does not match "{{ .Model.Name }}" - - {{ end }} -
- - -
-{{ end }} - -{{/* Is called from a diffrent endpoint so that it does not matter where this is from :) which means that . can mean what ever I want */}} -{{ define "data-model-create-class-table-table" }} -
- - - - - - - - - - - {{range .List}} - - - - - - - {{end}} - -
- File Path - - Mode - - - - -
- {{ if eq .FilePath "id://" }} - Managed - {{ else }} - {{.FilePath}} - {{ end }} - - {{ if (eq .Mode 2) }} - Testing - {{ else }} - Training - {{ end }} - - {{ if startsWith .FilePath "id://" }} - - {{ else }} - TODO - img {{ .FilePath }} - {{ end }} - - {{ if eq .Status 1 }} - - {{ else }} - - {{ end }} -
-
-
- {{ if gt .Page 0 }} - - {{ end }} -
- -
- {{ .Page }} -
- -
- {{ if .ShowNext }} - - {{ end }} -
-
-
-{{ end }} - -{{ define "data-model-create-class-table" }} - {{ if eq (len .Classes) 0 }} - TODO CREATE TABLE - {{else}} -
- {{/* Handle the case where there are to many buttons */}} -
- {{ range .Classes }} - {{/* TODO Auto Load 1st */}} - - {{ end }} -
- {{ range $i, $a := .Classes }} - {{ if eq $i 0}} -
-
- {{ else }} -
-
- {{ end }} - {{ end }} -
- {{end}} -{{ end }} - -{{ define "data-model-card" }} -
-

- Training data -

- {{ if eq (len .Classes) 0 }} -

- You need to upload data so the model can train. -

-
- - - -
-
- -
- -
- Please provide a file that has the training and testing data
- The file must have 2 folders one with testing images and one with training images.
- Each of the folders will contain the classes of the model. The folders must be the same in testing and training. - The class folders must have the images for the classes. -
-    training\
-        class1\
-            img1.png
-            img2.png
-            img2.png
-            ...
-        class2\
-            img1.png
-            img2.png
-            img2.png
-            ...
-        ...
-    testing\
-        class1\
-            img1.png
-            img2.png
-            img2.png
-            ...
-        class2\
-            img1.png
-            img2.png
-            img2.png
-            ...
-        ...
-
-
-
- - -
-
- - -
-
-
- {{ template "data-model-create-class-table" . }} -
-
- TODO -
-
- {{ else }} -

- You need to upload data so the model can train. -

- {{ if gt .NumberOfInvalidImages 0 }} -

- There are images {{ .NumberOfInvalidImages }} that were loaded that do not have the correct format. These images will be delete when the model trains. -

- {{ end }} -
- - -
- {{ template "data-model-create-class-table" . }} -
-
- TODO -
-
- {{ end }} -
-{{ end }} - -{{ define "train-model-card" }} -
- {{ if .HasData }} - {{ if .NumberOfInvalidImages }} - {{ if gt .NumberOfInvalidImages 0 }} -

- There are images {{ .NumberOfInvalidImages }} that were loaded that do not have the correct format. These images will be delete when the model trains. -

- - {{ end }} - {{ end }} - {{ if .ErrorMessage }} -

- {{ .ErrorMessage }} -

- {{ end }} - {{/* TODO expading mode */}} - -
- - Model Type - -
- -
- - -
-
- {{/* TODO allow more models to be created */}} -
- - -
- {{/* TODO to Change the acc */}} -
- - -
- {{/* TODO allow to chose the base of the model */}} - {{/* TODO allow to change the shape of the model */}} - - {{ else }} -

- To train the model please provide data to the model first -

- {{ end }} -
-{{ end }} - -{{ define "run-model-card" }} -
- -
- -
- Run image through them model and get the result -
-
- - {{ if .ImageError }} - - The provided image was not valid for this model - - {{ end }} - -
-
- - {{ if .NotFound }} -
-

- The class was not found -

-
- {{ else if .Result }} -
-

- Result -

- The image was classified as {{.Result}} -
- {{ end }} -
-{{ end }} - -{{ define "mainbody" }} -
- {{ if (eq .Model.Status 1) }} -
-

- {{ .Model.Name }} -

- -

- Preparing the model -

-
- {{ else if (eq .Model.Status -1) }} -
-

- {{ .Model.Name }} -

- -

- Failed to prepare model -

- -
- - -
-
- {{/* PRE TRAINING STATUS */}} - {{ else if (eq .Model.Status 2) }} - {{ template "base-model-card" . }} - {{ template "data-model-card" . }} - {{ template "train-model-card" . }} - {{ template "delete-model-card" . }} - {{/* FAILED TO PROCCESS THE ZIPFILE */}} - {{ else if (eq .Model.Status -2)}} - {{ template "base-model-card" . }} -
- Failed to proccess the zip file.
- Delete file and proccess again.
-
-
- - -
- {{ template "delete-model-card" . }} - {{/* PROCCESS THE ZIPFILE */}} - {{ else if (eq .Model.Status 3)}} - {{ template "base-model-card" . }} -
- {{/* TODO improve this */}} - Processing zip file... -
- {{/* FAILED TO Prepare for training */}} - {{ else if or (eq .Model.Status -3) (eq .Model.Status -4)}} - {{ template "base-model-card" . }} -
- Failed Prepare for training.
-
- - -
- {{ template "delete-model-card" . }} - {{ else if (eq .Model.Status 4)}} - {{ template "base-model-card" . }} -
- {{/* TODO improve this */}} - Training the model...
- {{/* TODO Add progress status on definitions */}} - - - - - - - - - - - {{ range .Defs}} - - - - - - - {{ if (eq .Status 3) }} - - - - {{ end }} - {{ end }} - -
- Done Progress - - Training Round Progress - - Accuracy - - Status -
- {{.Epoch}} - - {{.EpochProgress}}/20 - - {{.Accuracy}}% - - {{ if (eq .Status 2) }} - - {{ else if (eq .Status 3) }} - - {{ else if (eq .Status 6) }} - - {{ else if (eq .Status -3) }} - - {{ else }} - {{.Status}} - {{ end }} -
- - {{ range $i, $layer := $.Layers }} - {{ if (eq $layer.LayerType 1)}} - - {{ else if (eq $layer.LayerType 4)}} - - - {{ else if (eq $layer.LayerType 3)}} - - - {{ else if (eq $layer.LayerType 2)}} - - - {{ else }} -
- {{ .LayerType }} - {{ .Shape }} -
- {{ end }} - {{ end }} - -
- {{/* TODO Add ability to stop training */}} -
- {{/* Model Ready */}} - {{ else if (eq .Model.Status 5)}} - {{ template "base-model-card" . }} - {{ template "run-model-card" . }} - {{ template "delete-model-card" . }} - {{ else }} -

- Unknown Status of the model. -

- {{ end }} -
-{{ end }} diff --git a/views/models/list.html b/views/models/list.html deleted file mode 100644 index 4c75ba2..0000000 --- a/views/models/list.html +++ /dev/null @@ -1,52 +0,0 @@ -{{define "title"}} - Models : AI Stuff -{{end}} - -{{define "mainbody"}} -
- {{ if (lt 0 (len .List)) }} -
-

My Models

-
- - New - -
- - - - - - - - - {{range .List}} - - - - - {{end}} - -
- Name - - -
- {{.Name}} - - - Edit - -
- {{else}} -

- You don't have any models -

-
- - Create a new model - -
- {{end}} -
-{{end}} diff --git a/views/partials/header.html b/views/partials/header.html deleted file mode 100644 index ed2d1c8..0000000 --- a/views/partials/header.html +++ /dev/null @@ -1,33 +0,0 @@ - diff --git a/views/register.html b/views/register.html deleted file mode 100644 index bfe1403..0000000 --- a/views/register.html +++ /dev/null @@ -1,49 +0,0 @@ -{{define "title"}} - Register : AI Stuff -{{end}} - -{{define "mainbody"}} -
-
-

- Register -

-
-
- - - {{if .UserError}} - - Username already in use - - {{end}} -
-
- - - {{if .EmailError}} - - Email already in use - - {{end}} -
-
- - - {{if .PasswordToLong}} - - Password is to long - - {{end}} -
- -
- - Login - -
-
-
-{{end}} diff --git a/views/styles/main.css b/views/styles/main.css deleted file mode 100644 index 8e2e54b..0000000 --- a/views/styles/main.css +++ /dev/null @@ -1,424 +0,0 @@ -*{box-sizing: border-box;font-family: 'Roboto', sans-serif;} - -:root { - --white: #ffffff; - --grey: #dbdcde; - --light-grey: #fafafa; - --main: #fca311; - --sec: #14213d; - --black: #000000; - --red: 212, 38, 38; - --green: 92, 199, 30; -} - -body { - margin: 0; - padding: 0; -} - -main { - padding: 20px 15vw; -} - -.flex { - display: flex; -} - -.justify-center { - justify-content: center; -} - -.justify-start { - justify-content: start; -} - -.justify-end { - justify-content: end; -} - -.align-center { - align-items: center; -} - -.grow-1 { - flex-grow: 1; -} - -.w100 { - width: 100%; - display: block; -} - -.text-center { - text-align: center; -} - -.simple-link { - color: var(--sec); - text-decoration: none; -} - -.bold { - font-weight: bold; -} - -.bigger { - font-size: 1.1rem; -} - -.danger { - color: red; -} - -/* Generic */ -.button, -button { - border-radius: 10px; - text-align: center; - padding: 3px 6px; - border: none; - box-shadow: 0 2px 8px 1px #66666655; - background: var(--main); - color: var(--black); -} - -.button.padded, -button.padded { - padding: 10px; -} - -.button.danger, -button.danger { - background: rgb(var(--red)); - color: white; - font-weight: bold; -} - -/* Nav bar */ - -nav { - background: #ececec; - margin: 0; - box-shadow: 0 0 8px 1px #888888ef; - height: 60px; -} - -nav ul { - display: flex; - margin: 0; - padding: 20px 40px; -} - -nav ul li { - list-style: none; - padding-left: 10px; -} - -nav ul li:first-child { - padding: 0; -} - -nav ul .expand { - flex-grow: 1 -} - -nav ul li a { - text-decoration: none; - color: black; -} - -/* 404 page */ -.page404 { - display: grid; - place-items: center; - height: calc(100vh - 60px); - text-align: center; -} -.page404 h1 { - font-size: 10em; - margin: 0; -} -.page404 h2 { - font-size: 5em; - margin: 0; - margin-bottom: 0.3em; -} -.page404 div.description { - font-size: 1.5em; -} - -/* Login Page */ -.login-page { - display: grid; - place-items: center; - margin-bottom: 40px; -} -.login-page > div { - width: 40vw; -} -.login-page h1 { - text-align: center; -} - - -/* forms */ - -a { - cursor: pointer; -} - -form { - padding: 30px; - margin: 20px 0; - border-radius: 10px; - box-shadow: 2px 5px 8px 2px #66666655; -} - -.card form { - padding: 0; - border-radius: none; - box-shadow: none; -} - -form label, -form fieldset legend { - display: block; - padding-bottom: 5px; - font-size: 1.2rem; -} - -form input { - border: none; - box-shadow: 0 2px 5px 1px #66666655; - border-radius: 5px; - padding: 10px; - width: 100%; -} - -form input:invalid:focus, -form.submitted input:invalid { - box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2); -} - -form.submitted input:valid { - box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2); -} - -form .spacer { - padding-bottom: 10px; -} - -form fieldset { - padding-bottom: 15px; - border: none; -} - -form fieldset .form-msg { - font-size: 0.9rem; -} - -form fieldset .error { - color: rgb(var(--red)) -} - -form button { - font-size: 1.2rem; - margin-left: 50%; - width: 50%; - transform: translateX(-50%); - padding: 10px; -} - -form .input-radial input[type="radio"] { - width: auto; - box-shadow: none; -} - -form .input-radial label { - display: inline; - font-size: 1rem; -} - -/* Upload files */ - -form fieldset.file-upload input[type="file"] { - height: 1px; - width: 1px; - padding: 0; - box-shadow: none; -} - -form fieldset.file-upload .icon-holder { - padding: 10px; - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - font-size: 1rem; -} - -form fieldset.file-upload .icon-holder .icon { - height: 150px; - width: 150px; - padding: 20px; - border-radius: 10px; - background: none; - transform: none; - margin: 0; - font-size: 1rem; - transition: all 1s; -} - -form fieldset.file-upload .icon-holder .icon.adapt { - width: auto; - height: auto; - max-width: 80%; - max-height: 80%; - min-height: 150px; - min-width: 150px; - padding: 20px; -} - -form fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon, -form.submitted fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon { - box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2); -} - -form.submitted fieldset.file-upload:has(input[type="file"]:valid:focus) .icon{ - box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2); -} - -form fieldset.file-upload .icon-holder .icon img { - display: block; - width: 100%; - height: 80%; - object-fit: contain; - text-align: center; - transition: all 1s; -} - -form fieldset.file-upload .icon-holder .icon span { - display: block; - width: 100%; - padding-top: 10px; - text-align: center; -} - -/* Lists */ - -.list-header { - display: flex; - padding-bottom: 10px; -} - -.list-header h2 { - margin: 0; - padding: 10px 5px; -} - -.list-header .expand { - flex-grow: 1; -} - -.list-header .button, -.list-header button { - padding: 10px 10px; - height: calc(100% - 20px); - margin-top: 5px; -} - -/* Table */ -table { - width: 100%; - box-shadow: 0 2px 8px 1px #66666622; - border-radius: 10px; - border-collapse: collapse; - overflow: hidden; -} - -table thead { - background: #60606022; -} - -table tr td, -table tr th { - border-left: 1px solid #22222244; - padding: 15px; -} - -table tr td:first-child, -table tr th:first-child { - border-left: none; -} - -table tr td button, -table tr td .button { - padding: 5px 10px; - box-shadow: 0 2px 5px 1px #66666655; -} - -.card { - box-shadow: 0 2px 5px 1px #66666655; - padding: 20px; - border-radius: 10px; - margin: 20px 0; -} - -.card h3 { - margin-top: 0; -} - -/* Model stuff */ -.model-card h1 { - margin: 0; - padding-bottom: 10px; -} - -.model-card img { - width: 25%; - height: 100%; - object-fit: contain; -} - -.model-card .second-line { - display: flex; - gap: 20px; -} - -/* Tabs code */ -.tabs { - border-radius: 5px; - overflow: hidden; - display: flex; - flex-wrap: wrap; - box-shadow: 0 2px 8px 1px #66666655; - gap: 0 5px; -} - -.tabs-header .header { - display: flex; - overflow-x: scroll; -} - -.tabs .content { - display: none; - padding: 5px; - width: 100%; -} - -.tabs .tab { - padding: 5px; - background: var(--light-grey); - border-radius: 5px 5px 0 0; - box-shadow: none; - font-size: 1.1rem; -} - -.tabs .tab.selected { - box-shadow: inset 0 2px 8px 1px #66666655; -} - -.tabs .content.selected { - display: block; - box-shadow: 0 2px 2px 1px #66666655; -} diff --git a/views/users/edit.html b/views/users/edit.html deleted file mode 100644 index 70e26d6..0000000 --- a/views/users/edit.html +++ /dev/null @@ -1,50 +0,0 @@ -{{ define "title" }} - User Info -{{ end }} - -{{define "mainbody"}} -
-
-

- User Infomation -

-
-
- - -
- -
-
-
- - - {{if .NoUserOrPassword}} - - Either the password is incorrect - - {{end}} -
-
- - -
-
- - - {{if .PasswordNotTheSame}} - - Either the passwords are not the same - - {{end}} -
- -
-
-
-{{end}} -