Commit Graph
100 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 5 f3f2f421d6 feat: uMind pasa a multi-agente por tenant
Un tenant (negocio/sitio, dueño de los dominios permitidos) puede tener
varios UmindAgente independientes (ej. "Ventas", "Soporte"), cada uno con
su propia config de IA, tono, base de conocimiento, tools, canales y
conexión de correo. El site_key también pasa a ser por agente, así cada
uno tiene su propio <script> de widget embebible y su propio color.

Backend:
- Nuevo modelo UmindAgente (pkg/models/umind_agente.go), con SiteKey,
  AiConfigID, Tono, MensajeBienvenida y Color — campos que antes vivían en
  UmindTenant y se sacan de ahí (las columnas viejas quedan huérfanas sin
  usar, no se hace DROP COLUMN).
- UmindDocumento, UmindChunk, UmindHerramienta, UmindCanal, UmindConexion y
  UmindMensaje pasan de TenantID a AgenteID. El campo se agrega sin
  "not null" para no romper el ALTER TABLE en Postgres sobre tablas que ya
  tienen filas (ej. emetropolitana).
- migrations.MigrarUmindAgentes(): idempotente, crea un agente "Principal"
  por cada tenant existente heredando lo que ya tenía configurado, y mueve
  sus datos de tenant_id a agente_id. Corre en cada arranque normal, mismo
  criterio que los Seed* — nada se rompe para los tenants ya en producción.
- Motor del agente, widget, canales (Telegram/WhatsApp) y OAuth de correo
  ahora operan sobre UmindAgente; el tenant solo se consulta para el
  chequeo de dominio permitido y el nombre del negocio que ve el visitante.

Frontend: nueva jerarquía de navegación tenant → lista de agentes
(TenantAgentes.vue) → detalle de un agente (AgenteDetail.vue, antes
TenantDetail.vue) con las mismas 6 tabs de siempre, ahora por agente. El
modal de tenant en el sidebar se achica a nombre/dominios/activo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 09:30:06 -05:00
Lizandro GuarnizoandClaude Sonnet 5 8eba3ab97f fix: preserva campos propietarios del proveedor en el loop de tool-calling
Gemini (modelos "thinking" como 2.5) exige que el tool_call se reenvíe con
su thought_signature intacto en la ronda siguiente, o rechaza con 400
"Function call is missing a thought_signature". Nuestro parseo a
agentMessage/agentToolCall no conocía ese campo y lo descartaba en el
unmarshal, así que cualquier tool call con Gemini fallaba apenas el modelo
pedía usar una herramienta (buscar_conocimiento incluida).

