mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-05 03:12:34 -06:00
116 lines
4.1 KiB
Go
116 lines
4.1 KiB
Go
package rms
|
||
|
||
import (
|
||
"fmt"
|
||
"sync"
|
||
|
||
"github.com/google/uuid"
|
||
"go.uber.org/zap"
|
||
|
||
"rmser/internal/domain/account"
|
||
"rmser/pkg/crypto"
|
||
"rmser/pkg/logger"
|
||
)
|
||
|
||
// Factory управляет созданием и переиспользованием клиентов RMS
|
||
type Factory struct {
|
||
accountRepo account.Repository
|
||
cryptoManager *crypto.CryptoManager
|
||
|
||
// Кэш активных клиентов: ServerID -> *Client
|
||
mu sync.RWMutex
|
||
clients map[uuid.UUID]*Client
|
||
}
|
||
|
||
func NewFactory(repo account.Repository, cm *crypto.CryptoManager) *Factory {
|
||
return &Factory{
|
||
accountRepo: repo,
|
||
cryptoManager: cm,
|
||
clients: make(map[uuid.UUID]*Client),
|
||
}
|
||
}
|
||
|
||
// GetClientByServerID возвращает готовый клиент для конкретного сервера
|
||
func (f *Factory) GetClientByServerID(serverID uuid.UUID) (ClientI, error) {
|
||
// 1. Пытаемся найти в кэше (быстрый путь)
|
||
f.mu.RLock()
|
||
client, exists := f.clients[serverID]
|
||
f.mu.RUnlock()
|
||
|
||
if exists {
|
||
return client, nil
|
||
}
|
||
|
||
// 2. Если нет в кэше - ищем в БД (медленный путь)
|
||
// Здесь нам нужен метод GetServerByID, но в репо есть только GetAll/GetActive.
|
||
// Для MVP загрузим все сервера юзера и найдем нужный, либо (лучше) добавим метод в репо позже.
|
||
// ПОКА: предполагаем, что factory используется в контексте User, поэтому лучше метод GetClientForUser
|
||
return nil, fmt.Errorf("client not found in cache (use GetClientForUser or implement GetServerByID)")
|
||
}
|
||
|
||
// GetClientForUser находит активный сервер пользователя и возвращает клиент
|
||
func (f *Factory) GetClientForUser(userID uuid.UUID) (ClientI, error) {
|
||
// 1. Получаем настройки активного сервера из БД
|
||
server, err := f.accountRepo.GetActiveServer(userID)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("db error: %w", err)
|
||
}
|
||
if server == nil {
|
||
return nil, fmt.Errorf("у пользователя нет активного сервера RMS")
|
||
}
|
||
|
||
// 2. Проверяем кэш по ID сервера
|
||
f.mu.RLock()
|
||
cachedClient, exists := f.clients[server.ID]
|
||
f.mu.RUnlock()
|
||
|
||
if exists {
|
||
return cachedClient, nil
|
||
}
|
||
|
||
// 3. Создаем новый клиент под блокировкой (защита от гонки)
|
||
f.mu.Lock()
|
||
defer f.mu.Unlock()
|
||
|
||
// Double check
|
||
if cachedClient, exists := f.clients[server.ID]; exists {
|
||
return cachedClient, nil
|
||
}
|
||
|
||
// Расшифровка пароля
|
||
plainPass, err := f.cryptoManager.Decrypt(server.EncryptedPassword)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("ошибка расшифровки пароля RMS: %w", err)
|
||
}
|
||
|
||
// Создание клиента
|
||
newClient := NewClient(server.BaseURL, server.Login, plainPass)
|
||
|
||
// Можно сразу проверить авторизацию (опционально, но полезно для fail-fast)
|
||
// if err := newClient.Auth(); err != nil { ... }
|
||
// Но лучше лениво, чтобы не тормозить старт.
|
||
|
||
f.clients[server.ID] = newClient
|
||
|
||
// Запускаем очистку старых клиентов из мапы? Пока нет, iiko токены живут не вечно,
|
||
// но структура Client легкая. Можно добавить TTL позже.
|
||
|
||
logger.Log.Info("RMS Factory: Client created and cached",
|
||
zap.String("server_name", server.Name),
|
||
zap.String("user_id", userID.String()))
|
||
|
||
return newClient, nil
|
||
}
|
||
|
||
// CreateClientFromRawCredentials создает клиент без сохранения в кэш (для тестов подключения)
|
||
func (f *Factory) CreateClientFromRawCredentials(url, login, password string) *Client {
|
||
return NewClient(url, login, password)
|
||
}
|
||
|
||
// ClearCache сбрасывает кэш для сервера (например, при смене пароля)
|
||
func (f *Factory) ClearCache(serverID uuid.UUID) {
|
||
f.mu.Lock()
|
||
defer f.mu.Unlock()
|
||
delete(f.clients, serverID)
|
||
}
|