Dev273 (#1868)
* fixed: 修复addFunction下前端api.js无法创建的bug。 * feature: 增加严格角色模式 * Update system.vue * fixed: 多点登录拦截模式下,jwt换票期间不需要拉黑token。 * fixed: 修复使用ast时候产生无意义的换行的问题 * fixed: 修复跨级操作角色权限的越权问题 * feature: 优化严格模式角色鉴权操作。 * fixed: 增加菜单和api设置越权问题的限制 * feature: 增加插件打包前的自动化同步所需菜单和api的功能 * feature: 自动化代码可以默认生成导入导出 * feature: 自动化导入导出对模板进行回滚 * feature: 剔除无用的packfile代码包 * feature: 发布V2.7.3版本公测。 --------- Co-authored-by: task <ms.yangdan@gmail.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
@@ -48,6 +49,115 @@ func FindFunction(astNode ast.Node, FunctionName string) *ast.FuncDecl {
|
||||
return funcDeclP
|
||||
}
|
||||
|
||||
// FindArray 查询特定数组方法
|
||||
func FindArray(astNode ast.Node, identName, selectorExprName string) *ast.CompositeLit {
|
||||
var assignStmt *ast.CompositeLit
|
||||
ast.Inspect(astNode, func(n ast.Node) bool {
|
||||
switch node := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range node.Rhs {
|
||||
if exprType, ok := expr.(*ast.CompositeLit); ok {
|
||||
if arrayType, ok := exprType.Type.(*ast.ArrayType); ok {
|
||||
sel, ok1 := arrayType.Elt.(*ast.SelectorExpr)
|
||||
x, ok2 := sel.X.(*ast.Ident)
|
||||
if ok1 && ok2 && x.Name == identName && sel.Sel.Name == selectorExprName {
|
||||
assignStmt = exprType
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return assignStmt
|
||||
}
|
||||
|
||||
func CreateMenuStructAst(menus []system.SysBaseMenu) *[]ast.Expr {
|
||||
var menuElts []ast.Expr
|
||||
for i := range menus {
|
||||
elts := []ast.Expr{ // 结构体的字段
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "ParentId"},
|
||||
Value: &ast.BasicLit{Kind: token.INT, Value: "0"},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Path"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Path)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Name"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Name)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Hidden"},
|
||||
Value: &ast.Ident{Name: "false"},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Component"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Component)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Sort"},
|
||||
Value: &ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%d", menus[i].Sort)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Meta"},
|
||||
Value: &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "model"},
|
||||
Sel: &ast.Ident{Name: "Meta"},
|
||||
},
|
||||
Elts: []ast.Expr{
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Title"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Title)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Icon"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Icon)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
menuElts = append(menuElts, &ast.CompositeLit{
|
||||
Type: nil,
|
||||
Elts: elts,
|
||||
})
|
||||
}
|
||||
return &menuElts
|
||||
}
|
||||
|
||||
func CreateApiStructAst(apis []system.SysApi) *[]ast.Expr {
|
||||
var apiElts []ast.Expr
|
||||
for i := range apis {
|
||||
elts := []ast.Expr{ // 结构体的字段
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Path"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Path)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Description"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Description)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "ApiGroup"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].ApiGroup)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Method"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Method)},
|
||||
},
|
||||
}
|
||||
apiElts = append(apiElts, &ast.CompositeLit{
|
||||
Type: nil,
|
||||
Elts: elts,
|
||||
})
|
||||
}
|
||||
return &apiElts
|
||||
}
|
||||
|
||||
// 检查是否存在Import
|
||||
func CheckImport(file *ast.File, importPath string) bool {
|
||||
for _, imp := range file.Imports {
|
||||
@@ -62,11 +172,39 @@ func CheckImport(file *ast.File, importPath string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func clearPosition(astNode ast.Node) {
|
||||
ast.Inspect(astNode, func(n ast.Node) bool {
|
||||
switch node := n.(type) {
|
||||
case *ast.Ident:
|
||||
// 清除位置信息
|
||||
node.NamePos = token.NoPos
|
||||
case *ast.CallExpr:
|
||||
// 清除位置信息
|
||||
node.Lparen = token.NoPos
|
||||
node.Rparen = token.NoPos
|
||||
case *ast.BasicLit:
|
||||
// 清除位置信息
|
||||
node.ValuePos = token.NoPos
|
||||
case *ast.SelectorExpr:
|
||||
// 清除位置信息
|
||||
node.Sel.NamePos = token.NoPos
|
||||
case *ast.BinaryExpr:
|
||||
node.OpPos = token.NoPos
|
||||
case *ast.UnaryExpr:
|
||||
node.OpPos = token.NoPos
|
||||
case *ast.StarExpr:
|
||||
node.Star = token.NoPos
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func CreateStmt(statement string) *ast.ExprStmt {
|
||||
expr, err := parser.ParseExpr(statement)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
clearPosition(expr)
|
||||
return &ast.ExprStmt{X: expr}
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ClearToken(c *gin.Context) {
|
||||
@@ -40,7 +41,14 @@ func SetToken(c *gin.Context, token string, maxAge int) {
|
||||
func GetToken(c *gin.Context) string {
|
||||
token, _ := c.Cookie("x-token")
|
||||
if token == "" {
|
||||
j := NewJWT()
|
||||
token = c.Request.Header.Get("x-token")
|
||||
claims, err := j.ParseToken(token)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("重新写入cookie token失败,未能成功解析token,请检查请求头是否存在x-token且claims是否为规定结构")
|
||||
return token
|
||||
}
|
||||
SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
@@ -1,180 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//@author: [LeonardWang](https://github.com/WangLeonard)
|
||||
//@function: AutoInjectionCode
|
||||
//@description: 向文件中固定注释位置写入代码
|
||||
//@param: filepath string, funcName string, codeData string
|
||||
//@return: error
|
||||
|
||||
const (
|
||||
startComment = "Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT."
|
||||
endComment = "Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT."
|
||||
)
|
||||
|
||||
//@author: [LeonardWang](https://github.com/WangLeonard)
|
||||
//@function: AutoInjectionCode
|
||||
//@description: 向文件中固定注释位置写入代码
|
||||
//@param: filepath string, funcName string, codeData string
|
||||
//@return: error
|
||||
|
||||
func AutoInjectionCode(filepath string, funcName string, codeData string) error {
|
||||
srcData, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcDataLen := len(srcData)
|
||||
fset := token.NewFileSet()
|
||||
fparser, err := parser.ParseFile(fset, filepath, srcData, parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
codeData = strings.TrimSpace(codeData)
|
||||
codeStartPos := -1
|
||||
codeEndPos := srcDataLen
|
||||
var expectedFunction *ast.FuncDecl
|
||||
|
||||
startCommentPos := -1
|
||||
endCommentPos := srcDataLen
|
||||
|
||||
// 如果指定了函数名,先寻找对应函数
|
||||
if funcName != "" {
|
||||
for _, decl := range fparser.Decls {
|
||||
if funDecl, ok := decl.(*ast.FuncDecl); ok && funDecl.Name.Name == funcName {
|
||||
expectedFunction = funDecl
|
||||
codeStartPos = int(funDecl.Body.Lbrace)
|
||||
codeEndPos = int(funDecl.Body.Rbrace)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历所有注释
|
||||
for _, comment := range fparser.Comments {
|
||||
if int(comment.Pos()) > codeStartPos && int(comment.End()) <= codeEndPos {
|
||||
if startComment != "" && strings.Contains(comment.Text(), startComment) {
|
||||
startCommentPos = int(comment.Pos()) // Note: Pos is the second '/'
|
||||
}
|
||||
if endComment != "" && strings.Contains(comment.Text(), endComment) {
|
||||
endCommentPos = int(comment.Pos()) // Note: Pos is the second '/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if endCommentPos == srcDataLen {
|
||||
return fmt.Errorf("comment:%s not found", endComment)
|
||||
}
|
||||
|
||||
// 在指定函数名,且函数中startComment和endComment都存在时,进行区间查重
|
||||
if (codeStartPos != -1 && codeEndPos <= srcDataLen) && (startCommentPos != -1 && endCommentPos != srcDataLen) && expectedFunction != nil {
|
||||
if exist := checkExist(&srcData, startCommentPos, endCommentPos, expectedFunction.Body, codeData); exist {
|
||||
fmt.Printf("文件 %s 待插入数据 %s 已存在\n", filepath, codeData)
|
||||
return nil // 这里不需要返回错误?
|
||||
}
|
||||
}
|
||||
|
||||
// 两行注释中间没有换行时,会被认为是一条Comment
|
||||
if startCommentPos == endCommentPos {
|
||||
endCommentPos = startCommentPos + strings.Index(string(srcData[startCommentPos:]), endComment)
|
||||
for srcData[endCommentPos] != '/' {
|
||||
endCommentPos--
|
||||
}
|
||||
}
|
||||
|
||||
// 记录"//"之前的空字符,保持写入后的格式一致
|
||||
tmpSpace := make([]byte, 0, 10)
|
||||
for tmp := endCommentPos - 2; tmp >= 0; tmp-- {
|
||||
if srcData[tmp] != '\n' {
|
||||
tmpSpace = append(tmpSpace, srcData[tmp])
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
reverseSpace := make([]byte, 0, len(tmpSpace))
|
||||
for index := len(tmpSpace) - 1; index >= 0; index-- {
|
||||
reverseSpace = append(reverseSpace, tmpSpace[index])
|
||||
}
|
||||
|
||||
// 插入数据
|
||||
indexPos := endCommentPos - 1
|
||||
insertData := []byte(append([]byte(codeData+"\n"), reverseSpace...))
|
||||
|
||||
remainData := append([]byte{}, srcData[indexPos:]...)
|
||||
srcData = append(append(srcData[:indexPos], insertData...), remainData...)
|
||||
|
||||
// 写回数据
|
||||
return os.WriteFile(filepath, srcData, 0o600)
|
||||
}
|
||||
|
||||
func checkExist(srcData *[]byte, startPos int, endPos int, blockStmt *ast.BlockStmt, target string) bool {
|
||||
for _, list := range blockStmt.List {
|
||||
switch stmt := list.(type) {
|
||||
case *ast.ExprStmt:
|
||||
if callExpr, ok := stmt.X.(*ast.CallExpr); ok &&
|
||||
int(callExpr.Pos()) > startPos && int(callExpr.End()) < endPos {
|
||||
text := string((*srcData)[int(callExpr.Pos()-1):int(callExpr.End())])
|
||||
key := strings.TrimSpace(text)
|
||||
if key == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case *ast.BlockStmt:
|
||||
if checkExist(srcData, startPos, endPos, stmt, target) {
|
||||
return true
|
||||
}
|
||||
case *ast.AssignStmt:
|
||||
// 为 model 中的代码进行检查
|
||||
if len(stmt.Rhs) > 0 {
|
||||
if callExpr, ok := stmt.Rhs[0].(*ast.CallExpr); ok {
|
||||
for _, arg := range callExpr.Args {
|
||||
if int(arg.Pos()) > startPos && int(arg.End()) < endPos {
|
||||
text := string((*srcData)[int(arg.Pos()-1):int(arg.End())])
|
||||
key := strings.TrimSpace(text)
|
||||
if key == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AutoClearCode(filepath string, codeData string) error {
|
||||
srcData, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcData, err = cleanCode(codeData, string(srcData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath, srcData, 0o600)
|
||||
}
|
||||
|
||||
func cleanCode(clearCode string, srcData string) ([]byte, error) {
|
||||
bf := make([]rune, 0, 1024)
|
||||
for i, v := range srcData {
|
||||
if v == '\n' {
|
||||
if strings.TrimSpace(string(bf)) == clearCode {
|
||||
return append([]byte(srcData[:i-len(bf)]), []byte(srcData[i+1:])...), nil
|
||||
}
|
||||
bf = (bf)[:0]
|
||||
continue
|
||||
}
|
||||
bf = append(bf, v)
|
||||
}
|
||||
return []byte(srcData), errors.New("未找到内容")
|
||||
}
|
Reference in New Issue
Block a user