Commit Graph
209 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 5 cfa91a5c09 fix(portal): el avance del dashboard mostraba siempre el valor anterior
Auditoría de /portal/dashboard. Cuatro problemas, todos en el mismo bucle:

1. El avance se veía un render atrasado. ActualizarProgresoProyecto corría
   DESPUÉS de cargar los proyectos: escribía el valor nuevo en la base pero
   los structs ya cargados seguían con el viejo, que es lo que se renderiza.
   El usuario veía el cálculo de la visita anterior.

2. N+1 con escrituras en un GET: dos COUNT y un UPDATE por proyecto. Con 10
   proyectos, 30 consultas y 10 escrituras por cada carga del dashboard —
   incluyendo las de cualquier bot que pase. Ahora es UNA consulta agrupada
   y ninguna escritura; los caminos que tocan una fase ya mantienen la
   columna al día, así que recalcular en el GET no aportaba nada.

3. Orden aleatorio de los grupos: se recorría un map de Go, así que un
   partner veía sus clientes en distinto orden en cada recarga.

4. isPartner se decidía con u.Rol mientras el alcance se decidía con
   Role.EsPortalPartner. Desincronizados, un usuario veía proyectos de
   varios clientes sin agrupar, o la vista agrupada vacía. Ahora hay una
   sola definición (PortalUser.EsPartner) con test de los dos sentidos.

Además, el error de carga se descartaba con `_` y el usuario terminaba
viendo "no tenés proyectos", indistinguible de una caída de la base.

Sin hallazgos de seguridad: el chequeo de acceso por cliente está en todas
las rutas, la sesión revalida Activo en cada request, y las plantillas no
usan x-html ni template.HTML, así que Go escapa todo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 22:26:44 -05:00
Lizandro GuarnizoandClaude Sonnet 5 62c644b815 feat(portal): descarga del cronograma del proyecto en Excel
El cliente ya veía el roadmap en pantalla pero no tenía cómo pasárselo a
alguien que no entra al portal.

El .xlsx se genera con archive/zip + encoding/xml de la stdlib en vez de
sumar una dependencia de Excel para una sola pantalla. Un CSV renombrado
no servía: en Excel en español el separador y los acentos se rompen, y
esto es un archivo que el cliente le entrega a un tercero.

Detalles que hacen que Excel lo acepte y se lea bien: inline strings (sin
tabla sharedStrings, así ningún índice puede apuntar a la cadena
equivocada), encabezado en negrita y congelado, anchos de columna según
el contenido, y nombre de hoja saneado (Excel rechaza el archivo si pasa
de 31 caracteres o trae : \ / ? * [ ]).

El endpoint repite el chequeo de acceso de PortalProyecto: sin eso,
cualquiera con sesión de portal se bajaba el cronograma de otro cliente
adivinando el slug.

Verificado abriendo el archivo generado con openpyxl: 6 partes, CRC OK,
XML bien formado en todas, acentos y CJK intactos, negrita y panel
congelado donde corresponde.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 22:12:45 -05:00
Lizandro GuarnizoandClaude Sonnet 5 5c931c9400 fix(ui): los modales largos ya no dejan los botones fuera de pantalla
En pantallas bajas (o con formularios largos como el de portal-usuarios)
el panel del modal se salía de la ventana y no había forma de llegar a
Guardar ni a Cancelar.

La regla va en el layout y no en cada vista: son ~214 modales repartidos
en las plantillas y arreglarlos uno por uno garantizaba olvidarse varios.
El selector toma solo los overlays centrados y deja afuera los 4 paneles
laterales, que ya manejan su propio scroll.

El alto se mide en % y no en vh/dvh: el overlay es inset-0, o sea que ya
es exactamente la caja visible. Medido en Edge headless a 600/900/700/420
de alto: el panel queda siempre en ventana-2rem, el tope nunca se recorta,
y Guardar se alcanza scrolleando. Con ventana alta el panel conserva su
alto natural y no scrollea.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 21:52:54 -05:00
Lizandro GuarnizoandClaude Sonnet 5 693ff608b1 feat(portal): enlace a uMind Studio en la navegación del cliente
La ruta /portal/studio existía pero no había ningún enlace: el cliente
tenía que escribir la URL a mano, o sea que en la práctica no podía
entrar.

