diff --git a/.github/workflows/mockery.yml b/.github/workflows/mockery.yml index 957cec16..b108edd8 100644 --- a/.github/workflows/mockery.yml +++ b/.github/workflows/mockery.yml @@ -3,7 +3,6 @@ on: push: branches: - main - pull_request: jobs: mockery: runs-on: ubuntu-latest diff --git a/cmd/web/wire_gen.go b/cmd/web/wire_gen.go index 0945f54e..fb932fa5 100644 --- a/cmd/web/wire_gen.go +++ b/cmd/web/wire_gen.go @@ -73,6 +73,8 @@ func initWeb() (*app.Web, error) { middlewares := middleware.NewMiddlewares(koanf, logger, manager, appRepo) userRepo := data.NewUserRepo(locale, db) userService := service.NewUserService(locale, koanf, manager, userRepo) + userTokenRepo := data.NewUserTokenRepo(locale, db) + userTokenService := service.NewUserTokenService(locale, userTokenRepo) databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger) databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo) databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo) @@ -142,7 +144,7 @@ func initWeb() (*app.Web, error) { supervisorApp := supervisor.NewApp(locale) toolboxApp := toolbox.NewApp(locale) loader := bootstrap.NewLoader(benchmarkApp, codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp) - http := route.NewHttp(userService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader) + http := route.NewHttp(userService, userTokenService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader) wsService := service.NewWsService(locale, koanf, sshRepo) ws := route.NewWs(wsService) mux, err := bootstrap.NewRouter(locale, middlewares, http, ws) diff --git a/go.sum b/go.sum index bb5a64a4..3669d7aa 100644 --- a/go.sum +++ b/go.sum @@ -121,6 +121,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= @@ -403,6 +404,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -512,6 +515,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/biz/ssh.go b/internal/biz/ssh.go index b88367a2..3f7332d0 100644 --- a/internal/biz/ssh.go +++ b/internal/biz/ssh.go @@ -38,7 +38,6 @@ func (r *SSH) BeforeSave(tx *gorm.DB) error { } return nil - } func (r *SSH) AfterFind(tx *gorm.DB) error { diff --git a/internal/biz/user.go b/internal/biz/user.go index a0054b08..0191b455 100644 --- a/internal/biz/user.go +++ b/internal/biz/user.go @@ -21,7 +21,7 @@ type User struct { type UserRepo interface { List(page, limit uint) ([]*User, int64, error) Get(id uint) (*User, error) - Create(username, password string) (*User, error) + Create(username, password, email string) (*User, error) UpdatePassword(id uint, password string) error UpdateEmail(id uint, email string) error Delete(id uint) error diff --git a/internal/biz/user_token.go b/internal/biz/user_token.go index 12776146..7b3c7615 100644 --- a/internal/biz/user_token.go +++ b/internal/biz/user_token.go @@ -2,20 +2,37 @@ package biz import ( "time" + + "github.com/go-rat/utils/hash" + "gorm.io/gorm" ) type UserToken struct { ID uint `gorm:"primaryKey" json:"id"` UserID uint `gorm:"index" json:"user_id"` - Token string `gorm:"not null;default:'';unique" json:"token"` + Token string `gorm:"not null;default:'';unique" json:"-"` IPs []string `gorm:"not null;default:'[]';serializer:json" json:"ips"` ExpiredAt time.Time `json:"expired_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } -type UserTokenRepo interface { - Create(userID uint, ips []string) (*UserToken, error) - Get(id uint) (*UserToken, error) - Save(user *UserToken) error +func (r *UserToken) BeforeSave(tx *gorm.DB) error { + hasher := hash.NewArgon2id() + var err error + + r.Token, err = hasher.Make(r.Token) + if err != nil { + return err + } + + return nil +} + +type UserTokenRepo interface { + List(userID, page, limit uint) ([]*UserToken, int64, error) + Create(userID uint, ips []string, expired time.Time) (*UserToken, error) + Get(id uint) (*UserToken, error) + Delete(id uint) error + Update(id uint, ips []string, expired time.Time) (*UserToken, error) } diff --git a/internal/data/data.go b/internal/data/data.go index 4b374816..102dbacd 100644 --- a/internal/data/data.go +++ b/internal/data/data.go @@ -25,5 +25,6 @@ var ProviderSet = wire.NewSet( NewSSHRepo, NewTaskRepo, NewUserRepo, + NewUserTokenRepo, NewWebsiteRepo, ) diff --git a/internal/data/user.go b/internal/data/user.go index 134faa74..bd51c84b 100644 --- a/internal/data/user.go +++ b/internal/data/user.go @@ -44,7 +44,7 @@ func (r *userRepo) Get(id uint) (*biz.User, error) { return user, nil } -func (r *userRepo) Create(username, password string) (*biz.User, error) { +func (r *userRepo) Create(username, password, email string) (*biz.User, error) { value, err := r.hasher.Make(password) if err != nil { return nil, err @@ -53,6 +53,7 @@ func (r *userRepo) Create(username, password string) (*biz.User, error) { user := &biz.User{ Username: username, Password: value, + Email: email, } if err = r.db.Create(user).Error; err != nil { return nil, err diff --git a/internal/data/user_token.go b/internal/data/user_token.go new file mode 100644 index 00000000..4fddd369 --- /dev/null +++ b/internal/data/user_token.go @@ -0,0 +1,89 @@ +package data + +import ( + "time" + + "github.com/go-rat/utils/hash" + "github.com/go-rat/utils/str" + "github.com/leonelquinteros/gotext" + "gorm.io/gorm" + + "github.com/tnb-labs/panel/internal/biz" +) + +type userTokenRepo struct { + t *gotext.Locale + db *gorm.DB + hasher hash.Hasher +} + +func NewUserTokenRepo(t *gotext.Locale, db *gorm.DB) biz.UserTokenRepo { + return &userTokenRepo{ + t: t, + db: db, + hasher: hash.NewArgon2id(), + } +} + +func (r userTokenRepo) List(userID, page, limit uint) ([]*biz.UserToken, int64, error) { + userTokens := make([]*biz.UserToken, 0) + var total int64 + err := r.db.Model(&biz.UserToken{}).Where("user_id = ?", userID).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&userTokens).Error + return userTokens, total, err +} + +func (r userTokenRepo) Create(userID uint, ips []string, expired time.Time) (*biz.UserToken, error) { + token := str.Random(32) + hashedToken, err := r.hasher.Make(token) + if err != nil { + return nil, err + } + + userToken := &biz.UserToken{ + UserID: userID, + Token: hashedToken, + IPs: ips, + ExpiredAt: expired, + } + if err = r.db.Create(userToken).Error; err != nil { + return nil, err + } + + userToken.Token = token + + return userToken, nil +} + +func (r userTokenRepo) Get(id uint) (*biz.UserToken, error) { + userToken := new(biz.UserToken) + if err := r.db.First(userToken, id).Error; err != nil { + return nil, err + } + + return userToken, nil +} + +func (r userTokenRepo) Delete(id uint) error { + userToken := new(biz.UserToken) + if err := r.db.First(userToken, id).Error; err != nil { + return err + } + + return r.db.Delete(userToken).Error +} + +func (r userTokenRepo) Update(id uint, ips []string, expired time.Time) (*biz.UserToken, error) { + userToken := new(biz.UserToken) + if err := r.db.First(userToken, id).Error; err != nil { + return nil, err + } + + userToken.IPs = ips + userToken.ExpiredAt = expired + + if err := r.db.Save(userToken).Error; err != nil { + return nil, err + } + + return userToken, nil +} diff --git a/internal/http/request/common.go b/internal/http/request/common.go index fe85395d..16ece4be 100644 --- a/internal/http/request/common.go +++ b/internal/http/request/common.go @@ -1,5 +1,5 @@ package request type ID struct { - ID uint `json:"id" form:"id" query:"id" validate:"required|min:1"` + ID uint `json:"id" form:"id" query:"id" uri:"id" validate:"required|min:1"` } diff --git a/internal/http/request/user.go b/internal/http/request/user.go index b2d0b317..1b2347b5 100644 --- a/internal/http/request/user.go +++ b/internal/http/request/user.go @@ -18,6 +18,7 @@ type UserIsTwoFA struct { type UserCreate struct { Username string `json:"username" validate:"required|notExists:users,username"` Password string `json:"password" validate:"required|password"` + Email string `json:"email" validate:"required|email"` } type UserUpdatePassword struct { diff --git a/internal/http/request/user_token.go b/internal/http/request/user_token.go new file mode 100644 index 00000000..f741ce78 --- /dev/null +++ b/internal/http/request/user_token.go @@ -0,0 +1,32 @@ +package request + +import "net/http" + +type UserTokenList struct { + UserID uint `query:"user_id"` + Paginate +} + +type UserTokenCreate struct { + UserID uint `json:"user_id" validate:"required|exists:users,id"` + IPs []string `json:"ips"` + ExpiredAt int64 `json:"expired_at" validate:"required"` +} + +func (r *UserTokenCreate) Rules(_ *http.Request) map[string]string { + return map[string]string{ + "IPs.*": "required|ip", + } +} + +type UserTokenUpdate struct { + ID uint `uri:"id"` + IPs []string `json:"ips"` + ExpiredAt int64 `json:"expired_at" validate:"required"` +} + +func (r *UserTokenUpdate) Rules(_ *http.Request) map[string]string { + return map[string]string{ + "IPs.*": "required|ip", + } +} diff --git a/internal/route/http.go b/internal/route/http.go index 973b60c4..e6d73db7 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -16,6 +16,7 @@ import ( type Http struct { user *service.UserService + userToken *service.UserTokenService dashboard *service.DashboardService task *service.TaskService website *service.WebsiteService @@ -46,6 +47,7 @@ type Http struct { func NewHttp( user *service.UserService, + userToken *service.UserTokenService, dashboard *service.DashboardService, task *service.TaskService, website *service.WebsiteService, @@ -75,6 +77,7 @@ func NewHttp( ) *Http { return &Http{ user: user, + userToken: userToken, dashboard: dashboard, task: task, website: website, @@ -125,6 +128,13 @@ func (route *Http) Register(r *chi.Mux) { r.Delete("/{id}", route.user.Delete) }) + r.Route("/user_tokens", func(r chi.Router) { + r.Get("/", route.userToken.List) + r.Post("/", route.userToken.Create) + r.Put("/{id}", route.userToken.Update) + r.Delete("/{id}", route.userToken.Delete) + }) + r.Route("/dashboard", func(r chi.Router) { r.Get("/panel", route.dashboard.Panel) r.Get("/home_apps", route.dashboard.HomeApps) diff --git a/internal/service/cli.go b/internal/service/cli.go index 4acc0b6c..b12b150c 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -138,9 +138,6 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error { } user.Username = str.Random(8) user.Password = hashed - if user.Email == "" { - user.Email = str.Random(8) + "@yourdomain.com" - } if err = s.db.Save(user).Error; err != nil { return errors.New(s.t.Get("Failed to save user info: %v", err)) @@ -882,7 +879,7 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error { return errors.New(s.t.Get("Initialization failed: %v", err)) } - _, err = s.userRepo.Create("admin", value) + _, err = s.userRepo.Create("admin", value, str.Random(8)+"@yourdomain.com") if err != nil { return errors.New(s.t.Get("Initialization failed: %v", err)) } diff --git a/internal/service/service.go b/internal/service/service.go index 0c7ccba7..cda167aa 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -30,6 +30,7 @@ var ProviderSet = wire.NewSet( NewSystemctlService, NewTaskService, NewUserService, + NewUserTokenService, NewWebsiteService, NewWsService, ) diff --git a/internal/service/user.go b/internal/service/user.go index 410b9ebb..1b8cee0c 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -197,7 +197,7 @@ func (s *UserService) Create(w http.ResponseWriter, r *http.Request) { return } - user, err := s.userRepo.Create(req.Username, req.Password) + user, err := s.userRepo.Create(req.Username, req.Password, req.Email) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return diff --git a/internal/service/user_token.go b/internal/service/user_token.go new file mode 100644 index 00000000..52c27b1e --- /dev/null +++ b/internal/service/user_token.go @@ -0,0 +1,119 @@ +package service + +import ( + "net/http" + "time" + + "github.com/go-rat/chix" + "github.com/leonelquinteros/gotext" + + "github.com/tnb-labs/panel/internal/biz" + "github.com/tnb-labs/panel/internal/http/request" +) + +type UserTokenService struct { + t *gotext.Locale + userTokenRepo biz.UserTokenRepo +} + +func NewUserTokenService(t *gotext.Locale, userToken biz.UserTokenRepo) *UserTokenService { + return &UserTokenService{ + t: t, + userTokenRepo: userToken, + } +} + +func (s *UserTokenService) List(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.UserTokenList](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + userTokens, total, err := s.userTokenRepo.List(req.UserID, req.Page, req.Limit) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, chix.M{ + "total": total, + "items": userTokens, + }) +} + +func (s *UserTokenService) Create(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.UserTokenCreate](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + expiredAt := time.Unix(0, req.ExpiredAt*int64(time.Millisecond)) + if expiredAt.Before(time.Now()) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("expiration time must be greater than current time")) + return + } + if expiredAt.After(time.Now().AddDate(10, 0, 0)) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("expiration time must be less than 10 years")) + return + } + + userToken, err := s.userTokenRepo.Create(req.UserID, req.IPs, expiredAt) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + // 手动组装响应,因为 Token 设置了 json:"-" + Success(w, chix.M{ + "id": userToken.ID, + "user_id": userToken.UserID, + "token": userToken.Token, + "ips": userToken.IPs, + "expired_at": userToken.ExpiredAt, + "created_at": userToken.CreatedAt, + "updated_at": userToken.UpdatedAt, + }) +} + +func (s *UserTokenService) Update(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.UserTokenUpdate](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + expiredAt := time.Unix(0, req.ExpiredAt*int64(time.Millisecond)) + if expiredAt.Before(time.Now()) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("expiration time must be greater than current time")) + return + } + if expiredAt.After(time.Now().AddDate(10, 0, 0)) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("expiration time must be less than 10 years")) + return + } + + userToken, err := s.userTokenRepo.Update(req.ID, req.IPs, expiredAt) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, userToken) +} + +func (s *UserTokenService) Delete(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ID](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.userTokenRepo.Delete(req.ID); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} diff --git a/web/src/api/apps/podman/index.ts b/web/src/api/apps/podman/index.ts index afa496fa..ecc57ebb 100644 --- a/web/src/api/apps/podman/index.ts +++ b/web/src/api/apps/podman/index.ts @@ -4,7 +4,8 @@ export default { // 获取注册表配置 registryConfig: (): any => http.Get('/apps/podman/registry_config'), // 保存注册表配置 - saveRegistryConfig: (config: string): any => http.Post('/apps/podman/registry_config', { config }), + saveRegistryConfig: (config: string): any => + http.Post('/apps/podman/registry_config', { config }), // 获取存储配置 storageConfig: (): any => http.Get('/apps/podman/storage_config'), // 保存存储配置 diff --git a/web/src/api/panel/database/index.ts b/web/src/api/panel/database/index.ts index ad9cda75..e747a664 100644 --- a/web/src/api/panel/database/index.ts +++ b/web/src/api/panel/database/index.ts @@ -27,7 +27,8 @@ export default { serverRemark: (id: number, remark: string) => http.Put(`/database_server/${id}/remark`, { remark }), // 获取数据库用户列表 - userList: (page: number, limit: number) => http.Get('/database_user', { params: { page, limit } }), + userList: (page: number, limit: number) => + http.Get('/database_user', { params: { page, limit } }), // 创建数据库用户 userCreate: (data: any) => http.Post('/database_user', data), // 获取数据库用户 diff --git a/web/src/api/panel/user/index.ts b/web/src/api/panel/user/index.ts index 4038d2d0..9291bd14 100644 --- a/web/src/api/panel/user/index.ts +++ b/web/src/api/panel/user/index.ts @@ -35,5 +35,17 @@ export default { generateTwoFA: (id: number): any => http.Get(`/users/${id}/2fa`), // 保存2FA密钥 updateTwoFA: (id: number, code: string, secret: string): any => - http.Post(`/users/${id}/2fa`, { code, secret }) + http.Post(`/users/${id}/2fa`, { code, secret }), + + // 获取用户Token列表 + tokenList: (user_id: number, page: number, limit: number): any => + http.Get(`/user_tokens`, { params: { user_id, page, limit } }), + // 创建用户Token + tokenCreate: (user_id: number, ips: string[], expired_at: number): any => + http.Post('/user_tokens', { user_id, ips, expired_at }), + // 删除用户Token + tokenDelete: (id: number): any => http.Delete(`/user_tokens/${id}`), + // 更新用户Token + tokenUpdate: (id: number, ips: string[], expired_at: number): any => + http.Put(`/user_tokens/${id}`, { ips, expired_at }) } diff --git a/web/src/views/setting/CreateModal.vue b/web/src/views/setting/CreateModal.vue new file mode 100644 index 00000000..1aa475fd --- /dev/null +++ b/web/src/views/setting/CreateModal.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/web/src/views/setting/IndexView.vue b/web/src/views/setting/IndexView.vue index 965be6f9..83e32b1a 100644 --- a/web/src/views/setting/IndexView.vue +++ b/web/src/views/setting/IndexView.vue @@ -8,6 +8,7 @@ import { useGettext } from 'vue3-gettext' import setting from '@/api/panel/setting' import TheIcon from '@/components/custom/TheIcon.vue' import { useThemeStore } from '@/store' +import CreateModal from '@/views/setting/CreateModal.vue' import SettingBase from '@/views/setting/SettingBase.vue' import SettingSafe from '@/views/setting/SettingSafe.vue' import SettingUser from '@/views/setting/SettingUser.vue' @@ -15,6 +16,7 @@ import SettingUser from '@/views/setting/SettingUser.vue' const { $gettext } = useGettext() const themeStore = useThemeStore() const currentTab = ref('base') +const createModal = ref(false) const { data: model } = useRequest(setting.list, { initialData: { @@ -50,7 +52,9 @@ const handleSave = () => { }) } -const handleCreate = () => {} +const handleCreate = () => { + createModal.value = true +} diff --git a/web/src/views/setting/SettingUser.vue b/web/src/views/setting/SettingUser.vue index 7d346798..e4ffca25 100644 --- a/web/src/views/setting/SettingUser.vue +++ b/web/src/views/setting/SettingUser.vue @@ -2,6 +2,7 @@ import user from '@/api/panel/user' import { formatDateTime, renderIcon } from '@/utils' import PasswordModal from '@/views/setting/PasswordModal.vue' +import TokenModal from '@/views/setting/TokenModal.vue' import TwoFaModal from '@/views/setting/TwoFaModal.vue' import { NButton, NDataTable, NInput, NPopconfirm, NSwitch } from 'naive-ui' import { useGettext } from 'vue3-gettext' @@ -11,6 +12,7 @@ const { $gettext } = useGettext() const currentID = ref(0) const passwordModal = ref(false) const twoFaModal = ref(false) +const tokenModal = ref(false) const columns: any = [ { @@ -73,7 +75,7 @@ const columns: any = [ { title: $gettext('Actions'), key: 'actions', - width: 260, + width: 380, hideInExcel: true, render(row: any) { return [ @@ -82,6 +84,22 @@ const columns: any = [ { size: 'small', type: 'primary', + onClick: () => { + currentID.value = row.id + tokenModal.value = true + } + }, + { + default: () => $gettext('Access Token'), + icon: renderIcon('material-symbols:edit-outline', { size: 14 }) + } + ), + h( + NButton, + { + size: 'small', + type: 'primary', + style: 'margin-left: 15px;', onClick: () => { currentID.value = row.id passwordModal.value = true @@ -176,6 +194,7 @@ onMounted(() => { + diff --git a/web/src/views/setting/TokenModal.vue b/web/src/views/setting/TokenModal.vue new file mode 100644 index 00000000..8d2ee9c4 --- /dev/null +++ b/web/src/views/setting/TokenModal.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/web/src/views/setting/TwoFaModal.vue b/web/src/views/setting/TwoFaModal.vue index 1c9f612a..891b4888 100644 --- a/web/src/views/setting/TwoFaModal.vue +++ b/web/src/views/setting/TwoFaModal.vue @@ -63,7 +63,7 @@ watch( }} - {{ model.url }} + {{ model.url }}