diff --git a/README.md b/README.md index 7397849..0eee0df 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,9 @@ then `Goffee` locates the matching [handler](https://git.smarteching.com/goffee/ │ ├── config/ --------------------------> main configs │ ├── events/ --------------------------> contains events │ │ ├── jobs/ ------------------------> contains the event jobs -│ ├── controllers/ ---------------------> route's controllers +│ ├── controllers/ ------------------------> route's controllers │ ├── logs/ ----------------------------> app log files -│ ├── hooks/ ---------------------------> app hooks +│ ├── hooks/ ---------------------> app hooks │ ├── models/ --------------------------> database models │ ├── storage/ -------------------------> a place to store files │ ├── tls/ -----------------------------> tls certificates @@ -86,7 +86,7 @@ then `Goffee` locates the matching [handler](https://git.smarteching.com/goffee/ │ ├── main.go --------------------------> go main file │ ├── README.md ------------------------> readme file │ ├── register-events.go ---------------> register events and jobs -│ ├── register-global-hooks.go ---------> register global middlewares +│ ├── register-global-hooks.go ---> register global middlewares │ ├── routes.go ------------------------> app routes │ ├── run-auto-migrations.go -----------> database migrations ``` \ No newline at end of file diff --git a/controllers/adminusers.go b/controllers/adminusers.go deleted file mode 100644 index b051308..0000000 --- a/controllers/adminusers.go +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright (c) 2024 Zeni Kim -// Use of this source code is governed by MIT-style -// license that can be found in the LICENSE file. - -package controllers - -import ( - "errors" - "fmt" - "strconv" - - "git.smarteching.com/goffee/core" - "git.smarteching.com/goffee/core/template/components" - "git.smarteching.com/goffee/cup/events" - "git.smarteching.com/goffee/cup/models" - "git.smarteching.com/goffee/cup/utils" - "gorm.io/gorm" -) - -func AdminUsersList(c *core.Context) *core.Response { - - var users []models.User - db := c.GetGorm() - db.Find(&users) - - // -- response template - type templateData struct { - TableUsers components.ContentTable - AddButton components.ContentHref - } - - cols := []components.ContentTableTH{ - { - Value: "UID", - ValueType: "number", - }, - { - Value: "Name", - ValueType: "string", - }, - { - Value: "Fullname", - ValueType: "string", - }, - { - Value: "Email", - ValueType: "string", - }, - { - Value: "Created", - }, - { - Value: "Updated", - }, - { - ValueType: "href", - }, - } - - rows := make([][]components.ContentTableTD, len(users)) - for i, u := range users { - row := []components.ContentTableTD{ - {Value: strconv.Itoa(int(u.ID))}, - {Value: u.Name}, - {Value: u.Fullname}, - {Value: u.Email}, - {Value: utils.FormatUnix(u.Created)}, - {Value: utils.FormatUnix(u.Updated)}, - {Value: components.ContentHref{ - Text: "edit", - Link: "/admin/users/edit/" + strconv.Itoa(int(u.ID)), - TypeClass: "outline-secondary", - }}, - } - rows[i] = row - } - - tmplData := templateData{ - TableUsers: components.ContentTable{ - ID: "table_demo", - AllTH: cols, - AllTD: rows, - }, - AddButton: components.ContentHref{ - Text: "Register", - Link: "/admin/users/add", - IsButton: true, - TypeClass: "outline-primary", - }, - } - return c.Response.Template("admin_userlist.html", tmplData) -} - -func AdminUsersAdd(c *core.Context) *core.Response { - - // check if is submit - submit := c.GetRequestParam("submit").(string) - - errormessages := make([]string, 0) - - name := "" - fullname := "" - email := "" - password := "" - - if submit != "" { - - name = c.GetRequestParam("name").(string) - fullname = c.GetRequestParam("fullname").(string) - email = c.GetRequestParam("email").(string) - password = c.GetRequestParam("password").(string) - - // check if email exists - var user models.User - res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user) - if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) { - c.GetLogger().Error(res.Error.Error()) - errormessages = append(errormessages, "Error") - } - if res.Error == nil { - errormessages = append(errormessages, "Error, email already exists in the database") - } - - // validation data - data := map[string]interface{}{ - "name": name, - "fullname": fullname, - "email": email, - "password": password, - } - // validation rules - rules := map[string]interface{}{ - "name": "required|alphaNumeric", - "fullname": "required", - "email": "required|email", - "password": "required|length:6,20", - } - // validate - v := c.GetValidator().Validate(data, rules) - if v.Failed() { - c.GetLogger().Error(v.GetErrorMessagesJson()) - for _, v := range v.GetErrorMessagesMap() { - errormessages = append(errormessages, v) - } - } - - if len(errormessages) == 0 { - - //hash the password - passwordHashed, _ := c.GetHashing().HashPassword(c.CastToString(password)) - // store the record in db - user = models.User{ - Name: c.CastToString(name), - Fullname: c.CastToString(fullname), - Email: c.CastToString(email), - Password: passwordHashed, - } - res = c.GetGorm().Create(&user) - if res.Error != nil { - c.GetLogger().Error(res.Error.Error()) - errormessages = append(errormessages, res.Error.Error()) - } else { - - // fire user registered event - err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_REGISTERED, Payload: map[string]interface{}{ - "user": user, - }}) - if err != nil { - c.GetLogger().Error(err.Error()) - errormessages = append(errormessages, "Internal server error") - } else { - // redirect to list - return c.Response.Redirect("/admin/users") - } - } - } - } - // -- response template - type templateData struct { - FieldName components.FormInput - FieldFullname components.FormInput - FieldEmail components.FormInput - FieldPassword components.FormInput - ErrorMessages []string - SubmitButton components.FormButton - } - - tmplData := templateData{ - FieldName: components.FormInput{ - ID: "name", - Label: "Name", - Type: "text", - Value: name, - IsRequired: true, - }, - FieldFullname: components.FormInput{ - ID: "fullname", - Label: "Full name", - Type: "text", - Value: fullname, - IsRequired: true, - }, - FieldEmail: components.FormInput{ - ID: "email", - Label: "e-mail", - Type: "text", - Value: email, - //Autocomplete: true, - IsRequired: true, - }, - FieldPassword: components.FormInput{ - ID: "password", - Label: "Password", - Type: "password", - Value: password, - IsRequired: true, - }, - SubmitButton: components.FormButton{ - ID: "submit", - Text: "Add user", - Value: "submit", - IsSubmit: true, - TypeClass: "primary", - }, - ErrorMessages: errormessages, - } - return c.Response.Template("admin_useradd.html", tmplData) - -} - -func AdminUsersEdit(c *core.Context) *core.Response { - - // check if is submit - submit := c.GetRequestParam("submit").(string) - - user_id := c.GetPathParam("id") - - errormessages := make([]string, 0) - - var origin_user models.User - - db := c.GetGorm() - // check if existes - result_db := db.First(&origin_user, user_id) - if result_db.RowsAffected == 0 { - c.GetLogger().Error("User ID not found") - return c.Response.Redirect("/admin/users") - } - - name := origin_user.Name - fullname := origin_user.Fullname - email := origin_user.Email - password := "" - user_id_string := c.GetPathParam("id").(string) - - if submit != "" { - - name = c.GetRequestParam("name").(string) - fullname = c.GetRequestParam("fullname").(string) - email = c.GetRequestParam("email").(string) - password = c.GetRequestParam("password").(string) - key := c.GetRequestParam("key") - - // check if email exists - var user models.User - res := c.GetGorm().Where("email = ? AND id != ?", c.CastToString(email), key).First(&user) - if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) { - c.GetLogger().Error(res.Error.Error()) - errormessages = append(errormessages, "Error") - } - if res.Error == nil { - errormessages = append(errormessages, "Error, email already exists to another user") - } - - // validation data - data := map[string]interface{}{ - "name": name, - "fullname": fullname, - "email": email, - } - // validation rules - rules := map[string]interface{}{ - "name": "required|alphaNumeric", - "fullname": "required", - "email": "required|email", - } - - // nee update password - if password != "" { - data["password"] = password - rules["password"] = "required|length:6,20" - } - // validate - v := c.GetValidator().Validate(data, rules) - if v.Failed() { - c.GetLogger().Error(v.GetErrorMessagesJson()) - for _, v := range v.GetErrorMessagesMap() { - errormessages = append(errormessages, v) - } - } - - if len(errormessages) == 0 { - - //hash the password - if password != "" { - passwordHashed, _ := c.GetHashing().HashPassword(c.CastToString(password)) - origin_user.Password = passwordHashed - } - // store the record in db - origin_user.Name = name - origin_user.Fullname = fullname - origin_user.Email = email - - result_db = db.Save(&origin_user) - if result_db.RowsAffected == 0 { - c.GetLogger().Error("Admin user: error updating") - errormessages = append(errormessages, fmt.Sprintf("Error updating user %s:", user_id_string)) - } else { - return c.Response.Redirect("/admin/users") - } - } - } - // -- response template - type templateData struct { - FieldName components.FormInput - FieldFullname components.FormInput - FieldEmail components.FormInput - FieldPassword components.FormInput - FieldKey components.FormInput - ErrorMessages []string - SubmitButton components.FormButton - DeleteButton components.FormButton - } - - tmplData := templateData{ - FieldName: components.FormInput{ - ID: "name", - Label: "Name", - Type: "text", - Value: name, - IsRequired: true, - }, - FieldFullname: components.FormInput{ - ID: "fullname", - Label: "Full name", - Type: "text", - Value: fullname, - IsRequired: true, - }, - FieldEmail: components.FormInput{ - ID: "email", - Label: "e-mail", - Type: "text", - Value: email, - //Autocomplete: true, - IsRequired: true, - }, - FieldPassword: components.FormInput{ - ID: "password", - Label: "Password", - Type: "password", - Hint: "Leave blank if you don't want to change it", - Value: password, - IsRequired: false, - }, - FieldKey: components.FormInput{ - ID: "key", - Type: "hidden", - Value: user_id_string, - }, - SubmitButton: components.FormButton{ - ID: "submit", - Text: "Update user", - Value: "submit", - IsSubmit: true, - TypeClass: "primary", - }, - DeleteButton: components.FormButton{ - ID: "submit", - Text: "Delete user", - Value: "submit", - IsSubmit: true, - TypeClass: "warning", - }, - ErrorMessages: errormessages, - } - return c.Response.Template("admin_useredit.html", tmplData) - -} - -func AdminUsersDelete(c *core.Context) *core.Response { - - user_id := c.GetRequestParam("key").(string) - - errormessages := make([]string, 0) - warningmessages := make([]string, 0) - - var origin_user models.User - - db := c.GetGorm() - // check if existes - result_db := db.First(&origin_user, user_id) - if result_db.RowsAffected == 0 { - errormessages = append(errormessages, "User ID not found") - } else { - // check if is the seed user - seed := "1" - if user_id == seed { - errormessages = append(errormessages, "You can't delete the seed user") - } - - } - - // sample warning message - warningmessages = append(warningmessages, fmt.Sprintf("Are you sure you want to cancel the account %s?", origin_user.Name)) - - // -- response template - type templateData struct { - ErrorMessages []string - WarningMessages []string - FieldKey components.FormInput - ConfirmButton components.FormButton - BackButton components.ContentHref - } - - tmplData := templateData{ - FieldKey: components.FormInput{ - ID: "key", - Type: "hidden", - Value: user_id, - }, - ConfirmButton: components.FormButton{ - ID: "submit", - Text: "Confirm", - Value: "submit", - IsSubmit: true, - TypeClass: "primary", - }, - BackButton: components.ContentHref{ - Link: "/admin/users", - Text: "Cancel", - IsButton: true, - }, - ErrorMessages: errormessages, - WarningMessages: warningmessages, - } - return c.Response.Template("admin_confirmuserdel.html", tmplData) - -} - -func AdminUsersDelConfirm(c *core.Context) *core.Response { - - user_id := c.GetRequestParam("key").(string) - - var origin_user models.User - - db := c.GetGorm() - // check if existes - result_db := db.First(&origin_user, user_id) - if result_db.RowsAffected != 0 { - // check if is the seed user - seed := "1" - if user_id != seed { - // Delete the user - // fire user delete event - err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_DELETED, Payload: map[string]interface{}{ - "user": origin_user, - }}) - if err != nil { - c.GetLogger().Error(err.Error()) - } - result_db.Unscoped().Delete(&origin_user) - } - } - - return c.Response.Redirect("/admin/users") - -} diff --git a/controllers/authentication.go b/controllers/authentication.go index 9e3eff9..9e7e940 100644 --- a/controllers/authentication.go +++ b/controllers/authentication.go @@ -26,7 +26,6 @@ import ( func Signup(c *core.Context) *core.Response { name := c.GetRequestParam("name") - fullname := c.GetRequestParam("fullname") email := c.GetRequestParam("email") password := c.GetRequestParam("password") // check if email exists @@ -47,16 +46,14 @@ func Signup(c *core.Context) *core.Response { // validation data data := map[string]interface{}{ "name": name, - "fullname": fullname, "email": email, "password": password, } // validation rules rules := map[string]interface{}{ "name": "required|alphaNumeric", - "fullname": "required", "email": "required|email", - "password": "required|length:6,20", + "password": "required|length:6,10", } // validate v := c.GetValidator().Validate(data, rules) @@ -76,7 +73,6 @@ func Signup(c *core.Context) *core.Response { // store the record in db user = models.User{ Name: c.CastToString(name), - Fullname: c.CastToString(fullname), Email: c.CastToString(email), Password: passwordHashed, } diff --git a/events/event-names.go b/events/event-names.go index 14afec8..0e5f431 100644 --- a/events/event-names.go +++ b/events/event-names.go @@ -2,6 +2,5 @@ package events // event names const USER_REGISTERED = "user-registered" -const USER_DELETED = "user-deleted" const USER_PASSWORD_RESET_REQUESTED = "user-password-reset-requested" const PASSWORD_CHANGED = "password-changed" diff --git a/models/base.go b/models/base.go deleted file mode 100644 index 5350de2..0000000 --- a/models/base.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2024 Zeni Kim -// Use of this source code is governed by MIT-style -// license that can be found in the LICENSE file. - -package models - -type BaseModel struct { - ID uint `json:"id" gorm:"primaryKey; column:id"` - Created int64 `gorm:"autoCreateTime"` - Updated int64 `gorm:"autoUpdateTime"` -} diff --git a/models/user.go b/models/user.go index 3a229d9..c85a386 100644 --- a/models/user.go +++ b/models/user.go @@ -5,10 +5,11 @@ package models +import "gorm.io/gorm" + type User struct { - BaseModel + gorm.Model Name string - Fullname string Email string Password string } diff --git a/routes.go b/routes.go index 785c59e..00f1906 100644 --- a/routes.go +++ b/routes.go @@ -34,18 +34,6 @@ func registerRoutes() { controller.Post("/reset-password", controllers.ResetPasswordRequest) controller.Post("/reset-password/code/:code", controllers.SetNewPassword) - // Uncomment the lines below to enable user administration - controller.Get("/admin/users", controllers.AdminUsersList) - controller.Post("/admin/users", controllers.AdminUsersList) - controller.Get("/admin/users/add", controllers.AdminUsersAdd) - controller.Post("/admin/users/add", controllers.AdminUsersAdd) - controller.Get("/admin/users/edit/:id", controllers.AdminUsersEdit) - controller.Post("/admin/users/edit/:id", controllers.AdminUsersEdit) - controller.Post("/admin/users/delete", controllers.AdminUsersDelete) - controller.Post("/admin/users/deleteconfirm", controllers.AdminUsersDelConfirm) - //controller.Get("/admin/users/roles", controllers.Signout) - //controller.Get("/admin/users/permissions", controllers.ResetPasswordRequest) - controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck) // templates demos diff --git a/storage/templates/admin_confirmuserdel.html b/storage/templates/admin_confirmuserdel.html deleted file mode 100644 index 5fd0d71..0000000 --- a/storage/templates/admin_confirmuserdel.html +++ /dev/null @@ -1,34 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
-

User delete confirmation

- {{if .ErrorMessages }} -
- -
    - {{range $i, $a := .ErrorMessages}} -
  • {{$a}}
  • - {{end}} -
-
- {{else}} - {{if .WarningMessages }} -
    - {{range $o, $u := .WarningMessages}} -
  • {{$u}}
  • - {{end}} -
- {{end}} -
- {{template "form_input" .FieldKey}} - {{template "form_button" .ConfirmButton}} -
- {{end}} -
- {{template "content_href" .BackButton}} -
- {{template "page_footer"}} - - diff --git a/storage/templates/admin_useradd.html b/storage/templates/admin_useradd.html deleted file mode 100644 index ccdb363..0000000 --- a/storage/templates/admin_useradd.html +++ /dev/null @@ -1,26 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
-

Add user form

- {{if .ErrorMessages }}
- -
    - {{range $i, $a := .ErrorMessages}} -
  • {{$a}}
  • - {{end}} -
-
{{end}} -
- {{template "form_input" .FieldName}} - {{template "form_input" .FieldFullname}} - {{template "form_input" .FieldEmail}} - {{template "form_input" .FieldPassword}} -
- {{template "form_button" .SubmitButton}} -
-
- {{template "page_footer"}} - - diff --git a/storage/templates/admin_useredit.html b/storage/templates/admin_useredit.html deleted file mode 100644 index 36ebfdf..0000000 --- a/storage/templates/admin_useredit.html +++ /dev/null @@ -1,31 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
-

Edit user form

- {{if .ErrorMessages }}
- -
    - {{range $i, $a := .ErrorMessages}} -
  • {{$a}}
  • - {{end}} -
-
{{end}} -
- {{template "form_input" .FieldName}} - {{template "form_input" .FieldFullname}} - {{template "form_input" .FieldEmail}} - {{template "form_input" .FieldPassword}} - {{template "form_input" .FieldKey}} - {{template "form_button" .SubmitButton}} -
-
-
- {{template "form_input" .FieldKey}} - {{template "form_button" .DeleteButton}} -
-
- {{template "page_footer"}} - - diff --git a/storage/templates/admin_userlist.html b/storage/templates/admin_userlist.html deleted file mode 100644 index 9981d46..0000000 --- a/storage/templates/admin_userlist.html +++ /dev/null @@ -1,18 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
-
-

Users

-
- {{template "content_href" .AddButton}} -
-
-
- {{template "content_table" .TableUsers}} -
-
- {{template "page_footer"}} - - diff --git a/utils/helpers.go b/utils/helpers.go index 36ac76b..00525b7 100644 --- a/utils/helpers.go +++ b/utils/helpers.go @@ -8,7 +8,6 @@ package utils import ( "crypto/md5" "fmt" - "time" ) // generate a hashed string to be used as key for caching auth jwt token @@ -18,7 +17,3 @@ func CreateAuthTokenHashedCacheKey(userID uint, userAgent string) string { return hashedCacheKey } - -func FormatUnix(value int64) string { - return time.Unix(value, 0).Format("2006-01-02 15:04:05") -}