El enlace solo aparece si ese cliente tiene al menos un espacio de uMind
asignado — se consulta el mismo endpoint con alcance que usa el SPA, así
ningún cliente sin uMind ve un enlace que lo llevaría a una pantalla
vacía.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 21:39:24 -05:00
Lizandro GuarnizoandClaude Sonnet 5 6817ad1e6c fix(whisper): acepta la transcripción en texto plano y permite descargarla
El servicio devolvía la transcripción correcta y la reportábamos como
error: "respuesta inesperada del servicio de transcripción: <la
transcripción completa>". La causa es que mandábamos response_format=json,
que es la convención de la API de OpenAI — whisper-asr-webservice usa el
query param `output`, así que ignoró el campo y respondió en su formato
por defecto (txt), y el parser exigía JSON.

- textoDeRespuestaWhisper acepta las dos formas (JSON {"text":...} o texto
  pelado) en vez de adivinar qué variante corre del otro lado. Con test:
  8 casos, incluidos JSON sin campo text y un texto que empieza con "{".
- Whisper y OCR: la caja de resultado ahora muestra el conteo de
  caracteres, scrollea si es largo, y tiene Copiar y Descargar .txt (Blob
  + <a download>, el texto ya está en el navegador).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 20:49:08 -05:00
Lizandro GuarnizoandClaude Sonnet 5 c69b904b03 feat(umind): vincula tenants a clientes y agrega planes con precios
Base para que el cliente administre uMind desde su portal y para el cobro
por consumo. Todavía no cambia nada del comportamiento actual.

- UmindTenant gana ClienteID (punteros, sin not null: los tenants creados
  antes del portal quedan sin asignar y el ALTER TABLE no falla sobre
  datos existentes) y PlanID.
- GetUmindTenantsByClientes es fail-closed: sin clientes, no ve nada.
- UmindPlan define el máximo de agentes y los precios por 1k tokens, por
  imagen OCR, por transcripción y la mensualidad, más un tope de consumo
  que solo avisa. CRUD para staff en /app/umind-planes.
- El modelo va en AMBAS listas de AutoMigrate (main.go y migrations),
  no repetir el error de agregarlo solo en una.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 11:32:38 -05:00
Lizandro GuarnizoandClaude Sonnet 5 d493d6dee6 feat: agrega integraciones OCR y Whisper ASR (servicios propios)
Nuevos submódulos en Integraciones para conectar el servicio propio de
OCR (extracción de texto de imágenes) y el de transcripción de audio
self-hosted (whisper-asr-webservice, Basic Auth), con panel de
configuración y prueba en vivo, siguiendo el patrón ya usado por
WebSMS/Hostinger.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 10:09:40 -05:00
Lizandro GD 40dfaf5773 Agrega API Keys scoped (token + IP obligatoria + alcance) para /api/v2
Alternativa al ADMIN_API_KEY único de entorno, que sigue funcionando
como llave maestra para no romper integraciones existentes.

- Modelo ApiKey: token hasheado, IP/CIDR obligatoria (fail-closed sin
  IP), scopes habilitados, último uso (fecha + IP).
- AdminApiAuth() acepta ahora tanto la llave maestra como una ApiKey;
  nuevo middleware RequireScope(scope) para gatear grupos de rutas.
- Fase 1: scopes aplicados a lo más sensible — oss (archivos),
  query_runner (SQL arbitrario), usuarios (usuarios/roles/módulos),
  pasarelas (credenciales de pago). El resto de /api/v2 sigue con
  la llave maestra hasta una fase 2.
- Panel /app/api-keys: crear/editar/revocar, token visible solo al
  crear, scopes por checkbox, IP obligatoria.
2026-08-10 16:33:57 +00:00
Lizandro GD 21d8cd5260 Agrega diagrama de relaciones (FK) al Query Runner
- GetSchemaRelations: trae todas las llaves foráneas de la base con
  consulta específica por motor (Postgres/MySQL/MSSQL/SQLite). No
  aplica a Redis/Mongo (schemaless).
- Endpoint GET /query-runner/relations, con el mismo chequeo de
  autorización por conexión que ya usa RunQuery — de paso se lo
  agrego también a GetDatabases/GetTables, que no lo tenían.
- UI: botón "Ver relaciones" (diagrama completo) y un ícono por tabla
  en el árbol lateral (relaciones solo de esa tabla), renderizado con
  Mermaid.js servido localmente (public/js/mermaid.min.js, sin CDN).
2026-08-08 23:11:48 +00:00
Lizandro GD 5ee5880dc2 Agrega uMind: chat con IA embebible por tenant (F1 — widget web + RAG)
Multi-tenant dentro de soft_usite, reutilizando la infraestructura ya
existente (AiConfig, motor de function-calling del agente de Telegram)
en vez de un servicio nuevo aparte:

