gorm update

This commit is contained in:
2025-05-07 15:32:05 +08:00
parent 68606c76f9
commit 38ee553cf3
18 changed files with 283 additions and 301 deletions

View File

@@ -150,7 +150,7 @@ func UploadFile(file *multipart.FileHeader, t FileType) (string, error) {
}
func GetPath() string {
return fmt.Sprintf("upload/%s/%s/%s/", time.Now().Format("2006"), time.Now().Format("01"), time.Now().Format("02"))
return fmt.Sprintf("public/%s/%s/%s/", time.Now().Format("2006"), time.Now().Format("01"), time.Now().Format("02"))
}
func GenFilename(ext string) string {

View File

@@ -1,33 +1,52 @@
package middleware
import (
"context"
"errors"
"net/http"
"time"
systemmodel "management/internal/erpserver/model/system"
v1 "management/internal/erpserver/service/v1"
"management/internal/pkg/know"
"management/internal/pkg/session"
"github.com/drhin/logger"
"go.uber.org/zap"
)
func (m *middleware) Audit(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer func(res http.ResponseWriter, req *http.Request) {
// 记录审计日志
go m.writeLog(req, start)
}(w, r)
next.ServeHTTP(w, r)
func Audit(sess session.Manager, auditLogService v1.AuditLogService, log *logger.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer func() {
go func() {
ctx := r.Context()
user, err := sess.GetUser(ctx, know.StoreName)
if err != nil {
log.Error(err.Error(), err)
return
}
if user.ID == 0 {
log.Error("scs get user is empty", errors.New("scs get user is empty"))
return
}
al := systemmodel.NewAuditLog(r, user.Email, user.OS, user.Browser, start, time.Now())
if err := auditLogService.Create(ctx, al); err != nil {
log.Error(err.Error(), err,
zap.Int32("user_id", user.ID),
zap.String("user", user.Email),
zap.String("ip", al.Ip),
zap.String("os", al.Os),
zap.String("method", al.Method),
zap.String("path", al.Url),
)
}
}()
}()
next.ServeHTTP(w, r)
})
}
return http.HandlerFunc(fn)
}
func (m *middleware) writeLog(req *http.Request, start time.Time) {
end := time.Now()
user := m.AuthUser(req.Context())
al := systemmodel.NewAuditLog(req, user.Email, user.OS, user.Browser, start, end)
c, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
_ = m.auditLogService.Create(c, al)
}

View File

