Skip to content

Commit

Permalink
refactor: user curd
Browse files Browse the repository at this point in the history
  • Loading branch information
0xJacky committed Jan 31, 2025
1 parent 80f01e6 commit 465eae9
Show file tree
Hide file tree
Showing 19 changed files with 1,600 additions and 1,880 deletions.
9 changes: 0 additions & 9 deletions api/user/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@ func InitAuthRouter(r *gin.RouterGroup) {
r.GET("/passkeys/config", GetPasskeyConfigStatus)
}

func InitManageUserRouter(r *gin.RouterGroup) {
r.GET("users", GetUsers)
r.GET("users/:id", GetUser)
r.POST("users", AddUser)
r.POST("users/:id", EditUser)
r.DELETE("users/:id", DeleteUser)
r.PATCH("users/:id", RecoverUser)
}

func InitUserRouter(r *gin.RouterGroup) {
r.GET("/2fa_status", Get2FAStatus)
r.GET("/2fa_secure_session/status", SecureSessionStatus)
Expand Down
151 changes: 32 additions & 119 deletions api/user/user.go
Original file line number Diff line number Diff line change
@@ -1,140 +1,53 @@
package user

import (
"github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/user"
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/gin-gonic/gin"
"github.com/spf13/cast"
"github.com/uozi-tech/cosy"
"golang.org/x/crypto/bcrypt"
"net/http"
)

func GetUsers(c *gin.Context) {
cosy.Core[model.User](c).SetFussy("name").PagingList()
}

func GetUser(c *gin.Context) {
id := cast.ToUint64(c.Param("id"))

u := query.User

user, err := u.FirstByID(id)

if err != nil {
api.ErrHandler(c, err)
return
}

c.JSON(http.StatusOK, user)
}

type UserJson struct {
Name string `json:"name" binding:"required,max=255"`
Password string `json:"password" binding:"max=255"`
}

func AddUser(c *gin.Context) {
var json UserJson
ok := cosy.BindAndValid(c, &json)
if !ok {
return
}

u := query.User

pwd, err := bcrypt.GenerateFromPassword([]byte(json.Password), bcrypt.DefaultCost)
if err != nil {
api.ErrHandler(c, err)
func encryptPassword(ctx *cosy.Ctx[model.User]) {
if ctx.Payload["password"] == nil {
return
}
json.Password = string(pwd)

user := model.User{
Name: json.Name,
Password: json.Password,
}

// duplicate name
_, err = u.Where(u.Name.Eq(json.Name)).First()
if !(err != nil && err.Error() == "record not found") {
c.JSON(http.StatusConflict, gin.H{
"message": "name already exists",
})
}

err = u.Create(&user)

if err != nil {
api.ErrHandler(c, err)
return
}

c.JSON(http.StatusOK, user)

}

func EditUser(c *gin.Context) {
userId := cast.ToUint64(c.Param("id"))

if settings.NodeSettings.Demo && userId == 1 {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "Changing user password is forbidden in demo mode",
})
return
}

var json UserJson
ok := cosy.BindAndValid(c, &json)
if !ok {
return
}

u := query.User
user, err := u.FirstByID(userId)

if err != nil {
api.ErrHandler(c, err)
return
}
edit := &model.User{
Name: json.Name,
}

// encrypt password
if json.Password != "" {
var pwd []byte
pwd, err = bcrypt.GenerateFromPassword([]byte(json.Password), bcrypt.DefaultCost)
pwd := ctx.Payload["password"].(string)
if pwd != "" {
pwdBytes, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
if err != nil {
api.ErrHandler(c, err)
ctx.AbortWithError(err)
return
}
edit.Password = string(pwd)
ctx.Model.Password = string(pwdBytes)
} else {
delete(ctx.Payload, "password")
}
}

_, err = u.Where(u.ID.Eq(userId)).Updates(&edit)

if err != nil {
api.ErrHandler(c, err)
return
}
func InitManageUserRouter(g *gin.RouterGroup) {
c := cosy.Api[model.User]("users")

c.JSON(http.StatusOK, user)
}
c.CreateHook(func(c *cosy.Ctx[model.User]) {
c.BeforeDecodeHook(encryptPassword)
})

func DeleteUser(c *gin.Context) {
id := cast.ToInt(c.Param("id"))
if cast.ToInt(id) == 1 {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "Prohibit deleting the default user",
c.ModifyHook(func(c *cosy.Ctx[model.User]) {
c.BeforeDecodeHook(func(ctx *cosy.Ctx[model.User]) {
if ctx.ID == 1 {
ctx.AbortWithError(user.ErrChangeInitUserPwdInDemo)
}
})
return
}
cosy.Core[model.User](c).Destroy()
}
c.BeforeDecodeHook(encryptPassword)
})

c.DestroyHook(func(c *cosy.Ctx[model.User]) {
c.BeforeExecuteHook(func(ctx *cosy.Ctx[model.User]) {
if ctx.ID == 1 {
ctx.AbortWithError(user.ErrCannotRemoveInitUser)
}
})
})

func RecoverUser(c *gin.Context) {
cosy.Core[model.User](c).Recover()
c.InitRouter(g)
}
2 changes: 2 additions & 0 deletions app/src/constants/errors/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ export default {
50000: () => $gettext('WebAuthn settings are not configured'),
50001: () => $gettext('User not enabled otp as 2fa'),
50002: () => $gettext('Otp or recovery code empty'),
50003: () => $gettext('Cannot remove initial user'),
50004: () => $gettext('Cannot change initial user password in demo mode'),
40401: () => $gettext('Session not found'),
}
Loading

0 comments on commit 465eae9

Please sign in to comment.