1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: 增加系统设置接口

This commit is contained in:
ssongliu 2022-09-08 18:47:15 +08:00 committed by ssongliu
parent 2a1403904f
commit 5342b758cb
36 changed files with 288 additions and 518 deletions

View File

@ -3,25 +3,6 @@ system:
db_type: sqlite db_type: sqlite
level: debug level: debug
jwt:
header_name: Authorization
signing_key: 1panelKey
expires_time: 604800 #过期时间
buffer_time: 86400 #缓冲时间 缓冲时间内会获取新的token刷新令牌
issuer: 1Panel
session:
session_name: psession
expires_time: 604800
captcha:
enable: true
source: "1234567890QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuioplkjhgfdsazxcvbnm"
length: 4
noise-count: 0
img-width: 120
img-height: 50
mysql: mysql:
path: localhost path: localhost
port: 3306 port: 3306

View File

@ -0,0 +1,51 @@
package v1
import (
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/captcha"
"github.com/gin-gonic/gin"
)
type BaseApi struct{}
func (b *BaseApi) Login(c *gin.Context) {
var req dto.Login
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
user, err := authService.Login(c, req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, user)
}
func (b *BaseApi) LogOut(c *gin.Context) {
if err := authService.LogOut(c); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) Captcha(c *gin.Context) {
captcha, err := captcha.CreateCaptcha()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
}
helper.SuccessWithData(c, captcha)
}

View File

@ -9,7 +9,7 @@ type ApiGroup struct {
var ApiGroupApp = new(ApiGroup) var ApiGroupApp = new(ApiGroup)
var ( var (
userService = service.ServiceGroupApp.UserService authService = service.ServiceGroupApp.AuthService
hostService = service.ServiceGroupApp.HostService hostService = service.ServiceGroupApp.HostService
groupService = service.ServiceGroupApp.GroupService groupService = service.ServiceGroupApp.GroupService
commandService = service.ServiceGroupApp.CommandService commandService = service.ServiceGroupApp.CommandService

View File

@ -28,7 +28,25 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
return return
} }
if err := settingService.Update(req.Key, req.Value); err != nil { if err := settingService.Update(c, req.Key, req.Value); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) UpdatePassword(c *gin.Context) {
var req dto.PasswordUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := settingService.UpdatePassword(c, req.OldPassword, req.NewPassword); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }

View File

@ -1,145 +0,0 @@
package v1
import (
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/captcha"
"github.com/gin-gonic/gin"
)
type BaseApi struct{}
func (b *BaseApi) Login(c *gin.Context) {
var req dto.Login
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
user, err := userService.Login(c, req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, user)
}
func (b *BaseApi) LogOut(c *gin.Context) {
if err := userService.LogOut(c); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) Captcha(c *gin.Context) {
captcha, err := captcha.CreateCaptcha()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
}
helper.SuccessWithData(c, captcha)
}
func (b *BaseApi) Register(c *gin.Context) {
var req dto.UserCreate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := userService.Register(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) PageUsers(c *gin.Context) {
var req dto.SearchWithPage
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, list, err := userService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
func (b *BaseApi) DeleteUser(c *gin.Context) {
var req dto.BatchDeleteReq
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := userService.BatchDelete(req.Ids); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) UpdateUser(c *gin.Context) {
var req dto.UserUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
upMap := make(map[string]interface{})
upMap["email"] = req.Email
upMap["name"] = req.Name
if err := userService.Update(id, upMap); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) GetUserInfo(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
user, err := userService.Get(id)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, user)
}

View File

@ -30,3 +30,8 @@ type SettingUpdate struct {
Key string `json:"key" validate:"required"` Key string `json:"key" validate:"required"`
Value string `json:"value"` Value string `json:"value"`
} }
type PasswordUpdate struct {
OldPassword string `json:"oldPassword" validate:"required"`
NewPassword string `json:"newPassword" validate:"required"`
}

View File

