Commit Graph
119 Commits
Author SHA1 Message Date
Lizandro GDandClaude Sonnet 5 a0b2e9ae0a feat: registrar proyectos con fases, adjuntar documentos y asignar tareas por Telegram
- crear_fase_proyecto: agrega fases/etapas a un proyecto (se puede llamar
  varias veces seguidas para cargar todas las fases de un proyecto nuevo).
- adjuntar_documento_proyecto: guarda el archivo que el usuario acaba de
  enviar por Telegram como documento del proyecto (mismo mecanismo de
  adjuntos ya usado para facturas — se generalizó TelegramAttachment para
  servir a ambos casos).
- listar_usuarios + asignado_id en crear_tarea + asignar_tarea: permite
  asignar tareas a alguien del equipo por Telegram, disparando la misma
  notificación (Telegram/email/sistema) que ya dispara el dashboard.
- El prompt del sistema ahora indica cómo decidir entre adjuntar_factura y
  adjuntar_documento_proyecto según el contexto del archivo recibido.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 02:11:02 +00:00
Lizandro GDandClaude Sonnet 5 cd0ca6d232 feat: leer el contenido real del documento adjunto con Claude
Antes solo se avisaba en texto "llegó un documento" — la IA nunca veía el
archivo, por eso siempre tenía que preguntar cliente y monto. Ahora, cuando
el proveedor activo es Anthropic, se le adjunta la imagen/PDF real (base64)
al mensaje para que Claude lo lea directamente y extraiga cliente, monto y
número de factura, llamando a adjuntar_factura sin pedir confirmación salvo
que de verdad no pueda determinar cliente o monto.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 02:01:12 +00:00
Lizandro GDandClaude Sonnet 5 b292d98cf6 feat: enviar factura por Telegram como documento adjunto
El bot solo procesaba mensajes de texto: un documento o foto enviado al chat
se ignoraba por completo (Telegram lo manda en message.document/photo con
caption, no en el campo text). Ahora el webhook detecta el adjunto, lo
descarga desde la API de Telegram y lo deja pendiente para el chat; el agente
usa la nueva tool adjuntar_factura para asociarlo a un cliente (preguntando
cliente/monto si el caption no los trae) y crear la Factura con el archivo ya
vinculado, igual que si se subiera desde el dashboard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 01:37:13 +00:00
Lizandro GDandClaude Sonnet 5 d2bf699b60 fix: seguridad del webhook de soporte, pagos en contabilidad y Telegram para tareas
- soporte: el webhook de correo entrante era público sin ninguna validación;
  ahora exige una API key (query ?key= o header) comparada en tiempo constante.
  Además evita tickets duplicados por reintentos del proveedor (dedup por
  Message-Id) y enhebra respuestas del mismo remitente en vez de abrir un
  ticket nuevo por cada correo.
- contabilidad: marcar una cuenta por cobrar/pagar como pagada ahora crea y
  vincula la Transaccion correspondiente (antes el dashboard de ingresos/
  egresos nunca reflejaba esos pagos). Se corrige además que actualizar una
  cuenta por cobrar borraba su transaccion_id en cada PUT.
- tareas: se activa por defecto el canal Telegram para tarea_asignada (estaba
  apagado desde el seed original) y se agrega un flujo real de vinculación de
  Telegram para el staff interno (código temporal + verificación), igual al
  que ya existía para los usuarios del portal — sin esto el chat_id de cada
  usuario había que pegarlo a mano y la notificación nunca llegaba.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 01:25:17 +00:00
Lizandro GD a36e2e1048 Merge remote-tracking branch 'origin/main'
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

# Conflicts:
#	main.go
2026-08-03 00:47:00 +00:00
Lizandro GDandClaude Sonnet 5 516220a8a1 feat: automatización de cotizaciones, contratos, actas y cuentas de cobro con IA
Implementa las 4 fases de la especificación de automatización: módulo de
plantillas/tarifas editable por el equipo, generación de PDF (HTML+JS vía
Chrome headless) para cotizaciones/contratos/arquitecturas/cuentas de cobro,
chat propio en el dashboard reutilizando el mismo motor y tools del bot de
Telegram, y nuevas tools del agente para crear estos documentos end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 00:45:58 +00:00
Lizandro Guarnizo 03b18418eb feat: SMTP salida propio en webhook de soporte 2026-07-30 12:19:56 -05:00
Lizandro Guarnizo 333fe03de0 fix: historial por usuario (user_id en query_history) 2026-07-30 11:02:19 -05:00
Lizandro Guarnizo 28affb5a55 feat: área de soporte con webhook de correo y asignación de tickets 2026-07-28 23:39:03 -05:00
Lizandro GDandClaude Opus 4.6 c7b71d914f perf(agent): reducir tokens ~75% guardando solo user+assistant en historial
Los tool_calls y tool_results intermedios (JSON crudo de listas) se
mantenían en BD y se reenviaban a la IA en cada turno, consumiendo
la mayoría de tokens sin aportar contexto nuevo.

