未验证 提交 0076c7cd 编写于 作者: Mr.奇淼('s avatar Mr.奇淼( 提交者: GitHub

增加插件安装功能并提升其他功能稳定性 (#1155)

* 增加安装插件demo

* 前端上传demo

* 插件安装初版

* 调整菜单结构

* fix: 修改密码校验 jwt 中的用户 id

* router.go:fix  logger的中间件是默认的,这边日志可以删除;cors的日志打印应当在上面两行启用的时候再进行注释

* 登陆==>登录

* 抽离插件安装方法

* 调整插件功能 提升灵活度
Co-authored-by: Nzeromake <a390720046@gmail.com>
Co-authored-by: NLu JJ <275955589@qq.com>
Co-authored-by: Npnck <hio131@gmail.com>
Co-authored-by: Nfeixuanyu <994085848@qq.com>
上级 6d5dc43f
...@@ -3,17 +3,16 @@ package system ...@@ -3,17 +3,16 @@ package system
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
"net/url" "net/url"
"os" "os"
"strings" "strings"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.uber.org/zap" "go.uber.org/zap"
) )
...@@ -240,3 +239,18 @@ func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) { ...@@ -240,3 +239,18 @@ func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
response.Ok(c) response.Ok(c)
} }
} }
func (autoApi *AutoCodeApi) InstallPlugin(c *gin.Context) {
header, err := c.FormFile("plug")
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = autoCodeService.InstallPlugin(header)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
} else {
response.OkWithMessage("插件安装成功,请按照说明配置使用", c)
}
}
...@@ -138,18 +138,19 @@ func (b *BaseApi) Register(c *gin.Context) { ...@@ -138,18 +138,19 @@ func (b *BaseApi) Register(c *gin.Context) {
// @Summary 用户修改密码 // @Summary 用户修改密码
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Produce application/json // @Produce application/json
// @Param data body systemReq.ChangePasswordStruct true "用户名, 原密码, 新密码" // @Param data body systemReq.ChangePasswordReq true "用户名, 原密码, 新密码"
// @Success 200 {object} response.Response{msg=string} "用户修改密码" // @Success 200 {object} response.Response{msg=string} "用户修改密码"
// @Router /user/changePassword [post] // @Router /user/changePassword [post]
func (b *BaseApi) ChangePassword(c *gin.Context) { func (b *BaseApi) ChangePassword(c *gin.Context) {
var user systemReq.ChangePasswordStruct var req systemReq.ChangePasswordReq
_ = c.ShouldBindJSON(&user) _ = c.ShouldBindJSON(&req)
if err := utils.Verify(user, utils.ChangePasswordVerify); err != nil { if err := utils.Verify(req, utils.ChangePasswordVerify); err != nil {
response.FailWithMessage(err.Error(), c) response.FailWithMessage(err.Error(), c)
return return
} }
u := &system.SysUser{Username: user.Username, Password: user.Password} uid := utils.GetUserID(c)
if _, err := userService.ChangePassword(u, user.NewPassword); err != nil { u := &system.SysUser{GVA_MODEL: global.GVA_MODEL{ID: uid}, Password: req.Password}
if _, err := userService.ChangePassword(u, req.NewPassword); err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err)) global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage("修改失败,原密码与当前账户不符", c) response.FailWithMessage("修改失败,原密码与当前账户不符", c)
} else { } else {
...@@ -201,8 +202,7 @@ func (b *BaseApi) SetUserAuthority(c *gin.Context) { ...@@ -201,8 +202,7 @@ func (b *BaseApi) SetUserAuthority(c *gin.Context) {
return return
} }
userID := utils.GetUserID(c) userID := utils.GetUserID(c)
uuid := utils.GetUserUuid(c) if err := userService.SetUserAuthority(userID, sua.AuthorityId); err != nil {
if err := userService.SetUserAuthority(userID, uuid, sua.AuthorityId); err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err)) global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c) response.FailWithMessage(err.Error(), c)
} else { } else {
......
...@@ -29,6 +29,7 @@ require ( ...@@ -29,6 +29,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/mapstructure v1.2.2 // indirect github.com/mitchellh/mapstructure v1.2.2 // indirect
github.com/mojocn/base64Captcha v1.3.1 github.com/mojocn/base64Captcha v1.3.1
github.com/otiai10/copy v1.7.0
github.com/pelletier/go-toml v1.6.0 // indirect github.com/pelletier/go-toml v1.6.0 // indirect
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/qiniu/api.v7/v7 v7.4.1 github.com/qiniu/api.v7/v7 v7.4.1
......
...@@ -424,6 +424,13 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J ...@@ -424,6 +424,13 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI=
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
......
package initialize package initialize
import ( import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global" "github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email" "github.com/flipped-aurora/gin-vue-admin/server/plugin/email"
"github.com/flipped-aurora/gin-vue-admin/server/utils/plugin" "github.com/flipped-aurora/gin-vue-admin/server/utils/plugin"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
...@@ -14,7 +16,12 @@ func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) { ...@@ -14,7 +16,12 @@ func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) {
} }
} }
func InstallPlugin(PublicGroup *gin.RouterGroup, PrivateGroup *gin.RouterGroup) { func InstallPlugin(Router *gin.Engine) {
PublicGroup := Router.Group("")
fmt.Println("无鉴权插件安装==》", PublicGroup)
PrivateGroup := Router.Group("")
fmt.Println("鉴权插件安装==》", PrivateGroup)
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
// 添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同 // 添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同
PluginInit(PrivateGroup, email.CreateEmailPlug( PluginInit(PrivateGroup, email.CreateEmailPlug(
global.GVA_CONFIG.Email.To, global.GVA_CONFIG.Email.To,
......
...@@ -29,11 +29,10 @@ func Routers() *gin.Engine { ...@@ -29,11 +29,10 @@ func Routers() *gin.Engine {
Router.StaticFS(global.GVA_CONFIG.Local.Path, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址 Router.StaticFS(global.GVA_CONFIG.Local.Path, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址
// Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件") // Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
global.GVA_LOG.Info("use middleware logger")
// 跨域,如需跨域可以打开下面的注释 // 跨域,如需跨域可以打开下面的注释
// Router.Use(middleware.Cors()) // 直接放行全部跨域请求 // Router.Use(middleware.Cors()) // 直接放行全部跨域请求
//Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求 //Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
global.GVA_LOG.Info("use middleware cors") //global.GVA_LOG.Info("use middleware cors")
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
global.GVA_LOG.Info("register swagger handler") global.GVA_LOG.Info("register swagger handler")
// 方便统一添加路由组前缀 多服务器上线使用 // 方便统一添加路由组前缀 多服务器上线使用
...@@ -75,7 +74,7 @@ func Routers() *gin.Engine { ...@@ -75,7 +74,7 @@ func Routers() *gin.Engine {
// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT. // Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
} }
InstallPlugin(PublicGroup, PrivateGroup) // 安装插件 InstallPlugin(Router) // 安装插件
global.GVA_LOG.Info("router register success") global.GVA_LOG.Info("router register success")
return Router return Router
......
...@@ -22,8 +22,8 @@ type Login struct { ...@@ -22,8 +22,8 @@ type Login struct {
} }
// Modify password structure // Modify password structure
type ChangePasswordStruct struct { type ChangePasswordReq struct {
Username string `json:"username"` // 用户名 ID uint `json:"-"` // 从 JWT 中提取 user id,避免越权
Password string `json:"password"` // 密码 Password string `json:"password"` // 密码
NewPassword string `json:"newPassword"` // 新密码 NewPassword string `json:"newPassword"` // 新密码
} }
...@@ -42,7 +42,7 @@ type SetUserAuthorities struct { ...@@ -42,7 +42,7 @@ type SetUserAuthorities struct {
type ChangeUserInfo struct { type ChangeUserInfo struct {
ID uint `gorm:"primarykey"` // 主键ID ID uint `gorm:"primarykey"` // 主键ID
NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称
Phone string `json:"phone" gorm:"comment:用户手机号"` // 用户角色ID Phone string `json:"phone" gorm:"comment:用户手机号"` // 用户手机号
AuthorityIds []uint `json:"authorityIds" gorm:"-"` // 角色ID AuthorityIds []uint `json:"authorityIds" gorm:"-"` // 角色ID
Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱 Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱
HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像 HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
......
package system package system
type SysUseAuthority struct { // SysUserAuthority 是 sysUser 和 sysAuthority 的连接表
type SysUserAuthority struct {
SysUserId uint `gorm:"column:sys_user_id"` SysUserId uint `gorm:"column:sys_user_id"`
SysAuthorityAuthorityId uint `gorm:"column:sys_authority_authority_id"` SysAuthorityAuthorityId uint `gorm:"column:sys_authority_authority_id"`
} }
func (s *SysUseAuthority) TableName() string { func (s *SysUserAuthority) TableName() string {
return "sys_user_authority" return "sys_user_authority"
} }
...@@ -20,5 +20,6 @@ func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup) { ...@@ -20,5 +20,6 @@ func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup) {
autoCodeRouter.POST("getPackage", autoCodeApi.GetPackage) // 获取package包 autoCodeRouter.POST("getPackage", autoCodeApi.GetPackage) // 获取package包
autoCodeRouter.POST("delPackage", autoCodeApi.DelPackage) // 删除package包 autoCodeRouter.POST("delPackage", autoCodeApi.DelPackage) // 删除package包
autoCodeRouter.POST("createPlug", autoCodeApi.AutoPlug) // 自动插件包模板 autoCodeRouter.POST("createPlug", autoCodeApi.AutoPlug) // 自动插件包模板
autoCodeRouter.POST("installPlugin", autoCodeApi.InstallPlugin) // 自动安装插件
} }
} }
...@@ -131,7 +131,7 @@ func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthor ...@@ -131,7 +131,7 @@ func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthor
return return
} }
} }
err = global.GVA_DB.Delete(&[]system.SysUseAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error err = global.GVA_DB.Delete(&[]system.SysUserAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error
err = global.GVA_DB.Delete(&[]system.SysAuthorityBtn{}, "authority_id = ?", auth.AuthorityId).Error err = global.GVA_DB.Delete(&[]system.SysAuthorityBtn{}, "authority_id = ?", auth.AuthorityId).Error
authorityId := strconv.Itoa(int(auth.AuthorityId)) authorityId := strconv.Itoa(int(auth.AuthorityId))
CasbinServiceApp.ClearCasbin(0, authorityId) CasbinServiceApp.ClearCasbin(0, authorityId)
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
cp "github.com/otiai10/copy"
"go.uber.org/zap" "go.uber.org/zap"
"go/ast" "go/ast"
"go/format" "go/format"
...@@ -12,8 +13,10 @@ import ( ...@@ -12,8 +13,10 @@ import (
"go/token" "go/token"
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"mime/multipart"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
...@@ -681,7 +684,7 @@ func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor { ...@@ -681,7 +684,7 @@ func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor {
case *ast.StructType: case *ast.StructType:
f := &ast.Field{ f := &ast.Field{
Names: []*ast.Ident{ Names: []*ast.Ident{
&ast.Ident{ {
Name: vi.StructName, Name: vi.StructName,
Obj: &ast.Object{ Obj: &ast.Object{
Kind: ast.Var, Kind: ast.Var,
...@@ -834,3 +837,81 @@ func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) erro ...@@ -834,3 +837,81 @@ func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) erro
} }
return nil return nil
} }
func (autoCodeService *AutoCodeService) InstallPlugin(file *multipart.FileHeader) (err error) {
const GVAPLUGPATH = "./gva-plug-temp/"
defer os.RemoveAll(GVAPLUGPATH)
_, err = os.Stat(GVAPLUGPATH)
if os.IsNotExist(err) {
os.Mkdir(GVAPLUGPATH, os.ModePerm)
}
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
out, err := os.Create(GVAPLUGPATH + file.Filename)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, src)
paths, err := utils.Unzip(GVAPLUGPATH+file.Filename, GVAPLUGPATH)
var webIndex = 0
var serverIndex = 0
for i := range paths {
paths[i] = filepath.ToSlash(paths[i])
pathArr := strings.Split(paths[i], "/")
if pathArr[len(pathArr)-2] == "server" && pathArr[len(pathArr)-1] == "plugin" {
serverIndex = i + 1
}
if pathArr[len(pathArr)-2] == "web" && pathArr[len(pathArr)-1] == "plugin" {
webIndex = i + 1
}
}
if webIndex == 0 && serverIndex == 0 {
fmt.Println("非标准插件,请按照文档自动迁移使用")
return errors.New("非标准插件,请按照文档自动迁移使用")
}
if webIndex != 0 {
webNameArr := strings.Split(filepath.ToSlash(paths[webIndex]), "/")
webName := webNameArr[len(webNameArr)-1]
var form = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + global.GVA_CONFIG.AutoCode.Server + "/" + paths[webIndex])
var to = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + global.GVA_CONFIG.AutoCode.Web + "/plugin/" + webName)
_, err := os.Stat(to)
if err == nil {
fmt.Println("web 已存在同名插件,请自行手动安装")
return errors.New("web 已存在同名插件,请自行手动安装")
}
err = cp.Copy(form, to)
if err != nil {
return err
}
}
if serverIndex != 0 {
serverNameArr := strings.Split(filepath.ToSlash(paths[serverIndex]), "/")
serverName := serverNameArr[len(serverNameArr)-1]
var form = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + global.GVA_CONFIG.AutoCode.Server + "/" + paths[serverIndex])
var to = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + global.GVA_CONFIG.AutoCode.Server + "/plugin/" + serverName)
_, err := os.Stat(to)
if err == nil {
fmt.Println("server 已存在同名插件,请自行手动安装")
return errors.New("server 已存在同名插件,请自行手动安装")
}
err = cp.Copy(form, to)
if err != nil {
return err
}
}
if err != nil {
return err
} else {
return nil
}
}
...@@ -79,8 +79,7 @@ func (userService *UserService) Login(u *system.SysUser) (userInter *system.SysU ...@@ -79,8 +79,7 @@ func (userService *UserService) Login(u *system.SysUser) (userInter *system.SysU
func (userService *UserService) ChangePassword(u *system.SysUser, newPassword string) (userInter *system.SysUser, err error) { func (userService *UserService) ChangePassword(u *system.SysUser, newPassword string) (userInter *system.SysUser, err error) {
var user system.SysUser var user system.SysUser
err = global.GVA_DB.Where("username = ?", u.Username).First(&user).Error if err = global.GVA_DB.Where("id = ?", u.ID).First(&user).Error; err != nil {
if err != nil {
return nil, err return nil, err
} }
if ok := utils.BcryptCheck(u.Password, user.Password); !ok { if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
...@@ -117,12 +116,12 @@ func (userService *UserService) GetUserInfoList(info request.PageInfo) (list int ...@@ -117,12 +116,12 @@ func (userService *UserService) GetUserInfoList(info request.PageInfo) (list int
//@param: uuid uuid.UUID, authorityId string //@param: uuid uuid.UUID, authorityId string
//@return: err error //@return: err error
func (userService *UserService) SetUserAuthority(id uint, uuid uuid.UUID, authorityId uint) (err error) { func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err error) {
assignErr := global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&system.SysUseAuthority{}).Error assignErr := global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&system.SysUserAuthority{}).Error
if errors.Is(assignErr, gorm.ErrRecordNotFound) { if errors.Is(assignErr, gorm.ErrRecordNotFound) {
return errors.New("该用户无此角色") return errors.New("该用户无此角色")
} }
err = global.GVA_DB.Where("uuid = ?", uuid).First(&system.SysUser{}).Update("authority_id", authorityId).Error err = global.GVA_DB.Where("id = ?", id).First(&system.SysUser{}).Update("authority_id", authorityId).Error
return err return err
} }
...@@ -134,13 +133,13 @@ func (userService *UserService) SetUserAuthority(id uint, uuid uuid.UUID, author ...@@ -134,13 +133,13 @@ func (userService *UserService) SetUserAuthority(id uint, uuid uuid.UUID, author
func (userService *UserService) SetUserAuthorities(id uint, authorityIds []uint) (err error) { func (userService *UserService) SetUserAuthorities(id uint, authorityIds []uint) (err error) {
return global.GVA_DB.Transaction(func(tx *gorm.DB) error { return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
TxErr := tx.Delete(&[]system.SysUseAuthority{}, "sys_user_id = ?", id).Error TxErr := tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error
if TxErr != nil { if TxErr != nil {
return TxErr return TxErr
} }
var useAuthority []system.SysUseAuthority var useAuthority []system.SysUserAuthority
for _, v := range authorityIds { for _, v := range authorityIds {
useAuthority = append(useAuthority, system.SysUseAuthority{ useAuthority = append(useAuthority, system.SysUserAuthority{
SysUserId: id, SysAuthorityAuthorityId: v, SysUserId: id, SysAuthorityAuthorityId: v,
}) })
} }
...@@ -169,7 +168,7 @@ func (userService *UserService) DeleteUser(id int) (err error) { ...@@ -169,7 +168,7 @@ func (userService *UserService) DeleteUser(id int) (err error) {
if err != nil { if err != nil {
return err return err
} }
err = global.GVA_DB.Delete(&[]system.SysUseAuthority{}, "sys_user_id = ?", id).Error err = global.GVA_DB.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error
return err return err
} }
......
...@@ -111,7 +111,8 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) { ...@@ -111,7 +111,8 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/createTemp", Description: "自动化代码"}, {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/createTemp", Description: "自动化代码"},
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/preview", Description: "预览自动化代码"}, {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/preview", Description: "预览自动化代码"},
{ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getColumn", Description: "获取所选table的所有字段"}, {ApiGroup: "代码生成器", Method: "GET", Path: "/autoCode/getColumn", Description: "获取所选table的所有字段"},
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/createPlug", Description: "自从创建插件包"}, {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/createPlug", Description: "自动创建插件包"},
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/installPlug", Description: "安装插件"},
{ApiGroup: "包(pkg)生成器", Method: "POST", Path: "/autoCode/createPackage", Description: "生成包(package)"}, {ApiGroup: "包(pkg)生成器", Method: "POST", Path: "/autoCode/createPackage", Description: "生成包(package)"},
{ApiGroup: "包(pkg)生成器", Method: "POST", Path: "/autoCode/getPackage", Description: "获取所有包(package)"}, {ApiGroup: "包(pkg)生成器", Method: "POST", Path: "/autoCode/getPackage", Description: "获取所有包(package)"},
......
...@@ -2,6 +2,7 @@ package system ...@@ -2,6 +2,7 @@ package system
import ( import (
"context" "context"
sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system" sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/service/system" "github.com/flipped-aurora/gin-vue-admin/server/service/system"
"github.com/pkg/errors" "github.com/pkg/errors"
...@@ -73,10 +74,13 @@ func (i *initMenuAuthority) DataInserted(ctx context.Context) bool { ...@@ -73,10 +74,13 @@ func (i *initMenuAuthority) DataInserted(ctx context.Context) bool {
if !ok { if !ok {
return false return false
} }
var count int64 auth := &sysModel.SysAuthority{}
if err := db.Model(&sysModel.SysAuthority{}). if ret := db.Model(auth).
Where("authority_id = ?", "9528").Preload("SysBaseMenus").Count(&count); err != nil { Where("authority_id = ?", 9528).Preload("SysBaseMenus").Find(auth); ret != nil {
return count == 16 if ret.Error != nil {
return false
}
return len(auth.SysBaseMenus) > 0
} }
return false return false
} }
...@@ -2,6 +2,7 @@ package system ...@@ -2,6 +2,7 @@ package system
import ( import (
"context" "context"
adapter "github.com/casbin/gorm-adapter/v3" adapter "github.com/casbin/gorm-adapter/v3"
"github.com/flipped-aurora/gin-vue-admin/server/service/system" "github.com/flipped-aurora/gin-vue-admin/server/service/system"
"github.com/pkg/errors" "github.com/pkg/errors"
...@@ -120,6 +121,7 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error ...@@ -120,6 +121,7 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error
{PType: "p", V0: "888", V1: "/autoCode/getPackage", V2: "POST"}, {PType: "p", V0: "888", V1: "/autoCode/getPackage", V2: "POST"},
{PType: "p", V0: "888", V1: "/autoCode/delPackage", V2: "POST"}, {PType: "p", V0: "888", V1: "/autoCode/delPackage", V2: "POST"},
{PType: "p", V0: "888", V1: "/autoCode/createPlug", V2: "POST"}, {PType: "p", V0: "888", V1: "/autoCode/createPlug", V2: "POST"},
{PType: "p", V0: "888", V1: "/autoCode/installPlugin", V2: "POST"},
{PType: "p", V0: "888", V1: "/sysDictionaryDetail/findSysDictionaryDetail", V2: "GET"}, {PType: "p", V0: "888", V1: "/sysDictionaryDetail/findSysDictionaryDetail", V2: "GET"},
{PType: "p", V0: "888", V1: "/sysDictionaryDetail/updateSysDictionaryDetail", V2: "PUT"}, {PType: "p", V0: "888", V1: "/sysDictionaryDetail/updateSysDictionaryDetail", V2: "PUT"},
...@@ -250,7 +252,7 @@ func (i *initCasbin) DataInserted(ctx context.Context) bool { ...@@ -250,7 +252,7 @@ func (i *initCasbin) DataInserted(ctx context.Context) bool {
if !ok { if !ok {
return false return false
} }
if errors.Is(db.Where(adapter.CasbinRule{PType: "p", V0: "9528", V1: "GET", V2: "/user/getUserInfo"}). if errors.Is(db.Where(adapter.CasbinRule{PType: "p", V0: "9528", V1: "/user/getUserInfo", V2: "GET"}).
First(&adapter.CasbinRule{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 First(&adapter.CasbinRule{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据
return false return false
} }
......
...@@ -76,10 +76,11 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er ...@@ -76,10 +76,11 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
{MenuLevel: 0, Hidden: false, ParentId: "14", Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}}, {MenuLevel: 0, Hidden: false, ParentId: "14", Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}},
{MenuLevel: 0, Hidden: true, ParentId: "14", Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}}, {MenuLevel: 0, Hidden: true, ParentId: "14", Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}},
{MenuLevel: 0, Hidden: false, ParentId: "14", Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "自动化package", Icon: "folder"}}, {MenuLevel: 0, Hidden: false, ParentId: "14", Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "自动化package", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: "28", Path: "autoPlug", Name: "autoPlug", Component: "view/systemTools/autoPlug/autoPlug.vue", Sort: 4, Meta: Meta{Title: "自动化插件模板", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "plugin", Name: "plugin", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "插件系统", Icon: "cherry"}}, {MenuLevel: 0, Hidden: false, ParentId: "0", Path: "plugin", Name: "plugin", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "插件系统", Icon: "cherry"}},
{MenuLevel: 0, Hidden: false, ParentId: "28", Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 1, Meta: Meta{Title: "邮件插件", Icon: "message"}}, {MenuLevel: 0, Hidden: false, ParentId: "27", Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}},
{MenuLevel: 0, Hidden: false, ParentId: "28", Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}}, {MenuLevel: 0, Hidden: false, ParentId: "27", Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}},
{MenuLevel: 0, Hidden: false, ParentId: "27", Path: "autoPlug", Name: "autoPlug", Component: "view/systemTools/autoPlug/autoPlug.vue", Sort: 2, Meta: Meta{Title: "插件模板", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: "27", Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 3, Meta: Meta{Title: "邮件插件", Icon: "message"}},
} }
if err = db.Create(&entities).Error; err != nil { if err = db.Create(&entities).Error; err != nil {
return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"表数据初始化失败!") return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"表数据初始化失败!")
......
...@@ -14,6 +14,6 @@ var ( ...@@ -14,6 +14,6 @@ var (
AuthorityVerify = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}} AuthorityVerify = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}}
AuthorityIdVerify = Rules{"AuthorityId": {NotEmpty()}} AuthorityIdVerify = Rules{"AuthorityId": {NotEmpty()}}
OldAuthorityVerify = Rules{"OldAuthorityId": {NotEmpty()}} OldAuthorityVerify = Rules{"OldAuthorityId": {NotEmpty()}}
ChangePasswordVerify = Rules{"Username": {NotEmpty()}, "Password": {NotEmpty()}, "NewPassword": {NotEmpty()}} ChangePasswordVerify = Rules{"Password": {NotEmpty()}, "NewPassword": {NotEmpty()}}
SetUserAuthorityVerify = Rules{"AuthorityId": {NotEmpty()}} SetUserAuthorityVerify = Rules{"AuthorityId": {NotEmpty()}}
) )
package utils
import (
"archive/zip"
"io"
"os"
"path/filepath"
)
//解压
func Unzip(zipFile string, destDir string) ([]string, error) {
zipReader, err := zip.OpenReader(zipFile)
var paths []string
if err != nil {
return []string{}, err
}
defer zipReader.Close()
for _, f := range zipReader.File {
fpath := filepath.Join(destDir, f.Name)
paths = append(paths, fpath)
if f.FileInfo().IsDir() {
os.MkdirAll(fpath, os.ModePerm)
} else {
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return []string{}, err
}
inFile, err := f.Open()
if err != nil {
return []string{}, err
}
defer inFile.Close()
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return []string{}, err
}
defer outFile.Close()
_, err = io.Copy(outFile, inFile)
if err != nil {
return []string{}, err
}
}
}
return paths, nil
}
...@@ -124,3 +124,10 @@ export const createPlugApi = (data) => { ...@@ -124,3 +124,10 @@ export const createPlugApi = (data) => {
}) })
} }
export const installPlug = (data) => {
return service({
url: '/autoCode/installPlug',
method: 'post',
data
})
}
...@@ -41,11 +41,6 @@ export const viteLogo = (env) => { ...@@ -41,11 +41,6 @@ export const viteLogo = (env) => {
`> 默认自动化文档地址:http://127.0.0.1:${env.VITE_SERVER_PORT}/swagger/index.html` `> 默认自动化文档地址:http://127.0.0.1:${env.VITE_SERVER_PORT}/swagger/index.html`
) )
) )
console.log(
chalk.green(
`> 默认自动化文档地址:http://127.0.0.1:${env.VITE_SERVER_PORT}/swagger/index.html`
)
)
console.log( console.log(
chalk.green( chalk.green(
`> 默认前端文件运行地址:http://127.0.0.1:${env.VITE_CLI_PORT}` `> 默认前端文件运行地址:http://127.0.0.1:${env.VITE_CLI_PORT}`
......
...@@ -49,12 +49,25 @@ router.beforeEach(async(to, from, next) => { ...@@ -49,12 +49,25 @@ router.beforeEach(async(to, from, next) => {
asyncRouterFlag++ asyncRouterFlag++
await getRouter(userStore) await getRouter(userStore)
} }
next({ name: userStore.userInfo.authority.defaultRouter }) // token 可以解析但是却是不存在的用户 id 或角色 id 会导致无限调用
if (userStore.userInfo?.authority?.defaultRouter != null) {
next({ name: userStore.userInfo.authority.defaultRouter })
} else {
// 强制退出账号
userStore.ClearStorage()
next({
name: 'Login',
query: {
redirect: document.location.hash
}
})
return
}
} else { } else {
next() next()
} }
} else { } else {
// 不在白名单中并且已经登的时候 // 不在白名单中并且已经登的时候
if (token) { if (token) {
// 添加flag防止多次获取动态路由和栈溢出 // 添加flag防止多次获取动态路由和栈溢出
if (!asyncRouterFlag && whiteList.indexOf(from.name) < 0) { if (!asyncRouterFlag && whiteList.indexOf(from.name) < 0) {
...@@ -76,7 +89,7 @@ router.beforeEach(async(to, from, next) => { ...@@ -76,7 +89,7 @@ router.beforeEach(async(to, from, next) => {
} }
} }
} }
// 不在白名单中并且未登的时候 // 不在白名单中并且未登的时候
if (!token) { if (!token) {
next({ next({
name: 'Login', name: 'Login',
......
...@@ -52,7 +52,7 @@ export const useUserStore = defineStore('user', () => { ...@@ -52,7 +52,7 @@ export const useUserStore = defineStore('user', () => {
const LoginIn = async(loginInfo) => { const LoginIn = async(loginInfo) => {
loadingInstance.value = ElLoading.service({ loadingInstance.value = ElLoading.service({
fullscreen: true, fullscreen: true,
text: '中,请稍候...', text: '中,请稍候...',
}) })
try { try {
const res = await login(loginInfo) const res = await login(loginInfo)
...@@ -85,6 +85,12 @@ export const useUserStore = defineStore('user', () => { ...@@ -85,6 +85,12 @@ export const useUserStore = defineStore('user', () => {
window.location.reload() window.location.reload()
} }
} }
/* 清理数据 */
const ClearStorage = async() => {
token.value = ''
sessionStorage.clear()
localStorage.clear()
}
/* 设置侧边栏模式*/ /* 设置侧边栏模式*/
const changeSideMode = async(data) => { const changeSideMode = async(data) => {
const res = await setSelfInfo({ sideMode: data }) const res = await setSelfInfo({ sideMode: data })
...@@ -141,6 +147,7 @@ export const useUserStore = defineStore('user', () => { ...@@ -141,6 +147,7 @@ export const useUserStore = defineStore('user', () => {
setToken, setToken,
baseColor, baseColor,
activeColor, activeColor,
loadingInstance loadingInstance,
ClearStorage
} }
}) })
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
.input-icon{ .input-icon {
padding-right: 6px; padding-right: 6px;
padding-top: 4px; padding-top: 4px;
} }
...@@ -47,10 +47,10 @@ ...@@ -47,10 +47,10 @@
padding-left: 20px; padding-left: 20px;
} }
} }
.vPicBox{ .vPicBox {
display:flex; display: flex;
justify-content:space-between; justify-content: space-between;
width:100%; width: 100%;
} }
.vPic { .vPic {
width: 33%; width: 33%;
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
} }
} }
//小屏幕不显示右侧,将登框居中 //小屏幕不显示右侧,将登框居中
@media (max-width: 750px) { @media (max-width: 750px) {
.login_panle_right { .login_panle_right {
display: none; display: none;
......
...@@ -297,7 +297,7 @@ export default { ...@@ -297,7 +297,7 @@ export default {
@include flex-center; @include flex-center;
} }
//小屏幕不显示右侧,将登框居中 //小屏幕不显示右侧,将登框居中
@media (max-width: 750px) { @media (max-width: 750px) {
.gva-card { .gva-card {
padding: 20px 10px !important; padding: 20px 10px !important;
......
...@@ -274,7 +274,6 @@ const savePassword = async() => { ...@@ -274,7 +274,6 @@ const savePassword = async() => {
modifyPwdForm.value.validate((valid) => { modifyPwdForm.value.validate((valid) => {
if (valid) { if (valid) {
changePassword({ changePassword({
username: userStore.userInfo.userName,
password: pwdModify.value.password, password: pwdModify.value.password,
newPassword: pwdModify.value.newPassword, newPassword: pwdModify.value.newPassword,
}).then((res) => { }).then((res) => {
......
...@@ -96,12 +96,14 @@ ...@@ -96,12 +96,14 @@
<el-button type="primary" @click="createPlug">创建</el-button> <el-button type="primary" @click="createPlug">创建</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { toUpperCase } from '@/utils/stringFun' import { toUpperCase } from '@/utils/stringFun'
import { import {
Plus, Plus,
Minus Minus
......
<template>
<div>
<el-upload
class="upload-demo"
drag
:action="`${path}/autoCode/installPlugin`"
:headers="{'x-token':userStore.token}"
:show-file-list="false"
:on-success="handleSuccess"
:on-error="handleSuccess"
name="plug"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
拖拽或<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
请把安装包的zip拖拽至此处上传
</div>
</template>
</el-upload>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useUserStore } from '@/pinia/modules/user'
import { ElMessage } from 'element-plus'
const userStore = useUserStore()
const path = ref(import.meta.env.VITE_BASE_API)
const handleSuccess = (res) => {
if (res.code === 0) {
ElMessage.success(res.msg)
} else {
ElMessage.error(res.msg)
}
}
</script>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册