@ -1,33 +1,10 @@
package dto package dto
import (
"time"
)
type UserCreate struct {
Name string `json:"name" validate:"name,required"`
Password string `json:"password" validate:"required"`
Email string `json:"email" validate:"required,email"`
}
type CaptchaResponse struct { type CaptchaResponse struct {
CaptchaID string `json:"captchaID"` CaptchaID string `json:"captchaID"`
ImagePath string `json:"imagePath"` ImagePath string `json:"imagePath"`
} }
type UserUpdate struct {
ID uint `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
}
type UserInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"createdAt"`
}
type UserLoginInfo struct { type UserLoginInfo struct {
Name string `json:"name"` Name string `json:"name"`
Token string `json:"token"` Token string `json:"token"`

View File

@ -1,10 +0,0 @@
package model
import "gorm.io/gorm"
type User struct {
gorm.Model
Name string `json:"name" gorm:"type:varchar(64);not null;"`
Password string `json:"password" gorm:"type:varchar(64)"`
Email string `json:"email" gorm:"type:varchar(64);not null;"`
}

View File

@ -1,7 +1,6 @@
package repo package repo
type RepoGroup struct { type RepoGroup struct {
UserRepo
HostRepo HostRepo
GroupRepo GroupRepo
CommandRepo CommandRepo

View File

@ -3,20 +3,23 @@ package repo
import ( import (
"github.com/1Panel-dev/1Panel/app/model" "github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/global" "github.com/1Panel-dev/1Panel/global"
"gorm.io/gorm"
) )
type SettingRepo struct{} type SettingRepo struct{}
type ISettingRepo interface { type ISettingRepo interface {
Get(opts ...DBOption) ([]model.Setting, error) GetList(opts ...DBOption) ([]model.Setting, error)
Get(opts ...DBOption) (model.Setting, error)
Update(key, value string) error Update(key, value string) error
WithByKey(key string) DBOption
} }
func NewISettingService() ISettingRepo { func NewISettingRepo() ISettingRepo {
return &SettingRepo{} return &SettingRepo{}
} }
func (u *SettingRepo) Get(opts ...DBOption) ([]model.Setting, error) { func (u *SettingRepo) GetList(opts ...DBOption) ([]model.Setting, error) {
var settings []model.Setting var settings []model.Setting
db := global.DB.Model(&model.Setting{}) db := global.DB.Model(&model.Setting{})
for _, opt := range opts { for _, opt := range opts {
@ -26,6 +29,22 @@ func (u *SettingRepo) Get(opts ...DBOption) ([]model.Setting, error) {
return settings, err return settings, err
} }
func (u *SettingRepo) Get(opts ...DBOption) (model.Setting, error) {
var settings model.Setting
db := global.DB.Model(&model.Setting{})
for _, opt := range opts {
db = opt(db)
}
err := db.First(&settings).Error
return settings, err
}
func (c *SettingRepo) WithByKey(key string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("key = ?", key)
}
}
func (u *SettingRepo) Update(key, value string) error { func (u *SettingRepo) Update(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
} }

View File

@ -1,75 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/global"
)
type UserRepo struct{}
type IUserRepo interface {
Get(opts ...DBOption) (model.User, error)
GetList(opts ...DBOption) ([]model.User, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.User, error)
Create(user *model.User) error
Update(id uint, vars map[string]interface{}) error
Save(user model.User) error
Delete(opts ...DBOption) error
}
func NewIUserService() IUserRepo {
return &UserRepo{}
}
func (u *UserRepo) Get(opts ...DBOption) (model.User, error) {
var user model.User
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&user).Error
return user, err
}
func (u *UserRepo) GetList(opts ...DBOption) ([]model.User, error) {
var users []model.User
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&users).Error
return users, err
}
func (u *UserRepo) Page(page, size int, opts ...DBOption) (int64, []model.User, error) {
var users []model.User
db := global.DB.Model(&model.User{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
return count, users, err
}
func (u *UserRepo) Create(user *model.User) error {
return global.DB.Create(user).Error
}
func (u *UserRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.User{}).Where("id = ?", id).Updates(vars).Error
}
func (u *UserRepo) Save(user model.User) error {
return global.DB.Save(user).Error
}
func (u *UserRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.User{}).Error
}

View File

@ -0,0 +1,91 @@
package service
import (
"strconv"
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/encrypt"
"github.com/1Panel-dev/1Panel/utils/jwt"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
)
type AuthService struct{}
type IAuthService interface {
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error
}
func NewIAuthService() IAuthService {
return &AuthService{}
}
func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) {
nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
pass, err := encrypt.StringDecrypt(passwrodSetting.Value)
if err != nil {
return nil, err
}
if info.Password != pass {
return nil, errors.New("login failed")
}
setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout"))
if err != nil {
return nil, err
}
lifeTime, err := strconv.Atoi(setting.Value)
if err != nil {
return nil, err
}
if info.AuthMethod == constant.AuthMethodJWT {
j := jwt.NewJWT()
claims := j.CreateClaims(jwt.BaseClaims{
Name: nameSetting.Value,
}, lifeTime)
token, err := j.CreateToken(claims)
if err != nil {
return nil, err
}
return &dto.UserLoginInfo{Name: nameSetting.Value, Token: token}, err
}
sID, _ := c.Cookie(constant.SessionName)
sessionUser, err := global.SESSION.Get(sID)
if err != nil {
sID = uuid.NewV4().String()
c.SetCookie(constant.SessionName, sID, lifeTime, "", "", false, false)
err := global.SESSION.Set(sID, sessionUser, lifeTime)
if err != nil {
return nil, err
}
return &dto.UserLoginInfo{Name: nameSetting.Value}, nil
}
if err := global.SESSION.Set(sID, sessionUser, lifeTime); err != nil {
return nil, err
}
return &dto.UserLoginInfo{Name: nameSetting.Value}, nil
}
func (u *AuthService) LogOut(c *gin.Context) error {
sID, _ := c.Cookie(constant.SessionName)
if sID != "" {
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
err := global.SESSION.Delete(sID)
if err != nil {
return err
}
}
return nil
}

View File

@ -3,7 +3,7 @@ package service
import "github.com/1Panel-dev/1Panel/app/repo" import "github.com/1Panel-dev/1Panel/app/repo"
type ServiceGroup struct { type ServiceGroup struct {
UserService AuthService
HostService HostService
GroupService GroupService
CommandService CommandService
@ -15,7 +15,6 @@ type ServiceGroup struct {
var ServiceGroupApp = new(ServiceGroup) var ServiceGroupApp = new(ServiceGroup)
var ( var (
userRepo = repo.RepoGroupApp.UserRepo
hostRepo = repo.RepoGroupApp.HostRepo hostRepo = repo.RepoGroupApp.HostRepo
groupRepo = repo.RepoGroupApp.GroupRepo groupRepo = repo.RepoGroupApp.GroupRepo
commandRepo = repo.RepoGroupApp.CommandRepo commandRepo = repo.RepoGroupApp.CommandRepo

View File

@ -6,13 +6,17 @@ import (
"github.com/1Panel-dev/1Panel/app/dto" "github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/encrypt"
"github.com/gin-gonic/gin"
) )
type SettingService struct{} type SettingService struct{}
type ISettingService interface { type ISettingService interface {
GetSettingInfo() (*dto.SettingInfo, error) GetSettingInfo() (*dto.SettingInfo, error)
Update(key, value string) error Update(c *gin.Context, key, value string) error
UpdatePassword(c *gin.Context, old, new string) error
} }
func NewISettingService() ISettingService { func NewISettingService() ISettingService {
@ -20,7 +24,7 @@ func NewISettingService() ISettingService {
} }
func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) { func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
setting, err := settingRepo.Get() setting, err := settingRepo.GetList()
if err != nil { if err != nil {
return nil, constant.ErrRecordNotFound return nil, constant.ErrRecordNotFound
} }
@ -40,6 +44,50 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
return &info, err return &info, err
} }
func (u *SettingService) Update(key, value string) error { func (u *SettingService) Update(c *gin.Context, key, value string) error {
if err := settingRepo.Update(key, value); err != nil {
return err
}
switch key {
case "UserName":
sID, _ := c.Cookie(constant.SessionName)
if sID != "" {
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
err := global.SESSION.Delete(sID)
if err != nil {
return err
}
}
}
return settingRepo.Update(key, value) return settingRepo.Update(key, value)
} }
func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
setting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
if err != nil {
return err
}
passwordFromDB, err := encrypt.StringDecrypt(setting.Value)
if err != nil {
return err
}
if passwordFromDB == old {
newPassword, err := encrypt.StringEncrypt(new)
if err != nil {
return err
}
if err := settingRepo.Update("Password", newPassword); err != nil {
return err
}
sID, _ := c.Cookie(constant.SessionName)
if sID != "" {
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
err := global.SESSION.Delete(sID)
if err != nil {
return err
}
}
return nil
}
return constant.ErrInitialPassword
}

View File

@ -1,139 +0,0 @@
package service
import (
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/encrypt"
"github.com/1Panel-dev/1Panel/utils/jwt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
)
type UserService struct{}
type IUserService interface {
Get(id uint) (*dto.UserInfo, error)
Page(search dto.SearchWithPage) (int64, interface{}, error)
Register(userDto dto.UserCreate) error
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error
Delete(name string) error
Save(req model.User) error
Update(id uint, upMap map[string]interface{}) error
BatchDelete(ids []uint) error
}
func NewIUserService() IUserService {
return &UserService{}
}
func (u *UserService) Get(id uint) (*dto.UserInfo, error) {
user, err := userRepo.Get(commonRepo.WithByID(id))
if err != nil {
return nil, constant.ErrRecordNotFound
}
var dtoUser dto.UserInfo
if err := copier.Copy(&dtoUser, &user); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
return &dtoUser, err
}
func (u *UserService) Page(search dto.SearchWithPage) (int64, interface{}, error) {
total, users, err := userRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info))
var dtoUsers []dto.UserInfo
for _, user := range users {
var item dto.UserInfo
if err := copier.Copy(&item, &user); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoUsers = append(dtoUsers, item)
}
return total, dtoUsers, err
}
func (u *UserService) Register(userDto dto.UserCreate) error {
user, _ := userRepo.Get(commonRepo.WithByName(userDto.Name))
if user.ID != 0 {
return constant.ErrRecordExist
}
if err := copier.Copy(&user, &userDto); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
return userRepo.Create(&user)
}
func (u *UserService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) {
user, err := userRepo.Get(commonRepo.WithByName(info.Name))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
pass, err := encrypt.StringDecrypt(user.Password)
if err != nil {
return nil, err
}
if info.Password != pass {
return nil, errors.New("login failed")
}
if info.AuthMethod == constant.AuthMethodJWT {
j := jwt.NewJWT()
claims := j.CreateClaims(jwt.BaseClaims{
ID: user.ID,
Name: user.Name,
})
token, err := j.CreateToken(claims)
if err != nil {
return nil, err
}
return &dto.UserLoginInfo{Name: user.Name, Token: token}, err
}
lifeTime := global.CONF.Session.ExpiresTime
sID, _ := c.Cookie(global.CONF.Session.SessionName)
sessionUser, err := global.SESSION.Get(sID)
if err != nil {
sID = uuid.NewV4().String()
c.SetCookie(global.CONF.Session.SessionName, sID, lifeTime, "", "", false, false)
err := global.SESSION.Set(sID, sessionUser, lifeTime)
if err != nil {
return nil, err
}
return &dto.UserLoginInfo{Name: user.Name}, nil
}
if err := global.SESSION.Set(sID, sessionUser, lifeTime); err != nil {
return nil, err
}
return &dto.UserLoginInfo{Name: user.Name}, nil
}
func (u *UserService) LogOut(c *gin.Context) error {
sID, _ := c.Cookie(global.CONF.Session.SessionName)
if sID != "" {
c.SetCookie(global.CONF.Session.SessionName, sID, -1, "", "", false, false)
err := global.SESSION.Delete(sID)
if err != nil {
return err
}
}
return nil
}
func (u *UserService) Delete(name string) error {
return userRepo.Delete(commonRepo.WithByName(name))
}
func (u *UserService) BatchDelete(ids []uint) error {
return userRepo.Delete(commonRepo.WithIdsIn(ids))
}
func (u *UserService) Save(req model.User) error {
return userRepo.Save(req)
}
func (u *UserService) Update(id uint, upMap map[string]interface{}) error {
return userRepo.Update(id, upMap)
}

View File

@ -1,10 +0,0 @@
package configs
type Captcha struct {
Enable bool `mapstructure:"enable" json:"enable" yaml:"enable"`
Source string `mapstructure:"source" json:"source" yaml:"source"`
Length int `mapstructure:"length" json:"length" yaml:"length"`
NoiseCount int `mapstructure:"noise-count" json:"noise-count" yaml:"noise-count"`
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"`
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"`
}

View File

@ -5,10 +5,7 @@ type ServerConfig struct {
Mysql Mysql `mapstructure:"mysql"` Mysql Mysql `mapstructure:"mysql"`
System System `mapstructure:"system"` System System `mapstructure:"system"`
LogConfig LogConfig `mapstructure:"log"` LogConfig LogConfig `mapstructure:"log"`
JWT JWT `mapstructure:"jwt"`
Session Session `mapstructure:"session"`
CORS CORS `mapstructure:"cors"` CORS CORS `mapstructure:"cors"`
Captcha Captcha `mapstructure:"captcha"`
Encrypt Encrypt `mapstructure:"encrypt"` Encrypt Encrypt `mapstructure:"encrypt"`
Csrf Csrf `mapstructure:"csrf"` Csrf Csrf `mapstructure:"csrf"`
Cache Cache `mapstructure:"cache"` Cache Cache `mapstructure:"cache"`

View File

@ -1,9 +0,0 @@
package configs
type JWT struct {
HeaderName string `mapstructure:"header_name"`
SigningKey string `mapstructure:"signing_key"`
ExpiresTime int64 `mapstructure:"expires_time"`
BufferTime int64 `mapstructure:"buffer_time"`
Issuer string `mapstructure:"issuer"`
}

View File

@ -1,6 +0,0 @@
package configs
type Session struct {
SessionName string `mapstructure:"session_name"`
ExpiresTime int `mapstructure:"expires_time"`
}

View File

@ -20,6 +20,7 @@ var (
ErrRecordExist = errors.New("ErrRecordExist") ErrRecordExist = errors.New("ErrRecordExist")
ErrRecordNotFound = errors.New("ErrRecordNotFound") ErrRecordNotFound = errors.New("ErrRecordNotFound")
ErrStructTransform = errors.New("ErrStructTransform") ErrStructTransform = errors.New("ErrStructTransform")
ErrInitialPassword = errors.New("ErrInitialPassword")
ErrTokenParse = errors.New("ErrTokenParse") ErrTokenParse = errors.New("ErrTokenParse")

View File

@ -2,5 +2,11 @@ package constant
const ( const (
AuthMethodSession = "session" AuthMethodSession = "session"
SessionName = "psession"
AuthMethodJWT = "jwt" AuthMethodJWT = "jwt"
JWTHeaderName = "Authorization"
JWTSigningKey = "1panelKey"
JWTBufferTime = 86400
JWTIssuer = "1Panel"
) )

View File

@ -3,6 +3,7 @@ ErrToken: "Token information is incorrect.: {{ .detail }}"
ErrTokenParse: "Token generation error: {{ .detail }}" ErrTokenParse: "Token generation error: {{ .detail }}"
ErrTokenTimeOut: "Login information is out of date: {{ .detail }}" ErrTokenTimeOut: "Login information is out of date: {{ .detail }}"
ErrCaptchaCode: "The verification code information is incorrect" ErrCaptchaCode: "The verification code information is incorrect"
ErrInitialPassword: "Initial password error: {{ .detail }}"
ErrInternalServer: "Service internal error: {{ .detail }}" ErrInternalServer: "Service internal error: {{ .detail }}"
ErrRecordExist: "Record already exists: {{ .detail }}" ErrRecordExist: "Record already exists: {{ .detail }}"
ErrRecordNotFound: "Records not found: {{ .detail }}" ErrRecordNotFound: "Records not found: {{ .detail }}"

View File

@ -3,6 +3,7 @@ ErrToken: "Token 信息错误: {{ .detail }}"
ErrTokenParse: "Token 生成错误: {{ .detail }}" ErrTokenParse: "Token 生成错误: {{ .detail }}"
ErrTokenTimeOut: "登陆信息已过期: {{ .detail }}" ErrTokenTimeOut: "登陆信息已过期: {{ .detail }}"
ErrCaptchaCode: "错误的验证码信息" ErrCaptchaCode: "错误的验证码信息"
ErrInitialPassword: "初始密码错误: {{ .detail }}"
ErrInternalServer: "服务内部错误: {{ .detail }}" ErrInternalServer: "服务内部错误: {{ .detail }}"
ErrRecordExist: "记录已存在: {{ .detail }}" ErrRecordExist: "记录已存在: {{ .detail }}"
ErrRecordNotFound: "记录未能找到: {{ .detail }}" ErrRecordNotFound: "记录未能找到: {{ .detail }}"

View File

@ -9,8 +9,6 @@ import (
func Init() { func Init() {
m := gormigrate.New(global.DB, gormigrate.DefaultOptions, []*gormigrate.Migration{ m := gormigrate.New(global.DB, gormigrate.DefaultOptions, []*gormigrate.Migration{
migrations.InitTable,
migrations.AddData,
migrations.AddTableOperationLog, migrations.AddTableOperationLog,
migrations.AddTableHost, migrations.AddTableHost,
migrations.AddTableMonitor, migrations.AddTableMonitor,

View File

@ -9,24 +9,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
var InitTable = &gormigrate.Migration{
ID: "20220803-init-table",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.User{})
},
}
var user = model.User{
Name: "admin", Email: "admin@fit2cloud.com", Password: "5WYEZ4XcitdomVvAyimt9WwJwBJJSbTTHncZoqyOraQ=",
}
var AddData = &gormigrate.Migration{
ID: "20200803-add-data",
Migrate: func(tx *gorm.DB) error {
return tx.Create(&user).Error
},
}
var AddTableOperationLog = &gormigrate.Migration{ var AddTableOperationLog = &gormigrate.Migration{
ID: "20200809-add-table-operation-log", ID: "20200809-add-table-operation-log",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {

View File

@ -38,7 +38,6 @@ func Routers() *gin.Engine {
PrivateGroup := Router.Group("/api/v1") PrivateGroup := Router.Group("/api/v1")
{ {
systemRouter.InitBaseRouter(PrivateGroup) systemRouter.InitBaseRouter(PrivateGroup)
systemRouter.InitUserRouter(PrivateGroup)
systemRouter.InitHostRouter(PrivateGroup) systemRouter.InitHostRouter(PrivateGroup)
systemRouter.InitGroupRouter(PrivateGroup) systemRouter.InitGroupRouter(PrivateGroup)
systemRouter.InitCommandRouter(PrivateGroup) systemRouter.InitCommandRouter(PrivateGroup)

View File

@ -1,9 +1,11 @@
package middleware package middleware
import ( import (
"strconv"
"time" "time"
"github.com/1Panel-dev/1Panel/app/api/v1/helper" "github.com/1Panel-dev/1Panel/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/app/repo"
"github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global" "github.com/1Panel-dev/1Panel/global"
jwtUtils "github.com/1Panel-dev/1Panel/utils/jwt" jwtUtils "github.com/1Panel-dev/1Panel/utils/jwt"
@ -15,7 +17,7 @@ import (
func JwtAuth() gin.HandlerFunc { func JwtAuth() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
c.Set("authMethod", "") c.Set("authMethod", "")
token := c.Request.Header.Get(global.CONF.JWT.HeaderName) token := c.Request.Header.Get(constant.JWTHeaderName)
if token == "" { if token == "" {
c.Next() c.Next()
return return
@ -27,7 +29,13 @@ func JwtAuth() gin.HandlerFunc {
return return
} }
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime { if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(global.CONF.JWT.ExpiresTime))) settingRepo := repo.NewISettingRepo()
setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout"))
if err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
}
lifeTime, _ := strconv.Atoi(setting.Value)
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(lifeTime)))
} }
c.Set("claims", claims) c.Set("claims", claims)
c.Set("authMethod", constant.AuthMethodJWT) c.Set("authMethod", constant.AuthMethodJWT)

View File

@ -12,7 +12,7 @@ func SessionAuth() gin.HandlerFunc {
if method, exist := c.Get("authMethod"); exist && method == constant.AuthMethodJWT { if method, exist := c.Get("authMethod"); exist && method == constant.AuthMethodJWT {
c.Next() c.Next()
} }
sId, err := c.Cookie(global.CONF.Session.SessionName) sId, err := c.Cookie(constant.SessionName)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeNotLogin, nil) helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeNotLogin, nil)
return return

View File

@ -2,13 +2,13 @@ package router
type RouterGroup struct { type RouterGroup struct {
BaseRouter BaseRouter
UserRouter
HostRouter HostRouter
GroupRouter GroupRouter
CommandRouter CommandRouter
MonitorRouter MonitorRouter
OperationLogRouter OperationLogRouter
FileRouter FileRouter
TerminalRouter
SettingRouter SettingRouter
} }

View File

@ -14,5 +14,6 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
{ {
settingRouter.POST("/search", baseApi.GetSettingInfo) settingRouter.POST("/search", baseApi.GetSettingInfo)
settingRouter.PUT("", baseApi.UpdateSetting) settingRouter.PUT("", baseApi.UpdateSetting)
settingRouter.PUT("/password", baseApi.UpdatePassword)
} }
} }

View File

@ -9,7 +9,7 @@ import (
type TerminalRouter struct{} type TerminalRouter struct{}
func (s *UserRouter) InitTerminalRouter(Router *gin.RouterGroup) { func (s *TerminalRouter) InitTerminalRouter(Router *gin.RouterGroup) {
terminalRouter := Router.Group("terminals").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) terminalRouter := Router.Group("terminals").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
baseApi := v1.ApiGroupApp.BaseApi baseApi := v1.ApiGroupApp.BaseApi
{ {

View File

@ -1,24 +0,0 @@
package router
import (
v1 "github.com/1Panel-dev/1Panel/app/api/v1"
"github.com/1Panel-dev/1Panel/middleware"
"github.com/gin-gonic/gin"
)
type UserRouter struct{}
func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) {
userRouter := Router.Group("users")
userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
withRecordRouter := userRouter.Use(middleware.OperationRecord())
baseApi := v1.ApiGroupApp.BaseApi
{
withRecordRouter.POST("", baseApi.Register)
withRecordRouter.POST("/del", baseApi.DeleteUser)
userRouter.POST("/search", baseApi.PageUsers)
userRouter.GET(":id", baseApi.GetUserInfo)
userRouter.PUT(":id", baseApi.UpdateUser)
}
}

View File

@ -5,16 +5,12 @@ import (
"github.com/1Panel-dev/1Panel/app/dto" "github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/mojocn/base64Captcha" "github.com/mojocn/base64Captcha"
) )
var store = base64Captcha.DefaultMemStore var store = base64Captcha.DefaultMemStore
func VerifyCode(codeID string, code string) error { func VerifyCode(codeID string, code string) error {
if !global.CONF.Captcha.Enable {
return nil
}
if codeID == "" { if codeID == "" {
return constant.ErrCaptchaCode return constant.ErrCaptchaCode
} }
@ -30,11 +26,11 @@ func VerifyCode(codeID string, code string) error {
func CreateCaptcha() (*dto.CaptchaResponse, error) { func CreateCaptcha() (*dto.CaptchaResponse, error) {
var driverString base64Captcha.DriverString var driverString base64Captcha.DriverString
driverString.Source = global.CONF.Captcha.Source driverString.Source = "1234567890QWERTYUPLKJHGFDSAZXCVBNMqwertyuplkjhgfdsazxcvbnm"
driverString.Width = global.CONF.Captcha.ImgWidth driverString.Width = 120
driverString.Height = global.CONF.Captcha.ImgHeight driverString.Height = 50
driverString.NoiseCount = global.CONF.Captcha.NoiseCount driverString.NoiseCount = 0
driverString.Length = global.CONF.Captcha.Length driverString.Length = 4
driverString.Fonts = []string{"wqy-microhei.ttc"} driverString.Fonts = []string{"wqy-microhei.ttc"}
driver := driverString.ConvertFonts() driver := driverString.ConvertFonts()
c := base64Captcha.NewCaptcha(driver, store) c := base64Captcha.NewCaptcha(driver, store)

View File

@ -4,7 +4,6 @@ import (
"time" "time"
"github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
@ -32,17 +31,17 @@ type BaseClaims struct {
func NewJWT() *JWT { func NewJWT() *JWT {
return &JWT{ return &JWT{
[]byte(global.CONF.JWT.SigningKey), []byte(constant.JWTSigningKey),
} }
} }
func (j *JWT) CreateClaims(baseClaims BaseClaims) CustomClaims { func (j *JWT) CreateClaims(baseClaims BaseClaims, ttl int) CustomClaims {
claims := CustomClaims{ claims := CustomClaims{
BaseClaims: baseClaims, BaseClaims: baseClaims,
BufferTime: global.CONF.JWT.BufferTime, BufferTime: constant.JWTBufferTime,
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(global.CONF.JWT.ExpiresTime))), ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(ttl))),
Issuer: global.CONF.JWT.Issuer, Issuer: constant.JWTIssuer,
}, },
} }
return claims return claims

View File

@ -8,3 +8,7 @@ export const getSettingInfo = () => {
export const updateSetting = (param: Setting.SettingUpdate) => { export const updateSetting = (param: Setting.SettingUpdate) => {
return http.put(`/settings`, param); return http.put(`/settings`, param);
}; };
export const updatePassword = (param: Setting.PasswordUpdate) => {
return http.put(`/settings/password`, param);
};

View File

@ -144,12 +144,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { ElMessage, ElForm } from 'element-plus'; import { ElMessage, ElForm } from 'element-plus';
import { updateSetting } from '@/api/modules/setting'; import { updateSetting, updatePassword } from '@/api/modules/setting';
import { Setting } from '@/api/interface/setting'; import { Setting } from '@/api/interface/setting';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import { useTheme } from '@/hooks/use-theme'; import { useTheme } from '@/hooks/use-theme';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rues';
import router from '@/routers/router';
const i18n = useI18n(); const i18n = useI18n();
const globalStore = GlobalStore(); const globalStore = GlobalStore();
@ -210,5 +211,11 @@ function checkPassword(rule: any, value: any, callback: any) {
} }
callback(); callback();
} }
function submitChangePassword() {} const submitChangePassword = async () => {
await updatePassword(passForm);
passwordVisiable.value = false;
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
router.push({ name: 'login' });
globalStore.setLogStatus(false);
};
</script> </script>