Ahora solo se persisten mensajes user y la respuesta final assistant.
Los datos de herramientas viven únicamente en memoria durante el round
actual. También se reduce el límite de historial de 20 a 10 mensajes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 19:45:34 +00:00
Lizandro GDandClaude Opus 4.6 622a89e966 fix: restaurar tool_calls en historial al cargar conversación del agente
Cuando el agente hacía múltiples rondas de herramientas, los mensajes
del asistente con tool_calls se guardaban como JSON en Content. Al
recargar el historial, ToolCalls quedaba nil y Anthropic recibía
tool_result sin el tool_use correspondiente → error 400.

Ahora se detecta si Content es un array JSON y se deserializa de vuelta
a []agentToolCall antes de enviarlo a la API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 19:36:48 +00:00
Lizandro GDandClaude Sonnet 4.6 4f309ad9d5 feat: soporte nativo Anthropic en el agente (tool use format)
Agrega callAnthropicAI() que convierte mensajes y herramientas al formato
nativo de Anthropic Messages API (tool_use / tool_result), con conversión
de vuelta al formato interno OpenAI para mantener el historial unificado.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 19:16:11 +00:00
Lizandro GDandClaude Sonnet 4.6 5c81939bbf feat: agente - agregar listar_facturas + campos agente en AI Config create/update
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 18:31:15 +00:00
Lizandro GDandClaude Sonnet 4.6 33cfe4fdb0 feat: agente Telegram con IA + Coolify multi-instancia
- Coolify: soporte multi-instancia (CRUD de configs, ?config_id= en todos
  los endpoints, endpoints expandidos para services/databases/teams/envs)
- AiConfig: campos es_agente_bot + telegram_config_id para marcar qué
  config de IA actúa como cerebro del bot administrador
- TelegramAgentHistory + TelegramAgentAuth: historial de conversación por
  chat_id y whitelist de chats autorizados
- Agent Engine: function calling OpenAI-compatible con 25+ herramientas
  (clientes, contratos, contabilidad, proyectos, tickets, tareas,
  Coolify multi-instancia, servidores, monitores URL)
- Webhook POST /webhooks/telegram-agent/:bot_token (público, sin sesión)
- API /api/v2/agent/auth y /api/v2/agent/history para administrar el agente
- AutoMigrate: AiConfig, TelegramAgentHistory, TelegramAgentAuth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 18:29:06 +00:00
Lizandro Guarnizo 62397f2f8c feat: telegram chat ID en usuarios (UI + notificacion al asignado) 2026-07-07 00:26:31 -05:00
Lizandro Guarnizo 9dfa3afab0 fix: ia controller usa endpoint default si no hay config en BD + valida status HTTP de Ollama 2026-07-06 23:37:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 37648724f8 feat(notif): integrar notificaciones de tareas con NotifEventoConfig
- SeedNotifDefaults: poblar 10 eventos por defecto (tickets, servidores, tareas)
- notif_dispatch: NotificarTareaAsignada/Estado/Comentario ahora respetan
  canal_email, canal_telegram y canal_sistema de la config de notificaciones
