feat: moved files into a better position for organization closes #8

This commit is contained in:
Andre Henriques 2023-09-21 16:43:11 +01:00
parent b8278bacf6
commit 1986be1a84
16 changed files with 582 additions and 500 deletions

2
go.mod
View File

@ -1,4 +1,4 @@
module andr3h3nriqu3s.com/m module git.andr3h3nriqu3s.com/andr3/fyp
go 1.20 go 1.20

39
logic/db_types/user.go Normal file
View File

@ -0,0 +1,39 @@
package dbtypes
import (
"database/sql"
"errors"
)
type User struct {
Id string
Username string
Email string
UserType int
}
var ErrUserNotFound = errors.New("User Not found")
func UserFromToken(db *sql.DB, token string) (*User, error) {
row, err := db.Query("select users.id, users.username, users.email, users.user_type from users inner join tokens on tokens.user_id = users.id where tokens.token = $1;", token)
if err != nil {
return nil, err
}
var id string
var username string
var email string
var user_type int
if !row.Next() {
return nil, ErrUserNotFound
}
err = row.Scan(&id, &username, &email, &user_type)
if err != nil {
return nil, err
}
return &User{id, username, email, user_type}, nil
}

183
logic/models/add.go Normal file
View File

@ -0,0 +1,183 @@
package models
import (
"bytes"
"fmt"
"image"
_ "image/png"
"image/color"
"io"
"net/http"
"os"
"path"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func loadBaseImage(handle *Handle, id string) {
// TODO handle more types than png
infile, err := os.Open(path.Join("savedData", id, "baseimage.png"))
if err != nil {
// TODO better logging
fmt.Println(err)
fmt.Printf("Failed to read image for model with id %s\n", id)
modelUpdateStatus(handle, id, -1)
return
}
defer infile.Close()
src, format, err := image.Decode(infile)
if err != nil {
// TODO better logging
fmt.Println(err)
fmt.Printf("Failed to load image for model with id %s\n", id)
modelUpdateStatus(handle, id, -1)
return
}
if format != "png" {
// TODO better logging
fmt.Printf("Found unkown format '%s'\n", format)
panic("Handle diferent files than .png")
}
var model_color string
bounds := src.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
switch src.ColorModel() {
case color.Gray16Model:
fallthrough
case color.GrayModel:
model_color = "greyscale"
default:
fmt.Println("Do not know how to handle this color model")
if src.ColorModel() == color.RGBA64Model {
fmt.Println("Color is rgb")
} else if src.ColorModel() == color.NRGBAModel {
fmt.Println("Color is nrgb")
} else if src.ColorModel() == color.YCbCrModel {
fmt.Println("Color is ycbcr")
} else if src.ColorModel() == color.AlphaModel {
fmt.Println("Color is alpha")
} else if src.ColorModel() == color.CMYKModel {
fmt.Println("Color is cmyk")
} else {
fmt.Println("Other so assuming color")
}
modelUpdateStatus(handle, id, -1)
return
}
// Note: this also updates the status to 2
_, err = handle.Db.Exec("update models set width=$1, height=$2, color_mode=$3, status=$4 where id=$5", width, height, model_color, CONFIRM_PRE_TRAINING, id)
if err != nil {
// TODO better logging
fmt.Println(err)
fmt.Printf("Could not update model\n")
modelUpdateStatus(handle, id, -1)
return
}
}
func handleAdd(handle *Handle) {
handle.GetHTML("/models/add", AnswerTemplate("models/add.html", nil, 1))
// TODO json
handle.Post("/models/add", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
panic("TODO JSON")
}
if !CheckAuthLevel(1, w, r, c) {
return nil
}
read_form, err := r.MultipartReader()
if err != nil {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil))
return nil
}
var name string
var file []byte
for {
part, err_part := read_form.NextPart()
if err_part == io.EOF {
break
} else if err_part != nil {
return &Error{Code: http.StatusBadRequest}
}
if part.FormName() == "name" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
name = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
if name == "" || len(file) == 0 {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil))
return nil
}
row, err := handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id)
if err != nil {
return Error500(err)
}
if row.Next() {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(AnyMap{
"NameFoundError": true,
"Name": name,
}))
return nil
}
_, err = handle.Db.Exec("insert into models (user_id, name) values ($1, $2)", c.User.Id, name)
if err != nil {
return Error500(err)
}
row, err = handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id)
if err != nil {
return Error500(err)
}
if !row.Next() {
return &Error{Code: http.StatusInternalServerError}
}
var id string
err = row.Scan(&id)
if err != nil {
return Error500(err)
}
// TODO mk this path configurable
dir_path := path.Join("savedData", id)
err = os.Mkdir(dir_path, os.ModePerm)
if err != nil {
return Error500(err)
}
f, err := os.Create(path.Join(dir_path, "baseimage.png"))
if err != nil {
return Error500(err)
}
defer f.Close()
f.Write(file)
fmt.Printf("Created model with id %s! Started to proccess image!\n", id)
go loadBaseImage(handle, id)
Redirect("/models/edit?id="+id, c.Mode, w, r)
return nil
})
}

