Merge branch 'main' into i18n-dev-new

This commit is contained in:
Mohamed Hassan
2022-09-14 16:13:55 +02:00
38 changed files with 359 additions and 217 deletions
@@ -2,7 +2,7 @@ package example
import (
"fmt"
"io/ioutil"
"io"
"mime/multipart"
"strconv"
@@ -48,7 +48,7 @@ func (b *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
fmt.Println(err)
}
}(f)
cen, _ := ioutil.ReadAll(f)
cen, _ := io.ReadAll(f)
if !utils.CheckMd5(cen, chunkMd5) {
global.GVA_LOG.Error("检查md5失败!", zap.Error(err))
response.FailWithMessage("检查md5失败", c)
+72 -72
View File
@@ -2,43 +2,43 @@
# jwt configuration
jwt:
signing-key: 'qmPlus'
expires-time: 604800
buffer-time: 86400
issuer: 'qmPlus'
signing-key: qmPlus
expires-time: 7d
buffer-time: 1d
issuer: qmPlus
# zap logger configuration
zap:
level: 'info'
format: 'console'
prefix: '[github.com/flipped-aurora/gin-vue-admin/server]'
director: 'log'
level: info
format: console
prefix: [github.com/flipped-aurora/gin-vue-admin/server]
director: log
show-line: true
encode-level: 'LowercaseColorLevelEncoder'
stacktrace-key: 'stacktrace'
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
log-in-console: true
# redis configuration
redis:
db: 0
addr: '127.0.0.1:6379'
password: ''
addr: 127.0.0.1:6379
password: ""
# email configuration
email:
to: 'xxx@qq.com'
to: xxx@qq.com
port: 465
from: 'xxx@163.com'
host: 'smtp.163.com'
from: xxx@163.com
host: smtp.163.com
is-ssl: true
secret: 'xxx'
nickname: 'test'
secret: xxx
nickname: test
# system configuration
system:
env: 'public' # Change to "develop" to skip authentication for development mode
env: public # Change to "develop" to skip authentication for development mode
addr: 8888
db-type: 'mysql'
oss-type: 'local' # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-multipoint: false
# IP限制次数 一个小时15000次
@@ -55,12 +55,12 @@ captcha:
# mysql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://www.github.com/flipped-aurora/gin-vue-admin/server.com/docs/first
mysql:
path: ''
port: ''
config: ''
db-name: ''
username: ''
password: ''
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
@@ -69,12 +69,12 @@ mysql:
# pgsql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://www.github.com/flipped-aurora/gin-vue-admin/server.com/docs/first
pgsql:
path: ''
port: ''
config: ''
db-name: ''
username: ''
password: ''
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
@@ -84,12 +84,12 @@ db-list:
- disabled: true # 是否启用
type: "" # 数据库的类型,目前支持mysql、pgsql
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
path: ''
port: ''
config: ''
db-name: ''
username: ''
password: ''
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
@@ -98,8 +98,8 @@ db-list:
# local configuration
local:
path: 'uploads/file' # 访问路径
store-path: 'uploads/file' # 存储路径
path: uploads/file
store-path: uploads/file
# autocode configuration
autocode:
@@ -122,67 +122,67 @@ autocode:
# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
qiniu:
zone: 'ZoneHuaDong'
bucket: ''
img-path: ''
zone: ZoneHuaDong
bucket: ""
img-path: ""
use-https: false
access-key: ''
secret-key: ''
access-key: ""
secret-key: ""
use-cdn-domains: false
# aliyun oss configuration
aliyun-oss:
endpoint: 'yourEndpoint'
access-key-id: 'yourAccessKeyId'
access-key-secret: 'yourAccessKeySecret'
bucket-name: 'yourBucketName'
bucket-url: 'yourBucketUrl'
base-path: 'yourBasePath'
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
bucket-url: yourBucketUrl
base-path: yourBasePath
# tencent cos configuration
tencent-cos:
bucket: 'xxxxx-10005608'
region: 'ap-shanghai'
secret-id: 'xxxxxxxx'
secret-key: 'xxxxxxxx'
base-url: 'https://gin.vue.admin'
path-prefix: 'github.com/flipped-aurora/gin-vue-admin/server'
bucket: xxxxx-10005608
region: ap-shanghai
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
# aws s3 configuration (minio compatible)
aws-s3:
bucket: xxxxx-10005608
region: ap-shanghai
endpoint: ''
endpoint: ""
s3-force-path-style: false
disable-ssl: false
secret-id: xxxxxxxx
secret-key: xxxxxxxx
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
# huawei obs configuration
hua-wei-obs:
path: 'you-path'
bucket: 'you-bucket'
endpoint: 'you-endpoint'
access-key: 'you-access-key'
secret-key: 'you-secret-key'
path: you-path
bucket: you-bucket
endpoint: you-endpoint
access-key: you-access-key
secret-key: you-secret-key
# excel configuration
excel:
dir: './resource/excel/'
dir: ./resource/excel/
# timer task db clear table
Timer:
start: true
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
detail:
- tableName: "sys_operation_records"
compareField: "created_at"
interval: "2160h"
- tableName: "jwt_blacklists"
compareField: "created_at"
interval: "168h"
- tableName: sys_operation_records
compareField: created_at
interval: 2160h
- tableName: jwt_blacklists
compareField: created_at
interval: 168h
# 跨域配置
# 需要配合 server/initialize/router.go#L32 使用
+2 -2
View File
@@ -2,7 +2,7 @@ package config
type JWT struct {
SigningKey string `mapstructure:"signing-key" json:"signing-key" yaml:"signing-key"` // jwt签名
ExpiresTime int64 `mapstructure:"expires-time" json:"expires-time" yaml:"expires-time"` // 过期时间
BufferTime int64 `mapstructure:"buffer-time" json:"buffer-time" yaml:"buffer-time"` // 缓冲时间
ExpiresTime string `mapstructure:"expires-time" json:"expires-time" yaml:"expires-time"` // 过期时间
BufferTime string `mapstructure:"buffer-time" json:"buffer-time" yaml:"buffer-time"` // 缓冲时间
Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者
}
+2 -7
View File
@@ -7,14 +7,12 @@ import (
"github.com/gin-gonic/gin"
"os"
"path/filepath"
"time"
"github.com/songzhibin97/gkit/cache/local_cache"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"github.com/flipped-aurora/gin-vue-admin/server/global"
_ "github.com/flipped-aurora/gin-vue-admin/server/packfile"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
// Viper //
@@ -72,8 +70,5 @@ func Viper(path ...string) *viper.Viper {
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
global.BlackCache = local_cache.NewCache(
local_cache.SetDefaultExpire(time.Second * time.Duration(global.GVA_CONFIG.JWT.ExpiresTime)),
)
return v
}
+23
View File
@@ -0,0 +1,23 @@
package initialize
import (
"github.com/songzhibin97/gkit/cache/local_cache"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
)
func OtherInit() {
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
if err != nil {
panic(err)
}
_, err = utils.ParseDuration(global.GVA_CONFIG.JWT.BufferTime)
if err != nil {
panic(err)
}
global.BlackCache = local_cache.NewCache(
local_cache.SetDefaultExpire(dr),
)
}
+4 -1
View File
@@ -1,6 +1,8 @@
package main
import (
"go.uber.org/zap"
"github.com/flipped-aurora/gin-vue-admin/server/core"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize"
@@ -22,7 +24,8 @@ import (
// @BasePath /
func main() {
global.GVA_VP = core.Viper() // 初始化Viper
global.GVA_LOG = core.Zap() // 初始化zap日志库
initialize.OtherInit()
global.GVA_LOG = core.Zap() // 初始化zap日志库
zap.ReplaceGlobals(global.GVA_LOG)
global.GVA_DB = initialize.Gorm() // gorm连接数据库
initialize.Timer()
+3 -3
View File
@@ -2,7 +2,7 @@ package middleware
import (
"bytes"
"io/ioutil"
"io"
"strconv"
"time"
@@ -32,9 +32,9 @@ func ErrorToEmail() gin.HandlerFunc {
}
username = user.Username
}
body, _ := ioutil.ReadAll(c.Request.Body)
body, _ := io.ReadAll(c.Request.Body)
// 再重新写回请求体body中,ioutil.ReadAll会清空c.Request.Body中的数据
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
record := system.SysOperationRecord{
Ip: c.ClientIP(),
Method: c.Request.Method,
+2 -1
View File
@@ -54,7 +54,8 @@ func JWTAuth() gin.HandlerFunc {
// c.Abort()
//}
if claims.ExpiresAt-time.Now().Unix() < claims.BufferTime {
claims.ExpiresAt = time.Now().Unix() + global.GVA_CONFIG.JWT.ExpiresTime
dr, _ := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
claims.ExpiresAt = time.Now().Add(dr).Unix()
newToken, _ := j.CreateTokenByOldToken(token, *claims)
newClaims, _ := j.ParseToken(newToken)
c.Header("new-token", newToken)
+2 -2
View File
@@ -4,7 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"strings"
"time"
@@ -47,7 +47,7 @@ func (l Logger) SetLoggerMiddleware() gin.HandlerFunc {
if l.Filter != nil && !l.Filter(c) {
body, _ = c.GetRawData()
// 将原body塞回去
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
}
c.Next()
cost := time.Since(start)
+13 -13
View File
@@ -3,7 +3,7 @@ package middleware
import (
"bytes"
"encoding/json"
"io/ioutil"
"io"
"net/http"
"net/url"
"strconv"
@@ -36,11 +36,11 @@ func OperationRecord() gin.HandlerFunc {
var userId int
if c.Request.Method != http.MethodGet {
var err error
body, err = ioutil.ReadAll(c.Request.Body)
body, err = io.ReadAll(c.Request.Body)
if err != nil {
global.GVA_LOG.Error("read body from request error:", zap.Error(err))
} else {
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
}
} else {
query := c.Request.URL.RawQuery
@@ -75,7 +75,7 @@ func OperationRecord() gin.HandlerFunc {
}
// 上传文件时候 中间件日志进行裁断操作
if strings.Index(c.GetHeader("Content-Type"), "multipart/form-data") > -1 {
if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
if len(record.Body) > 1024 {
// 截断
newBody := respPool.Get().([]byte)
@@ -100,15 +100,15 @@ func OperationRecord() gin.HandlerFunc {
record.Latency = latency
record.Resp = writer.body.String()
if strings.Index(c.Writer.Header().Get("Pragma"), "public") > -1 ||
strings.Index(c.Writer.Header().Get("Expires"), "0") > -1 ||
strings.Index(c.Writer.Header().Get("Cache-Control"), "must-revalidate, post-check=0, pre-check=0") > -1 ||
strings.Index(c.Writer.Header().Get("Content-Type"), "application/force-download") > -1 ||
strings.Index(c.Writer.Header().Get("Content-Type"), "application/octet-stream") > -1 ||
strings.Index(c.Writer.Header().Get("Content-Type"), "application/vnd.ms-excel") > -1 ||
strings.Index(c.Writer.Header().Get("Content-Type"), "application/download") > -1 ||
strings.Index(c.Writer.Header().Get("Content-Disposition"), "attachment") > -1 ||
strings.Index(c.Writer.Header().Get("Content-Transfer-Encoding"), "binary") > -1 {
if strings.Contains(c.Writer.Header().Get("Pragma"), "public") ||
strings.Contains(c.Writer.Header().Get("Expires"), "0") ||
strings.Contains(c.Writer.Header().Get("Cache-Control"), "must-revalidate, post-check=0, pre-check=0") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/force-download") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/octet-stream") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/vnd.ms-excel") ||
strings.Contains(c.Writer.Header().Get("Content-Type"), "application/download") ||
strings.Contains(c.Writer.Header().Get("Content-Disposition"), "attachment") ||
strings.Contains(c.Writer.Header().Get("Content-Transfer-Encoding"), "binary") {
if len(record.Resp) > 1024 {
// 截断
newBody := respPool.Get().([]byte)
+1 -1
View File
@@ -1,7 +1,7 @@
package request
import (
"github.com/golang-jwt/jwt/v4"
jwt "github.com/golang-jwt/jwt/v4"
uuid "github.com/satori/go.uuid"
)
+1 -2
View File
@@ -5,7 +5,6 @@ package packfile
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -24,7 +23,7 @@ func writeFile(path string, data []byte) {
// 已存在的文件,不应该覆盖重写,可能在前端更改了配置文件等
if _, err := os.Stat(path); os.IsNotExist(err) {
if err2 := ioutil.WriteFile(path, data, os.ModePerm); err2 != nil {
if err2 := os.WriteFile(path, data, os.ModePerm); err2 != nil {
fmt.Printf("Write file failed: %s\n", path)
}
} else {
@@ -3,9 +3,18 @@ package request
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/{{.Package}}"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"time"
)
type {{.StructName}}Search struct{
{{.Package}}.{{.StructName}}
StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"`
EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"`
{{- range .Fields}}
{{- if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
Start{{.FieldName}} *{{.FieldType}} `json:"start{{.FieldName}}" form:"start{{.FieldName}}"`
End{{.FieldName}} *{{.FieldType}} `json:"end{{.FieldName}}" form:"end{{.FieldName}}"`
{{- end }}
{{- end }}
request.PageInfo
}
@@ -54,29 +54,24 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
db := global.GVA_DB.Model(&{{.Package}}.{{.StructName}}{})
var {{.Abbreviation}}s []{{.Package}}.{{.StructName}}
//
if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil {
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
}
{{- range .Fields}}
{{- if .FieldSearchType}}
{{- if eq .FieldType "string" }}
if info.{{.FieldName}} != "" {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+ {{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
}
{{- else if eq .FieldType "bool" }}
{{- else if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
if info.Start{{.FieldName}} != nil && info.End{{.FieldName}} != nil {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ? AND ? ",info.Start{{.FieldName}},info.End{{.FieldName}})
}
{{- else}}
if info.{{.FieldName}} != nil {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+{{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
}
{{- else if eq .FieldType "int" }}
if info.{{.FieldName}} != nil {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+{{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
}
{{- else if eq .FieldType "float64" }}
if info.{{.FieldName}} != nil {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+{{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
}
{{- else if eq .FieldType "time.Time" }}
if info.{{.FieldName}} != nil {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+{{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
err = db.Count(&total).Error
@@ -33,8 +33,8 @@
</el-form-item>
{{- end }}
<el-form-item>
<el-button size="mini" type="primary" @click="save">{{ "{{ t('general.save') }}" }}</el-button>
<el-button size="mini" type="primary" @click="back">{{ "{{ t('general.back') }}" }}</el-button>
<el-button size="small" type="primary" @click="save">{{ "{{ t('general.save') }}" }}</el-button>
<el-button size="small" type="primary" @click="back">{{ "{{ t('general.back') }}" }}</el-button>
</el-form-item>
</el-form>
</div>
@@ -2,6 +2,11 @@
<div>
<div class="gva-search-box">
<el-form :inline="true" :model="searchInfo" class="demo-form-inline">
<el-form-item label="创建时间">
<el-date-picker v-model="searchInfo.startCreatedAt" type="datetime" placeholder="开始时间"></el-date-picker>
<el-date-picker v-model="searchInfo.endCreatedAt" type="datetime" placeholder="结束时间"></el-date-picker>
</el-form-item>
{{- range .Fields}} {{- if .FieldSearchType}} {{- if eq .FieldType "bool" }}
<el-form-item label="{{.FieldDesc}}" prop="{{.FieldJson}}">
<el-select v-model="searchInfo.{{.FieldJson}}" clearable :placeholder="t('general.pleaseSelect')">
@@ -23,9 +28,36 @@
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
{{- else }}
{{- else}}
<el-form-item label="{{.FieldDesc}}">
<el-input v-model="searchInfo.{{.FieldJson}}" :placeholder="t('general.searchCriteria')" />
{{- if eq .FieldType "float64" "int"}}
{{if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
<el-input v-model.number="searchInfo.start{{.FieldName}}" placeholder="搜索条件(起)" />
<el-input v-model.number="searchInfo.end{{.FieldName}}" placeholder="搜索条件(止)" />
{{- else}}
{{- if .DictType}}
<el-select v-model="searchInfo.{{.FieldJson}}" placeholder="请选择" style="width:100%" :clearable="true" >
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else}}
<el-input v-model.number="searchInfo.{{.FieldJson}}" placeholder="搜索条件" />
{{- end }}
{{- end}}
{{- else if eq .FieldType "time.Time"}}
{{if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
<el-date-picker v-model="searchInfo.start{{.FieldName}}" type="datetime" placeholder="搜索条件(起)"></el-date-picker>
<el-date-picker v-model="searchInfo.end{{.FieldName}}" type="datetime" placeholder="搜索条件(止)"></el-date-picker>
{{- else}}
<el-date-picker v-model="searchInfo.{{.FieldJson}}" type="datetime" placeholder="搜索条件"></el-date-picker>
{{- end}}
{{- else}}
<el-input v-model="searchInfo.{{.FieldJson}}" placeholder="搜索条件" />
{{- end}}
</el-form-item>{{ end }}{{ end }}{{ end }}
<el-form-item>
<el-button size="small" type="primary" icon="search" @click="onSubmit">{{ "{{ t('general.search') }}" }}</el-button>
@@ -69,7 +101,12 @@
{{- else if eq .FieldType "bool" }}
<el-table-column align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">{{"{{"}} formatBoolean(scope.row.{{.FieldJson}}) {{"}}"}}</template>
</el-table-column> {{- else }}
</el-table-column>
{{- else if eq .FieldType "time.Time" }}
<el-table-column align="left" label="{{.FieldDesc}}" width="180">
<template #default="scope">{{"{{"}} formatDate(scope.row.{{.FieldJson}}) {{"}}"}}</template>
</el-table-column>
{{- else }}
<el-table-column align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
{{- end }}
{{- end }}
@@ -56,7 +56,11 @@ func (e *FileUploadAndDownloadService) CreateFileChunk(id uint, fileChunkPath st
func (e *FileUploadAndDownloadService) DeleteFileChunk(fileMd5 string, filePath string) error {
var chunks []example.ExaFileChunk
var file example.ExaFile
err := global.GVA_DB.Where("file_md5 = ? ", fileMd5).First(&file).Update("IsFinish", true).Update("file_path", filePath).Error
err := global.GVA_DB.Where("file_md5 = ? ", fileMd5).First(&file).
Updates(map[string]interface{}{
"IsFinish": true,
"file_path": filePath,
}).Error
if err != nil {
return err
}
+6 -2
View File
@@ -2,12 +2,12 @@ package system
import (
"context"
"time"
"go.uber.org/zap"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
)
type JwtService struct{}
@@ -60,7 +60,11 @@ func (jwtService *JwtService) GetRedisJWT(userName string) (redisJWT string, err
func (jwtService *JwtService) SetRedisJWT(jwt string, userName string) (err error) {
// 此处过期时间等于jwt过期时间
timer := time.Duration(global.GVA_CONFIG.JWT.ExpiresTime) * time.Second
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
if err != nil {
return err
}
timer := dr
err = global.GVA_REDIS.Set(context.Background(), userName, jwt, timer).Err()
return err
}
+26 -7
View File
@@ -3,7 +3,6 @@ package system
import (
"errors"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
@@ -44,7 +43,15 @@ func (apiService *ApiService) DeleteApi(api system.SysApi) (err error) {
if err != nil {
return err
}
CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method)
success := CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method)
if !success {
return errors.New(entity.Path + ":" + entity.Method + "casbin同步清理失败")
}
e := CasbinServiceApp.Casbin()
err = e.InvalidateCache()
if err != nil {
return err
}
return nil
}
@@ -166,10 +173,22 @@ func (apiService *ApiService) UpdateApi(api system.SysApi) (err error) {
//@return: err error
func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) {
err = global.GVA_DB.Delete(&[]system.SysApi{}, "id in ?", ids.Ids).Error
var apis []system.SysApi
err = global.GVA_DB.Find(&apis, "id in ?", ids.Ids).Delete(&apis).Error
if err != nil {
return err
} else {
for _, sysApi := range apis {
success := CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
if !success {
return errors.New(sysApi.Path + ":" + sysApi.Method + "casbin同步清理失败")
}
}
e := CasbinServiceApp.Casbin()
err = e.InvalidateCache()
if err != nil {
return err
}
}
return err
}
func (apiService *ApiService) DeleteApiByIds(ids []string) (err error) {
return global.GVA_DB.Delete(&system.SysApi{}, "id in ?", ids).Error
}
+3 -4
View File
@@ -10,7 +10,6 @@ import (
"go/parser"
"go/token"
"io"
"io/ioutil"
"log"
"mime/multipart"
"os"
@@ -201,7 +200,7 @@ func (autoCodeService *AutoCodeService) PreviewTemp(autoCode system.AutoCodeStru
builder.WriteString(strings.Replace(ext, ".", "", -1))
}
builder.WriteString("\n\n")
data, err := ioutil.ReadAll(f)
data, err := io.ReadAll(f)
if err != nil {
return nil, err
}
@@ -372,7 +371,7 @@ func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruc
//@return: []string, error
func (autoCodeService *AutoCodeService) GetAllTplFile(pathName string, fileList []string) ([]string, error) {
files, err := ioutil.ReadDir(pathName)
files, err := os.ReadDir(pathName)
for _, fi := range files {
if fi.IsDir() {
fileList, err = autoCodeService.GetAllTplFile(pathName+"/"+fi.Name(), fileList)
@@ -816,7 +815,7 @@ func ImportReference(filepath, importCode, structName, packageName, groupName st
log.Fatal(err)
}
// 写回数据
return ioutil.WriteFile(filepath, buffer.Bytes(), 0o600)
return os.WriteFile(filepath, buffer.Bytes(), 0o600)
}
// CreatePlug 自动创建插件模板
+12 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"path/filepath"
"strconv"
"strings"
"time"
@@ -67,7 +68,17 @@ func (autoCodeHistoryService *AutoCodeHistoryService) RollBack(info *systemReq.R
return err
}
// 清除API表
err := ApiServiceApp.DeleteApiByIds(strings.Split(md.ApiIDs, ";"))
ids := request.IdsReq{}
idsStr := strings.Split(md.ApiIDs, ";")
for i := range idsStr[0 : len(idsStr)-1] {
id, err := strconv.Atoi(idsStr[i])
if err != nil {
return err
}
ids.Ids = append(ids.Ids, id)
}
err := ApiServiceApp.DeleteApisByIds(ids)
if err != nil {
global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err))
}
+15 -5
View File
@@ -35,6 +35,10 @@ func (casbinService *CasbinService) UpdateCasbin(AuthorityID uint, casbinInfos [
if !success {
return errors.New("存在相同api,添加失败,请联系管理员")
}
err := e.InvalidateCache()
if err != nil {
return err
}
return nil
}
@@ -49,6 +53,11 @@ func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath stri
"v1": newPath,
"v2": newMethod,
}).Error
e := casbinService.Casbin()
err = e.InvalidateCache()
if err != nil {
return err
}
return err
}
@@ -89,11 +98,11 @@ func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool {
//@return: *casbin.Enforcer
var (
syncedEnforcer *casbin.SyncedEnforcer
cachedEnforcer *casbin.CachedEnforcer
once sync.Once
)
func (casbinService *CasbinService) Casbin() *casbin.SyncedEnforcer {
func (casbinService *CasbinService) Casbin() *casbin.CachedEnforcer {
once.Do(func() {
a, _ := gormadapter.NewAdapterByDB(global.GVA_DB)
text := `
@@ -117,8 +126,9 @@ func (casbinService *CasbinService) Casbin() *casbin.SyncedEnforcer {
zap.L().Error("字符串加载模型失败!", zap.Error(err))
return
}
syncedEnforcer, _ = casbin.NewSyncedEnforcer(m, a)
cachedEnforcer, _ = casbin.NewCachedEnforcer(m, a)
cachedEnforcer.SetExpireTime(60 * 60)
_ = cachedEnforcer.LoadPolicy()
})
_ = syncedEnforcer.LoadPolicy()
return syncedEnforcer
return cachedEnforcer
}
+15
View File
@@ -218,3 +218,18 @@ func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (
// err = global.GVA_DB.Raw(sql, authorityId).Scan(&menus).Error
return menus, err
}
// UserAuthorityDefaultRouter 用户角色默认路由检查
// Author [SliverHorn](https://github.com/SliverHorn)
func (menuService *MenuService) UserAuthorityDefaultRouter(user *system.SysUser) {
var menuIds []string
err := global.GVA_DB.Model(&system.SysAuthorityMenu{}).Where("sys_authority_authority_id = ?", user.AuthorityId).Pluck("sys_base_menu_id", &menuIds).Error
if err != nil {
return
}
var am system.SysBaseMenu
err = global.GVA_DB.First(&am, "name = ? and id in (?)", user.Authority.DefaultRouter, menuIds).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
user.Authority.DefaultRouter = "404"
}
}
+4 -37
View File
@@ -32,6 +32,7 @@ func (userService *UserService) Register(u system.SysUser) (userInter system.Sys
}
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: Login
//@description: 用户登录
//@param: u *model.SysUser
@@ -48,26 +49,8 @@ func (userService *UserService) Login(u *system.SysUser) (userInter *system.SysU
if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
return nil, errors.New("密码错误")
}
var SysAuthorityMenus []system.SysAuthorityMenu
err = global.GVA_DB.Where("sys_authority_authority_id = ?", user.AuthorityId).Find(&SysAuthorityMenus).Error
if err != nil {
return
}
var MenuIds []string
for i := range SysAuthorityMenus {
MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId)
}
var am system.SysBaseMenu
ferr := global.GVA_DB.First(&am, "name = ? and id in (?)", user.Authority.DefaultRouter, MenuIds).Error
if errors.Is(ferr, gorm.ErrRecordNotFound) {
user.Authority.DefaultRouter = "404"
}
MenuServiceApp.UserAuthorityDefaultRouter(&user)
}
return &user, err
}
@@ -183,6 +166,7 @@ func (userService *UserService) SetUserInfo(req system.SysUser) error {
}
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: GetUserInfo
//@description: 获取用户信息
//@param: uuid uuid.UUID
@@ -194,24 +178,7 @@ func (userService *UserService) GetUserInfo(uuid uuid.UUID) (user system.SysUser
if err != nil {
return reqUser, err
}
var SysAuthorityMenus []system.SysAuthorityMenu
err = global.GVA_DB.Where("sys_authority_authority_id = ?", reqUser.AuthorityId).Find(&SysAuthorityMenus).Error
if err != nil {
return
}
var MenuIds []string
for i := range SysAuthorityMenus {
MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId)
}
var am system.SysBaseMenu
ferr := global.GVA_DB.First(&am, "name = ? and id in (?)", reqUser.Authority.DefaultRouter, MenuIds).Error
if errors.Is(ferr, gorm.ErrRecordNotFound) {
reqUser.Authority.DefaultRouter = "404"
}
MenuServiceApp.UserAuthorityDefaultRouter(&user)
return reqUser, err
}
+2 -2
View File
@@ -54,7 +54,7 @@ func (i *initUser) InitializeData(ctx context.Context) (next context.Context, er
UUID: uuid.NewV4(),
Username: "admin",
Password: adminPassword,
NickName: "超级管理员",
NickName: "Mr.奇淼",
HeaderImg: "https://qmplusimg.henrongyi.top/gva_header.jpg",
AuthorityId: 888,
Phone: "17611111111",
@@ -64,7 +64,7 @@ func (i *initUser) InitializeData(ctx context.Context) (next context.Context, er
UUID: uuid.NewV4(),
Username: "a303176530",
Password: password,
NickName: "QMPlusUser",
NickName: "用户1",
HeaderImg: "https:///qmplusimg.henrongyi.top/1572075907logo.png",
AuthorityId: 9528,
Phone: "17611111111",
+2 -3
View File
@@ -1,7 +1,6 @@
package utils
import (
"io/ioutil"
"os"
"strconv"
)
@@ -75,7 +74,7 @@ func makeFileContent(content []byte, fileName string, FileDir string, contentNum
//@return: error, string
func MakeFile(fileName string, FileMd5 string) (string, error) {
rd, err := ioutil.ReadDir(breakpointDir + FileMd5)
rd, err := os.ReadDir(breakpointDir + FileMd5)
if err != nil {
return finishDir + fileName, err
}
@@ -86,7 +85,7 @@ func MakeFile(fileName string, FileMd5 string) (string, error) {
}
defer fd.Close()
for k := range rd {
content, _ := ioutil.ReadFile(breakpointDir + FileMd5 + "/" + fileName + "_" + strconv.Itoa(k))
content, _ := os.ReadFile(breakpointDir + FileMd5 + "/" + fileName + "_" + strconv.Itoa(k))
_, err = fd.Write(content)
if err != nil {
_ = os.Remove(finishDir + fileName)
+23
View File
@@ -0,0 +1,23 @@
package utils
import (
"strconv"
"strings"
"time"
)
func ParseDuration(d string) (time.Duration, error) {
dr, err := time.ParseDuration(d)
if err == nil {
return dr, nil
}
if strings.HasSuffix(d, "d") {
h := strings.TrimSuffix(d, "d")
hour, _ := strconv.Atoi(h)
dr = time.Hour * 24 * time.Duration(hour)
return dr, nil
}
dv, err := strconv.ParseInt(d, 10, 64)
return time.Duration(dv), err
}
+5 -5
View File
@@ -6,7 +6,7 @@ import (
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"os"
"strings"
)
@@ -28,7 +28,7 @@ const (
//@return: error
func AutoInjectionCode(filepath string, funcName string, codeData string) error {
srcData, err := ioutil.ReadFile(filepath)
srcData, err := os.ReadFile(filepath)
if err != nil {
return err
}
@@ -113,7 +113,7 @@ func AutoInjectionCode(filepath string, funcName string, codeData string) error
srcData = append(append(srcData[:indexPos], insertData...), remainData...)
// 写回数据
return ioutil.WriteFile(filepath, srcData, 0o600)
return os.WriteFile(filepath, srcData, 0o600)
}
func checkExist(srcData *[]byte, startPos int, endPos int, blockStmt *ast.BlockStmt, target string) bool {
@@ -153,7 +153,7 @@ func checkExist(srcData *[]byte, startPos int, endPos int, blockStmt *ast.BlockS
}
func AutoClearCode(filepath string, codeData string) error {
srcData, err := ioutil.ReadFile(filepath)
srcData, err := os.ReadFile(filepath)
if err != nil {
return err
}
@@ -161,7 +161,7 @@ func AutoClearCode(filepath string, codeData string) error {
if err != nil {
return err
}
return ioutil.WriteFile(filepath, srcData, 0o600)
return os.WriteFile(filepath, srcData, 0o600)
}
func cleanCode(clearCode string, srcData string) ([]byte, error) {
+8 -5
View File
@@ -4,9 +4,10 @@ import (
"errors"
"time"
jwt "github.com/golang-jwt/jwt/v4"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/golang-jwt/jwt/v4"
)
type JWT struct {
@@ -27,13 +28,15 @@ func NewJWT() *JWT {
}
func (j *JWT) CreateClaims(baseClaims request.BaseClaims) request.CustomClaims {
bf, _ := ParseDuration(global.GVA_CONFIG.JWT.BufferTime)
ep, _ := ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
claims := request.CustomClaims{
BaseClaims: baseClaims,
BufferTime: global.GVA_CONFIG.JWT.BufferTime, // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失
BufferTime: int64(bf), // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix() - 1000, // 签名生效时间
ExpiresAt: time.Now().Unix() + global.GVA_CONFIG.JWT.ExpiresTime, // 过期时间 7天 配置文件
Issuer: global.GVA_CONFIG.JWT.Issuer, // 签名的发行者
NotBefore: time.Now().Unix() - 1000, // 签名生效时间
ExpiresAt: time.Now().Add(ep).Unix(), // 过期时间 7天 配置文件
Issuer: global.GVA_CONFIG.JWT.Issuer, // 签名的发行者
},
}
return claims
+1
View File
@@ -45,6 +45,7 @@ func LoadPlugin(path string) error {
return err
}
if fileInfo.IsDir() {
// TODO 返回的参数不一样暂时不修改(https://golang.google.cn/doc/go1.16#ioutil)
fileSlice, err := ioutil.ReadDir(path)
if err != nil {
return err
+1
View File
@@ -19,6 +19,7 @@
"js-cookie": "^3.0.1",
"marked": "^2.0.0",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"pinia": "^2.0.9",
"qs": "^6.8.0",
+12
View File
@@ -13,6 +13,18 @@ import auth from '@/directive/auth'
import { store } from '@/pinia'
import App from './App.vue'
import i18n from './i18n' // added by mohamed hassan to multilangauge
/**
* @description 导入加载进度条,防止首屏加载时间过长,用户等待
*
* */
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
Nprogress.configure({ showSpinner: false, ease: 'ease', speed: 500 })
Nprogress.start()
/**
* 无需在这块结束,会在路由中间件中结束此块内容
* */
const app = createApp(App)
app.config.productionTip = false
+12
View File
@@ -2,6 +2,7 @@ import { useUserStore } from '@/pinia/modules/user'
import { useRouterStore } from '@/pinia/modules/router'
import getPageTitle from '@/utils/page'
import router from '@/router'
import Nprogress from 'nprogress'
let asyncRouterFlag = 0
@@ -37,6 +38,7 @@ async function handleKeepAlive(to) {
}
router.beforeEach(async(to, from) => {
Nprogress.start()
const userStore = useUserStore()
to.meta.matched = [...to.matched]
handleKeepAlive(to)
@@ -99,3 +101,13 @@ router.beforeEach(async(to, from) => {
}
}
})
router.afterEach(() => {
// 路由加载完成后关闭进度条
Nprogress.done()
})
router.onError(() => {
// 路由发生错误后销毁进度条
Nprogress.remove()
})
+1 -1
View File
@@ -77,7 +77,7 @@ export const useUserStore = defineStore('user', () => {
asyncRouters.forEach(asyncRouter => {
router.addRoute(asyncRouter)
})
router.push({ name: userInfo.value.authority.defaultRouter })
await router.push({ name: userInfo.value.authority.defaultRouter })
loadingInstance.value.close()
return true
}
+5 -1
View File
@@ -58,4 +58,8 @@
.keyword{
width: 220px;
margin: 0 0 0 30px;
}
}
#nprogress .bar {
background: #4D70FF !important; //自定义颜色
}
+1 -17
View File
@@ -461,22 +461,6 @@ a {
text-decoration: none;
}
a:link {
color: #fff;
}
a:visited {
color: #fff;
}
a:hover {
color: #fff;
}
a:active {
color: #fff;
}
input::-ms-clear {
display: none;
}
@@ -597,7 +581,7 @@ li {
}
.aside {
.el-menu--collapse {
>.el-menu-item{
>.el-menu-item {
display: flex;
justify-content: center;
}
+1
View File
@@ -28,6 +28,7 @@
ref="loginForm"
:model="loginFormData"
:rules="rules"
:validate-on-rule-change="false"
@keyup.enter="submitForm"
>
<el-form-item prop="username">
@@ -58,7 +58,10 @@
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="middleDate.fieldType!=='string'&&item.value==='LIKE'"
:disabled="
(middleDate.fieldType!=='string'&&item.value==='LIKE')||
((middleDate.fieldType!=='int'&&middleDate.fieldType!=='time.Time'&&middleDate.fieldType!=='float64')&&(item.value==='BETWEEN' || item.value==='NOT BETWEEN'))
"
/>
</el-select>
</el-form-item>
@@ -133,6 +136,14 @@ const typeSearchOptions = ref([
{
label: 'LIKE',
value: 'LIKE'
},
{
label: 'BETWEEN',
value: 'BETWEEN'
},
{
label: 'NOT BETWEEN',
value: 'NOT BETWEEN'
}
])
const typeOptions = ref([