- mail_service: añadir SendTareaAsignadaEmail y SendTareaNotifAdmin
- notif_config.html: agregar eventos tarea_asignada, tarea_estado, tarea_comentario
  al panel de configuración para que el admin pueda activar/desactivar canales

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 22:06:40 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 107178aa97 feat(users): reemplazar confirmación por envío de credenciales con link de reset
- Eliminar envío automático de confirmación al crear usuario
- Nuevo botón en cada fila: enviar credenciales (usuario + link establecer contraseña)
- POST /app/users/:id/credenciales → SendUserCredencialesEmail
- Email incluye username, link al login y link para establecer contraseña

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 21:57:36 -05:00
Lizandro Guarnizo 1ffc695154 fix(mail): fallback HTML cuando PrepareHtml falla, mejor logging, simplificar goroutine 2026-07-06 21:44:18 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 bc79553b7e feat(tareas): módulo de tablero kanban con drag & drop, comentarios y Telegram
- Modelos Tarea y TareaComentario con GORM, migración automática
- Estados: por_hacer → en_progreso → revisión → hecho; prioridades: baja/media/alta/urgente
- Drag & drop entre columnas via SortableJS (PUT /tarea/:id/estado)
- CRUD completo: crear, editar, eliminar; asignación a usuario + fecha límite
- Panel lateral de detalle: historial de comentarios, adjuntar archivos (uploads/tareas/:id)
- Telegram: notifica asignación, cambio de estado y comentarios nuevos via sendTelegramAdmin
- Seed automático bajo módulo "Administración", solo rol Administrador

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 20:18:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5f92934fba feat(url-monitor): agregar monitoreo de disponibilidad de URLs desde servidor
- Modelos UrlMonitor y UrlMonitorLog con purga automática a 7 días
- Cron cada minuto ejecuta chequeos HEAD→GET con fallback, alerta Telegram en cambio de estado
- CRUD completo + chequeo manual + historial de logs
- Migración automática de tablas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 09:33:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 66088ce691 feat(agent): auto-renice top CPU process after 2 consecutive high-CPU reports
- Agent tracks consecutive reports above cpu_threshold (default 90%)
- After renice_consecutive reports (default 2 = 60s sustained), runs
  renice -n 19 on the top CPU process
- Reports the action back in the heartbeat payload
- Backend sends Telegram alert when renice is applied
- All params configurable in agent.yml and install.sh flags

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:17:07 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ae74d0ef90 feat(agent): persist 7-day metrics history per server heartbeat
- New table servidor_metricas_history (cpu%, ram%, swap%, disco%, tcp, load1)
- Each heartbeat inserts a compact row (~60 bytes)
- GET /app/servidor/:id/metricas-history?horas=24 returns the data
- Daily cron at 3AM purges rows older than 7 days
- ~1.2 MB per server per 7 days at 30s interval

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:03:16 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4b98161070 feat(cron): include top 3 processes in CPU/RAM alert Telegram messages
When CPU or RAM exceeds the threshold, the alert now lists the top 3
processes sorted by the relevant metric (CPU% or RAM MB).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:53:43 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 73fe73b04f fix(cron): parse discos array correctly for disk alert threshold checks
The metrics JSON uses 'discos' (array) not 'disco' (single object),
so disk alerts were never firing. Also reports per-mountpoint label.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:47:04 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cb9d984ee8 fix: capturar subid de LabsMobile como MsgID en logs de SMS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 18:36:14 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2136c46f42 fix: tratar code != 0 de LabsMobile como error
La API devuelve HTTP 200 con code:"401" para credenciales inválidas.
Ahora el service retorna error cuando code != "0".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 17:16:24 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f3d9794096 fix: corregir URL del API de LabsMobile
URL incorrecta: https://websms.labsmobile.com/SY0204/api
URL correcta:   https://api.labsmobile.com/json/send

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 17:05:20 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e5c6dba787 fix: exponer cuerpo real de respuesta en error de websms
El error "unmarshal: invalid character '<'" ocultaba qué devolvía
LabsMobile. Ahora el error incluye Content-Type y los primeros 200
bytes del cuerpo para diagnóstico.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 16:59:49 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e28e2823dc fix: compilar agent/dist en builder stage y agregar módulos contabilidad/websms
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 10:13:17 -05:00
Lizandro Guarnizo fa0991eed7 Update oss_service.go 2026-06-11 17:47:46 -05:00
Lizandro Guarnizo cf078a6e5f S3: add public_url, fix Content-Disposition, add oss-file proxy 2026-06-10 09:34:17 -05:00
Lizandro Guarnizo 72744a1325 Update oss_service.go 2026-06-03 23:49:00 -05:00
Lizandro Guarnizo 357d9e3404 Update oss_service.go 2026-06-03 23:01:15 -05:00
Lizandro Guarnizo b2617c1906 Update oss_service.go 2026-06-03 21:41:42 -05:00
Lizandro Guarnizo c05a2a8137 up 2026-06-03 21:32:50 -05:00
Lizandro Guarnizo ba76754331 up 2026-06-02 10:35:37 -05:00
Lizandro Guarnizo 52ce8638a8 replace cta with social_media question; add SocialMedia field to landing_session 2026-05-28 20:49:35 -05:00
Lizandro Guarnizo d8f38a6669 up 2026-05-26 21:28:35 -05:00
Lizandro Guarnizo 7b1c260af5 Update notif_dispatch.go 2026-05-25 12:49:19 -05:00
Lizandro Guarnizo 1a96caeb83 ip 2026-05-21 23:49:37 -05:00
Lizandro Guarnizo d7bcddc0b3 up 2026-05-21 23:11:01 -05:00
Lizandro Guarnizo d121f399ed up 2026-05-21 22:52:24 -05:00
Lizandro Guarnizo 0cf7c6d279 up 2026-05-21 22:37:31 -05:00
Lizandro GuarnizoandCopilot 2779c0a15d fix: ping real de BD + campo Host en ConxDb + reactividad Alpine
Problema raíz:
1. El ping TCP usaba Servidor.IpServidor → fallaba si la BD solo escucha
   en localhost del servidor remoto (comportamiento normal en producción)
