conflicto resuelto
This commit is contained in:
commit
96e80a1948
12
.env
12
.env
@ -1,6 +1,6 @@
|
|||||||
#Configuración de la base de datos
|
#Configuración de la base de datos
|
||||||
DB_HOST=localhost
|
DB_HOST=db
|
||||||
DB_PORT=5434
|
DB_PORT=5432
|
||||||
DB_USER=admin
|
DB_USER=admin
|
||||||
DB_PASSWORD=admin
|
DB_PASSWORD=admin
|
||||||
DB_NAME=gotestdb
|
DB_NAME=gotestdb
|
||||||
@ -8,7 +8,7 @@ DB_MAX_OPEN_CONNS=25
|
|||||||
DB_MAX_IDLE_CONNS=10
|
DB_MAX_IDLE_CONNS=10
|
||||||
DB_CONN_MAX_LIFETIME=30m
|
DB_CONN_MAX_LIFETIME=30m
|
||||||
#Redis
|
#Redis
|
||||||
REDIS_HOST=localhost
|
REDIS_HOST=redis
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_PASSWORD=TuPasswordSegura123
|
REDIS_PASSWORD=TuPasswordSegura123
|
||||||
REDIS_DB=0
|
REDIS_DB=0
|
||||||
@ -16,7 +16,7 @@ REDIS_TTL=10m
|
|||||||
REDIS_SUBSCRIBE=cron:reload
|
REDIS_SUBSCRIBE=cron:reload
|
||||||
#Log
|
#Log
|
||||||
LOG_FILE_PATH=logs/syncronizador.log
|
LOG_FILE_PATH=logs/syncronizador.log
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=info
|
||||||
LOG_MAX_SIZE=10
|
LOG_MAX_SIZE=10
|
||||||
LOG_MAX_BACKUPS=7
|
LOG_MAX_BACKUPS=7
|
||||||
LOG_MAX_AGE=30
|
LOG_MAX_AGE=30
|
||||||
@ -41,13 +41,13 @@ ENCRYPTION_KEY=12345678901234567890123456789012
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
AUTH_ENDPOINT= http://localhost:8085/auth/headers
|
AUTH_ENDPOINT=http://authsvc:8080/auth/headers
|
||||||
AUTH_AUTORIZATION='Basic d29ya2VyLXJlbmRpY2lvbjpwcnVlYmE='
|
AUTH_AUTORIZATION='Basic d29ya2VyLXJlbmRpY2lvbjpwcnVlYmE='
|
||||||
AUTH_METHOD=POST
|
AUTH_METHOD=POST
|
||||||
|
|
||||||
TM_HEADER_ORIGIN=https://azure-function.timemanagerweb.com
|
TM_HEADER_ORIGIN=https://azure-function.timemanagerweb.com
|
||||||
TM_HEADER_TENANT_NAME=pruebas-dos
|
TM_HEADER_TENANT_NAME=pruebas-dos
|
||||||
TM_HEADER_USER_AGENT='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
|
TM_HEADER_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
|
||||||
|
|
||||||
|
|
||||||
# Configuración de correo SMTP
|
# Configuración de correo SMTP
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/tuusuario/go-sync-service/internal/config"
|
"github.com/tuusuario/go-sync-service/internal/config"
|
||||||
@ -16,25 +17,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Configurar zona horaria por defecto del proceso
|
||||||
|
initTimezone()
|
||||||
|
|
||||||
// Cargar configuración
|
// Cargar configuración
|
||||||
conf, err := config.LoadConfig()
|
conf, err := config.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("❌ Error cargando configuración:", err)
|
fmt.Println("Error cargando configuración:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializar logger con configuración
|
// Inicializar logger con configuración
|
||||||
config.InitLogger(conf)
|
config.InitLogger(conf)
|
||||||
config.Log.Info("🚀 Iniciando servicio: go-sync-service")
|
config.Log.Info("Iniciando servicio: go-sync-service")
|
||||||
|
|
||||||
emailService := email.NewSMTPEmailService(conf)
|
emailService := email.NewSMTPEmailService(conf)
|
||||||
|
|
||||||
// Conexión a Redis
|
// Conexión a Redis
|
||||||
redisClient := config.GetRedisClient(conf)
|
redisClient := config.GetRedisClient(conf)
|
||||||
if err := redisClient.Ping(context.Background()).Err(); err != nil {
|
if err := redisClient.Ping(context.Background()).Err(); err != nil {
|
||||||
config.Log.Fatalf("❌ Redis no disponible: %v", err)
|
config.Log.Fatalf("Redis no disponible: %v", err)
|
||||||
}
|
}
|
||||||
config.Log.Info("✅ Redis conectado")
|
config.Log.Info("Redis conectado")
|
||||||
|
|
||||||
// Crear proveedor de configuración desde Redis
|
// Crear proveedor de configuración desde Redis
|
||||||
redisManager := config.NewRedisManager(redisClient)
|
redisManager := config.NewRedisManager(redisClient)
|
||||||
@ -43,14 +47,14 @@ func main() {
|
|||||||
// Conexión a Base de Datos
|
// Conexión a Base de Datos
|
||||||
database := config.GetDatabaseConnection(conf)
|
database := config.GetDatabaseConnection(conf)
|
||||||
if database == nil {
|
if database == nil {
|
||||||
config.Log.Fatal("❌ No se pudo establecer la conexión con la base de datos.")
|
config.Log.Fatal("No se pudo establecer la conexión con la base de datos.")
|
||||||
}
|
}
|
||||||
config.Log.Info("✅ Conexión a base de datos establecida")
|
config.Log.Info("Conexión a base de datos establecida")
|
||||||
|
|
||||||
scheduler.Start(context.Background(), redisClient, redisConfigProvider, database, *emailService)
|
scheduler.Start(context.Background(), redisClient, redisConfigProvider, database, *emailService)
|
||||||
config.Log.Info("✅ Scheduler en ejecución y escuchando recargas")
|
config.Log.Info("Scheduler en ejecución y escuchando recargas")
|
||||||
|
|
||||||
//metrics Grafana
|
// Metrics Grafana/Prometheus
|
||||||
metrics.Register()
|
metrics.Register()
|
||||||
startMetricsServer()
|
startMetricsServer()
|
||||||
|
|
||||||
@ -59,13 +63,24 @@ func main() {
|
|||||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||||
<-stop
|
<-stop
|
||||||
|
|
||||||
config.Log.Info("🛑 Señal de apagado recibida, cerrando servicio...")
|
config.Log.Info("Señal de apagado recibida, cerrando servicio...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initTimezone() {
|
||||||
|
loc, err := time.LoadLocation("America/La_Paz")
|
||||||
|
if err != nil {
|
||||||
|
// Si falla la carga, mantenemos la zona por defecto del entorno
|
||||||
|
// y solo registramos el problema en stdout para no depender del logger aún.
|
||||||
|
fmt.Println("No se pudo cargar la zona horaria America/La_Paz:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Local = loc
|
||||||
}
|
}
|
||||||
|
|
||||||
func startMetricsServer() {
|
func startMetricsServer() {
|
||||||
go func() {
|
go func() {
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
config.Log.Info("📊 Servidor de métricas en :9100/metrics")
|
config.Log.Info("Servidor de métricas en :9100/metrics")
|
||||||
http.ListenAndServe(":9100", nil)
|
_ = http.ListenAndServe(":9100", nil)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
services:
|
services:
|
||||||
syncronizador:
|
syncronizador:
|
||||||
container_name: rendicion_gasto_syncronizador
|
container_name: rendicion_gasto_syncronizador
|
||||||
|
environment:
|
||||||
|
- TZ=America/La_Paz
|
||||||
|
- LANG=es_BO.UTF-8
|
||||||
|
- LC_ALL=es_BO.UTF-8
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
@ -11,7 +15,12 @@ services:
|
|||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
networks:
|
networks:
|
||||||
- network_emba
|
- network_emba
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
compress: "true"
|
||||||
networks:
|
networks:
|
||||||
network_emba:
|
network_emba:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
@ -17,9 +17,34 @@ import (
|
|||||||
|
|
||||||
// Log instancia global del logger
|
// Log instancia global del logger
|
||||||
var Log = logrus.New()
|
var Log = logrus.New()
|
||||||
|
type LocalTimeHook struct {
|
||||||
|
loc *time.Location
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h LocalTimeHook) Levels() []logrus.Level {
|
||||||
|
return logrus.AllLevels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h LocalTimeHook) Fire(e *logrus.Entry) error {
|
||||||
|
if h.loc != nil {
|
||||||
|
e.Time = e.Time.In(h.loc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func InitLogger(cfg *Config) {
|
func InitLogger(cfg *Config) {
|
||||||
|
// =========================
|
||||||
|
// Zona horaria Bolivia
|
||||||
|
// =========================
|
||||||
|
loc, err := time.LoadLocation("America/La_Paz")
|
||||||
|
if err != nil {
|
||||||
|
// Fallback robusto para contenedores sin tzdata
|
||||||
|
loc = time.FixedZone("America/La_Paz", -4*60*60)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Local = loc
|
||||||
|
|
||||||
|
// Hook para forzar timestamps de Logrus a hora Bolivia
|
||||||
|
Log.AddHook(LocalTimeHook{loc: loc})
|
||||||
// Configurar rotación de logs con Lumberjack
|
// Configurar rotación de logs con Lumberjack
|
||||||
rotator := &lumberjack.Logger{
|
rotator := &lumberjack.Logger{
|
||||||
Filename: cfg.LogFilePath, // Archivo de logs
|
Filename: cfg.LogFilePath, // Archivo de logs
|
||||||
@ -56,7 +81,7 @@ func InitLogger(cfg *Config) {
|
|||||||
|
|
||||||
//elastic
|
//elastic
|
||||||
if cfg.ElasticEnabled {
|
if cfg.ElasticEnabled {
|
||||||
Log.Debug("✅ Elasticsearch enabled")
|
Log.Info("✅ Elasticsearch enabled")
|
||||||
es, err := elasticsearch.NewClient(elasticsearch.Config{
|
es, err := elasticsearch.NewClient(elasticsearch.Config{
|
||||||
Addresses: []string{cfg.ElasticURL},
|
Addresses: []string{cfg.ElasticURL},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -64,7 +64,7 @@ func (g *GormDatabase) SyncRows(persistencia dto.Persistencia, rawData *[]map[st
|
|||||||
|
|
||||||
// Procesar lote
|
// Procesar lote
|
||||||
if len(batch) == batchSize || i == len(*rawData)-1 {
|
if len(batch) == batchSize || i == len(*rawData)-1 {
|
||||||
config.Log.Debugf(logPrefix+" Procesando batch de %d registros", len(batch))
|
config.Log.Println(logPrefix+" Procesando batch de %d registros", len(batch))
|
||||||
|
|
||||||
if len(persistencia.UpdateBy) > 0 {
|
if len(persistencia.UpdateBy) > 0 {
|
||||||
// Updates con múltiples campos
|
// Updates con múltiples campos
|
||||||
@ -81,7 +81,7 @@ func (g *GormDatabase) SyncRows(persistencia dto.Persistencia, rawData *[]map[st
|
|||||||
whereValues = append(whereValues, val)
|
whereValues = append(whereValues, val)
|
||||||
}
|
}
|
||||||
if len(whereParts) < len(persistencia.UpdateBy) {
|
if len(whereParts) < len(persistencia.UpdateBy) {
|
||||||
config.Log.Warnf("⚠️ Registro incompleto para update (faltan claves): %+v", row)
|
config.Log.Debugf("⚠️ Registro incompleto para update (faltan claves): %+v", row)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,10 +108,10 @@ func (g *GormDatabase) SyncRows(persistencia dto.Persistencia, rawData *[]map[st
|
|||||||
config.Log.Errorf("%s ❌ Error en update: %v", logPrefix, res.Error)
|
config.Log.Errorf("%s ❌ Error en update: %v", logPrefix, res.Error)
|
||||||
return res.Error
|
return res.Error
|
||||||
}
|
}
|
||||||
if res.RowsAffected == 0 {
|
/* if res.RowsAffected == 0 {
|
||||||
config.Log.Warnf("%s ⚠️ Ninguna fila afectada con campos: %v valores: %v",
|
config.Log.Debugf("%s ⚠️ Ninguna fila afectada con campos: %v valores: %v",
|
||||||
logPrefix, strings.Join(whereParts, " AND "), printWhereValues(whereValues))
|
logPrefix, strings.Join(whereParts, " AND "), printWhereValues(whereValues))
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Inserts con conflicto por PK (UPSERT)
|
// Inserts con conflicto por PK (UPSERT)
|
||||||
@ -173,17 +173,17 @@ func (g *GormDatabase) GetCredencialesFromTemplate(whereTemplate string, variabl
|
|||||||
query := whereTemplate
|
query := whereTemplate
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
|
||||||
config.Log.Debugf("🔎 Variables recibidas:")
|
config.Log.Info("🔎 Variables recibidas:")
|
||||||
for k, v := range variables {
|
for k, v := range variables {
|
||||||
placeholder := "@" + k
|
placeholder := "@" + k
|
||||||
query = strings.ReplaceAll(query, placeholder, "?")
|
query = strings.ReplaceAll(query, placeholder, "?")
|
||||||
args = append(args, v)
|
args = append(args, v)
|
||||||
config.Log.Debugf(" %s = %v", k, v)
|
config.Log.Infof(" %s = %v", k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Log.Debugf("📝 Consulta final construida:")
|
config.Log.Info("📝 Consulta final construida:")
|
||||||
config.Log.Debugf(" Query: %s", query)
|
config.Log.Infof(" Query: %s", query)
|
||||||
config.Log.Debugf(" Args: %v", args)
|
config.Log.Infof(" Args: %v", args)
|
||||||
|
|
||||||
err := g.db.Where(query, args...).First(&cred).Error
|
err := g.db.Where(query, args...).First(&cred).Error
|
||||||
return &cred, err
|
return &cred, err
|
||||||
|
|||||||
@ -155,13 +155,13 @@ func prepareAuthBody(auth dto.ServiceConfig, cred *model.CredencialesSAP, logPre
|
|||||||
}
|
}
|
||||||
|
|
||||||
if auth.GQL {
|
if auth.GQL {
|
||||||
config.Log.Debugf("%v 🧠 Preparando auth para GraphQL", logPrefix)
|
config.Log.Infof("%v 🧠 Preparando auth para GraphQL", logPrefix)
|
||||||
auth.Rest.Body = map[string]string{
|
auth.Rest.Body = map[string]string{
|
||||||
"username": cred.UserName,
|
"username": cred.UserName,
|
||||||
"password": cred.Password,
|
"password": cred.Password,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config.Log.Debugf("%v🧠 Preparando auth para REST", logPrefix)
|
config.Log.Infof("%v🧠 Preparando auth para REST", logPrefix)
|
||||||
auth.Rest.Body = map[string]string{
|
auth.Rest.Body = map[string]string{
|
||||||
"CompanyDB": cred.CompanyDB,
|
"CompanyDB": cred.CompanyDB,
|
||||||
"UserName": cred.UserName,
|
"UserName": cred.UserName,
|
||||||
|
|||||||
@ -3,11 +3,12 @@ package fetcher
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/tuusuario/go-sync-service/internal/email"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tuusuario/go-sync-service/internal/email"
|
||||||
|
|
||||||
"github.com/tuusuario/go-sync-service/internal/config"
|
"github.com/tuusuario/go-sync-service/internal/config"
|
||||||
"github.com/tuusuario/go-sync-service/internal/db"
|
"github.com/tuusuario/go-sync-service/internal/db"
|
||||||
"github.com/tuusuario/go-sync-service/internal/domain/dto"
|
"github.com/tuusuario/go-sync-service/internal/domain/dto"
|
||||||
@ -122,6 +123,9 @@ func FetchAllPaginatedManual[T any](host string, service dto.ServiceConfig, logP
|
|||||||
return nil, fmt.Errorf("%s ❌ error en la petición: %w", logPrefix, err)
|
return nil, fmt.Errorf("%s ❌ error en la petición: %w", logPrefix, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEBUG: imprimir lo que devuelve el servicio
|
||||||
|
config.Log.Debugf("%s Body recibido (REST paginado): %s", logPrefix, string(resp.Body()))
|
||||||
|
|
||||||
var result struct {
|
var result struct {
|
||||||
Value []T `json:"value"`
|
Value []T `json:"value"`
|
||||||
}
|
}
|
||||||
@ -206,6 +210,10 @@ func FetchAllPaginatedManual[T any](host string, service dto.ServiceConfig, logP
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s ❌ error en la petición: %w", logPrefix, err)
|
return nil, fmt.Errorf("%s ❌ error en la petición: %w", logPrefix, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEBUG: imprimir lo que devuelve el servicio sin paginación
|
||||||
|
config.Log.Debugf("%s Body recibido (REST sin paginación): %s", logPrefix, string(resp.Body()))
|
||||||
|
|
||||||
var result struct {
|
var result struct {
|
||||||
Value []T `json:"value"`
|
Value []T `json:"value"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ func CargarDesdeRedis[T any](cfg ports.RedisConfigProvider, clave string) (*T, e
|
|||||||
return nil, fmt.Errorf("error al obtener clave [%s] de redis: %w", clave, err)
|
return nil, fmt.Errorf("error al obtener clave [%s] de redis: %w", clave, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
config.Log.Debugf("🔑 Clave [%s] obtenida de Redis: %s", clave, data)
|
config.Log.Infof("🔑 Clave [%s] obtenida de Redis: %s", clave, data)
|
||||||
var result T
|
var result T
|
||||||
if err := json.Unmarshal([]byte(data), &result); err != nil {
|
if err := json.Unmarshal([]byte(data), &result); err != nil {
|
||||||
config.Log.Errorf("❌ error al parsear JSON [%s]: %s", clave, err)
|
config.Log.Errorf("❌ error al parsear JSON [%s]: %s", clave, err)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user