@@ -1,94 +1,59 @@
package middleware
import (
"context"
"encoding/json"
"net/http"
"management/internal/erpserver/model/dto"
v1 "management/internal/erpserver/service/v1"
"management/internal/pkg/know"
"management/internal/pkg/session"
)
var defaultMenus = map[string]bool{
"/home.html": true,
"/dashboard": true,
"/system/menus": true,
"/upload/img": true,
"/upload/file": true,
"/upload/mutilfile": true,
"/pear.json": true,
var publicRoutes = map[string]bool{
"/home.html": true,
"/dashboard": true,
"/system/menus": true,
"/upload/img": true,
"/upload/file": true,
"/upload/multi_files": true,
"/pear.json": true,
}
func (m *middleware) Authorize(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
user, ok := m.isLogin(ctx)
if !ok {
http.Redirect(w, r, "/", http.StatusFound)
return
}
func Authorize(
sess session.Manager,
menuService v1.MenuService,
) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
path := r.URL.Path
// 登陆检查
user, err := sess.GetUser(ctx, know.StoreName)
if err != nil || user.ID == 0 {
http.Redirect(w, r, "/", http.StatusFound)
return
}
// 公共路由放行
if publicRoutes[path] {
next.ServeHTTP(w, r)
return
}
// 权限检查
menus, err := menuService.ListByRoleIDToMap(ctx, user.RoleID)
if err != nil || !hasPermission(menus, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
// 登陆成功 判断权限
path := r.URL.Path
if b, ok := defaultMenus[path]; ok && b {
next.ServeHTTP(w, r)
return
}
menus, err := m.menuService.ListByRoleIDToMap(ctx, user.RoleID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if _, ok := menus[path]; ok {
next.ServeHTTP(w, r)
return
}
http.Error(w, "Unauthorized", http.StatusUnauthorized)
})
}
return http.HandlerFunc(fn)
}
func (m *middleware) isLogin(ctx context.Context) (*dto.AuthorizeUser, bool) {
if exists := m.session.Exists(ctx, know.StoreName); exists {
b := m.session.GetBytes(ctx, know.StoreName)
var user dto.AuthorizeUser
if err := json.Unmarshal(b, &user); err == nil && user.ID > 0 {
return &user, true
}
}
return nil, false
}
func (m *middleware) AuthUser(ctx context.Context) dto.AuthorizeUser {
var user dto.AuthorizeUser
if exists := m.session.Exists(ctx, know.StoreName); exists {
b := m.session.GetBytes(ctx, know.StoreName)
_ = json.Unmarshal(b, &user)
}
return user
}
func (m *middleware) IsAuth(ctx context.Context) bool {
var user dto.AuthorizeUser
b := m.session.GetBytes(ctx, know.StoreName)
if err := json.Unmarshal(b, &user); err == nil && user.ID > 0 {
return true
}
return false
}
func (m *middleware) RefreshToken(ctx context.Context) bool {
if err := m.session.RenewToken(ctx); err == nil {
return true
}
return false
}
func (m *middleware) Destroy(ctx context.Context) error {
return m.session.Destroy(ctx)
func hasPermission(menus map[string]*dto.OwnerMenuDto, path string) bool {
_, ok := menus[path]
return ok
}

View File

@@ -6,6 +6,6 @@ import (
"github.com/justinas/nosurf"
)
func (m *middleware) NoSurf(next http.Handler) http.Handler {
func NoSurf(next http.Handler) http.Handler {
return nosurf.New(next)
}

View File

@@ -1,35 +0,0 @@
package middleware
import (
"context"
"net/http"
"management/internal/erpserver/model/dto"
v1 "management/internal/erpserver/service/v1"
"management/internal/pkg/session"
)
type Middleware interface {
Audit(next http.Handler) http.Handler
NoSurf(next http.Handler) http.Handler
LoadSession(next http.Handler) http.Handler
Authorize(next http.Handler) http.Handler
AuthUser(ctx context.Context) dto.AuthorizeUser
IsAuth(ctx context.Context) bool
RefreshToken(ctx context.Context) bool
Destroy(ctx context.Context) error
}
type middleware struct {
session session.Session
menuService v1.MenuService
auditLogService v1.AuditLogService
}
func New(session session.Session, menuService v1.MenuService, auditLogService v1.AuditLogService) Middleware {
return &middleware{
session: session,
menuService: menuService,
auditLogService: auditLogService,
}
}

View File

@@ -2,8 +2,10 @@ package middleware
import (
"net/http"
"management/internal/pkg/session"
)
func (m *middleware) LoadSession(next http.Handler) http.Handler {
return m.session.LoadAndSave(next)
func LoadSession(sm session.Manager) func(http.Handler) http.Handler {
return sm.Load
}

View File

@@ -3,7 +3,6 @@ package render
import (
"bytes"
"context"
"encoding/json"
"fmt"
"html/template"
"net/http"
@@ -60,15 +59,15 @@ func (r *render) setDefaultData(req *http.Request, data map[string]any) map[stri
}
ctx := req.Context()
isAuth := r.session.Exists(ctx, know.StoreName)
data["IsAuthenticated"] = isAuth
if isAuth {
var authUser dto.AuthorizeUser
u := r.session.GetBytes(ctx, know.StoreName)
_ = json.Unmarshal(u, &authUser)
authUser, err := r.session.GetUser(ctx, know.StoreName)
if err != nil || authUser == nil {
data["IsAuthenticated"] = false
} else {
data["IsAuthenticated"] = true
data["AuthorizeMenus"] = r.getCurrentPathButtons(ctx, authUser.RoleID, req.URL.Path)
}
token := nosurf.Token(req)
data["CsrfToken"] = token
data["CsrfTokenField"] = template.HTML(fmt.Sprintf(`<input type="hidden" name="csrf_token" value="%s" />`, token))

View File

@@ -28,12 +28,12 @@ type jsonRender interface {
type render struct {
templateConfig *TemplateConfig
templates map[string]*template.Template
session session.Session
session session.Manager
menuService v1.MenuService
}
func New(session session.Session, menuService v1.MenuService) (Render, error) {
func New(session session.Manager, menuService v1.MenuService) (Render, error) {
r := &render{
templateConfig: &TemplateConfig{
Root: ".",

View File

@@ -2,9 +2,12 @@ package session
import (
"context"
"encoding/json"
"errors"
"net/http"
"time"
"management/internal/erpserver/model/dto"
"management/internal/pkg/config"
"github.com/alexedwards/scs/postgresstore"
@@ -12,20 +15,22 @@ import (
"gorm.io/gorm"
)
type Session interface {
Destroy(ctx context.Context) error
LoadAndSave(next http.Handler) http.Handler
Put(ctx context.Context, key string, val any)
GetBytes(ctx context.Context, key string) []byte
Exists(ctx context.Context, key string) bool
var ErrNoSession = errors.New("session user not found")
// Manager 抽象核心会话操作
type Manager interface {
Load(next http.Handler) http.Handler
GetUser(ctx context.Context, key string) (*dto.AuthorizeUser, error)
PutUser(ctx context.Context, key string, user *dto.AuthorizeUser) error
RenewToken(ctx context.Context) error
Destroy(ctx context.Context) error
}
type session struct {
sessionManager *scs.SessionManager
type SCSSession struct {
manager *scs.SessionManager
}
func New(db *gorm.DB, config *config.Config) Session {
func NewSCSManager(db *gorm.DB, config *config.Config) (Manager, error) {
sessionManager := scs.New()
sessionManager.Lifetime = 24 * time.Hour
sessionManager.IdleTimeout = 2 * time.Hour
@@ -35,7 +40,11 @@ func New(db *gorm.DB, config *config.Config) Session {
sessionManager.Cookie.SameSite = http.SameSiteStrictMode
sessionManager.Cookie.Secure = config.App.Prod
sqlDB, _ := db.DB()
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
// postgres
// github.com/alexedwards/scs/postgresstore
sessionManager.Store = postgresstore.New(sqlDB)
@@ -44,32 +53,39 @@ func New(db *gorm.DB, config *config.Config) Session {
// sessionManager.Store = pgxstore.New(pool)
// redis
// sessionManager.Store = newRedisStore()
return &SCSSession{manager: sessionManager}, nil
}
return &session{
sessionManager: sessionManager,
func (s *SCSSession) Load(next http.Handler) http.Handler {
return s.manager.LoadAndSave(next)
}
func (s *SCSSession) GetUser(ctx context.Context, key string) (*dto.AuthorizeUser, error) {
data, ok := s.manager.Get(ctx, key).([]byte)
if !ok || len(data) == 0 {
return nil, ErrNoSession
}
var user dto.AuthorizeUser
if err := json.Unmarshal(data, &user); err != nil {
return nil, err
}
return &user, nil
}
func (s *session) Destroy(ctx context.Context) error {
return s.sessionManager.Destroy(ctx)
func (s *SCSSession) PutUser(ctx context.Context, key string, user *dto.AuthorizeUser) error {
data, err := json.Marshal(user)
if err != nil {
return err
}
s.manager.Put(ctx, key, data)
return nil
}
func (s *session) LoadAndSave(next http.Handler) http.Handler {
return s.sessionManager.LoadAndSave(next)
func (s *SCSSession) RenewToken(ctx context.Context) error {
return s.manager.RenewToken(ctx)
}
func (s *session) Put(ctx context.Context, key string, val any) {
s.sessionManager.Put(ctx, key, val)
}
func (s *session) GetBytes(ctx context.Context, key string) []byte {
return s.sessionManager.GetBytes(ctx, key)
}
func (s *session) Exists(ctx context.Context, key string) bool {
return s.sessionManager.Exists(ctx, key)
}
func (s *session) RenewToken(ctx context.Context) error {
return s.sessionManager.RenewToken(ctx)
func (s *SCSSession) Destroy(ctx context.Context) error {
return s.manager.Destroy(ctx)
}