107
logic/models/delete.go Normal file
View File

@ -0,0 +1,107 @@
package models
import (
"fmt"
"net/http"
"os"
"path"
"strconv"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func deleteModel(handle *Handle, id string, w http.ResponseWriter, c *Context, model BaseModel) {
fmt.Printf("Removing model with id: %s\n", id)
_, err := handle.Db.Exec("delete from models where id=$1;", id)
if err != nil {
fmt.Println(err)
panic("TODO handle better deleteModel failed delete database query")
}
model_path := path.Join("./savedData", id)
fmt.Printf("Removing folder of model with id: %s at %s\n", id, model_path)
err = os.RemoveAll(model_path)
if err != nil {
fmt.Println(err)
panic("TODO handle better deleteModel failed to delete folder")
}
if c.Mode == HTML {
// TODO move this to a constant so i don't forget
w.WriteHeader(309)
c.Mode = HTMLFULL
}
LoadBasedOnAnswer(c.Mode, w, "/models/delete.html", c.AddMap(AnyMap{
"Model": model,
}))
}
func handleDelete(handle *Handle) {
handle.Delete("/models/delete", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
panic("TODO handle json on models/delete")
}
f, err := MyParseForm(r)
if err != nil {
return ErrorCode(err, 400, nil)
}
if !CheckId(f, "id") {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
id := f.Get("id")
// TODO handle admin users
rows, err := handle.Db.Query("select name, status from models where id=$1 and user_id=$2;", id, c.User.Id)
if err != nil {
return Error500(err)
}
if !rows.Next() {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
var model BaseModel = BaseModel{}
model.Id = id
err = rows.Scan(&model.Name, &model.Status)
if err != nil {
return Error500(err)
}
switch model.Status {
case FAILED_PREPARING:
deleteModel(handle, id, w, c, model)
return nil
case CONFIRM_PRE_TRAINING:
if CheckEmpty(f, "name") {
// TODO improve result
return ErrorCode(nil, http.StatusBadRequest, nil)
}
name := f.Get("name")
if name != model.Name {
LoadError(w, "/models/edit.html", "delete-model-card", c.AddMap(AnyMap{
"NameDoesNotMatch": true,
"Model": model,
}))
return nil
}
deleteModel(handle, id, w, c, model)
return nil
default:
panic("Do not know how to handle model in status:" + strconv.Itoa(model.Status))
}
})
}

80
logic/models/edit.go Normal file
View File

@ -0,0 +1,80 @@
package models
import (
"fmt"
"net/http"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func handleEdit(handle *Handle) {
handle.GetHTML("/models/edit", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
return nil
}
id, err := GetIdFromUrl(r, "id")
if err != nil {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
// TODO handle admin users
rows, err := handle.Db.Query("select name, status, width, height, color_mode from models where id=$1 and user_id=$2;", id, c.User.Id)
if err != nil {
return Error500(err)
}
if !rows.Next() {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
type rowmodel struct {
Name string
Status int
Id string
Width *int
Height *int
Color_mode *string
}
var model rowmodel = rowmodel{}
model.Id = id
err = rows.Scan(&model.Name, &model.Status, &model.Width, &model.Height, &model.Color_mode)
if err != nil {
return Error500(err)
}
// Handle errors
// All errors will be negative
if model.Status < 0 {
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
}))
return nil
}
switch model.Status {
case PREPARING:
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
}))
case CONFIRM_PRE_TRAINING:
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
}))
default:
fmt.Printf("Unkown Status: %d\n", model.Status)
return Error500(nil)
}
return nil
})
}

12
logic/models/index.go Normal file
View File

