152 lines
3.5 KiB
Go
152 lines
3.5 KiB
Go
package pg
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/chaitin/panda-wiki/consts"
|
|
"github.com/chaitin/panda-wiki/domain"
|
|
"github.com/chaitin/panda-wiki/log"
|
|
"github.com/chaitin/panda-wiki/store/pg"
|
|
)
|
|
|
|
type UserAccessRepository struct {
|
|
db *pg.DB
|
|
logger *log.Logger
|
|
accessMap sync.Map
|
|
}
|
|
|
|
func NewUserAccessRepository(db *pg.DB, logger *log.Logger) *UserAccessRepository {
|
|
repo := &UserAccessRepository{
|
|
db: db,
|
|
logger: logger.WithModule("repo.pg.user_access"),
|
|
accessMap: sync.Map{},
|
|
}
|
|
// start sync task
|
|
go repo.startSyncTask()
|
|
return repo
|
|
}
|
|
|
|
// UpdateAccessTime update user access time
|
|
func (r *UserAccessRepository) UpdateAccessTime(userID string) {
|
|
r.accessMap.Store(userID, time.Now())
|
|
}
|
|
|
|
// GetAccessTime get user access time
|
|
func (r *UserAccessRepository) GetAccessTime(userID string) (time.Time, bool) {
|
|
if value, ok := r.accessMap.Load(userID); ok {
|
|
return value.(time.Time), true
|
|
}
|
|
return time.Time{}, false
|
|
}
|
|
|
|
// startSyncTask start sync task
|
|
func (r *UserAccessRepository) startSyncTask() {
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
defer ticker.Stop()
|
|
|
|
for range ticker.C {
|
|
r.syncToDatabase()
|
|
}
|
|
}
|
|
|
|
// syncToDatabase sync data to database
|
|
func (r *UserAccessRepository) syncToDatabase() {
|
|
// collect data to update
|
|
updates := make([]domain.UserAccessTime, 0)
|
|
r.accessMap.Range(func(key, value any) bool {
|
|
userID := key.(string)
|
|
timestamp := value.(time.Time)
|
|
updates = append(updates, domain.UserAccessTime{
|
|
UserID: userID,
|
|
Timestamp: timestamp,
|
|
})
|
|
return true
|
|
})
|
|
|
|
if len(updates) == 0 {
|
|
return
|
|
}
|
|
|
|
// batch update database
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
for _, update := range updates {
|
|
if err := tx.Model(&domain.User{}).
|
|
Where("id = ?", update.UserID).
|
|
Update("last_access", update.Timestamp).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
r.logger.Error("failed to sync user access time to database",
|
|
log.Error(err),
|
|
log.Int("update_count", len(updates)))
|
|
return
|
|
}
|
|
|
|
// clear synced data
|
|
for _, update := range updates {
|
|
if currentTime, ok := r.GetAccessTime(update.UserID); ok {
|
|
// only delete old data
|
|
if !currentTime.After(update.Timestamp) {
|
|
r.accessMap.Delete(update.UserID)
|
|
}
|
|
}
|
|
}
|
|
|
|
r.logger.Info("synced user access time to database",
|
|
log.Int("update_count", len(updates)))
|
|
}
|
|
|
|
func (r *UserAccessRepository) ValidateRole(userID string, role consts.UserRole) (bool, error) {
|
|
var user domain.User
|
|
if err := r.db.Model(&domain.User{}).Where("id = ?", userID).First(&user).Error; err != nil {
|
|
return false, fmt.Errorf("get user failed")
|
|
}
|
|
|
|
if user.Role == consts.UserRoleAdmin {
|
|
return true, nil
|
|
}
|
|
|
|
if user.Role == role {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func (r *UserAccessRepository) ValidateKBPerm(kbId, userId string, perm consts.UserKBPermission) (bool, error) {
|
|
var user domain.User
|
|
if err := r.db.Model(&domain.User{}).Where("id = ?", userId).First(&user).Error; err != nil {
|
|
return false, fmt.Errorf("get user failed %s", err)
|
|
}
|
|
|
|
if user.Role == consts.UserRoleAdmin {
|
|
return true, nil
|
|
}
|
|
|
|
var kbUser domain.KBUsers
|
|
err := r.db.Model(&domain.KBUsers{}).
|
|
Where("kb_id = ? AND user_id = ?", kbId, userId).
|
|
First(&kbUser).Error
|
|
if err != nil {
|
|
return false, fmt.Errorf("get kb user failed %s", err)
|
|
|
|
}
|
|
|
|
if perm == consts.UserKBPermissionNotNull {
|
|
return kbUser.Perm != consts.UserKBPermissionNull, nil
|
|
}
|
|
|
|
if kbUser.Perm == perm || kbUser.Perm == consts.UserKBPermissionFullControl {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|