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

* 增加安装插件demo

* 前端上传demo

* 插件安装初版

* 调整菜单结构

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

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

* 登陆==>登录

* 抽离插件安装方法

* 调整插件功能 提升灵活度

Co-authored-by: zeromake <a390720046@gmail.com>
Co-authored-by: Lu JJ <275955589@qq.com>
Co-authored-by: pnck <hio131@gmail.com>
Co-authored-by: feixuanyu <994085848@qq.com>
This commit is contained in:
奇淼(piexlmax
2022-07-07 12:34:54 +08:00
committed by GitHub
co-authored by zeromake Lu JJ pnck feixuanyu
parent 063ceffb11
commit 4bf872d292
27 changed files with 290 additions and 61 deletions
+19 -5
View File
@@ -3,17 +3,16 @@ package system
import (
"errors"
"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/language"
"net/url"
"os"
"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"
"go.uber.org/zap"
)
@@ -240,3 +239,18 @@ func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
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)
}
}
+8 -8
View File
@@ -138,18 +138,19 @@ func (b *BaseApi) Register(c *gin.Context) {
// @Summary 用户修改密码
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body systemReq.ChangePasswordStruct true "用户名, 原密码, 新密码"
// @Param data body systemReq.ChangePasswordReq true "用户名, 原密码, 新密码"
// @Success 200 {object} response.Response{msg=string} "用户修改密码"
// @Router /user/changePassword [post]
func (b *BaseApi) ChangePassword(c *gin.Context) {
var user systemReq.ChangePasswordStruct
_ = c.ShouldBindJSON(&user)
if err := utils.Verify(user, utils.ChangePasswordVerify); err != nil {
var req systemReq.ChangePasswordReq
_ = c.ShouldBindJSON(&req)
if err := utils.Verify(req, utils.ChangePasswordVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
u := &system.SysUser{Username: user.Username, Password: user.Password}
if _, err := userService.ChangePassword(u, user.NewPassword); err != nil {
uid := utils.GetUserID(c)
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))
response.FailWithMessage("修改失败,原密码与当前账户不符", c)
} else {
@@ -201,8 +202,7 @@ func (b *BaseApi) SetUserAuthority(c *gin.Context) {
return
}
userID := utils.GetUserID(c)
uuid := utils.GetUserUuid(c)
if err := userService.SetUserAuthority(userID, uuid, sua.AuthorityId); err != nil {
if err := userService.SetUserAuthority(userID, sua.AuthorityId); err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
} else {
+1
View File
@@ -29,6 +29,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/mapstructure v1.2.2 // indirect
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/pkg/errors v0.9.1
github.com/qiniu/api.v7/v7 v7.4.1
+7
View File
@@ -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.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
+8 -1
View File
@@ -1,7 +1,9 @@
package initialize
import (
"fmt"
"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/utils/plugin"
"github.com/gin-gonic/gin"
@@ -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 可以自行切换 效果相同
PluginInit(PrivateGroup, email.CreateEmailPlug(
global.GVA_CONFIG.Email.To,
+2 -3
View File
@@ -29,11 +29,10 @@ func Routers() *gin.Engine {
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文件")
global.GVA_LOG.Info("use middleware logger")
// 跨域,如需跨域可以打开下面的注释
// Router.Use(middleware.Cors()) // 直接放行全部跨域请求
//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))
global.GVA_LOG.Info("register swagger handler")
// 方便统一添加路由组前缀 多服务器上线使用
@@ -75,7 +74,7 @@ func Routers() *gin.Engine {
// 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")
return Router
+3 -3
View File
@@ -22,8 +22,8 @@ type Login struct {
}
// Modify password structure
type ChangePasswordStruct struct {
Username string `json:"username"` // 用户名
type ChangePasswordReq struct {
ID uint `json:"-"` // 从 JWT 中提取 user id,避免越权
Password string `json:"password"` // 密码
NewPassword string `json:"newPassword"` // 新密码
}
@@ -42,7 +42,7 @@ type SetUserAuthorities struct {
type ChangeUserInfo struct {
ID uint `gorm:"primarykey"` // 主键ID
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
Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱
HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
+3 -2
View File
@@ -1,10 +1,11 @@
package system
type SysUseAuthority struct {
// SysUserAuthority 是 sysUser 和 sysAuthority 的连接表
type SysUserAuthority struct {
SysUserId uint `gorm:"column:sys_user_id"`
SysAuthorityAuthorityId uint `gorm:"column:sys_authority_authority_id"`
}
func (s *SysUseAuthority) TableName() string {
func (s *SysUserAuthority) TableName() string {
return "sys_user_authority"
}
+1
View File
@@ -20,5 +20,6 @@ func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup) {
autoCodeRouter.POST("getPackage", autoCodeApi.GetPackage) // 获取package包
autoCodeRouter.POST("delPackage", autoCodeApi.DelPackage) // 删除package包
autoCodeRouter.POST("createPlug", autoCodeApi.AutoPlug) // 自动插件包模板
autoCodeRouter.POST("installPlugin", autoCodeApi.InstallPlugin) // 自动安装插件
}
}
+1 -1
View File
@@ -131,7 +131,7 @@ func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthor
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
authorityId := strconv.Itoa(int(auth.AuthorityId))
CasbinServiceApp.ClearCasbin(0, authorityId)
+82 -1
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
cp "github.com/otiai10/copy"
"go.uber.org/zap"
"go/ast"
"go/format"
@@ -12,8 +13,10 @@ import (
"go/token"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"io"
"io/ioutil"
"log"
"mime/multipart"
"os"
"path/filepath"
"strconv"
@@ -681,7 +684,7 @@ func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor {
case *ast.StructType:
f := &ast.Field{
Names: []*ast.Ident{
&ast.Ident{
{
Name: vi.StructName,
Obj: &ast.Object{
Kind: ast.Var,
@@ -834,3 +837,81 @@ func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) erro
}
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
}
}
+8 -9
View File
@@ -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) {
var user system.SysUser
err = global.GVA_DB.Where("username = ?", u.Username).First(&user).Error
if err != nil {
if err = global.GVA_DB.Where("id = ?", u.ID).First(&user).Error; err != nil {
return nil, err
}
if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
@@ -117,12 +116,12 @@ func (userService *UserService) GetUserInfoList(info request.PageInfo) (list int
//@param: uuid uuid.UUID, authorityId string
//@return: err error
func (userService *UserService) SetUserAuthority(id uint, uuid uuid.UUID, authorityId uint) (err error) {
assignErr := global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&system.SysUseAuthority{}).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.SysUserAuthority{}).Error
if errors.Is(assignErr, gorm.ErrRecordNotFound) {
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
}
@@ -134,13 +133,13 @@ func (userService *UserService) SetUserAuthority(id uint, uuid uuid.UUID, author
func (userService *UserService) SetUserAuthorities(id uint, authorityIds []uint) (err 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 {
return TxErr
}
var useAuthority []system.SysUseAuthority
var useAuthority []system.SysUserAuthority
for _, v := range authorityIds {
useAuthority = append(useAuthority, system.SysUseAuthority{
useAuthority = append(useAuthority, system.SysUserAuthority{
SysUserId: id, SysAuthorityAuthorityId: v,
})
}
@@ -169,7 +168,7 @@ func (userService *UserService) DeleteUser(id int) (err error) {
if err != nil {
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
}
+2 -1
View File
@@ -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/preview", Description: "预览自动化代码"},
{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/getPackage", Description: "获取所有包(package)"},
+8 -4
View File
@@ -2,6 +2,7 @@ package system
import (
"context"
sysModel "github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"github.com/pkg/errors"
@@ -73,10 +74,13 @@ func (i *initMenuAuthority) DataInserted(ctx context.Context) bool {
if !ok {
return false
}
var count int64
if err := db.Model(&sysModel.SysAuthority{}).
Where("authority_id = ?", "9528").Preload("SysBaseMenus").Count(&count); err != nil {
return count == 16
auth := &sysModel.SysAuthority{}
if ret := db.Model(auth).
Where("authority_id = ?", 9528).Preload("SysBaseMenus").Find(auth); ret != nil {
if ret.Error != nil {
return false
}
return len(auth.SysBaseMenus) > 0
}
return false
}
+3 -1
View File
@@ -2,6 +2,7 @@ package system
import (
"context"
adapter "github.com/casbin/gorm-adapter/v3"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"github.com/pkg/errors"
@@ -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/delPackage", 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/updateSysDictionaryDetail", V2: "PUT"},
@@ -250,7 +252,7 @@ func (i *initCasbin) DataInserted(ctx context.Context) bool {
if !ok {
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) { // 判断是否存在数据
return false
}
+4 -3
View File
@@ -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: 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: "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: "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: "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: "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 {
return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"表数据初始化失败!")
+1 -1
View File
@@ -14,6 +14,6 @@ var (
AuthorityVerify = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}}
AuthorityIdVerify = Rules{"AuthorityId": {NotEmpty()}}
OldAuthorityVerify = Rules{"OldAuthorityId": {NotEmpty()}}
ChangePasswordVerify = Rules{"Username": {NotEmpty()}, "Password": {NotEmpty()}, "NewPassword": {NotEmpty()}}
ChangePasswordVerify = Rules{"Password": {NotEmpty()}, "NewPassword": {NotEmpty()}}
SetUserAuthorityVerify = Rules{"AuthorityId": {NotEmpty()}}
)
+48
View File
@@ -0,0 +1,48 @@
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
}