- UmindTenant: sitio/cliente con dominios permitidos, config de IA para
  el chat y personalidad/tono.
- Ingesta: crawler simple (mismo dominio, N páginas) + chunking +
  embeddings (config global con módulo "umind_embeddings", pensada
  para OpenAI ya que Claude no ofrece embeddings) guardados como JSON,
  con búsqueda por similitud coseno en memoria (sin pgvector todavía).
- Agente acotado: única herramienta buscar_conocimiento, sin acceso a
  nada interno — si no encuentra la respuesta, lo dice en vez de
  inventar.
- Widget público (/widget/umind.js + /widget/:site_key/*), autenticado
  por site_key + validación de dominio (Origin/Referer), no por
  secreto, ya que la key viaja en el HTML público del sitio instalado.
- Panel /app/umind: tenants, estado de ingesta, historial de
  conversaciones por sesión.
2026-08-07 16:18:16 +00:00
Lizandro GD 0eef3f80a3 Corrige tablas faltantes en producción y bug de listado en Entidades
- AutoMigrate migraba ~50 modelos en un solo llamado que se detiene en
  el primer error, dejando todo lo posterior (plantillas_documento,
  tarifas, documentos_generados, arquitecturas, telegram_staff_tokens)
  sin tabla, en silencio. Ahora se migra modelo por modelo: uno roto
  ya no bloquea al resto.
- /app/contabilidad/entidades pedía datos a la misma URL de la página
  (devuelve HTML) en vez de /list (JSON) — el listado nunca traía nada.
- Mensaje de bienvenida del asistente: estaba generado por la IA cada
  vez con calidad variable; ahora es determinístico para /ayuda y
  /start, con redacción revisada, y el system prompt pide el mismo
  formato cuando el usuario solo saluda.
2026-08-07 03:00:02 +00:00
Lizandro GD e5849549a0 Agrega API de pagos externos: tokens por servicio, atados a pasarela e IP
Permite emitir credenciales (token hasheado + IP/CIDR opcional) desde
/app/pagos-externos para que aplicaciones de terceros pidan cobros a
través de Bold/dLocal/PayPal sin acceso a nada más del sistema:

- POST /api/v1/pagos-externos/solicitar genera el link de cobro real
  usando solo las pasarelas habilitadas para ese servicio.
- Los webhooks existentes de Bold/dLocal/PayPal (firma obligatoria,
  idempotentes) ahora también resuelven referencias "extpay-…" sin
  tocar el flujo de contratos ("contrato-{id}").
- Al confirmarse el pago se notifica por webhook firmado (HMAC) y/o
  Telegram, configurable por servicio.
- CRUD de servicios protegido con SoloAdmin; token y callback_secret
  solo se muestran una vez, en DB se guardan hasheados.
2026-08-04 14:16:13 +00:00
Lizandro GD 999c5fcf08 Agrega transcripción de audio (Whisper) en Telegram y corrige adjuntos en tareas
- Nueva config "whisper" reutilizando /app/ai-config (credenciales/endpoint
  validables) y servicio de transcripción compatible con OpenAI/Groq.
- El webhook de Telegram ahora detecta notas de voz/audio, las transcribe
  y procesa el texto resultante como si el usuario lo hubiera escrito.
- Fix: subir un archivo adjunto en un comentario de tarea sin escribir
  texto era rechazado por el backend ("contenido requerido") aunque el
  frontend sí lo permitía.
2026-08-03 20:09:43 +00:00
Lizandro GDandClaude Opus 5 5ac70a4d6e fix: tareas del agente invisibles en el Kanban + facturas de compra por Telegram
- El agente creaba tareas con estado='pendiente' (default), pero el tablero
  Kanban del dashboard solo reconoce por_hacer/en_progreso/revision/hecho. La
  tarea se guardaba bien (por eso llegaba el correo de notificación) pero no
  aparecía en ninguna columna. Se corrigen los enums y el default de las tools
  crear_tarea/actualizar_estado_tarea, y se agrega una reparación única al
  arranque que corrige las tareas ya creadas con el estado inválido.

- adjuntar_factura siempre asumía factura de VENTA (ligada a un cliente). Se
  agrega adjuntar_factura_compra: cuando un proveedor le factura a U-SITE (no
  al revés), busca/crea la Entidad proveedor y registra una cuenta por pagar
  con el documento adjunto (se agregan campos archivo/original_name/tipo_mime
  a CuentaPagar, que no los tenía). El prompt del sistema instruye a Claude a
  decidir la dirección leyendo quién emite y quién recibe el documento.
  De paso: endpoint de descarga del soporte de la factura de compra, expuesto
  también en el dashboard (/app/contabilidad/cuentas-pagar).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:53:47 +00:00
Lizandro GDandClaude Opus 5 c8e5afceb2 fix: seguridad de pagos y accesos, integración PayPal y Coolify ampliado
Seguridad (crítico):
- Los webhooks de Bold y dLocal solo validaban la firma si el atacante la
  enviaba: sin cabecera se aceptaba cualquier payload. Ahora es obligatoria.
- GET /pago-exitoso marcaba contratos como pagados leyendo un query param del
  navegador. Ahora solo muestra estado; la confirmación la hace la verificación
  contra la API de la pasarela o el webhook firmado.
- /uploads se servía como estático público: se descargaban RUTs, facturas y
  entregables sabiendo la ruta. Ahora exige sesión.
- Los secretos JWT no se podían sobreescribir por entorno (faltaba el tag env:)
  y su valor estaba en el repo, permitiendo firmarse una sesión de admin. Ahora
  son configurables y el arranque se detiene si siguen con el valor publicado.
- .env y session.db salen del control de versiones.
- Query Runner, gestión de usuarios/roles/módulos y seeds quedan restringidos a
  administradores; antes bastaba con tener sesión.

Pasarelas de pago:
- dLocal generaba enlaces que nunca se reconciliaban: mandaba el ID numérico en
  vez de "contrato-N", la URL de retorno apuntaba a la API de dLocal y nunca se
  enviaba notification_url, así que su webhook jamás se disparaba.
- PayPal solo tenía pantalla de configuración. Se implementa el servicio
  completo (OAuth, orden, captura, verificación de webhook) y queda
  seleccionable como pasarela.
- La moneda estaba fija en COP: un contrato en USD generaba un cobro por esa
  cifra en pesos.

Contratos:
- pago_confirmado nunca volvía a false, así que el segundo ciclo de renovación
  no se cobraba aunque el cliente pagara. Se reinicia al generar enlace nuevo.
- Los contratos vencidos nunca cambiaban de estado y recibían correo a diario
  de forma indefinida; ahora se cierran tras 30 días de gracia.

Otros:
- Coolify: coolifyCall ignoraba el status HTTP y reportaba errores como éxito.
  El agente pasa de 10 a cobertura completa (servicios, bases de datos,
  variables de entorno, proyectos, equipos y recursos de servidor).
- SeedBalanceData ya no corre en cada arranque (recreaba transacciones
  borradas); ahora se invoca con SEED_BALANCE=1.
- Los seeds dejan de devolver permisos revocados en cada despliegue.
- Timeouts en las llamadas HTTP a Telegram y dLocal que podían colgarse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 03:20:00 +00:00
Lizandro GDandClaude Sonnet 5 5607836261 feat: pantalla para administrar chats autorizados del agente de Telegram
Antes la whitelist de telegram_agent_auth solo se podía tocar con curl/Postman
contra /api/v2/agent/auth. Se agrega /app/agente/chats-autorizados con un
CRUD simple, más un botón "buscar mensajes recientes" (UpdatesRecientesDelBot,
vía getUpdates) para descubrir el chat_id de alguien que le acaba de escribir
al bot sin tener que pedírselo por otro medio. Entrada nueva en el menú lateral
del módulo Automatización IA.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 02:37:40 +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 GDandClaude Sonnet 5 c6227150a3 fix: evitar spinner de carga infinito si falla la API en plantillas/tarifas
loadData() no tenía try/catch: si la petición fallaba (deploy sin el código
nuevo, tabla inexistente, etc.) el estado "loading" nunca volvía a false y la
pantalla quedaba trabada en el overlay de carga sin ningún mensaje de error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 01:00:56 +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 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 GD 772a4a5656 fix: editar instancia Coolify trae URL fresca del server y valida base_url no vacio 2026-07-22 13:27:02 +00:00
Lizandro GD a1d4c9da23 style: mejorar UI de tarjetas de instancias con iconos y jerarquia visual 2026-07-22 13:02:20 +00:00
Lizandro GD ed19eb3fd6 feat: soporte multi-instancia Coolify en panel web 2026-07-22 02:07:25 +00:00
Lizandro GDandClaude Sonnet 5 6e281dfbda feat: cuentas por cobrar usa listado de clientes en vez de entidades contables
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-13 02:44:19 +00: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 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 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 9a3a5d4b25 feat: pagina principal configurable por rol (HomeUrl) 2026-07-06 23:05:02 -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 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 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 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 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 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 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 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 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