add ability to remove user and add task depndencies closes #69
This commit is contained in:
parent
00ddb91a22
commit
8ece8306dd
3
go.mod
3
go.mod
@ -21,6 +21,9 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.19.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
|
13
go.sum
13
go.sum
@ -10,6 +10,7 @@ github.com/charmbracelet/log v0.2.5 h1:1yVvyKCKVV639RR4LIq1iy1Cs1AKxuNO+Hx2LJtk7
|
||||
github.com/charmbracelet/log v0.2.5/go.mod h1:nQGK8tvc4pS9cvVEH/pWJiZ50eUq1aoXUOjGpXvdD0k=
|
||||
github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
|
||||
github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/galeone/tensorflow/tensorflow/go v0.0.0-20221023090153-6b7fa0680c3e h1:9+2AEFZymTi25FIIcDwuzcOPH04z9+fV6XeLiGORPDI=
|
||||
@ -34,6 +35,12 @@ github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
@ -53,11 +60,15 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
@ -87,3 +98,5 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -68,35 +68,25 @@ type BaseModel struct {
|
||||
Status int
|
||||
Id string
|
||||
|
||||
ModelType int
|
||||
ImageMode int
|
||||
ModelType int `db:"model_type"`
|
||||
ImageModeRaw string `db:"color_mode"`
|
||||
ImageMode int `db:"0"`
|
||||
Width int
|
||||
Height int
|
||||
Format string
|
||||
CanTrain int `db:"can_train"`
|
||||
}
|
||||
|
||||
var ModelNotFoundError = errors.New("Model not found error")
|
||||
|
||||
func GetBaseModel(db *sql.DB, id string) (base *BaseModel, err error) {
|
||||
rows, err := db.Query("select name, status, id, width, height, color_mode, format, model_type from models where id=$1;", id)
|
||||
var model BaseModel
|
||||
err = GetDBOnce(db, &model, "models where id=$1", id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return nil, ModelNotFoundError
|
||||
}
|
||||
|
||||
base = &BaseModel{}
|
||||
var colorMode string
|
||||
err = rows.Scan(&base.Name, &base.Status, &base.Id, &base.Width, &base.Height, &colorMode, &base.Format, &base.ModelType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.ImageMode = StringToImageMode(colorMode)
|
||||
return
|
||||
model.ImageMode = StringToImageMode(model.ImageModeRaw)
|
||||
return &model, nil
|
||||
}
|
||||
|
||||
func (m BaseModel) CanEval() bool {
|
||||
|
@ -7,7 +7,8 @@ import (
|
||||
type UserType int
|
||||
|
||||
const (
|
||||
User_Not_Auth UserType = iota
|
||||
User_Deleted UserType = iota - 1
|
||||
User_Not_Auth
|
||||
User_Normal
|
||||
User_Admin
|
||||
)
|
||||
|
@ -439,6 +439,8 @@ func handleDataUpload(handle *Handle) {
|
||||
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
||||
} else if err != nil {
|
||||
return c.Error500(err)
|
||||
} else if model.CanTrain == 0 {
|
||||
return c.JsonBadRequest("Model can not be trained!")
|
||||
}
|
||||
|
||||
// TODO mk this path configurable
|
||||
@ -468,6 +470,10 @@ func handleDataUpload(handle *Handle) {
|
||||
model, err := GetBaseModel(c.Db, obj.Id)
|
||||
if err == ModelNotFoundError {
|
||||
return c.JsonBadRequest("Model not found")
|
||||
} else if err != nil {
|
||||
return c.E500M("Failed to get model information", err)
|
||||
} else if model.CanTrain == 0 {
|
||||
return c.JsonBadRequest("Model can not be trained!")
|
||||
}
|
||||
|
||||
if model.ModelType != 2 && model.Status != CONFIRM_PRE_TRAINING || model.ModelType == 2 && model.Status != CONFIRM_PRE_TRAINING && model.Status != READY {
|
||||
@ -516,6 +522,8 @@ func handleDataUpload(handle *Handle) {
|
||||
return c.JsonBadRequest("Could not find the model")
|
||||
} else if err != nil {
|
||||
return c.E500M("Error getting model information", err)
|
||||
} else if model.CanTrain == 0 {
|
||||
return c.JsonBadRequest("Model can not be trained!")
|
||||
}
|
||||
|
||||
// TODO make this work for zip files as well
|
||||
@ -622,6 +630,8 @@ func handleDataUpload(handle *Handle) {
|
||||
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
||||
} else if err != nil {
|
||||
return c.Error500(err)
|
||||
} else if model.CanTrain == 0 {
|
||||
return c.JsonBadRequest("Model can not be trained!")
|
||||
}
|
||||
|
||||
// TODO work in allowing the model to add new in the pre ready moment
|
||||
|
@ -9,20 +9,27 @@ import (
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||
)
|
||||
|
||||
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)
|
||||
func DeleteModel(c BasePack, id string) (err error) {
|
||||
c.GetLogger().Warnf("Removing model with id: %s", id)
|
||||
_, err = c.GetDb().Exec("delete from models where id=$1;", id)
|
||||
if err != nil {
|
||||
return c.E500M("Failed to delete models", err)
|
||||
return
|
||||
}
|
||||
|
||||
model_path := path.Join("./savedData", id)
|
||||
c.Logger.Warnf("Removing folder of model with id: %s at %s", id, model_path)
|
||||
c.GetLogger().Warnf("Removing folder of model with id: %s at %s", id, model_path)
|
||||
err = os.RemoveAll(model_path)
|
||||
if err != nil {
|
||||
return c.E500M("Failed to remove data", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func deleteModelJSON(c *Context, id string) *Error {
|
||||
err := DeleteModel(c, id)
|
||||
if err != nil {
|
||||
return c.E500M("Failed to delete models", err)
|
||||
}
|
||||
return c.SendJSON(id)
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ func runModelExp(base BasePack, model *BaseModel, def_id string, inputImage *tf.
|
||||
func ClassifyTask(base BasePack, task Task) (err error) {
|
||||
task.UpdateStatusLog(base, TASK_RUNNING, "Runner running task")
|
||||
|
||||
model, err := GetBaseModel(base.GetDb(), task.ModelId)
|
||||
model, err := GetBaseModel(base.GetDb(), *task.ModelId)
|
||||
if err != nil {
|
||||
task.UpdateStatusLog(base, TASK_FAILED_RUNNING, "Failed to obtain the model")
|
||||
return err
|
||||
|
@ -1625,7 +1625,7 @@ func trainExpandable(c *Context, model *BaseModel) {
|
||||
func RunTaskTrain(b BasePack, task Task) (err error) {
|
||||
l := b.GetLogger()
|
||||
|
||||
model, err := GetBaseModel(b.GetDb(), task.ModelId)
|
||||
model, err := GetBaseModel(b.GetDb(), *task.ModelId)
|
||||
if err != nil {
|
||||
task.UpdateStatusLog(b, TASK_FAILED_RUNNING, "Failed to get model information")
|
||||
l.Error("Failed to get model information", "err", err)
|
||||
@ -1683,7 +1683,7 @@ func RunTaskTrain(b BasePack, task Task) (err error) {
|
||||
}
|
||||
|
||||
func RunTaskRetrain(b BasePack, task Task) (err error) {
|
||||
model, err := GetBaseModel(b.GetDb(), task.ModelId)
|
||||
model, err := GetBaseModel(b.GetDb(), *task.ModelId)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if model.Status != READY_RETRAIN {
|
||||
@ -1759,6 +1759,8 @@ func handleTrain(handle *Handle) {
|
||||
return c.JsonBadRequest("Model not found")
|
||||
} else if err != nil {
|
||||
return c.E500M("Failed to get model information", err)
|
||||
} else if model.CanTrain == 0 {
|
||||
return c.JsonBadRequest("Model can not be trained!")
|
||||
}
|
||||
|
||||
if model.Status != CONFIRM_PRE_TRAINING {
|
||||
@ -1813,6 +1815,8 @@ func handleTrain(handle *Handle) {
|
||||
return c.E500M("Faield to get model", err)
|
||||
} else if model.Status != READY && model.Status != READY_RETRAIN_FAILED && model.Status != READY_ALTERATION_FAILED {
|
||||
return c.JsonBadRequest("Model in invalid status for re-training")
|
||||
} else if model.CanTrain == 0 {
|
||||
return c.JsonBadRequest("Model can not be trained!")
|
||||
}
|
||||
|
||||
c.Logger.Info("Expanding definitions for models", "id", model.Id)
|
||||
|
@ -55,6 +55,8 @@ func handleUpload(handler *Handle) {
|
||||
model, err := GetBaseModel(c.Db, requestData.ModelId)
|
||||
if err != nil {
|
||||
return c.Error500(err)
|
||||
} else if model.CanTrain == 0 {
|
||||
return c.JsonBadRequest("Model can not be trained!")
|
||||
}
|
||||
|
||||
switch model.Status {
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/train"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/utils"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/users"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||
)
|
||||
|
||||
@ -71,6 +72,14 @@ func runner(config Config, db *sql.DB, task_channel chan Task, index int, back_c
|
||||
logger.Error("Failed to tain the model", "error", err)
|
||||
}
|
||||
|
||||
back_channel <- index
|
||||
continue
|
||||
} else if task.TaskType == int(TASK_TYPE_DELETE_USER) {
|
||||
logger.Warn("User deleting Task")
|
||||
if err = DeleteUser(base, task); err != nil {
|
||||
logger.Error("Failed to tain the model", "error", err)
|
||||
}
|
||||
|
||||
back_channel <- index
|
||||
continue
|
||||
}
|
||||
@ -168,16 +177,23 @@ func RunnerOrchestrator(db *sql.DB, config Config) {
|
||||
}
|
||||
|
||||
if task_to_dispatch == nil {
|
||||
var task Task
|
||||
err := GetDBOnce(db, &task, "tasks where status=$1 limit 1", TASK_TODO)
|
||||
var task TaskT
|
||||
err := GetDBOnce(db, &task, "tasks as t "+
|
||||
// Get depenencies
|
||||
"left join tasks_dependencies as td on t.id=td.main_id "+
|
||||
// Get the task that the depencey resolves to
|
||||
"left join tasks as t2 on t2.id=td.dependent_id "+
|
||||
"where t.status=1 "+
|
||||
"group by t.id having count(td.id) filter (where t2.status in (0,1,2,3)) = 0;")
|
||||
if err != NotFoundError && err != nil {
|
||||
log.Error("Failed to get tasks from db")
|
||||
log.Error("Failed to get tasks from db", "err", err)
|
||||
continue
|
||||
}
|
||||
if err == NotFoundError {
|
||||
task_to_dispatch = nil
|
||||
} else {
|
||||
task_to_dispatch = &task
|
||||
temp := Task(task)
|
||||
task_to_dispatch = &temp
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
type Task struct {
|
||||
Id string `db:"id" json:"id"`
|
||||
UserId string `db:"user_id" json:"user_id"`
|
||||
ModelId string `db:"model_id" json:"model_id"`
|
||||
ModelId *string `db:"model_id" json:"model_id"`
|
||||
Status int `db:"status" json:"status"`
|
||||
StatusMessage string `db:"status_message" json:"status_message"`
|
||||
UserConfirmed int `db:"user_confirmed" json:"user_confirmed"`
|
||||
@ -21,6 +21,27 @@ type Task struct {
|
||||
CreatedOn time.Time `db:"created_on" json:"created"`
|
||||
}
|
||||
|
||||
// Find better way todo this
|
||||
type TaskT struct {
|
||||
Id string `db:"t.id" json:"id"`
|
||||
UserId string `db:"t.user_id" json:"user_id"`
|
||||
ModelId *string `db:"t.model_id" json:"model_id"`
|
||||
Status int `db:"t.status" json:"status"`
|
||||
StatusMessage string `db:"t.status_message" json:"status_message"`
|
||||
UserConfirmed int `db:"t.user_confirmed" json:"user_confirmed"`
|
||||
Compacted int `db:"t.compacted" json:"compacted"`
|
||||
TaskType int `db:"t.task_type" json:"type"`
|
||||
ExtraTaskInfo string `db:"t.extra_task_info" json:"extra_task_info"`
|
||||
Result string `db:"t.result" json:"result"`
|
||||
CreatedOn time.Time `db:"t.created_on" json:"created"`
|
||||
}
|
||||
|
||||
type TaskDependents struct {
|
||||
Id string `db:"id" json:"id"`
|
||||
MainId string `db:"main_id" json:"main_id"`
|
||||
DependentId string `db:"dependent_id" json:"dependent_id"`
|
||||
}
|
||||
|
||||
type TaskStatus int
|
||||
|
||||
const (
|
||||
@ -39,6 +60,7 @@ const (
|
||||
TASK_TYPE_CLASSIFICATION TaskType = 1 + iota
|
||||
TASK_TYPE_TRAINING
|
||||
TASK_TYPE_RETRAINING
|
||||
TASK_TYPE_DELETE_USER
|
||||
)
|
||||
|
||||
type TaskAgreement int
|
||||
@ -82,3 +104,15 @@ func (t Task) SetResult(base BasePack, result any) (err error) {
|
||||
_, err = base.GetDb().Exec("update tasks set result=$1 where id=$2", text, t.Id)
|
||||
return
|
||||
}
|
||||
|
||||
func (t Task) Depend(base BasePack, depend_id string) (err error) {
|
||||
var dependency = struct {
|
||||
Main string `db:"main_id"`
|
||||
Dependent string `db:"dependent_id"`
|
||||
}{
|
||||
Main: t.Id,
|
||||
Dependent: depend_id,
|
||||
}
|
||||
_, err = InsertReturnId(base.GetDb(), &dependency, "tasks_dependencies", "id")
|
||||
return
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package users
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@ -12,6 +12,8 @@ import (
|
||||
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
||||
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/utils"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||
)
|
||||
|
||||
@ -80,7 +82,31 @@ func generateToken(db *sql.DB, email string, password string, name string) (stri
|
||||
return token, true
|
||||
}
|
||||
|
||||
func usersEndpints(db *sql.DB, handle *Handle) {
|
||||
func DeleteUser(base BasePack, task Task) (err error) {
|
||||
ids, err := GetDbMultitple[JustId](base.GetDb(), "models where user_id=$1;", task.ExtraTaskInfo)
|
||||
if err != nil {
|
||||
task.UpdateStatusLog(base, TASK_FAILED_RUNNING, "Could not get models list")
|
||||
return
|
||||
}
|
||||
|
||||
for i := range ids {
|
||||
err = DeleteModel(base, ids[i].Id)
|
||||
if err != nil {
|
||||
base.GetLogger().Error("Could not delete model", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = base.GetDb().Exec("delete from users where id=$1", task.ExtraTaskInfo)
|
||||
if err != nil {
|
||||
task.UpdateStatusLog(base, TASK_FAILED_RUNNING, "Could not delete user")
|
||||
return
|
||||
}
|
||||
|
||||
task.UpdateStatusLog(base, TASK_DONE, "User deleted with success")
|
||||
return
|
||||
}
|
||||
|
||||
func UsersEndpints(db *sql.DB, handle *Handle) {
|
||||
|
||||
type UserLogin struct {
|
||||
Email string `json:"email"`
|
||||
@ -98,6 +124,10 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
||||
return c.E500M("Failed to get user from token", err)
|
||||
}
|
||||
|
||||
if user.UserType == int(User_Deleted) {
|
||||
return c.JsonBadRequest("Your user is in the process of being deleted!")
|
||||
}
|
||||
|
||||
type UserReturn struct {
|
||||
Token string `json:"token"`
|
||||
Id string `json:"id"`
|
||||
@ -381,4 +411,82 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
||||
|
||||
return c.SendJSON("Ok")
|
||||
})
|
||||
|
||||
type DeleteUser struct {
|
||||
Id string `json:"id" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
DeleteAuthJson(handle, "/user/delete", User_Normal, func(c *Context, obj *DeleteUser) *Error {
|
||||
// TODO let admin delete users
|
||||
if c.User.Id != obj.Id {
|
||||
return c.E500M("TODO implement this for admins", nil)
|
||||
}
|
||||
|
||||
// TODO let admin delete users
|
||||
// Verify the user password
|
||||
_, generated := generateToken(c.Db, c.User.Email, obj.Password, "User Verification")
|
||||
if !generated {
|
||||
return c.JsonBadRequest("Password provided is incorrect")
|
||||
}
|
||||
|
||||
// Disable the ability for the user to create new tasks
|
||||
_, err := c.Db.Exec(
|
||||
"update models as m set can_train=0 "+
|
||||
"from users as u "+
|
||||
"where u.id=$1 and u.id=m.user_id;",
|
||||
obj.Id,
|
||||
)
|
||||
if err != nil {
|
||||
return c.E500M("Failed to stop the models", err)
|
||||
}
|
||||
|
||||
type CreateNewTask struct {
|
||||
UserId string `db:"user_id"`
|
||||
TaskType int `db:"task_type"`
|
||||
Status int `db:"status"`
|
||||
ExtraTaskInfo string `db:"extra_task_info"`
|
||||
}
|
||||
|
||||
newTask := CreateNewTask{
|
||||
UserId: c.Handle.Config.ServiceUser.UserId,
|
||||
// TODO move this to an enum
|
||||
TaskType: int(TASK_TYPE_DELETE_USER),
|
||||
Status: TASK_PREPARING,
|
||||
ExtraTaskInfo: obj.Id,
|
||||
}
|
||||
t_id, err := InsertReturnId(c, &newTask, "tasks", "id")
|
||||
if err != nil {
|
||||
return c.E500M("Failed to create task", err)
|
||||
}
|
||||
|
||||
task := Task{Id: t_id}
|
||||
|
||||
type taskId struct {
|
||||
Id string `db:"t.id"`
|
||||
}
|
||||
tasks, err := GetDbMultitple[taskId](c, "tasks as t "+
|
||||
"left join models as m on m.id=t.model_id "+
|
||||
"where (t.user_id=$1 or m.user_id=$1) and t.status in (0,1,2,3) "+
|
||||
"group by t.id;",
|
||||
obj.Id)
|
||||
|
||||
for i := range tasks {
|
||||
err = task.Depend(c, tasks[i].Id)
|
||||
if err != nil {
|
||||
c.Logger.Error("Failed to mark task as depency", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = task.UpdateStatus(c, TASK_TODO, "Task ready")
|
||||
if err != nil {
|
||||
return c.E500M("Failed to mark task as ready", err)
|
||||
}
|
||||
|
||||
_, err = c.Db.Exec("update users set user_type=-1 where id=$1", obj.Id)
|
||||
if err != nil {
|
||||
return c.E500M("Failed to delete user", err)
|
||||
}
|
||||
|
||||
return c.SendJSON("User to be deleted")
|
||||
})
|
||||
}
|
@ -97,7 +97,7 @@ func (c *Config) Cleanup(db *sql.DB) {
|
||||
failLog(err)
|
||||
_, err = db.Exec("update models set status=$1 where status=$2", FAILED_PREPARING, PREPARING)
|
||||
failLog(err)
|
||||
_, err = db.Exec("update tasks set status=$1 where status=$2", TASK_PICKED_UP, TASK_TODO)
|
||||
_, err = db.Exec("update tasks set status=$1 where status=$2", TASK_TODO, TASK_PICKED_UP)
|
||||
failLog(err)
|
||||
|
||||
tasks, err := GetDbMultitple[Task](db, "tasks where status=$1", TASK_RUNNING)
|
||||
|
3
main.go
3
main.go
@ -10,6 +10,7 @@ import (
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/runner"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/users"
|
||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||
)
|
||||
|
||||
@ -49,7 +50,7 @@ func main() {
|
||||
// TODO Handle this in other way
|
||||
handle.ReadTypesFilesApi("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"})
|
||||
|
||||
usersEndpints(db, handle)
|
||||
UsersEndpints(db, handle)
|
||||
HandleModels(handle)
|
||||
HandleTasks(handle)
|
||||
|
||||
|
@ -17,6 +17,7 @@ create table if not exists models (
|
||||
-- 2: expandable
|
||||
model_type integer default 0,
|
||||
|
||||
can_train integer default 1,
|
||||
width integer,
|
||||
height integer,
|
||||
color_mode varchar (20),
|
||||
@ -111,4 +112,3 @@ create table if not exists exp_model_head (
|
||||
created_on timestamp default current_timestamp,
|
||||
epoch_progress integer default 0
|
||||
);
|
||||
|
||||
|
@ -31,4 +31,10 @@ create table if not exists tasks (
|
||||
task_type integer,
|
||||
|
||||
created_on timestamp default current_timestamp
|
||||
)
|
||||
);
|
||||
|
||||
create table if not exists tasks_dependencies (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
main_id uuid references tasks (id) on delete cascade not null,
|
||||
dependent_id uuid references tasks (id) on delete cascade not null
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Notifications from './lib/Notifications.svelte';
|
||||
import { userStore } from './routes/UserStore.svelte';
|
||||
</script>
|
||||
|
||||
@ -26,6 +27,8 @@
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<Notifications />
|
||||
|
||||
<style class="scss">
|
||||
nav {
|
||||
background: #ececec;
|
||||
|
42
webpage/src/lib/Notifications.svelte
Normal file
42
webpage/src/lib/Notifications.svelte
Normal file
@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import { fly, fade } from 'svelte/transition';
|
||||
import { notificationStore } from './NotificationsStore.svelte';
|
||||
</script>
|
||||
|
||||
<div class="notifications">
|
||||
{#each notificationStore.notifications as noti}
|
||||
<div
|
||||
class="notification"
|
||||
class:noti-success={noti.type === 'success'}
|
||||
class:noti-danger={noti.type === 'danger'}
|
||||
class:noti-info={noti.type === 'info'}
|
||||
in:fly|global={{ duration: 300, y: -120, opacity: 0 }}
|
||||
out:fly|global={{ duration: 300, y: -120, opacity: 1 }}
|
||||
>
|
||||
{noti.message}
|
||||
{@html noti.html ?? ''}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.notifications {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: max(300px, 30%);
|
||||
|
||||
.notification {
|
||||
border: 1px solid black;
|
||||
padding: 20px;
|
||||
background: #ffffff;
|
||||
margin: 15px 0;
|
||||
border-radius: 20px;
|
||||
|
||||
&.noti-danger {
|
||||
background: var(--danger-ligther);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
68
webpage/src/lib/NotificationsStore.svelte.ts
Normal file
68
webpage/src/lib/NotificationsStore.svelte.ts
Normal file
@ -0,0 +1,68 @@
|
||||
type NotificationType = 'danger' | 'success' | 'info';
|
||||
type Notification = {
|
||||
type?: NotificationType;
|
||||
timeToLive?: number;
|
||||
message: string;
|
||||
html?: string;
|
||||
};
|
||||
|
||||
export function createNotificationStore(defaultTimetoLive: number) {
|
||||
type InternalNotifications = {
|
||||
notification: Notification;
|
||||
endDate: Date;
|
||||
};
|
||||
let notifications = $state<InternalNotifications[]>([]);
|
||||
let timeout = $state<number | undefined>();
|
||||
|
||||
function clearNotifications() {
|
||||
const now = new Date().getTime();
|
||||
let min: number | undefined = undefined;
|
||||
notifications = notifications.filter((a) => {
|
||||
const t = a.endDate.getTime();
|
||||
if (t <= now) {
|
||||
return false;
|
||||
}
|
||||
if (min == undefined) {
|
||||
min = t;
|
||||
} else if (min > t) {
|
||||
min = t;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (min != undefined) {
|
||||
timeout = setTimeout(clearNotifications, now - min);
|
||||
} else {
|
||||
timeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function add(noti: Notification) {
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(clearNotifications, noti.timeToLive ?? defaultTimetoLive);
|
||||
}
|
||||
const now = new Date();
|
||||
notifications.push({
|
||||
notification: noti,
|
||||
endDate: new Date(now.getTime() + (noti.timeToLive ?? defaultTimetoLive))
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
get notifications() {
|
||||
return notifications.map((a) => a.notification);
|
||||
},
|
||||
clear() {
|
||||
notifications.forEach((a) => clearInterval(a.timeout));
|
||||
notifications = [];
|
||||
},
|
||||
add,
|
||||
notify(message: string, type: NotificationType = 'danger') {
|
||||
add({ message, type });
|
||||
},
|
||||
display(message: string) {
|
||||
add({ message, type: 'danger' });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const notificationStore = createNotificationStore(5000);
|
@ -108,7 +108,11 @@ export async function showMessage(
|
||||
if (e == null) {
|
||||
return false;
|
||||
} else if (e instanceof Response) {
|
||||
try {
|
||||
messages.display(await e.json());
|
||||
} catch (ex) {
|
||||
showMessage(ex, messages, message);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
console.error(e);
|
||||
|
@ -25,7 +25,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
|
||||
import BaseModelInfo from './BaseModelInfo.svelte';
|
||||
import DeleteModel from './DeleteModel.svelte';
|
||||
@ -50,16 +50,22 @@
|
||||
|
||||
let id: string | undefined = $state();
|
||||
|
||||
let re_query_timeout: number | undefined = undefined;
|
||||
|
||||
async function getModel() {
|
||||
if (re_query_timeout) {
|
||||
clearTimeout(re_query_timeout);
|
||||
re_query_timeout = undefined;
|
||||
}
|
||||
try {
|
||||
let temp_model: Model = await get(`models/edit?id=${id}`);
|
||||
|
||||
if ([3, 7, 6].includes(temp_model.status)) {
|
||||
setTimeout(getModel, 2000);
|
||||
re_query_timeout = setTimeout(getModel, 2000);
|
||||
}
|
||||
|
||||
if (temp_model.status == 4) {
|
||||
setTimeout(getModel, 5000);
|
||||
re_query_timeout = setTimeout(getModel, 5000);
|
||||
|
||||
definitions = await get(`models/edit/definitions?id=${id}`);
|
||||
}
|
||||
@ -108,6 +114,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (re_query_timeout) {
|
||||
clearTimeout(re_query_timeout);
|
||||
}
|
||||
});
|
||||
|
||||
// Auto reload after 2s when model.status 3,4
|
||||
</script>
|
||||
|
||||
|
8
webpage/src/routes/user-deleted/+page.svelte
Normal file
8
webpage/src/routes/user-deleted/+page.svelte
Normal file
@ -0,0 +1,8 @@
|
||||
<h1>Your User has been deleted</h1>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
}
|
||||
</style>
|
@ -7,6 +7,7 @@
|
||||
import { post } from 'src/lib/requests.svelte';
|
||||
import MessageSimple, { type DisplayFn } from 'src/lib/MessageSimple.svelte';
|
||||
import TokenTable from './TokenTable.svelte';
|
||||
import DeleteUser from './DeleteUser.svelte';
|
||||
|
||||
onMount(() => {
|
||||
if (!userStore.isLogin()) {
|
||||
@ -110,8 +111,8 @@
|
||||
<button> Update </button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- TODO Delete -->
|
||||
<TokenTable />
|
||||
<DeleteUser />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
34
webpage/src/routes/user/info/DeleteUser.svelte
Normal file
34
webpage/src/routes/user/info/DeleteUser.svelte
Normal file
@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
|
||||
import { rdelete, showMessage } from 'src/lib/requests.svelte';
|
||||
import { userStore } from 'src/routes/UserStore.svelte';
|
||||
|
||||
let data = $state({ password: '' });
|
||||
|
||||
async function deleteUser() {
|
||||
if (!userStore.user) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await rdelete('user/delete', {
|
||||
id: userStore.user?.id,
|
||||
password: data.password
|
||||
});
|
||||
userStore.user = undefined;
|
||||
goto('/user-deleted');
|
||||
} catch (e) {
|
||||
showMessage(e, notificationStore);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<form class="danger-bg" on:submit|preventDefault={deleteUser}>
|
||||
<h2 class="no-top-margin">Delete user</h2>
|
||||
Deleting the user will delete all your data stored in the service including the images.
|
||||
<fieldset>
|
||||
To confirm please type your password
|
||||
<input name="password" type="password" required bind:value={data.password} />
|
||||
</fieldset>
|
||||
<button> Delete </button>
|
||||
</form>
|
@ -13,6 +13,7 @@
|
||||
--warning: #fca311;
|
||||
--warning-transparent: #fca31144;
|
||||
|
||||
--danger-ligther: #ffe0de;
|
||||
--danger: #ff8282;
|
||||
--danger-transparent: #ff828244;
|
||||
|
||||
@ -171,3 +172,20 @@ a.button {
|
||||
background-color: var(--danger-transparent);
|
||||
border: 1px solid var(--danger);
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 30px;
|
||||
margin: 20px 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 2px 5px 8px 2px #66666655;
|
||||
}
|
||||
|
||||
.no-top-margin {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.box.danger-bg,
|
||||
form.danger-bg {
|
||||
background-color: var(--danger-transparent);
|
||||
border: 1px solid var(--danger);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user