mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
сложно поддерживать однояйцевых близнецов - desktop и TMA, подготовил к рефакторингу структуры
584 lines
20 KiB
Go
584 lines
20 KiB
Go
package account
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"rmser/internal/domain/account"
|
||
"rmser/pkg/logger"
|
||
|
||
"github.com/google/uuid"
|
||
"go.uber.org/zap"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type pgRepository struct {
|
||
db *gorm.DB
|
||
}
|
||
|
||
func NewRepository(db *gorm.DB) account.Repository {
|
||
return &pgRepository{db: db}
|
||
}
|
||
|
||
// GetOrCreateUser находит пользователя или создает нового
|
||
func (r *pgRepository) GetOrCreateUser(telegramID int64, username, first, last string) (*account.User, error) {
|
||
var user account.User
|
||
err := r.db.Where("telegram_id = ?", telegramID).First(&user).Error
|
||
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
newUser := account.User{
|
||
TelegramID: telegramID,
|
||
Username: username,
|
||
FirstName: first,
|
||
LastName: last,
|
||
}
|
||
if err := r.db.Create(&newUser).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
return &newUser, nil
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
// Обновляем инфо
|
||
if user.Username != username || user.FirstName != first || user.LastName != last {
|
||
user.Username = username
|
||
user.FirstName = first
|
||
user.LastName = last
|
||
r.db.Save(&user)
|
||
}
|
||
|
||
return &user, nil
|
||
}
|
||
|
||
func (r *pgRepository) GetUserByTelegramID(telegramID int64) (*account.User, error) {
|
||
var user account.User
|
||
err := r.db.Where("telegram_id = ?", telegramID).First(&user).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &user, nil
|
||
}
|
||
|
||
func (r *pgRepository) GetUserByID(id uuid.UUID) (*account.User, error) {
|
||
var user account.User
|
||
err := r.db.Where("id = ?", id).First(&user).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &user, nil
|
||
}
|
||
|
||
// ConnectServer - Основная точка входа для добавления сервера
|
||
func (r *pgRepository) ConnectServer(userID uuid.UUID, rawURL, login, encryptedPass, name string) (*account.RMSServer, error) {
|
||
cleanURL := strings.TrimRight(strings.ToLower(strings.TrimSpace(rawURL)), "/")
|
||
|
||
var server account.RMSServer
|
||
var created bool
|
||
|
||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||
// Сначала ищем среди удаленных серверов
|
||
err := tx.Unscoped().Where("base_url = ?", cleanURL).First(&server).Error
|
||
if err != nil && err != gorm.ErrRecordNotFound {
|
||
return err
|
||
}
|
||
|
||
if err == gorm.ErrRecordNotFound {
|
||
// --- СЦЕНАРИЙ 1: НОВЫЙ СЕРВЕР (Приветственный бонус) ---
|
||
trialDays := 30
|
||
welcomeBalance := 10
|
||
paidUntil := time.Now().AddDate(0, 0, trialDays)
|
||
|
||
server = account.RMSServer{
|
||
BaseURL: cleanURL,
|
||
Name: name,
|
||
MaxUsers: 5,
|
||
Balance: welcomeBalance,
|
||
PaidUntil: &paidUntil,
|
||
}
|
||
if err := tx.Create(&server).Error; err != nil {
|
||
return err
|
||
}
|
||
created = true
|
||
} else if server.DeletedAt.Valid {
|
||
// --- СЦЕНАРИЙ 2: ВОССТАНОВЛЕНИЕ УДАЛЕННОГО СЕРВЕРА ---
|
||
// Восстанавливаем сервер, сохраняя старые значения Balance, InvoiceCount и ID
|
||
server.Name = name
|
||
server.DeletedAt = gorm.DeletedAt{} // Сбрасываем deleted_at
|
||
if err := tx.Save(&server).Error; err != nil {
|
||
return err
|
||
}
|
||
created = true // При восстановлении пользователь становится владельцем
|
||
} else {
|
||
// --- СЦЕНАРИЙ 3: СУЩЕСТВУЮЩИЙ АКТИВНЫЙ СЕРВЕР ---
|
||
var userCount int64
|
||
tx.Model(&account.ServerUser{}).Where("server_id = ?", server.ID).Count(&userCount)
|
||
if userCount >= int64(server.MaxUsers) {
|
||
var exists int64
|
||
tx.Model(&account.ServerUser{}).Where("server_id = ? AND user_id = ?", server.ID, userID).Count(&exists)
|
||
if exists == 0 {
|
||
return fmt.Errorf("достигнут лимит пользователей на сервере (%d)", server.MaxUsers)
|
||
}
|
||
}
|
||
}
|
||
|
||
targetRole := account.RoleOperator
|
||
if created {
|
||
targetRole = account.RoleOwner
|
||
}
|
||
|
||
if err := tx.Model(&account.ServerUser{}).Where("user_id = ?", userID).Update("is_active", false).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
var existingLink account.ServerUser
|
||
err = tx.Where("server_id = ? AND user_id = ?", server.ID, userID).First(&existingLink).Error
|
||
if err == nil {
|
||
existingLink.Login = login
|
||
existingLink.EncryptedPassword = encryptedPass
|
||
existingLink.IsActive = true
|
||
return tx.Save(&existingLink).Error
|
||
}
|
||
|
||
userLink := account.ServerUser{
|
||
ServerID: server.ID,
|
||
UserID: userID,
|
||
Role: targetRole,
|
||
IsActive: true,
|
||
Login: login,
|
||
EncryptedPassword: encryptedPass,
|
||
}
|
||
return tx.Create(&userLink).Error
|
||
})
|
||
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &server, nil
|
||
}
|
||
|
||
func (r *pgRepository) SaveServerSettings(server *account.RMSServer) error {
|
||
return r.db.Model(server).Updates(map[string]interface{}{
|
||
"name": server.Name,
|
||
"default_store_id": server.DefaultStoreID,
|
||
"root_group_guid": server.RootGroupGUID,
|
||
"auto_process": server.AutoProcess,
|
||
"max_users": server.MaxUsers,
|
||
"sync_interval": server.SyncInterval,
|
||
}).Error
|
||
}
|
||
|
||
// UpdateLastActivity обновляет время последней активности пользователя
|
||
func (r *pgRepository) UpdateLastActivity(serverID uuid.UUID) error {
|
||
result := r.db.Model(&account.RMSServer{}).
|
||
Where("id = ?", serverID).
|
||
Update("last_activity_at", gorm.Expr("NOW()"))
|
||
|
||
if result.Error != nil {
|
||
logger.Log.Error("Failed to update last_activity_at",
|
||
zap.String("server_id", serverID.String()),
|
||
zap.Error(result.Error))
|
||
return result.Error
|
||
}
|
||
|
||
if result.RowsAffected == 0 {
|
||
logger.Log.Warn("UpdateLastActivity: server not found",
|
||
zap.String("server_id", serverID.String()))
|
||
return fmt.Errorf("сервер не найден")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// UpdateLastSync обновляет время последней успешной синхронизации
|
||
func (r *pgRepository) UpdateLastSync(serverID uuid.UUID) error {
|
||
result := r.db.Model(&account.RMSServer{}).
|
||
Where("id = ?", serverID).
|
||
Update("last_sync_at", gorm.Expr("NOW()"))
|
||
|
||
if result.Error != nil {
|
||
logger.Log.Error("Failed to update last_sync_at",
|
||
zap.String("server_id", serverID.String()),
|
||
zap.Error(result.Error))
|
||
return result.Error
|
||
}
|
||
|
||
if result.RowsAffected == 0 {
|
||
logger.Log.Warn("UpdateLastSync: server not found",
|
||
zap.String("server_id", serverID.String()))
|
||
return fmt.Errorf("сервер не найден")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GetServersForSync возвращает серверы, готовые для синхронизации
|
||
func (r *pgRepository) GetServersForSync(idleThreshold time.Duration) ([]account.RMSServer, error) {
|
||
var servers []account.RMSServer
|
||
|
||
// Конвертируем duration в минуты для SQL
|
||
idleMinutes := int(idleThreshold.Minutes())
|
||
|
||
query := `
|
||
SELECT * FROM rms_servers
|
||
WHERE
|
||
deleted_at IS NULL
|
||
AND (
|
||
-- Случай 1: Настало время периодической синхронизации
|
||
(EXTRACT(EPOCH FROM (NOW() - COALESCE(last_sync_at, '1970-01-01'::timestamp))) / 60) >= sync_interval
|
||
OR
|
||
-- Случай 2: Прошло N мин с последней активности, и активность была ПОЗЖЕ синхронизации
|
||
(
|
||
last_activity_at > last_sync_at
|
||
AND (EXTRACT(EPOCH FROM (NOW() - last_activity_at)) / 60) >= ?
|
||
)
|
||
)
|
||
`
|
||
|
||
err := r.db.Raw(query, idleMinutes).Scan(&servers).Error
|
||
if err != nil {
|
||
logger.Log.Error("Failed to get servers for sync",
|
||
zap.Int("idle_threshold_minutes", idleMinutes),
|
||
zap.Error(err))
|
||
return nil, err
|
||
}
|
||
|
||
logger.Log.Info("Servers ready for sync",
|
||
zap.Int("count", len(servers)),
|
||
zap.Int("idle_threshold_minutes", idleMinutes))
|
||
|
||
return servers, nil
|
||
}
|
||
|
||
func (r *pgRepository) SetActiveServer(userID, serverID uuid.UUID) error {
|
||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||
// Проверка доступа
|
||
var count int64
|
||
tx.Model(&account.ServerUser{}).Where("user_id = ? AND server_id = ?", userID, serverID).Count(&count)
|
||
if count == 0 {
|
||
return errors.New("доступ к серверу запрещен")
|
||
}
|
||
|
||
if err := tx.Model(&account.ServerUser{}).Where("user_id = ?", userID).Update("is_active", false).Error; err != nil {
|
||
return err
|
||
}
|
||
return tx.Model(&account.ServerUser{}).Where("user_id = ? AND server_id = ?", userID, serverID).Update("is_active", true).Error
|
||
})
|
||
}
|
||
|
||
func (r *pgRepository) GetActiveServer(userID uuid.UUID) (*account.RMSServer, error) {
|
||
var server account.RMSServer
|
||
err := r.db.Table("rms_servers").
|
||
Select("rms_servers.*").
|
||
Joins("JOIN server_users ON server_users.server_id = rms_servers.id").
|
||
Where("server_users.user_id = ? AND server_users.is_active = ?", userID, true).
|
||
First(&server).Error
|
||
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil
|
||
}
|
||
return nil, err
|
||
}
|
||
return &server, nil
|
||
}
|
||
|
||
// GetActiveConnectionCredentials возвращает креды для подключения.
|
||
// Логика:
|
||
// 1. Берем личные креды из server_users (если активен)
|
||
// 2. Если личных нет (пустой пароль) -> ищем креды Владельца (Owner) этого сервера
|
||
func (r *pgRepository) GetActiveConnectionCredentials(userID uuid.UUID) (url, login, passHash string, err error) {
|
||
// 1. Получаем связь текущего юзера с активным сервером
|
||
type Result struct {
|
||
ServerID uuid.UUID
|
||
BaseURL string
|
||
Login string
|
||
EncryptedPassword string
|
||
}
|
||
var res Result
|
||
|
||
err = r.db.Table("server_users").
|
||
Select("server_users.server_id, rms_servers.base_url, server_users.login, server_users.encrypted_password").
|
||
Joins("JOIN rms_servers ON rms_servers.id = server_users.server_id").
|
||
Where("server_users.user_id = ? AND server_users.is_active = ?", userID, true).
|
||
Scan(&res).Error
|
||
|
||
if err != nil {
|
||
return "", "", "", err
|
||
}
|
||
if res.ServerID == uuid.Nil {
|
||
return "", "", "", errors.New("нет активного сервера")
|
||
}
|
||
|
||
// Если есть личные креды - возвращаем их
|
||
if res.Login != "" && res.EncryptedPassword != "" {
|
||
return res.BaseURL, res.Login, res.EncryptedPassword, nil
|
||
}
|
||
|
||
// 2. Фоллбэк: ищем креды владельца (OWNER)
|
||
var ownerLink account.ServerUser
|
||
err = r.db.Where("server_id = ? AND role = ?", res.ServerID, account.RoleOwner).
|
||
Order("created_at ASC"). // На случай коллизий, берем старейшего
|
||
First(&ownerLink).Error
|
||
|
||
if err != nil {
|
||
return "", "", "", fmt.Errorf("у вас нет учетных данных, а владелец сервера не найден: %w", err)
|
||
}
|
||
|
||
if ownerLink.Login == "" || ownerLink.EncryptedPassword == "" {
|
||
return "", "", "", errors.New("у владельца сервера отсутствуют учетные данные")
|
||
}
|
||
|
||
return res.BaseURL, ownerLink.Login, ownerLink.EncryptedPassword, nil
|
||
}
|
||
|
||
func (r *pgRepository) GetAllAvailableServers(userID uuid.UUID) ([]account.RMSServer, error) {
|
||
var servers []account.RMSServer
|
||
err := r.db.Table("rms_servers").
|
||
Select("rms_servers.*").
|
||
Joins("JOIN server_users ON server_users.server_id = rms_servers.id").
|
||
Where("server_users.user_id = ?", userID).
|
||
Find(&servers).Error
|
||
return servers, err
|
||
}
|
||
|
||
func (r *pgRepository) DeleteServer(serverID uuid.UUID) error {
|
||
// Мягкое удаление сервера и всех связей
|
||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||
if err := tx.Where("server_id = ?", serverID).Delete(&account.ServerUser{}).Error; err != nil {
|
||
return err
|
||
}
|
||
if err := tx.Delete(&account.RMSServer{}, serverID).Error; err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// --- Управление правами ---
|
||
|
||
func (r *pgRepository) GetUserRole(userID, serverID uuid.UUID) (account.Role, error) {
|
||
var link account.ServerUser
|
||
err := r.db.Select("role").Where("user_id = ? AND server_id = ?", userID, serverID).First(&link).Error
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return "", errors.New("access denied")
|
||
}
|
||
return "", err
|
||
}
|
||
return link.Role, nil
|
||
}
|
||
|
||
func (r *pgRepository) SetUserRole(serverID, targetUserID uuid.UUID, newRole account.Role) error {
|
||
return r.db.Model(&account.ServerUser{}).
|
||
Where("server_id = ? AND user_id = ?", serverID, targetUserID).
|
||
Update("role", newRole).Error
|
||
}
|
||
|
||
func (r *pgRepository) GetServerUsers(serverID uuid.UUID) ([]account.ServerUser, error) {
|
||
var users []account.ServerUser
|
||
// Preload User для отображения имен
|
||
err := r.db.Preload("User").Where("server_id = ?", serverID).Find(&users).Error
|
||
return users, err
|
||
}
|
||
|
||
func (r *pgRepository) AddUserToServer(serverID, userID uuid.UUID, role account.Role) error {
|
||
// Проверка лимита перед добавлением
|
||
var server account.RMSServer
|
||
if err := r.db.First(&server, serverID).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||
// 1. Сначала проверяем, существует ли пользователь на этом сервере
|
||
var existingLink account.ServerUser
|
||
err := tx.Where("server_id = ? AND user_id = ?", serverID, userID).First(&existingLink).Error
|
||
|
||
if err == nil {
|
||
// --- ПОЛЬЗОВАТЕЛЬ УЖЕ ЕСТЬ ---
|
||
// Защита от понижения прав:
|
||
// Если текущая роль OWNER или ADMIN, а мы пытаемся поставить OPERATOR (через инвайт),
|
||
// то игнорируем смену роли, просто делаем активным.
|
||
if (existingLink.Role == account.RoleOwner || existingLink.Role == account.RoleAdmin) && role == account.RoleOperator {
|
||
role = existingLink.Role
|
||
}
|
||
|
||
// Обновляем активность и (возможно) роль
|
||
return tx.Model(&existingLink).Updates(map[string]interface{}{
|
||
"role": role,
|
||
"is_active": true,
|
||
}).Error
|
||
}
|
||
|
||
// --- ПОЛЬЗОВАТЕЛЬ НОВЫЙ ---
|
||
// Проверяем лимит только для новых
|
||
var currentCount int64
|
||
tx.Model(&account.ServerUser{}).Where("server_id = ?", serverID).Count(¤tCount)
|
||
if currentCount >= int64(server.MaxUsers) {
|
||
return fmt.Errorf("лимит пользователей (%d) превышен", server.MaxUsers)
|
||
}
|
||
|
||
// Сбрасываем активность на других серверах
|
||
if err := tx.Model(&account.ServerUser{}).Where("user_id = ?", userID).Update("is_active", false).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
// Создаем связь
|
||
link := account.ServerUser{
|
||
ServerID: serverID,
|
||
UserID: userID,
|
||
Role: role,
|
||
IsActive: true,
|
||
}
|
||
return tx.Create(&link).Error
|
||
})
|
||
}
|
||
|
||
func (r *pgRepository) RemoveUserFromServer(serverID, userID uuid.UUID) error {
|
||
return r.db.Where("server_id = ? AND user_id = ?", serverID, userID).Delete(&account.ServerUser{}).Error
|
||
}
|
||
|
||
func (r *pgRepository) IncrementInvoiceCount(serverID uuid.UUID) error {
|
||
return r.db.Model(&account.RMSServer{}).
|
||
Where("id = ?", serverID).
|
||
UpdateColumn("invoice_count", gorm.Expr("invoice_count + ?", 1)).Error
|
||
}
|
||
|
||
// --- Super Admin Functions ---
|
||
|
||
func (r *pgRepository) GetAllServersSystemWide() ([]account.RMSServer, error) {
|
||
var servers []account.RMSServer
|
||
// Загружаем вместе с владельцем для отображения
|
||
err := r.db.Order("name ASC").Find(&servers).Error
|
||
return servers, err
|
||
}
|
||
|
||
func (r *pgRepository) TransferOwnership(serverID, newOwnerID uuid.UUID) error {
|
||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||
// 1. Находим текущего владельца
|
||
var currentOwnerLink account.ServerUser
|
||
if err := tx.Where("server_id = ? AND role = ?", serverID, account.RoleOwner).First(¤tOwnerLink).Error; err != nil {
|
||
return fmt.Errorf("current owner not found: %w", err)
|
||
}
|
||
|
||
// 2. Проверяем, что новый владелец вообще есть на сервере
|
||
var newOwnerLink account.ServerUser
|
||
if err := tx.Where("server_id = ? AND user_id = ?", serverID, newOwnerID).First(&newOwnerLink).Error; err != nil {
|
||
return fmt.Errorf("target user not found on server: %w", err)
|
||
}
|
||
|
||
// 3. Понижаем старого владельца до ADMIN
|
||
if err := tx.Model(¤tOwnerLink).Update("role", account.RoleAdmin).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
// 4. Повышаем нового до OWNER
|
||
if err := tx.Model(&newOwnerLink).Update("role", account.RoleOwner).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
// УДАЛЕНО: обновление server.owner_id, так как этого поля нет в модели
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
func (r *pgRepository) GetConnectionByID(id uuid.UUID) (*account.ServerUser, error) {
|
||
var link account.ServerUser
|
||
// Preload нужны, чтобы показать имена в админке
|
||
err := r.db.Preload("User").Preload("Server").Where("id = ?", id).First(&link).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &link, nil
|
||
}
|
||
|
||
func (r *pgRepository) GetServerByURL(rawURL string) (*account.RMSServer, error) {
|
||
cleanURL := strings.TrimRight(strings.ToLower(strings.TrimSpace(rawURL)), "/")
|
||
var server account.RMSServer
|
||
err := r.db.Where("base_url = ?", cleanURL).First(&server).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &server, nil
|
||
}
|
||
|
||
func (r *pgRepository) GetServerByID(id uuid.UUID) (*account.RMSServer, error) {
|
||
var server account.RMSServer
|
||
err := r.db.First(&server, id).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &server, nil
|
||
}
|
||
|
||
// UpdateBalance начисляет пакет или продлевает подписку
|
||
func (r *pgRepository) UpdateBalance(serverID uuid.UUID, amountChange int, newPaidUntil *time.Time) error {
|
||
return r.db.Model(&account.RMSServer{}).Where("id = ?", serverID).Updates(map[string]interface{}{
|
||
"balance": gorm.Expr("balance + ?", amountChange),
|
||
"paid_until": newPaidUntil,
|
||
}).Error
|
||
}
|
||
|
||
// DecrementBalance списывает 1 единицу при отправке накладной
|
||
func (r *pgRepository) DecrementBalance(serverID uuid.UUID) error {
|
||
return r.db.Model(&account.RMSServer{}).
|
||
Where("id = ? AND balance > 0", serverID).
|
||
UpdateColumn("balance", gorm.Expr("balance - ?", 1)).Error
|
||
}
|
||
|
||
// === Уведомления о черновиках ===
|
||
|
||
// GetServerUsersForDraftNotification возвращает Admin/Owner пользователей,
|
||
// которым нужно отправить уведомление о новом черновике
|
||
func (r *pgRepository) GetServerUsersForDraftNotification(serverID uuid.UUID, excludeUserID uuid.UUID) ([]account.ServerUser, error) {
|
||
var users []account.ServerUser
|
||
err := r.db.Preload("User").
|
||
Where("server_id = ?", serverID).
|
||
Where("role IN ?", []account.Role{account.RoleOwner, account.RoleAdmin}).
|
||
Where("mute_draft_notifications = ?", false).
|
||
Where("user_id != ?", excludeUserID).
|
||
Find(&users).Error
|
||
return users, err
|
||
}
|
||
|
||
// SetMuteDraftNotifications включает/выключает уведомления для пользователя
|
||
func (r *pgRepository) SetMuteDraftNotifications(userID, serverID uuid.UUID, mute bool) error {
|
||
return r.db.Model(&account.ServerUser{}).
|
||
Where("user_id = ? AND server_id = ?", userID, serverID).
|
||
Update("mute_draft_notifications", mute).Error
|
||
}
|
||
|
||
// === Session Management ===
|
||
|
||
// CreateSession создает новую сессию пользователя
|
||
func (r *pgRepository) CreateSession(session *account.Session) error {
|
||
return r.db.Create(session).Error
|
||
}
|
||
|
||
// GetSessionByToken ищет сессию по токену
|
||
func (r *pgRepository) GetSessionByToken(token string) (*account.Session, error) {
|
||
var session account.Session
|
||
if err := r.db.Where("refresh_token = ?", token).First(&session).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, nil
|
||
}
|
||
return nil, err
|
||
}
|
||
return &session, nil
|
||
}
|
||
|
||
// UpdateSessionExpiry обновляет время истечения сессии (sliding expiration)
|
||
func (r *pgRepository) UpdateSessionExpiry(sessionID uuid.UUID, newExpiry time.Time) error {
|
||
return r.db.Model(&account.Session{}).Where("id = ?", sessionID).Update("expires_at", newExpiry).Error
|
||
}
|
||
|
||
// DeleteSession удаляет сессию по токену
|
||
func (r *pgRepository) DeleteSession(token string) error {
|
||
return r.db.Where("refresh_token = ?", token).Delete(&account.Session{}).Error
|
||
}
|