@ -0,0 +1,12 @@
package models
import (
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func HandleModels (handle *Handle) {
handleAdd(handle)
handleEdit(handle)
handleDelete(handle)
handleList(handle)
}

47
logic/models/list.go Normal file
View File

@ -0,0 +1,47 @@
package models
import (
"net/http"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func handleList(handle *Handle) {
// TODO json
handle.GetHTML("/models", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
panic("TODO JSON")
}
if !CheckAuthLevel(1, w, r, c) {
return nil
}
rows, err := handle.Db.Query("select id, name from models where user_id=$1;", c.User.Id)
if err != nil {
return Error500(err)
}
type row struct {
Name string
Id string
}
got := []row{}
for rows.Next() {
var r row
err = rows.Scan(&r.Id, &r.Name)
if err != nil {
return Error500(err)
}
got = append(got, r)
}
LoadBasedOnAnswer(c.Mode, w, "/models/list.html", c.AddMap(AnyMap{
"List": got,
}))
return nil
})
}

14
logic/models/types.go Normal file
View File

@ -0,0 +1,14 @@
package models
type BaseModel struct {
Name string
Status int
Id string
}
const (
FAILED_PREPARING = -1
PREPARING = iota
CONFIRM_PRE_TRAINING
)

16
logic/models/utils.go Normal file
View File

