2025-10-29 06:14:57 -04:00

223 lines
6.8 KiB
Go

package http
import (
"encoding/json"
"fmt" // NEW
"github.com/tuusuario/go-sync-service/internal/http/external_api"
"log"
"sync"
"time"
"github.com/tuusuario/go-sync-service/internal/config"
"github.com/tuusuario/go-sync-service/internal/domain/dto"
"github.com/tuusuario/go-sync-service/internal/domain/model"
"github.com/tuusuario/go-sync-service/internal/domain/ports"
)
var (
redisKey = "session:SAP"
mutex sync.Mutex
)
type SAPSessionProvider struct{}
func GetSession(cfg ports.RedisConfigProvider, job dto.CronJob, auth dto.ServiceConfig, dbcore ports.Database, logPrefix string) (*dto.SessionData, error) {
//CLIENT HTTP
authenticacion, _, err := Authenticacion(job.UnidadNegocio.CompanyDB)
if err != nil {
log.Printf("Error en autenticacion: %v", err)
return nil, err
}
return &dto.SessionData{
Headers: authenticacion.Data.Headers,
ExpiresAt: authenticacion.Data.ExpiresAt,
EndPoint: authenticacion.Data.URL,
}, nil
/*
mutex.Lock()
defer mutex.Unlock()
redisKey = "session:" + job.UnidadNegocio.CompanyName + ":" + job.UnidadNegocio.CompanyDB
//REDIS
if sess, err := loadSessionFromRedis(cfg, logPrefix); err == nil && time.Now().Before(sess.ExpiresAt) {
config.Log.Printf("%v 🔑 Sesión obtenida de Redis %v", logPrefix, redisKey)
return sess, nil
}
parametros := map[string]interface{}{
"company_name": job.UnidadNegocio.CompanyName,
"company_db": job.UnidadNegocio.CompanyDB,
}
credencial, err := dbcore.GetCredencialesFromTemplate(config.GlobalConfig.WhereUnitsBusiness, parametros)
if err != nil {
config.Log.Printf("%v ❌ Error al obtener credenciales: %v", logPrefix, err)
return nil, err
}
// ==============================
// DESCIFRAR PASSWORD (AES-GCM)
// ==============================
key, err := security.LoadEncryptionKey()
if err != nil {
return nil, fmt.Errorf("%s %v", logPrefix, err)
}
// Suponemos que credencial.Password viene como base64(nonce||cipher) generado por EncryptAESGCM
plainPass, err := security.DecryptAESGCM(credencial.Password, key)
if err != nil {
config.Log.Printf("%v ❌ Error al descifrar password: %v", logPrefix, err)
return nil, err
}
credencial.Password = plainPass
// No loguees secretos. Si necesitas log, hazlo sin password:
config.Log.Debugf("%v Obteniendo credenciales para CompanyDB=%s UserName=%s (password oculto)",
logPrefix, credencial.CompanyDB, credencial.UserName)
config.Log.Printf("%v 🔑 Realizando login...", logPrefix)
mySession := &dto.SessionData{EndPoint: credencial.EndPoint}
if auth.Rest == nil {
auth.Rest = &dto.RestOptions{}
}
// 3. Preparar el body del login (usa cred.Password en claro ya descifrado)
auth = prepareAuthBody(auth, credencial, logPrefix)
config.Log.Debugf(" %v Url: %v + %v", logPrefix, credencial.EndPoint, auth.Path)
resp, err := SendRequest(credencial.EndPoint, auth)
if err != nil || resp.IsError() {
config.Log.Printf("%v ❌ Error al autenticar: %v", logPrefix, err)
return nil, err
}
if auth.GQL {
var dataGraphql struct {
Token string `json:"token"`
RefreshToken string `json:"refresh_token"`
}
if err := json.Unmarshal(resp.Body(), &dataGraphql); err != nil {
config.Log.Printf("%v ❌ Error al parsear sesión graphql: %v", logPrefix, err)
return nil, err
}
mySession.SessionId = dataGraphql.Token
mySession.ExpiresAt = time.Now().Add(10 * time.Minute)
} else {
var dataRest struct {
SessionId string `json:"SessionId"`
SessionTimeout int `json:"SessionTimeout"`
}
if err := json.Unmarshal(resp.Body(), &dataRest); err != nil {
config.Log.Printf("%v ❌ Error al parsear sesión rest: %v", logPrefix, err)
return nil, err
}
mySession.SessionId = dataRest.SessionId
mySession.ExpiresAt = time.Now().Add(time.Duration(dataRest.SessionTimeout) * time.Minute)
}
config.Log.Printf("%v ✅ Sesión obtenida", logPrefix)
saveSessionToRedis(mySession, cfg, logPrefix)
return mySession, nil
*/
}
func loadSessionFromRedis(cfg ports.RedisConfigProvider, logPrefix string) (*dto.SessionData, error) {
raw, err := cfg.GetString(redisKey)
if err != nil {
config.Log.Printf("%v ⚠️ No se pudo obtener sesión de Redis: %v", logPrefix, err)
return nil, err
}
var sess dto.SessionData
if err := json.Unmarshal([]byte(raw), &sess); err != nil {
config.Log.Printf("%v ⚠️ No se pudo parsear sesión de Redis: %v", logPrefix, err.Error())
return nil, err
}
return &sess, nil
}
func saveSessionToRedis(sess *dto.SessionData, cfg ports.RedisConfigProvider, logPrefix string) {
data, _ := json.Marshal(sess)
ttl := time.Until(sess.ExpiresAt)
err := cfg.UpdateParam(redisKey, string(data), ttl)
if err != nil {
config.Log.Printf("%v ⚠️ No se pudo guardar sesión en Redis: %v", logPrefix, err)
}
}
func prepareAuthBody(auth dto.ServiceConfig, cred *model.CredencialesSAP, logPrefix string) dto.ServiceConfig {
if auth.Rest == nil {
auth.Rest = &dto.RestOptions{}
}
if auth.GQL {
config.Log.Debugf("%v 🧠 Preparando auth para GraphQL", logPrefix)
auth.Rest.Body = map[string]string{
"username": cred.UserName,
"password": cred.Password,
}
} else {
config.Log.Debugf("%v🧠 Preparando auth para REST", logPrefix)
auth.Rest.Body = map[string]string{
"CompanyDB": cred.CompanyDB,
"UserName": cred.UserName,
"Password": cred.Password,
}
}
return auth
}
func Authenticacion(company string) (*dto.Response, *dto.RequestTrace, error) {
url := config.GlobalConfig.AUTH_ENDPOINT
headers := map[string]string{
"Content-Type": "application/json",
"Authorization": config.GlobalConfig.AUTH_AUTORIZATION,
}
// Crea el cuerpo de la solicitud con el id recibido
body := []byte(fmt.Sprintf(`{"name": "%s"}`, company))
client := external_api.NewGenericClient(3, 3*time.Second)
// Hacer la solicitud usando el cliente genérico
response, trace, err := client.DoRequest(config.GlobalConfig.AUTH_METHOD, url, headers, body)
// Manejo de errores
if err != nil {
log.Println("Error en la solicitud: ", err)
return nil, nil, err
}
// Deserializar la respuesta en el modelo de dto.Response
var responseModel dto.Response
err = mapToModel(response, &responseModel)
if err != nil {
log.Println("Error al deserializar la respuesta: ", err)
return nil, nil, err
}
log.Printf("TRACE: %+v", trace)
log.Printf("RESPUESTA MAP: %+v", response)
return &responseModel, trace, nil
}
// mapToModel convierte un mapa genérico en el modelo ResponseModel
func mapToModel(data map[string]interface{}, model *dto.Response) error {
// Utiliza json.Marshal y json.Unmarshal para convertir el mapa en el modelo
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
err = json.Unmarshal(jsonData, model)
if err != nil {
return err
}
return nil
}