2. La reactividad Alpine v3.13 no detectaba keys nuevas en objeto vacío {}
   dentro de x-for loops, spinner nunca aparecía

Cambios:
- pkg/models/conx_db.go: nuevo campo Host (vacío = usa Servidor.IpServidor)
  + método HostEfectivo() como fuente única de verdad para host
- pkg/services/query_runner_service.go: todos los puntos (openDynamicDB,
  openDynamicDBWithName, redisConnect, mongoURI) usan c.HostEfectivo()
- rest/controllers/servidor_controller.go: PingConexion reescrito —
  primero prueba conexión real a la BD (no solo TCP), si falla prueba TCP,
  devuelve host+puerto testeado y mensajes de error descriptivos
- servidor_dashboard.html: hacerPing usa spread-replace para forzar
  reactividad + $nextTick, resultado muestra host:puerto testeado y
  mensaje de error completo en múltiples líneas

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-21 11:56:12 -05:00
Lizandro GuarnizoandCopilot 63d124215b feat(query-runner): soporte completo Redis
Backend:
- Agrega github.com/redis/go-redis/v9 como dependencia
- isRedisDriver() detecta 'redis' y 'valkey' en nombre del tipo de BD
- redisConnect(): abre cliente Redis con auth, host, port, db index
- redisTestConnection(): PING para probar conexión
- redisListDatabases(): lee CONFIG GET databases → devuelve db0..dbN
- redisListKeys(): SCAN iterativo, hasta 200 keys del db seleccionado
- redisExecuteCommand(): parsea comandos línea a línea, multi-comando
  devuelve tabla command/result/error; comando único devuelve QueryResult
  tipado (string, int64, []any para KEYS/SMEMBERS/LRANGE, HGETALL como
  field/value table)
- redisParseArgs(): tokenizador respetando comillas simples y dobles
- Todos los comandos se guardan en query_history

Frontend:
- isRedis: false estado y reset en onConxChange()
- Detecta redis/valkey en tipo_db.nombre
- Editor cambia label 'Redis CLI', placeholder con ejemplos Redis
- Sidebar: 'Keys' en lugar de 'Tablas' para Redis
- insertTable(): TYPE + TTL + GET para inspeccionar una key de Redis
- Panel de chips rojos: KEYS *, DBSIZE, INFO, CONFIG GET, CLIENT LIST, SLOWLOG
- Generador Redis: select de 24 comandos + formulario contextual
  (key, value, ttl, field según el comando elegido)
- generateRedisCmd(): genera el comando en el editor
- Badge MySQL no aparece cuando es Redis

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-20 21:07:33 -05:00
Lizandro GuarnizoandCopilot fc647944b0 fix: ejecutar sentencias PostgreSQL individualmente para evitar CREATE DATABASE en transacción
- Agrega splitPostgresStatements() que parsea SQL respetando dollar-quoting, strings y comentarios
- En ExecuteSQL, si es PostgreSQL y hay múltiples sentencias, cada una se ejecuta con su propio db.Exec()
- Evita el error: 'CREATE DATABASE cannot run inside a transaction block'
- Error ocurría porque PostgreSQL envuelve multi-statement en transacción implícita al usar simple query protocol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-20 20:39:00 -05:00
Lizandro GuarnizoandCopilot 6e27032ac6 feat(portal): agregar flujo de recuperación de contraseña
- Nuevo modelo PortalPasswordResetToken con token seguro (32 bytes, 1h vigencia)
- AutoMigrate del nuevo modelo en main.go
- SendPortalPasswordResetEmail con diseño consistente al app
- Handlers: PortalForgotPasswordPage/Post y PortalResetPasswordPost/Page
- Rutas públicas GET/POST /portal/forgot-password y /portal/reset-password
- Vistas forgot_password.html y reset_password.html con validación JS
- Enlace ¿Olvidaste tu contraseña? en login.html + soporte mensaje success

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-19 15:25:53 -05:00
Lizandro Guarnizo 7f309186d6 up 2026-05-18 23:50:50 -05:00
Lizandro Guarnizo bbe83c79ba up 2026-05-18 23:45:11 -05:00