@ -0,0 +1,16 @@
package models
import (
"fmt"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func modelUpdateStatus(handle *Handle, id string, status int) {
_, err := handle.Db.Exec("update models set status = $1 where id = $2", status, id)
if err != nil {
fmt.Println("Failed to update model status")
fmt.Println(err)
panic("TODO handle better")
}
}

View File

@ -1,4 +1,4 @@
package main package utils
import ( import (
"database/sql" "database/sql"
@ -12,6 +12,8 @@ import (
"path" "path"
"strings" "strings"
"time" "time"
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
) )
func baseLoadTemplate(base string, path string) (*template.Template, any) { func baseLoadTemplate(base string, path string) (*template.Template, any) {
@ -127,8 +129,8 @@ func LoadError(writer http.ResponseWriter, path string, base string, data AnyMap
type AnyMap = map[string]interface{} type AnyMap = map[string]interface{}
type Error struct { type Error struct {
code int Code int
msg *string Msg *string
data AnyMap data AnyMap
} }
@ -180,7 +182,7 @@ type Handler interface {
} }
type Handle struct { type Handle struct {
db *sql.DB Db *sql.DB
gets []HandleFunc gets []HandleFunc
posts []HandleFunc posts []HandleFunc
deletes []HandleFunc deletes []HandleFunc
@ -189,7 +191,7 @@ type Handle struct {
func decodeBody(r *http.Request) (string, *Error) { func decodeBody(r *http.Request) (string, *Error) {
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err == nil { if err == nil {
return "", &Error{code: http.StatusBadRequest} return "", &Error{Code: http.StatusBadRequest}
} }
return string(body[:]), nil return string(body[:]), nil
@ -198,18 +200,18 @@ func decodeBody(r *http.Request) (string, *Error) {
func handleError(err *Error, w http.ResponseWriter, context *Context) { func handleError(err *Error, w http.ResponseWriter, context *Context) {
if err != nil { if err != nil {
data := context.addMap(err.data) data := context.AddMap(err.data)
w.WriteHeader(err.code) w.WriteHeader(err.Code)
if err.code == http.StatusNotFound { if err.Code == http.StatusNotFound {
LoadBasedOnAnswer(context.Mode, w, "404.html", data) LoadBasedOnAnswer(context.Mode, w, "404.html", data)
return return
} }
if err.code == http.StatusBadRequest { if err.Code == http.StatusBadRequest {
LoadBasedOnAnswer(context.Mode, w, "400.html", data) LoadBasedOnAnswer(context.Mode, w, "400.html", data)
return return
} }
if err.msg != nil { if err.Msg != nil {
w.Write([]byte(*err.msg)) w.Write([]byte(*err.Msg))
} }
} }
} }
@ -260,7 +262,7 @@ func (x *Handle) handleGets(w http.ResponseWriter, r *http.Request, context *Con
if context.Mode != HTMLFULL { if context.Mode != HTMLFULL {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
} }
LoadBasedOnAnswer(context.Mode, w, "404.html", context.addMap(nil)) LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
} }
func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Context) { func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Context) {
@ -273,7 +275,7 @@ func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Co
if context.Mode != HTMLFULL { if context.Mode != HTMLFULL {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
} }
LoadBasedOnAnswer(context.Mode, w, "404.html", context.addMap(nil)) LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
} }
func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context *Context) { func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context *Context) {
@ -286,16 +288,16 @@ func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context *
if context.Mode != HTMLFULL { if context.Mode != HTMLFULL {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
} }
LoadBasedOnAnswer(context.Mode, w, "404.html", context.addMap(nil)) LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
} }
func checkAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Context) bool { func CheckAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Context) bool {
if authLevel > 0 { if authLevel > 0 {
if c.requireAuth(w, r) { if c.requireAuth(w, r) {
logoff(c.Mode, w, r) Logoff(c.Mode, w, r)
return false return false
} }
if c.User.user_type < authLevel { if c.User.UserType < authLevel {
notAuth(c.Mode, w, r) notAuth(c.Mode, w, r)
return false return false
} }
@ -305,21 +307,21 @@ func checkAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Co
func AnswerTemplate(path string, data AnyMap, authLevel int) func(w http.ResponseWriter, r *http.Request, c *Context) *Error { 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 { return func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !checkAuthLevel(authLevel, w, r, c) { if !CheckAuthLevel(authLevel, w, r, c) {
return nil return nil
} }
LoadBasedOnAnswer(c.Mode, w, path, c.addMap(data)) LoadBasedOnAnswer(c.Mode, w, path, c.AddMap(data))
return nil return nil
} }
} }
type Context struct { type Context struct {
Token *string Token *string
User *User User *dbtypes.User
Mode AnswerType Mode AnswerType
} }
func (c Context) addMap(m AnyMap) AnyMap { func (c Context) AddMap(m AnyMap) AnyMap {
if m == nil { if m == nil {
return map[string]interface{}{ return map[string]interface{}{
"Context": c, "Context": c,
@ -356,7 +358,7 @@ func (x Handle) createContext(mode AnswerType, r *http.Request) (*Context, error
}, nil }, nil
} }
user, err := userFromToken(x.db, *token) user, err := dbtypes.UserFromToken(x.Db, *token)
if err != nil { if err != nil {
return nil, errors.Join(err, LogoffError) return nil, errors.Join(err, LogoffError)
} }
@ -365,7 +367,7 @@ func (x Handle) createContext(mode AnswerType, r *http.Request) (*Context, error
} }
// TODO check if I can use http.Redirect // TODO check if I can use http.Redirect
func redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Request) { func Redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", path) w.Header().Set("Location", path)
if mode == JSON { if mode == JSON {
w.WriteHeader(http.StatusSeeOther) w.WriteHeader(http.StatusSeeOther)
@ -380,7 +382,7 @@ func redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Reque
} }
} }
func logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) { func Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) {
// Delete cookie // Delete cookie
cookie := &http.Cookie{ cookie := &http.Cookie{
Name: "auth", Name: "auth",
@ -388,7 +390,7 @@ func logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) {
Expires: time.Unix(0, 0), Expires: time.Unix(0, 0),
} }
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
redirect("/login", mode, w, r) Redirect("/login", mode, w, r)
} }
func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) { func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) {
@ -405,7 +407,7 @@ func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) {
} }
} }
func (x Handle) staticFiles(pathTest string, fileType string, contentType string) { func (x Handle) StaticFiles(pathTest string, fileType string, contentType string) {
http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) { http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path[len(pathTest):] path := r.URL.Path[len(pathTest):]
@ -428,7 +430,7 @@ func (x Handle) staticFiles(pathTest string, fileType string, contentType string
}) })
} }
func errorCode(err error, code int, data AnyMap) *Error { func ErrorCode(err error, code int, data AnyMap) *Error {
// TODO Improve Logging // TODO Improve Logging
if err != nil { if err != nil {
fmt.Printf("Something went wrong returning with: %d\n.Err:\n", code) fmt.Printf("Something went wrong returning with: %d\n.Err:\n", code)
@ -437,11 +439,11 @@ func errorCode(err error, code int, data AnyMap) *Error {
return &Error{code, nil, data} return &Error{code, nil, data}
} }
func error500(err error) *Error { func Error500(err error) *Error {
return errorCode(err, http.StatusInternalServerError, nil) return ErrorCode(err, http.StatusInternalServerError, nil)
} }
func (x Handle) readFiles(pathTest string, baseFilePath string, fileType string, contentType string) { func (x Handle) ReadFiles(pathTest string, baseFilePath string, fileType string, contentType string) {
http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) { http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) {
user_path := r.URL.Path[len(pathTest):] user_path := r.URL.Path[len(pathTest):]
@ -487,7 +489,7 @@ func NewHandler(db *sql.DB) *Handle {
//Login state //Login state
context, err := x.createContext(ans, r) context, err := x.createContext(ans, r)
if err != nil { if err != nil {
logoff(ans, w, r) Logoff(ans, w, r)
return return
} }

View File

@ -1,4 +1,4 @@
package main package utils
import ( import (
"errors" "errors"
@ -10,20 +10,20 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
func checkEmpty(f url.Values, path string) bool { func CheckEmpty(f url.Values, path string) bool {
return !f.Has(path) || f.Get(path) == "" return !f.Has(path) || f.Get(path) == ""
} }
func checkId(f url.Values, path string) bool { func CheckId(f url.Values, path string) bool {
return !checkEmpty(f, path) && isValidUUID(f.Get(path)) return !CheckEmpty(f, path) && IsValidUUID(f.Get(path))
} }
func isValidUUID(u string) bool { func IsValidUUID(u string) bool {
_, err := uuid.Parse(u) _, err := uuid.Parse(u)
return err == nil return err == nil
} }
func getIdFromUrl(r *http.Request, target string) (string, error) { func GetIdFromUrl(r *http.Request, target string) (string, error) {
if !r.URL.Query().Has(target) { if !r.URL.Query().Has(target) {
return "", errors.New("Query does not have " + target) return "", errors.New("Query does not have " + target)
} }
@ -33,7 +33,7 @@ func getIdFromUrl(r *http.Request, target string) (string, error) {
return "", errors.New("Query is empty for " + target) return "", errors.New("Query is empty for " + target)
} }
if !isValidUUID(id) { if !IsValidUUID(id) {
return "", errors.New("Value of query is not a valid uuid for " + target) return "", errors.New("Value of query is not a valid uuid for " + target)
} }

13
main.go
View File

@ -5,6 +5,9 @@ import (
"fmt" "fmt"
_ "github.com/lib/pq" _ "github.com/lib/pq"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
) )
const ( const (
@ -33,15 +36,15 @@ func main() {
// TODO Handle this in other way // TODO Handle this in other way
handle.staticFiles("/styles/", ".css", "text/css"); handle.StaticFiles("/styles/", ".css", "text/css");
handle.staticFiles("/js/", ".js", "text/javascript"); handle.StaticFiles("/js/", ".js", "text/javascript");
handle.readFiles("/imgs/", "views", ".png", "image/png;"); handle.ReadFiles("/imgs/", "views", ".png", "image/png;");
handle.readFiles("/savedData/", ".", ".png", "image/png;"); handle.ReadFiles("/savedData/", ".", ".png", "image/png;");
handle.GetHTML("/", AnswerTemplate("index.html", nil, 0)) handle.GetHTML("/", AnswerTemplate("index.html", nil, 0))
usersEndpints(db, handle) usersEndpints(db, handle)
handleModelsEndpoints(handle) HandleModels(handle)
handle.Startup() handle.Startup()
} }

405
models.go
View File

@ -1,405 +0,0 @@
package main
import (
"bytes"
"fmt"
"image"
"image/color"
_ "image/png"
"io"
"net/http"
"os"
"path"
"strconv"
)
const (
FAILED_PREPARING = -1
PREPARING = iota
CONFIRM_PRE_TRAINING
)
type BaseModel struct {
Name string
Status int
Id string
}
func modelUpdateStatus(handle *Handle, id string, status int) {
_, err := handle.db.Exec("update models set status = $1 where id = $2", status, id)
if err != nil {
fmt.Println("Failed to update model status")
fmt.Println(err)
panic("TODO handle better")
}
}
func loadBaseImage(handle *Handle, id string) {
// TODO handle more types than png
infile, err := os.Open(path.Join("savedData", id, "baseimage.png"))
if err != nil {
// TODO better logging
fmt.Println(err)
fmt.Printf("Failed to read image for model with id %s\n", id)
modelUpdateStatus(handle, id, -1)
return
}
defer infile.Close()
src, format, err := image.Decode(infile)
if err != nil {
// TODO better logging
fmt.Println(err)
fmt.Printf("Failed to load image for model with id %s\n", id)
modelUpdateStatus(handle, id, -1)
return
}
if format != "png" {
// TODO better logging
fmt.Printf("Found unkown format '%s'\n", format)
panic("Handle diferent files than .png")
}
var model_color string
bounds := src.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
switch src.ColorModel() {
case color.Gray16Model:
fallthrough
case color.GrayModel:
model_color = "greyscale"
default:
fmt.Println("Do not know how to handle this color model")
if src.ColorModel() == color.RGBA64Model {
fmt.Println("Color is rgb")
} else if src.ColorModel() == color.NRGBAModel {
fmt.Println("Color is nrgb")
} else if src.ColorModel() == color.YCbCrModel {
fmt.Println("Color is ycbcr")
} else if src.ColorModel() == color.AlphaModel {
fmt.Println("Color is alpha")
} else if src.ColorModel() == color.CMYKModel {
fmt.Println("Color is cmyk")
} else {
fmt.Println("Other so assuming color")
}
modelUpdateStatus(handle, id, -1)
return
}
// Note: this also updates the status to 2
_, err = handle.db.Exec("update models set width=$1, height=$2, color_mode=$3, status=$4 where id=$5", width, height, model_color, CONFIRM_PRE_TRAINING, id)
if err != nil {
// TODO better logging
fmt.Println(err)
fmt.Printf("Could not update model\n")
modelUpdateStatus(handle, id, -1)
return
}
}
func deleteModel(handle *Handle, id string, w http.ResponseWriter, c *Context, model BaseModel) {
fmt.Printf("Removing model with id: %s\n", id)
_, err := handle.db.Exec("delete from models where id=$1;", id)
if err != nil {
fmt.Println(err)
panic("TODO handle better deleteModel failed delete database query")
}
model_path := path.Join("./savedData", id)
fmt.Printf("Removing folder of model with id: %s at %s\n", id, model_path)
err = os.RemoveAll(model_path)
if err != nil {
fmt.Println(err)
panic("TODO handle better deleteModel failed to delete folder")
}
if c.Mode == HTML {
// TODO move this to a constant so i don't forget
w.WriteHeader(309)
c.Mode = HTMLFULL
}
LoadBasedOnAnswer(c.Mode, w, "/models/delete.html", c.addMap(AnyMap{
"Model": model,
}))
}
func handleModelsEndpoints(handle *Handle) {
// TODO json
handle.GetHTML("/models", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
panic("TODO JSON")
}
if !checkAuthLevel(1, w, r, c) {
return nil
}
rows, err := handle.db.Query("select id, name from models where user_id=$1;", c.User.id)
if err != nil {
return error500(err)
}
type row struct {
Name string
Id string
}
got := []row{}
for rows.Next() {
var r row
err = rows.Scan(&r.Id, &r.Name)
if err != nil {
return error500(err)
}
got = append(got, r)
}
LoadBasedOnAnswer(c.Mode, w, "/models/list.html", c.addMap(AnyMap{
"List": got,
}))
return nil
})
handle.GetHTML("/models/add", AnswerTemplate("models/add.html", nil, 1))
// TODO json
handle.Post("/models/add", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
panic("TODO JSON")
}
if !checkAuthLevel(1, w, r, c) {
return nil
}
read_form, err := r.MultipartReader()
if err != nil {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.addMap(nil))
return nil
}
var name string
var file []byte
for {
part, err_part := read_form.NextPart()
if err_part == io.EOF {
break
} else if err_part != nil {
return &Error{code: http.StatusBadRequest}
}
if part.FormName() == "name" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
name = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
if name == "" || len(file) == 0 {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.addMap(nil))
return nil
}
row, err := handle.db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.id)
if err != nil {
return error500(err)
}
if row.Next() {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.addMap(AnyMap{
"NameFoundError": true,
"Name": name,
}))
return nil
}
_, err = handle.db.Exec("insert into models (user_id, name) values ($1, $2)", c.User.id, name)
if err != nil {
return error500(err)
}
row, err = handle.db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.id)
if err != nil {
return error500(err)
}
if !row.Next() {
return &Error{code: http.StatusInternalServerError}
}
var id string
err = row.Scan(&id)
if err != nil {
return error500(err)
}
// TODO mk this path configurable
dir_path := path.Join("savedData", id)
err = os.Mkdir(dir_path, os.ModePerm)
if err != nil {
return error500(err)
}
f, err := os.Create(path.Join(dir_path, "baseimage.png"))
if err != nil {
return error500(err)
}
defer f.Close()
f.Write(file)
fmt.Printf("Created model with id %s! Started to proccess image!\n", id)
go loadBaseImage(handle, id)
redirect("/models/edit?id="+id, c.Mode, w, r)
return nil
})
handle.GetHTML("/models/edit", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !checkAuthLevel(1, w, r, c) {
return nil
}
id, err := getIdFromUrl(r, "id")
if err != nil {
return errorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
// TODO handle admin users
rows, err := handle.db.Query("select name, status, width, height, color_mode from models where id=$1 and user_id=$2;", id, c.User.id)
if err != nil {
return error500(err)
}
if !rows.Next() {
return errorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
type rowmodel struct {
Name string
Status int
Id string
Width *int
Height *int
Color_mode *string
}
var model rowmodel = rowmodel{}
model.Id = id
err = rows.Scan(&model.Name, &model.Status, &model.Width, &model.Height, &model.Color_mode)
if err != nil {
return error500(err)
}
// Handle errors
// All errors will be negative
if model.Status < 0 {
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.addMap(AnyMap{
"Model": model,
}))
return nil
}
switch model.Status {
case PREPARING:
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.addMap(AnyMap{
"Model": model,
}))
case CONFIRM_PRE_TRAINING:
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.addMap(AnyMap{
"Model": model,
}))
default:
fmt.Printf("Unkown Status: %d\n", model.Status)
return error500(nil)
}
return nil
})
handle.Delete("/models/delete", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
panic("TODO handle json on models/delete")
}
f, err := MyParseForm(r)
if err != nil {
return errorCode(err, 400, nil)
}
if !checkId(f, "id") {
return errorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
id := f.Get("id")
// TODO handle admin users
rows, err := handle.db.Query("select name, status from models where id=$1 and user_id=$2;", id, c.User.id)
if err != nil {
return error500(err)
}
if !rows.Next() {
return errorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
var model BaseModel = BaseModel{}
model.Id = id
err = rows.Scan(&model.Name, &model.Status)
if err != nil {
return error500(err)
}
switch model.Status {
case FAILED_PREPARING:
deleteModel(handle, id, w, c, model)
return nil
case CONFIRM_PRE_TRAINING:
if checkEmpty(f, "name") {
// TODO improve result
return errorCode(nil, http.StatusBadRequest, nil)
}
name := f.Get("name")
if name != model.Name {
LoadError(w, "/models/edit.html", "delete-model-card", c.addMap(AnyMap{
"NameDoesNotMatch": true,
"Model": model,
}))
return nil
}
deleteModel(handle, id, w, c, model)
return nil
default:
panic("Do not know how to handle model in status:" + strconv.Itoa(model.Status))
}
})
}