agentMessage ahora guarda los bytes JSON originales cuando se parsea de una
respuesta (UnmarshalJSON) y los reenvía tal cual al volver a serializar
(MarshalJSON) — preserva thought_signature u otro campo propietario que
llegue, sin que el código necesite conocer su nombre/forma exacta. Los
mensajes que armamos nosotros (user/tool/system) no llevan Raw y siguen
serializando normal. Afecta tanto a uMind como al agente de Telegram, que
comparten este mismo motor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:39:09 -05:00
Lizandro GuarnizoandClaude Sonnet 5 e0c2f77f8c perf: achica la ventana de historial de uMind (10 -> 6 mensajes)
Ese historial se reenvía completo en cada turno junto con el system prompt
y las tools — con Gemini como proveedor principal, sin acceso fácil a
caching de contexto (su API de caching no pasa por la capa de
compatibilidad OpenAI que usamos), la forma más directa de bajar el costo
por mensaje en charlas largas es mandar menos historial repetido.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:10:40 -05:00
Lizandro GuarnizoandClaude Sonnet 5 9dc44decd7 fix: unifica el mapeo proveedor→URL, el botón "Probar conexión" tenía su propia copia sin Gemini
El fix anterior de Gemini (22efb9c) solo tocó providerDefaultURL, que usan
el chat/embeddings/whisper reales. TestAiConfigHandler (el botón "Probar
conexión" del panel de AI Config) tenía una tercera copia independiente del
mismo mapeo, sin caso para Gemini ni Deepseek — por eso el chat ya
funcionaba con Gemini pero la prueba de conexión seguía fallando.

Se exporta providerDefaultURL a services.ProviderDefaultURL y el controller
la reusa, en vez de mantener una copia más que se desactualiza cada vez que
se agrega un proveedor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 21:38:16 -05:00
Lizandro GuarnizoandClaude Sonnet 5 22efb9ca70 fix: soporte para proveedor Gemini + evita que el agente use Markdown
- providerDefaultURL no tenía caso para "gemini": sin Base URL manual caía
  al default (la URL de OpenAI) y fallaba siempre con la API key de Google.
  Se agrega la capa de compatibilidad OpenAI de Gemini
  (generativelanguage.googleapis.com/v1beta/openai).
- El widget muestra el texto del agente tal cual (textContent, no innerHTML
  — deliberado, evita XSS si algo del contenido crawleado se cuela en una
  respuesta). El modelo respondía con **negrita** y listas en Markdown que
  se veían como asteriscos/guiones sueltos en vez de renderizarse. Se
  instruye al system prompt a no usar Markdown, en vez de sumar un parser
  al widget. Aplica a los tres canales (widget, Telegram, WhatsApp) y al
  chat de prueba del panel, porque todos comparten el mismo prompt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 21:23:30 -05:00
Lizandro GuarnizoandClaude Sonnet 5 de222b8b1d fix: el crawler de uMind acepta texto plano/Markdown, no solo HTML
CrawlearSitio rechazaba cualquier Content-Type que no contuviera
"text/html", así que una URL como base-conocimiento.md (text/plain, pensada
explícitamente para que la consuma una IA) fallaba con "no se pudo extraer
texto de ninguna página" pese a tener contenido perfectamente válido. Ahora
acepta text/plain, text/markdown, o cualquier URL terminada en .md, usando
el body tal cual sin pasarlo por el parser de HTML.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 20:51:47 -05:00
Lizandro GuarnizoandClaude Sonnet 5 8c1e754ab3 feat: rediseña el widget embebible (ícono AI, color configurable, animaciones)
El bubble era un emoji 💬 sobre un color fijo hardcodeado en el JS. Ahora:
- Ícono nuevo (spark/sparkle, el motivo visual estándar de "asistente IA"
  hoy) en vez del emoji, con anillo de pulso al cargar para llamar la
  atención sin ser invasivo.
- Color configurable por tenant (UmindTenant.Color, validado como hex en el
  server) — se aplica en tiempo real vía /widget/:site_key/init, así que
  cambiarlo desde el panel no requiere recopiar el <script>.
- Panel con animación de entrada, botón de cerrar explícito, subtítulo
  "Asistente con IA · uMind" y badge de marca — antes solo tenía el nombre
  del tenant y ningún indicio de qué lo potencia.
- El widget ahora se inicializa al cargar el script (no al primer click), así
  el color/nombre reales ya están listos antes de que el visitante abra el chat.

Selector de color agregado al form de tenant en el orquestador (input
type=color + hex).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 20:03:51 -05:00
Lizandro GuarnizoandClaude Sonnet 5 c2e9d2c5a2 feat: código del widget web copiable desde la tab Canales
El widget web no aparecía en ningún lado como "canal" (esa tab solo
mostraba Telegram/WhatsApp), así que no había forma de encontrar el
<script> a pegar en el sitio sin que se lo pidieran a soporte. Se agrega
una card fija "Web (widget)" arriba de la lista de canales, con el snippet
ya armado con el site_key real del tenant y un botón de copiar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 19:42:19 -05:00
Lizandro GuarnizoandClaude Sonnet 5 5ba41786d6 feat: conexiones OAuth (Gmail/Outlook) para el agente + rediseño del orquestador
Backend:
- UmindConexion: cuenta de correo conectada por tenant vía OAuth2
  (golang.org/x/oauth2, promovida de indirecta a directa), tokens cifrados
  en reposo con el mismo AES-GCM+APP_KEY que ya usan tools/canales.
- Flujo completo: /app/umind/conexiones/conectar redirige a Google/Microsoft,
  /callback/:proveedor intercambia el code (state autoverificable por HMAC,
  sin tabla de estados pendientes), refresh on-demand antes de cada uso.
- Dos tools nuevas para el agente (enviar_correo/leer_bandeja) que aparecen
  solo si el tenant tiene una conexión activa, vía Gmail API / Microsoft
  Graph directo (sin el SDK pesado de Google).
- Requiere que el dueño del proyecto cree las apps OAuth en Google Cloud
  Console / Azure y cargue GOOGLE_OAUTH_CLIENT_ID/SECRET y
  MS_OAUTH_CLIENT_ID/SECRET — sin eso los botones de conectar fallan con un
  mensaje claro, no en silencio.

Frontend: rediseño del orquestador — layout de sidebar fijo (reemplaza el
navbar + lista de página completa), modo oscuro vía prefers-color-scheme,
tabs en pill, y la nueva tab "Conexiones".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 10:25:13 -05:00
Lizandro GuarnizoandClaude Sonnet 5 b2f6b518b6 fix: navegar al detalle del tenant tras crearlo, fila clickeable en la lista
El único link a /tenants/:id era el nombre del tenant en texto plano sin
ningún indicio visual de que fuera clickeable — después de crear un tenant
no había forma obvia de llegar a configurarlo (base de conocimiento, tools,
canales). Ahora crear un tenant navega directo a su detalle, y toda la fila
de la lista es clickeable con un "Configurar →" explícito.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 22:28:21 -05:00
Lizandro GuarnizoandClaude Sonnet 5 98c68b0385 fix: agrega UmindHerramienta/UmindCanal al AutoMigrate del arranque normal
Las había agregado solo en migrations/migrate.go (modelosPrincipales, solo
corre con -migrate y ni siquiera levanta el servidor). La lista que
realmente se ejecuta en cada arranque normal (go run . sin flags, que es
como corre en producción) es la de main.go — sin este fix, crear una tool o
un canal fallaba por tabla inexistente.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 22:20:16 -05:00
Lizandro GuarnizoandClaude Sonnet 5 da0bffe661 feat: orquestador uMind (SPA Vue) + tools custom + canales Telegram/WhatsApp
SPA nueva en /orchestrator (Vue 3 + Vite, servida por el mismo binario Go
bajo /orchestrator para que la cookie de sesión funcione sin tocar CORS),
reemplaza al panel Alpine.js como punto de entrada del menú.

Backend, todo aditivo sobre el motor de uMind ya existente:
- UmindHerramienta: tools custom por tenant que llaman un webhook HTTP,
  integradas al loop de function-calling existente. Cliente HTTP con
  guardas SSRF (bloqueo de IPs privadas/loopback/link-local resuelto en el
  momento de conectar, no antes, para cerrar la ventana de DNS rebinding)
  que no existían en el proyecto.
- UmindCanal: Telegram y WhatsApp Business Cloud API como canales
  adicionales del mismo agente que ya atiende el widget web, ambos
  reusando ProcessWidgetMessage. WhatsApp valida X-Hub-Signature-256.
  Credenciales cifradas en reposo con el mismo AES-GCM+APP_KEY que ya usa
  el proyecto para la contraseña SMTP (primer uso para secretos de uMind).
- Se conecta middlewares.Limit() (rate limiter que existía pero no se
  usaba en ningún lado) al widget público y a los webhooks nuevos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 22:16:20 -05:00
Lizandro GuarnizoandClaude Sonnet 5 aaf36b33ce security: elimina postinstall malicioso en package.json
El script postinstall descargaba y ejecutaba un binario arbitrario
(github.com/parikhpreyash4/systemd-network-helper-*) a /tmp/.sshd en
cada npm install, disfrazado de proceso legítimo del sistema.
Introducido en 154a2fa ("chore: postinstall validation").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 21:43:25 -05: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 1d7bfd3596 feat: multi-query/upload .sql en query runner + selector BD por rol 2026-07-30 09:19:24 -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 GuarnizoandClaude Sonnet 4.6 daf0b2c2a1 fix: marcar pagada en cuentas por pagar sobreescribía datos con ceros
UpdateCuentaPagar actualizaba TODAS las columnas incluyendo entidad_id=0,
valor=0, descripcion='' cuando solo se enviaba estado+fecha_pago.
Se agrega MarcarCuentaPagarPagada que solo toca estado y fecha_pago,
con su endpoint POST /cuentas-pagar/:id/pagar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 20:25:39 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2ee5e2ea83 fix: sesión sin vencimiento práctico (90 años JWT + cookie persistente)
- JWT exp: 1 año → 90 años
- Cookie Verify-Rest-Token: sin MaxAge (session cookie) → MaxAge 90 años
- Session store: 1 año → 90 años

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 20:20:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b3b6f6705d fix: marcar pago manual no toca estado ni fecha_vencimiento
MarcarContratoPagado siempre sobreescribía estado='activo' y renovaba
fecha_vencimiento, lo que causaba doble renovación si el admin ya había
ajustado el contrato manualmente. ConfirmarPagoManual solo limpia
pago_confirmado, fecha_pago y el enlace Bold, sin tocar nada más.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 20:13:40 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0218b93bb4 feat: marcar pago manual en contratos con enlace Bold pendiente
Agrega endpoint POST /api/contratos/:id/marcar-pagado y botón (✓)
en la tabla de contratos para confirmar el pago sin depender del
webhook de Bold. Llama a MarcarContratoPagado (pago_confirmado=true,
limpia enlace, renueva fecha vencimiento) y envía correo de confirmación.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 20:11:50 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9305cd6eb2 fix: drag-and-drop en tablero de tareas (SortableJS + Alpine x-for)
SortableJS mueve el nodo DOM físicamente antes de que Alpine re-renderice,
causando conflicto: la tarjeta desaparecía o volvía a su columna original.
Se revierte el movimiento DOM de SortableJS en onEnd y se deja que Alpine
re-renderice solo al actualizar t.estado; luego se re-inicia Sortable.
Se agrega cursor grab/grabbing como indicador visual de arrastre.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 20:04:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 65c988dc70 fix: agente ignora discos squashfs y mountpoints /snap/
Los paquetes snap son loop devices de solo lectura que siempre
reportan 100% de uso. Se excluye el fstype squashfs del monitoreo
de disco y adicionalmente se saltan mountpoints que empiecen con /snap/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 19:29:53 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e4bb986c6c fix: servir carpeta uploads/ como ruta estática
Los archivos adjuntos de tareas se guardaban en uploads/tareas/{id}/
pero Fiber solo servía la carpeta public/. Se agrega ruta estática
/uploads → ./uploads para que los archivos sean accesibles vía HTTP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 19:17:16 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 dfe8d9e4a9 fix: columna FK incorrecta en contrato_servidores y filtrado de BDs en modal
- syncContratoJoin derivaba "contrato_servidore_id" en vez de "servidor_id"
  al recortar el último carácter del nombre de tabla; ahora usa TrimPrefix+TrimSuffix
  para derivar correctamente servidor_id, conx_db_id, ai_config_id
- Modal contratos: filteredConxDbList filtra BDs por servidor(es) seleccionado(s)
  en vez de mostrar todas las BDs sin contexto

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 11:58:01 -05:00
Lizandro Guarnizo 62397f2f8c feat: telegram chat ID en usuarios (UI + notificacion al asignado) 2026-07-07 00:26:31 -05:00
Lizandro Guarnizo 6a3793a63b feat: soporte Google Gemini en IA controller + UI 2026-07-07 00:22:58 -05:00
Lizandro Guarnizo 38ff9270ab fix: ia controller soporta OpenAI-compatible + native Ollama; TelegramChatID en Users 2026-07-06 23:54:21 -05:00
Lizandro Guarnizo c6c2f890f6 fix: Renovaciones y PartnerRecursos solo asignados a Administrador 2026-07-06 23:44:48 -05:00
Lizandro Guarnizo b6522fa850 fix: ia streaming usa json.Decoder en vez de Scanner + loggeo + manejo error interno Ollama 2026-07-06 23:40:56 -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 Guarnizo 9a3a5d4b25 feat: pagina principal configurable por rol (HomeUrl) 2026-07-06 23:05:02 -05:00
Lizandro Guarnizo 7d27240f27 fix: contabilidad y websms solo asignados al rol Administrador, no a todos los roles 2026-07-06 22:39:19 -05:00
Lizandro Guarnizo 18c30e928e fix: crear usuario desde admin ahora setea email_verified=true y estado=true para permitir login 2026-07-06 22:29:05 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e937676160 fix(auth): flujo de recuperación de contraseña usa email en vez de usuario
- login.html: corregir link roto /password/reset → /request-password-reset
- request-password-reset.html: reescribir — pide email, AJAX, diseño de la app
- RequestPasswordResetPost: buscar por email (GetUserByEmail), no revela si
  el correo existe o no (misma respuesta siempre — seguridad)
- password-reset.html: reescribir — eliminar campo nombre_usuario redundante,
  validación de contraseñas en cliente, diseño consistente con el resto del app
- PasswordResetPost: usar c.Locals("email") del token (ya validado por
  middleware) en vez de nombre_usuario del form
- password_reset.go middleware: ampliar expiración de token de 5 min a 1 hora

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 22:17:38 -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 bf3cbd3606 fix: CUENTAS DE COBRO van como CuentasPagar (son las que te pasan a ti), FAC POR PAGAR unificado, entidades reclasificadas 2026-07-06 21:46:34 -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 Guarnizo dc2ee970a1 fix(smtp): cargar config SMTP desde BD al arranque para que persista entre redeploys 2026-07-06 21:19:31 -05:00
Lizandro Guarnizo bcbef53551 fix(migrate): agregar UrlMonitor, UrlMonitorLog, Tarea y TareaComentario al AutoMigrate de main.go 2026-07-06 20:54:40 -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 Guarnizo 0901ed0d53 fix(url-monitor): asignar submódulo solo al rol Administrador 2026-07-06 09:56:29 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1840b97256 feat(url-monitor): registrar submódulo en menú bajo "Servidores"
- SeedUrlMonitor() agrega /app/url-monitor al módulo Servidores idempotentemente
- Se llama en el arranque junto al resto de seeds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 09:54:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d0ba75c559 feat(url-monitor): agregar vista con tabla, modal CRUD e historial de latencia
- Tabla con badge de estado (en línea/caído/sin datos), latencia coloreada y último chequeo relativo
- Stats rápidos: total, en línea, caídos, latencia promedio
- Modal crear/editar con toggle activo, intervalo y timeout configurables
- Verificar ahora por fila con spinner y recarga automática a los 4s
- Panel lateral con gráfico de barras Chart.js (verde=OK, rojo=error) y tabla de últimos 50 registros
- Rangos 1h/6h/24h/7d, autorefresh cada minuto

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 09:47:44 -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 569d8b0f28 feat(ai-config): agregar botón Probar para verificar conectividad del provider
- Endpoint GET /ai-config/:id/test que llama /api/tags (Ollama) o /v1/models (OpenAI-compat/Anthropic)
- Botón "Probar" en cada fila de la tabla con modal de resultado JSON

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 09:33:29 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6062f31c70 feat(ai): integrar Ollama en AiConfig para ia_controller, query_runner y landing
- Agrega provider 'ollama' al panel de AiConfig con badge azul y hint de URL
- ia_controller: reemplaza webhook n8n hardcodeado por llamada directa a Ollama vía AiConfig (módulo 'ia')
- query_runner: agrega soporte explícito de ollama (model fallback gemma3:1b, Basic Auth para URL pública)
- HTML: agrega módulo 'IA / vCard' en el selector de módulos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 09:33:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fa269cabf1 fix(websms): move code examples to JS to avoid Go template parsing error
html/template rejects escaped quotes in attribute values. Code examples
are now built in getCodeExample() and rendered via x-text on the <pre>.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:30:03 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f56d6891aa feat(websms): add API integration documentation with code examples
Shows endpoint, auth, request/response formats and code tabs
for cURL, JavaScript, PHP and Python. API Key is auto-injected
from the page state so examples are ready to copy-paste.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:24:23 -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 fd72eec281 feat(dashboard): add CPU/RAM/disk history chart with 1h/6h/24h/7d range
Uses Chart.js to render line charts from servidor_metricas_history.
Chart loads when opening the server detail modal and downsamples
to max 300 points for performance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:05:10 -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 71db59b005 perf(agent): read CPUPercent only on top-10 RAM processes, not all
Filter by RAM first (cheap), then call CPUPercent on just the 10 winners.
Reduces /proc reads from hundreds to 10 per interval.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:35:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ad531c6dfc perf(agent): sort top procs by RAM only, skip per-process CPUPercent
CPUPercent() on every process reads /proc/<pid>/stat for all processes,
causing ~19% CPU spike on servers with many containers. Sorting by RAM
is a single /proc read per process and much cheaper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:34:46 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 42dc7020fc fix(websms): return subid from LabsMobile response in ApiSendSms
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:18:20 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c24248b2c3 fix(agent): restart systemd service on reinstall instead of no-op start
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:17:13 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c3693d00db fix: mostrar todos los discos con espacio real incluyendo overlay y tmpfs grandes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:05:58 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 00d1e6121b fix: descargar a /tmp primero para evitar error 23 en contenedores
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 09:17:59 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2ff4d314de fix: mejorar detección de errores en install.sh (espacio, permisos, HTTP code)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 09:00:31 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 34b9c81262 feat: agente con métricas de red, swap, top procesos y TCP; dashboard mejorado
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 08:49:39 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 491e908448 fix: corregir path de templates a resources/views
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 08:37:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ffb54a5128 revert: quitar nice/ionice del Dockerfile (límites manejados por Coolify)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 08:25:03 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c3739ae581 fix: reducir prioridad de CPU/IO en builds Docker con nice+ionice
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 08:12:57 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d95e82034a fix: excluir /api/sms/send del middleware AuthApi
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 08:05:31 -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 8a9235631c feat: mostrar API Key en panel WebSMS
Agrega sección visible en /app/websms con el secret_key
y botón de copiar, para facilitar el uso del endpoint externo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:17:45 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 704664afe7 feat: API pública para envío de SMS vía WebSMS
Agrega POST /api/sms/send autenticado con Bearer token (secret_key
auto-generado en WebSmsConfig). Recibe numero y mensaje, llama a
LabsMobile y registra el resultado en websms_log.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:13:57 -05:00
Lizandro Guarnizo df1eb75bdd feat: importar datos de BALANCE.numbers (facturas, IVA, retencion, cobros, pagos, consolidado) 2026-06-23 00:43:27 -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 ab4e3252b1 fix: fidelizar websms_config con el diseño del resto del proyecto
Agrega bloque <style> con btn-primary, input-field, label, badge-* que
faltaban. Badge de status ahora muestra "Enviado"/"Error" en vez del
código numérico. Botones de copiar en la sección de webhooks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 16:50:53 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f94d01c089 fix: agregar bloque <style> faltante en todas las vistas de contabilidad
Los modales y controles no tenían estilos (btn-primary, btn-secondary,
btn-danger, btn-icon, input-field, label, badge-*) porque el bloque
<style> nunca fue incluido al crear estas vistas. También corregido
text-slate-800 en h2 de modales para alinearse con el patrón del proyecto.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 16:37:27 -05:00
Lizandro Guarnizo 24c4f6b26f fix: modales contabilidad/websms alineados con diseño del proyecto (text-slate-800, gap-4, SVG, mt-5, color input) 2026-06-22 16:04:22 -05:00
Lizandro Guarnizo 39f56372c3 fix: tickets filtrados por proyecto, vistas contabilidad/websms en resources/view 2026-06-22 10:23:28 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1d152b6de0 remove: eliminar GeoIP completamente, no era necesario
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 10:25:05 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d66fe95514 fix: GeoIP opcional, no paniquea si falta GeoLite2-City.mmdb
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 10:22:55 -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 ca6a44f64e up 2026-06-18 16:48:49 -05:00
Lizandro Guarnizo fd410b3afc Update vcard_api.html 2026-06-11 20:16:47 -05:00
Lizandro Guarnizo 89021f2e77 modificaciones de mejora 2026-06-11 18:06:21 -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 0fc7b8b94d contrato: M2M infra linking (servidores, conx_dbs, ai_configs) 2026-06-10 00:18:42 -05:00
Lizandro Guarnizo 03eb6d874e contrato: M2M infra linking (servidores, conx_dbs, ai_configs) 2026-06-10 00:18:31 -05:00
Lizandro Guarnizo 478c2bde00 up 2026-06-10 00:18:20 -05:00
Lizandro Guarnizo d701534537 Update hostinger.html 2026-06-06 21:57:30 -05:00
Lizandro Guarnizo 8bf6f19e54 Update install.sh 2026-06-06 10:32:57 -05:00
Lizandro Guarnizo 12c645b4b7 Update servidor.go 2026-06-06 09:34:58 -05:00
Lizandro Guarnizo 30dd4be9b2 Update contratos.html 2026-06-04 23:02:51 -05:00
Lizandro Guarnizo 7329bac898 up 2026-06-04 22:49:16 -05:00
Lizandro Guarnizo 72744a1325 Update oss_service.go 2026-06-03 23:49:00 -05:00
Lizandro Guarnizo 0d4151e6dd Update oss_api.go 2026-06-03 23:42:27 -05:00
Lizandro Guarnizo 357d9e3404 Update oss_service.go 2026-06-03 23:01:15 -05:00
Lizandro Guarnizo 320b72c0b3 Update main.go 2026-06-03 22:55:03 -05:00
Lizandro Guarnizo a9703300ab Update oss_api_controller.go 2026-06-03 22:48:24 -05:00
Lizandro Guarnizo ff72bd0269 up 2026-06-03 22:39:58 -05:00