View File

@ -1,4 +1,4 @@
-- drop table if exists model_defenitions -- drop table if exists model_defenitions;
-- drop table if exists models; -- drop table if exists models;
create table if not exists models ( create table if not exists models (
id uuid primary key default gen_random_uuid(), id uuid primary key default gen_random_uuid(),
@ -14,7 +14,9 @@ create table if not exists models (
color_mode varchar (20) color_mode varchar (20)
); );
-- create table model_defenitions ( -- drop table if exists model_classes;
-- id uuid primary key default gen_random_uuid(), create table if not exists model_classes (
-- model_id uuid references models (id) not null, id uuid primary key default gen_random_uuid(),
-- ) model_id uuid references models (id) not null,
name varchar (70) not null
);

View File

@ -4,48 +4,16 @@ import (
"crypto/rand" "crypto/rand"
"database/sql" "database/sql"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"time" "time"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
) )
type User struct {
id string
username string
email string
user_type int
}
var ErrUserNotFound = errors.New("User Not found")
func userFromToken(db *sql.DB, token string) (*User, error) {
row, err := db.Query("select users.id, users.username, users.email, users.user_type from users inner join tokens on tokens.user_id = users.id where tokens.token = $1;", token)
if err != nil {
return nil, err
}
var id string
var username string
var email string
var user_type int
if !row.Next() {
return nil, ErrUserNotFound
}
err = row.Scan(&id, &username, &email, &user_type)
if err != nil {
return nil, err
}
return &User{id, username, email, user_type}, nil
}
func generateSalt() string { func generateSalt() string {
salt := make([]byte, 4) salt := make([]byte, 4)
_, err := io.ReadFull(rand.Reader, salt) _, err := io.ReadFull(rand.Reader, salt)
@ -112,14 +80,14 @@ func usersEndpints(db *sql.DB, handle *Handle) {
handle.Post("/login", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { handle.Post("/login", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON { if c.Mode == JSON {
fmt.Println("Handle JSON") fmt.Println("Handle JSON")
return &Error{code: 404} return &Error{Code: 404}
} }
r.ParseForm() r.ParseForm()
f := r.Form f := r.Form
if checkEmpty(f, "email") || checkEmpty(f, "password") { if CheckEmpty(f, "email") || CheckEmpty(f, "password") {
LoadBasedOnAnswer(c.Mode, w, "login.html", c.addMap(AnyMap{ LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{
"Submited": true, "Submited": true,
})) }))
return nil return nil
@ -132,7 +100,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
expiration := time.Now().Add(24 * time.Hour) expiration := time.Now().Add(24 * time.Hour)
token, login := generateToken(db, email, password) token, login := generateToken(db, email, password)
if !login { if !login {
LoadBasedOnAnswer(c.Mode, w, "login.html", c.addMap(AnyMap{ LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{
"Submited": true, "Submited": true,
"NoUserOrPassword": true, "NoUserOrPassword": true,
"Email": email, "Email": email,
@ -151,13 +119,13 @@ func usersEndpints(db *sql.DB, handle *Handle) {
handle.GetHTML("/register", AnswerTemplate("register.html", nil, 0)) handle.GetHTML("/register", AnswerTemplate("register.html", nil, 0))
handle.Post("/register", func(w http.ResponseWriter, r *http.Request, c *Context) *Error { handle.Post("/register", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON { if c.Mode == JSON {
return &Error{code: http.StatusNotFound} return &Error{Code: http.StatusNotFound}
} }
r.ParseForm() r.ParseForm()
f := r.Form f := r.Form
if checkEmpty(f, "email") || checkEmpty(f, "password") || checkEmpty(f, "username") { if CheckEmpty(f, "email") || CheckEmpty(f, "password") || CheckEmpty(f, "username") {
LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{ LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{
"Submited": true, "Submited": true,
}) })
@ -205,7 +173,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
hash_password, err := hashPassword(password, salt) hash_password, err := hashPassword(password, salt)
if err != nil { if err != nil {
return &Error{ return &Error{
code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
} }
} }
@ -213,7 +181,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
if err != nil { if err != nil {
return &Error{ return &Error{
code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
} }
} }
@ -224,8 +192,8 @@ func usersEndpints(db *sql.DB, handle *Handle) {
if !login { if !login {
msg := "Login failed" msg := "Login failed"
return &Error{ return &Error{
code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
msg: &msg, Msg: &msg,
} }
} }
@ -240,7 +208,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
if c.Mode == JSON { if c.Mode == JSON {
panic("TODO handle json") panic("TODO handle json")
} }
logoff(c.Mode, w, r) Logoff(c.Mode, w, r)
return nil return nil
}) })
} }

View File

@ -42,6 +42,18 @@
</form> </form>
{{ end }} {{ end }}
{{ define "data-model-card" }}
<form hx-delete="/models/train" hx-headers='{"REQUEST-TYPE": "html"}' hx-swap="outerHTML" {{ if .Error }} class="submitted" {{end}} >
data menu
</form>
{{ end }}
{{ define "train-model-card" }}
<form hx-delete="/models/train" hx-headers='{"REQUEST-TYPE": "html"}' hx-swap="outerHTML" {{ if .Error }} class="submitted" {{end}} >
tain menu
</form>
{{ end }}
{{ define "mainbody" }} {{ define "mainbody" }}
<main> <main>
{{ if (eq .Model.Status 1) }} {{ if (eq .Model.Status 1) }}
@ -73,6 +85,8 @@
</div> </div>
{{ else if (eq .Model.Status 2) }} {{ else if (eq .Model.Status 2) }}
{{ template "base-model-card" . }} {{ template "base-model-card" . }}
{{ template "data-model-card" . }}
{{ template "train-model-card" . }}
{{ template "delete-model-card" . }} {{ template "delete-model-card" . }}
{{ else }} {{ else }}
<h1> <h1>