Compare commits
10
Commits
847f0cad7c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfda6921f9 | ||
|
|
7b58170540 | ||
|
|
8fedd61318 | ||
|
|
69a3ee98e6 | ||
|
|
035238c08d | ||
|
|
55608c1abe | ||
|
|
7d72dcaa84 | ||
|
|
b3b8aac200 | ||
|
|
acd5fdb5d8 | ||
|
|
7b7b45e767 |
+77
-72
@@ -144,7 +144,7 @@ cada proyecto debera implementar un algoritomo de control de versiones de las ac
|
|||||||
| **app_fidelizacion** | [pendiente — crear repo en git.telcotronics.net] | Node.js (Express) + MySQL + EJS | pablinux | 🟡 En desarrollo activo | Plataforma de lealtad, escáner QR y canjes. Gestión de clientes y transacciones de partners. Puerto 2001. BD: MySQL `nexo_fd` en 192.168.10.149. |
|
| **app_fidelizacion** | [pendiente — crear repo en git.telcotronics.net] | Node.js (Express) + MySQL + EJS | pablinux | 🟡 En desarrollo activo | Plataforma de lealtad, escáner QR y canjes. Gestión de clientes y transacciones de partners. Puerto 2001. BD: MySQL `nexo_fd` en 192.168.10.149. |
|
||||||
| **api_service_ia** | `git@github.com:telcotronics/API_Service_IA.git` | Python 3.14 + FastAPI + uvicorn | pablinux | 🟡 En desarrollo activo | API REST de modelos de IA preentrenados (OCR, STT, TTS, visión facial, PDF). Único proyecto Python serio del ecosistema. Puerto 8000. BD: MariaDB `api_ia_python` en .149. |
|
| **api_service_ia** | `git@github.com:telcotronics/API_Service_IA.git` | Python 3.14 + FastAPI + uvicorn | pablinux | 🟡 En desarrollo activo | API REST de modelos de IA preentrenados (OCR, STT, TTS, visión facial, PDF). Único proyecto Python serio del ecosistema. Puerto 8000. BD: MariaDB `api_ia_python` en .149. |
|
||||||
| **API_CENTINEL_SECURITY** | `https://git.telcotronics.net/pablinux/API-CENTINEL-SECURITY.git` | Java 17 + Spring Boot 3.3 + WebClient | pablinux | ✅ Activo | API de Gestión de Seguridad Electrónica — control de acceso físico, biometría, apertura remota de puertas, sincronización de credenciales y eventos en tiempo real. Punto único de integración para cualquier app que gestione seguridad física en el ecosistema. |
|
| **API_CENTINEL_SECURITY** | `https://git.telcotronics.net/pablinux/API-CENTINEL-SECURITY.git` | Java 17 + Spring Boot 3.3 + WebClient | pablinux | ✅ Activo | API de Gestión de Seguridad Electrónica — control de acceso físico, biometría, apertura remota de puertas, sincronización de credenciales y eventos en tiempo real. Punto único de integración para cualquier app que gestione seguridad física en el ecosistema. |
|
||||||
| **audio_control** | `https://git.telcotronics.net/pablinux/audio_control.git` | Node.js (Express 4) + MariaDB + DLNA/UPnP + ffplay | pablinux | ✅ Activo / Producción | Backend SoundWave — control remoto de reproducción de audio, descarga YouTube, playlist MySQL, renderer DLNA y auth Google OAuth. Puerto 4005 en CT 132 (`192.168.10.160`). Dominio: `audiocontrol.sigmac.app`. Servicio systemd: `siax-app-AUDIO_CONTROL`. MCP ID: 15. |
|
| **audio_control** | `https://git.telcotronics.net/pablinux/audio_control.git` | Node.js (Express 4) + MariaDB + DLNA/UPnP + ffplay | pablinux | ✅ Activo / Producción | Backend SoundWave — control remoto de reproducción de audio, descarga YouTube, playlist MySQL, renderer DLNA y auth Google OAuth. Puerto 4005 en CT 132 (`192.168.10.160`). Dominio: `musica.sigmac.app`. Servicio systemd: `siax-app-AUDIO_CONTROL`. MCP ID: 15. |
|
||||||
| **SoundWave** | `https://git.telcotronics.net/pablinux/audio-player.git` | Vue 3 + Vite 7 (PWA) / Node.js (Express) + MongoDB | pablinux | 🟢 Activo en producción | Reproductor de música personal PWA. Desplegado en server-webaps (.160) puerto 2005. Auth Google OAuth, streaming con Range requests, descargador YouTube MP3 (yt-dlp) y monitoreado en SIAX Monitor. |
|
| **SoundWave** | `https://git.telcotronics.net/pablinux/audio-player.git` | Vue 3 + Vite 7 (PWA) / Node.js (Express) + MongoDB | pablinux | 🟢 Activo en producción | Reproductor de música personal PWA. Desplegado en server-webaps (.160) puerto 2005. Auth Google OAuth, streaming con Range requests, descargador YouTube MP3 (yt-dlp) y monitoreado en SIAX Monitor. |
|
||||||
| **app_sigma_inventario** (Control Inventario) | [pendiente — git.telcotronics.net] | Flutter (Android / Linux Desktop) / Dart | pablinux | 🟡 En desarrollo activo | App móvil para supervisores y bodegueros: conciliación física de stock, traslados con código de barras y recepción OCR externa. |
|
| **app_sigma_inventario** (Control Inventario) | [pendiente — git.telcotronics.net] | Flutter (Android / Linux Desktop) / Dart | pablinux | 🟡 En desarrollo activo | App móvil para supervisores y bodegueros: conciliación física de stock, traslados con código de barras y recepción OCR externa. |
|
||||||
| **agente_ventas** | [sin repositorio — local] | Flutter 2.11 / Dart 2.17-beta + SQLite | pablinux | 🟡 En desarrollo activo — integrando API real | App "Agente Ventas SIGMAC" — gestión de rutas y pedidos de campo para vendedores de Telcotronics. Ruta del día, catálogo, pedidos, firma digital, scanner QR/barcode, notificaciones in-app. Auth vía API-SIGMA-WEBCONTROL · datos vía API-SIGMA-CLOUD (BD `TELCOTRONICS`). |
|
| **agente_ventas** | [sin repositorio — local] | Flutter 2.11 / Dart 2.17-beta + SQLite | pablinux | 🟡 En desarrollo activo — integrando API real | App "Agente Ventas SIGMAC" — gestión de rutas y pedidos de campo para vendedores de Telcotronics. Ruta del día, catálogo, pedidos, firma digital, scanner QR/barcode, notificaciones in-app. Auth vía API-SIGMA-WEBCONTROL · datos vía API-SIGMA-CLOUD (BD `TELCOTRONICS`). |
|
||||||
@@ -153,8 +153,8 @@ cada proyecto debera implementar un algoritomo de control de versiones de las ac
|
|||||||
| **API-SIGMA-COMUNICACIONES** | `https://git.telcotronics.net/pablinux/API-SIGMA-COMUNICACIONES.git` | Node.js (Express) + MySQL | pablinux | 🔴 En desarrollo inicial | Gateway SaaS multi-tenant de canales de mensajería: cada cliente/organización crea y conecta su propio componente WhatsApp (bot o Meta), Telegram y redes sociales, aislado del resto. Independiente de `sigma-robot` (comunicaciones internas de Telcotronics). |
|
| **API-SIGMA-COMUNICACIONES** | `https://git.telcotronics.net/pablinux/API-SIGMA-COMUNICACIONES.git` | Node.js (Express) + MySQL | pablinux | 🔴 En desarrollo inicial | Gateway SaaS multi-tenant de canales de mensajería: cada cliente/organización crea y conecta su propio componente WhatsApp (bot o Meta), Telegram y redes sociales, aislado del resto. Independiente de `sigma-robot` (comunicaciones internas de Telcotronics). |
|
||||||
| **MCP-Infraestructura** | `https://git.telcotronics.net/pablinux/MCP-INFRAESTRUCTURA.git` | TypeScript (@modelcontextprotocol/sdk) + Nuxt 3 + MySQL | pablinux | ✅ Activo | Servidor MCP y panel web para inventario y gestión en tiempo real de la infraestructura del ecosistema (hardware, servicios, apps IA y externos). Fuente de verdad en MySQL. |
|
| **MCP-Infraestructura** | `https://git.telcotronics.net/pablinux/MCP-INFRAESTRUCTURA.git` | TypeScript (@modelcontextprotocol/sdk) + Nuxt 3 + MySQL | pablinux | ✅ Activo | Servidor MCP y panel web para inventario y gestión en tiempo real de la infraestructura del ecosistema (hardware, servicios, apps IA y externos). Fuente de verdad en MySQL. |
|
||||||
| **proxy_server_SRI** | `https://git.telcotronics.net/pablinux/proxy_server_SRI.git` | Python 3 (ThreadingHTTPServer + requests) | pablinux | ✅ Producción | Script proxy HTTP multihilo para consultas SRI en cascada de RUC y Cédula (Ecuador). Provee proxy y normalización para WebControl, SIGMAC y apps. |
|
| **proxy_server_SRI** | `https://git.telcotronics.net/pablinux/proxy_server_SRI.git` | Python 3 (ThreadingHTTPServer + requests) | pablinux | ✅ Producción | Script proxy HTTP multihilo para consultas SRI en cascada de RUC y Cédula (Ecuador). Provee proxy y normalización para WebControl, SIGMAC y apps. |
|
||||||
| **cloud-sync** | `https://git.telcotronics.net/pablinux/Service-Cloud-SINC.git` | Rust 2021 + Axum 0.7 + SQLite | pablinux | 🟡 En mantenimiento / primer deploy | Servidor central de backup cifrado y consolas web. Puerto 3000 en 192.168.10.136. MCP ID: 12. |
|
| **cloud-sync** | `https://git.telcotronics.net/pablinux/Service-Cloud-SINC.git` | Rust 2021 + Axum 0.7 + SQLite | pablinux | ✅ Activo / Producción HTTP | Servidor central de backup cifrado y consolas web. `192.168.10.136:3000`, health `ok` y harness remoto upload/list/download/restore verificados el 2026-10-17. HTTPS pendiente; MCP id 12. |
|
||||||
| **cloud-sync-clients** | [sin remoto aún — siax-amd] | Rust 2021 + Axum 0.7 + Tokio + aes-gcm + zstd | pablinux | 🟡 En desarrollo activo | Clientes server-to-server (Linux/Windows) para respaldo y cifrado local hacia cloud-sync. UI local :3001. MCP ID: 21. |
|
| **cloud-sync-clients** | [sin remoto aún — siax-amd] | Rust 2021 + Axum 0.7 + Tokio + aes-gcm + zstd + rusqlite | pablinux | 🟡 Operativo / En desarrollo | Clientes server-to-server (Linux/Windows) para respaldo y cifrado local hacia cloud-sync. UI local :3001. Harness remoto validado (192.168.10.136:3000). MCP ID: 21. Agente ID: 42. |
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1340,10 +1340,10 @@ Aplicación web en Go para transferencia inmediata de archivos, fotos, documento
|
|||||||
| Responsable | pablinux |
|
| Responsable | pablinux |
|
||||||
| Estado actual | ✅ Operativo / En pruebas |
|
| Estado actual | ✅ Operativo / En pruebas |
|
||||||
| Ubicación local | `siax-amd` — `/home/pablinux/Projects/GO/app_compartir` |
|
| Ubicación local | `siax-amd` — `/home/pablinux/Projects/GO/app_compartir` |
|
||||||
| Servidor de producción | `server-webapps` CT 132 (`192.168.10.160`) — pendiente deploy |
|
| Servidor de producción | `server-webapps` CT 132 (`192.168.10.160`) |
|
||||||
| Dominio público | `drop.sigmac.app` |
|
| Dominio público | `drop.sigmac.app` |
|
||||||
| Puerto | `2006` |
|
| Puerto | `2008` |
|
||||||
| Servicio systemd | `app-compartir.service` |
|
| Servicio systemd | `siax-app-DROP_SIGMAC_APP.service` (gestionado por SIAX Monitor) |
|
||||||
|
|
||||||
**Bases de Datos:**
|
**Bases de Datos:**
|
||||||
* **SQLite** (`data/compartir.db` local en modo WAL): Almacena metadatos de archivos compartidos (`shares`), usuarios vinculados (`users`) y tokens de sesión (`sessions`).
|
* **SQLite** (`data/compartir.db` local en modo WAL): Almacena metadatos de archivos compartidos (`shares`), usuarios vinculados (`users`) y tokens de sesión (`sessions`).
|
||||||
@@ -1456,7 +1456,7 @@ Backend de la app **SoundWave**. Permite controlar la reproducción de audio de
|
|||||||
| Estado actual | ✅ Activo / Desplegado en producción |
|
| Estado actual | ✅ Activo / Desplegado en producción |
|
||||||
| Equipo / ubicación local | `siax-amd` (`192.168.10.100`) — `/home/pablinux/Projects/Node/audio_control` |
|
| Equipo / ubicación local | `siax-amd` (`192.168.10.100`) — `/home/pablinux/Projects/Node/audio_control` |
|
||||||
| Servidor de producción | `server-webaps` CT 132 (`192.168.10.160`) — `/home/user_apps/apps/audio_control` |
|
| Servidor de producción | `server-webaps` CT 132 (`192.168.10.160`) — `/home/user_apps/apps/audio_control` |
|
||||||
| Dominio público | `audiocontrol.sigmac.app` (vhost activo en .160; enrutamiento WAN sujeto a proxy en .109 / NAT) |
|
| Dominio público | `musica.sigmac.app` (vhost activo en .160; enrutamiento WAN sujeto a proxy en .109 / NAT) |
|
||||||
| Puerto | `4005` (dev: `4000`) |
|
| Puerto | `4005` (dev: `4000`) |
|
||||||
| Servicio systemd | `siax-app-AUDIO_CONTROL.service` (activo) |
|
| Servicio systemd | `siax-app-AUDIO_CONTROL.service` (activo) |
|
||||||
| Registro MCP | Servicio ID `15` en `http://192.168.10.160:3006` |
|
| Registro MCP | Servicio ID `15` en `http://192.168.10.160:3006` |
|
||||||
@@ -2410,21 +2410,25 @@ SIAX es la inteligencia artificial del ecosistema. Aprende de forma autónoma y
|
|||||||
|
|
||||||
## api_service_ia — API de Servicios con IA
|
## api_service_ia — API de Servicios con IA
|
||||||
|
|
||||||
Microservicio REST que centraliza modelos de IA preentrenados para consumo interno del ecosistema. Es el **único proyecto Python serio de Telcotronics**: todos los demás son Node.js, Java, PHP, Rust o Flutter. Expone capacidades de procesamiento pesado (Whisper, InsightFace, Silero, rembg) como endpoints HTTP autenticados, evitando duplicar dependencias en cada proyecto que las necesite. Actualmente consumido por `sigma-robot` para STT y OCR.
|
Microservicio REST que centraliza modelos de IA preentrenados para consumo interno del ecosistema. Es el **único proyecto Python serio de Telcotronics**: todos los demás son Node.js, Java, PHP, Rust o Flutter. Expone capacidades de procesamiento pesado (Whisper, InsightFace, Silero, rembg) como endpoints HTTP autenticados, evitando duplicar dependencias en cada proyecto que las necesite. Actualmente consumido por `sigma-robot` para STT y OCR. Requiere GPU NVIDIA: Whisper corre en CUDA sin fallback a CPU, por decisión de diseño.
|
||||||
|
|
||||||
| Parámetro | Valor |
|
| Parámetro | Valor |
|
||||||
|-----------|-------|
|
|-----------|-------|
|
||||||
| Repositorio Git | `git@github.com:telcotronics/API_Service_IA.git` |
|
| Repositorio Git | `git@github.com:telcotronics/API_Service_IA.git` |
|
||||||
| Tecnología principal | Python 3.14 + FastAPI + uvicorn |
|
| Tecnología principal | Python 3.14 + FastAPI + uvicorn |
|
||||||
| Responsable | pablinux |
|
| Responsable | pablinux |
|
||||||
| Estado actual | ✅ Producción |
|
| Estado actual | ✅ Producción (verificado en vivo 2026-10-17) |
|
||||||
| Código local | `siax-amd:/home/pablinux/Projects/Python/api_service_ia/` |
|
| Código local | `siax-intel:/home/pablinux/Projects/Python/api_service_ia/` |
|
||||||
| Servidor producción | `Ubuntu-Docker` — CT 105 (`192.168.10.145`) — usuario `root` |
|
| Servidor producción | `APPS-SERVICE-IA` — CT 140, nodo cluster (`192.168.10.154`) — usuario `pablinux` — GPU RTX 5060 8 GB |
|
||||||
| Ruta en servidor | `/root/app/API_Service_IA` |
|
| Ruta en servidor | `/home/pablinux/apps/API_Service_IA` (venv `api_env/`, Python 3.12.3) |
|
||||||
| Dominio público | `api.telcotronics.net` (`whisper.telcotronics.net` — legacy, obsoleto) |
|
| Dominio público | `https://api.telcotronics.net` vía Nginx Proxy Manager .141 (`whisper.telcotronics.net` — legacy, obsoleto) |
|
||||||
|
| Swagger | `https://api.telcotronics.net/docs` |
|
||||||
| Puerto | `8000` |
|
| Puerto | `8000` |
|
||||||
| Servicio de arranque | `tmux` — sesión `api_service_ia` (pendiente migrar a systemd) |
|
| Servicio de arranque | `tmux` — hoy en la sesión `ia-services` (`desplegar.sh` usa `api_service_ia`; pendiente unificar o migrar a systemd) |
|
||||||
| Última actualización | 2026-06-03 |
|
| Servidor anterior | `192.168.10.145` — ya no sirve la API (puerto 8000 cerrado) |
|
||||||
|
| MCP de Infraestructura | App IA id 2 `api_service_ia` · hardware id 12 `CT 140 APPS-SERVICE-IA` · agente id 5 |
|
||||||
|
| Agente en sigma-robot | `Agente API Service IA` — ID 85 |
|
||||||
|
| Última actualización | 2026-10-19 |
|
||||||
|
|
||||||
**BD que usa:**
|
**BD que usa:**
|
||||||
|
|
||||||
@@ -2443,20 +2447,20 @@ Microservicio REST que centraliza modelos de IA preentrenados para consumo inter
|
|||||||
| Remover fondo | `POST /removerFondo_img/` | PNG con transparencia | ✅ |
|
| Remover fondo | `POST /removerFondo_img/` | PNG con transparencia | ✅ |
|
||||||
| PDF a texto | `POST /convertir_pdf_aTexto/` | JSON `{texto}` | ✅ |
|
| PDF a texto | `POST /convertir_pdf_aTexto/` | JSON `{texto}` | ✅ |
|
||||||
| PDF a Word | `POST /convertir_pdf_aWord/` | `.docx` descargable | ✅ |
|
| PDF a Word | `POST /convertir_pdf_aWord/` | `.docx` descargable | ✅ |
|
||||||
| PDF a Excel | `POST /convertir_pdf_aExcel/` | `.xlsx` descargable | ✅ |
|
| PDF a Excel | `POST /convertir_pdf_aExcel/` | `.xlsx` descargable | ❌ en producción (dependencia `tabula` equivocada); corregido en el repo, pendiente de desplegar |
|
||||||
| PDF tablas a JSON | `POST /pdf_tablas/` | JSON estructurado | ✅ |
|
| PDF tablas a JSON | `POST /pdf_tablas/` | JSON estructurado | ✅ |
|
||||||
| Reconocimiento facial | `POST /facial/detectar/` | JSON `{bbox, confianza}` | ✅ |
|
| Reconocimiento facial | `POST /facial/detectar/` | JSON `{bbox, confianza}` | ✅ |
|
||||||
| Comparación facial | `POST /facial/comparar/` | JSON `{es_misma_persona, similitud}` | ✅ |
|
| Comparación facial | `POST /facial/comparar/` | JSON `{es_misma_persona, similitud}` | ✅ |
|
||||||
| TTS online (gTTS) | `POST /texto_aVoz/` | MP3 descargable | ✅ requiere internet |
|
| TTS online (gTTS) | `POST /texto_aVoz/` | MP3 descargable | ✅ requiere internet |
|
||||||
| TTS offline (Silero) | `POST /texto_aVoz/silero/` | WAV 24kHz descargable | ✅ offline tras 1ª descarga |
|
| TTS offline (Silero) | `POST /texto_aVoz/silero/` | WAV 16 kHz descargable | ⚠️ en producción el audio sale truncado; corregido en el repo, pendiente de desplegar |
|
||||||
|
|
||||||
**Autenticación:** header `X-API-Key` — validado contra `api_ia_python.api_key` en MariaDB .149.
|
**Autenticación:** header `X-API-Key` — validado contra `api_ia_python.api_key` en MariaDB .149.
|
||||||
|
|
||||||
**Integración con el ecosistema:**
|
**Integración con el ecosistema:**
|
||||||
- **sigma-robot** la consume para STT (`/convertir_audio_aTexto/`) y OCR/imágenes (`https://api.telcotronics.net`)
|
- **sigma-robot** la consume para STT (`/convertir_audio_aTexto/`) y OCR/imágenes — debe usar `https://api.telcotronics.net`, no `192.168.10.145:8000`
|
||||||
- Cualquier proyecto del ecosistema puede consumirla con una API key válida
|
- Cualquier proyecto del ecosistema puede consumirla con una API key válida
|
||||||
|
|
||||||
**Dónde buscar más info:** `siax-amd:/home/pablinux/Projects/Python/api_service_ia/agents.md`
|
**Dónde buscar más info:** `siax-intel:/home/pablinux/Projects/Python/api_service_ia/agents.md`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -2561,74 +2565,75 @@ curl -X POST https://api.telcotronics.com/api/biometria/1001 \
|
|||||||
|
|
||||||
## cloud-sync — Sistema de Backup Automático y Recuperación ante Desastre
|
## cloud-sync — Sistema de Backup Automático y Recuperación ante Desastre
|
||||||
|
|
||||||
cloud-sync es el servidor central de backup y recuperación ante desastre para clientes SIGMA. El agente de origen comprime y cifra archivos y dumps de SQLite, MySQL, MariaDB o bases compatibles antes de subirlos; el servidor almacena metadatos y contenido cifrado, sin recibir la clave del cliente. El repositorio incluye la API Rust y las consolas root/empresa; los ejecutables server-to-server viven en `cloud-sync-clients`. La integración Flutter/Java y la aplicación de escritorio siguen siendo objetivos posteriores.
|
cloud-sync es un servidor central de backup y recuperación ante desastre para clientes SIGMA. Los datos se comprimen y cifran en el equipo de origen antes de subirlos; el servidor almacena contenido cifrado y metadatos, sin recibir la clave de cifrado. El repositorio mantiene la API Rust y las consolas root/empresa. Los ejecutables server-to-server están en el proyecto separado `cloud-sync-clients`; Tauri y los SDK Flutter/Java siguen pendientes.
|
||||||
|
|
||||||
| Parámetro | Valor |
|
| Parámetro | Valor |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Repositorio Git | `https://git.telcotronics.net/pablinux/Service-Cloud-SINC.git` (privado, rama `main`, último commit documentado `babf1cd`) |
|
| Repositorio Git | `https://git.telcotronics.net/pablinux/Service-Cloud-SINC.git` (privado, rama `main`) |
|
||||||
| Tecnología principal | Rust 2021 · Axum 0.7 · Tokio · rusqlite/SQLite · filesystem local · HTML/CSS/JavaScript para consolas web |
|
| Tecnología principal | Rust 2021 · Axum 0.7 · Tokio · rusqlite/SQLite · filesystem local · HTML/CSS/JavaScript |
|
||||||
| Responsable | pablinux |
|
| Responsable | pablinux |
|
||||||
| Estado actual | 🟡 Primer despliegue y smoke test HTTP aprobados; HTTPS público, auth Cloud dual y pruebas funcionales pendientes |
|
| MCP de Infraestructura | Servicio id 12 · categoría `service` · estado `ACTIVE` |
|
||||||
| Equipo / código local | `siax-amd` (`192.168.10.100`) — `/home/pablinux/Projects/Rust/cloud monitor sinc` |
|
| Estado operativo | Producción HTTP. El 2026-10-17 `/health` respondió HTTP 200, `status: ok`, versión `0.1.0`; harness remoto aislado completó upload/list/download/restore desde `cloud-sync-clients` |
|
||||||
| Servidor de producción | `192.168.10.136` — `/home/user_apps/apps/cloud-sync`; tmux activo, `/health` y consolas HTTP 200 verificados 2026-09-21 |
|
| Servidor de producción | `192.168.10.136:3000`; ruta documentada `/home/user_apps/apps/cloud-sync` |
|
||||||
| Dominio público | Pendiente — VirtualHost HTTPS en Apache |
|
| Hardware / CT | No hay entidad correspondiente a `192.168.10.136` en `infra://hardware`; sin CT confirmado. Feedback MISSING id 24, abierto el 2026-10-17 |
|
||||||
| Puerto interno | `3000` |
|
| Dominio público / HTTPS | Sin dominio confirmado; HTTPS público pendiente |
|
||||||
| Última actualización | 2026-09-30 |
|
| Feedback MCP | Cero reportes al iniciar la revisión del 2026-10-17; se abrió después el MISSING id 24 por la ficha de hardware faltante |
|
||||||
|
| Reglas de despliegue | Ninguna activa devuelta para alcance `cloud-sync` ni global. Hay una regla global SRI en borrador, no aplicable a este servicio |
|
||||||
|
| Última actualización | 2026-10-17 |
|
||||||
|
|
||||||
**BD que usa:**
|
**Almacenamiento de datos:**
|
||||||
|
|
||||||
| Motor | Host | Nombre | Uso |
|
| Motor | Host / ruta | Uso |
|
||||||
|---|---|---|---|
|
|---|---|---|
|
||||||
| SQLite (embebida, bundled) | `192.168.10.136` — `/home/user_apps/apps/cloud-sync/data` | `server.db` | Empresas, cuotas, tokens, perfiles, sesiones, backups (metadata) y eventos |
|
| SQLite embebida | `192.168.10.136` — `/home/user_apps/apps/cloud-sync/data/server.db` | Empresas, cuotas, perfiles, sesiones, metadatos de backups y eventos |
|
||||||
| SQLite (embebida, en el cliente) | Equipo de origen | `index.db` | Índice local de rutas, checksum SHA-256, backup_id y timestamps |
|
| SQLite local del cliente | Equipo de origen — `index.db` | Rutas, checksum SHA-256, identificadores y timestamps de backups |
|
||||||
|
| Filesystem | Servidor `192.168.10.136` | Contenido cifrado de los backups, organizado por empresa |
|
||||||
|
|
||||||
**Servicios que consume y expone:**
|
**Servicios que consume y expone:**
|
||||||
|
|
||||||
| Método | Endpoint | Auth | Descripción |
|
| Método | Endpoint | Auth | Descripción |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| GET | `/health` | — | Estado del servidor + info de disco |
|
| GET | `/health` | — | Estado del servidor y capacidad de disco |
|
||||||
| POST | `/auth/token` | — | Login cliente (token UUID) → sesión Bearer |
|
| POST | `/auth/token` | Token de empresa | Compatibilidad con agentes integradores |
|
||||||
| POST | `/auth/admin` | — | Login admin (argon2) → session token |
|
| POST | `/auth/root`, `/auth/admin` | Usuario y contraseña | Login root; `/auth/admin` es alias compatible |
|
||||||
| POST | `/backup/upload` | Bearer token | Subir archivo cifrado (multipart) |
|
| POST | `/auth/empresa` | Usuario y contraseña | Login de administrador de empresa |
|
||||||
| GET | `/backup/list` | Bearer token | Listar backups de la empresa |
|
| POST | `/auth/logout` | Sesión Bearer | Revocar la sesión presentada |
|
||||||
| GET | `/backup/download/{id}` | Bearer token | Descargar backup para restore |
|
| PUT | `/auth/admin/password` | Sesión root | Cambiar contraseña root |
|
||||||
| DELETE | `/backup/{id}` | Bearer token | Eliminar backup |
|
| PUT | `/auth/empresa/password` | Sesión empresa | Cambiar contraseña de empresa |
|
||||||
| GET | `/me/storage` | Bearer token | Uso actual vs cuota asignada |
|
| GET | `/empresa/cuenta` | Sesión empresa | Consultar su cuenta y perfil |
|
||||||
| GET | `/admin/storage` | X-Admin-Token | Capacidad CT146 + resumen global |
|
| POST | `/backup/upload` | Bearer de integración | Subir backup cifrado multipart; límite configurado de hasta 10 GiB y cuota por empresa |
|
||||||
| GET/POST | `/admin/clientes` | X-Admin-Token | Listar / crear empresas |
|
| GET | `/backup/list`, `/backup/status` | Bearer de integración | Listar respaldos y consultar estado |
|
||||||
| PUT | `/admin/clientes/{id}/cuota` | X-Admin-Token | Ajustar cuota por empresa |
|
| GET | `/backup/download/{id}` | Bearer de integración o sesión empresa | Descargar respaldo cifrado propio |
|
||||||
| GET | `/admin/eventos` | X-Admin-Token | Log de auditoría |
|
| DELETE | `/backup/{id}` | Bearer de integración | Eliminar backup propio |
|
||||||
| GET | `/root/` | — | Consola root para empresas, cuotas, estados, perfiles y accesos |
|
| GET | `/me/storage` | Bearer de integración | Uso y cuota de empresa |
|
||||||
| GET | `/admin/` | — | Portal admin de empresa para consultar cuenta y descargar respaldos propios |
|
| GET | `/admin/storage`, `/admin/clientes`, `/admin/eventos` | Sesión root | Capacidad, empresas y auditoría |
|
||||||
| GET | `/panel/` | Token de integración | Portal legado de cliente |
|
| POST | `/admin/clientes` | Sesión root | Crear empresa y token de integración |
|
||||||
| MCP Infraestructura | `http://192.168.10.160:3006/sse` | Uso interno del ecosistema | Inventario; `cloud-sync` registrado como servicio id 12; `cloud-sync-clients` registrado como servicio id 21. No transporta errores de runtime |
|
| PUT | `/admin/clientes/{id}/cuota`, `/estado` | Sesión root | Cambiar cuota o suspender empresa |
|
||||||
| API-SIGMA-WEBCONTROL | `https://api.siax-system.net/api/errores` | `x-api-key` por aplicación | Integración de bitácora de errores pendiente; no hay clave configurada |
|
| GET/PUT | `/admin/clientes/{id}/perfil` | Sesión root | Consultar o editar perfil |
|
||||||
|
| PUT | `/admin/clientes/{id}/acceso` | Sesión root | Asignar o reemplazar acceso de empresa |
|
||||||
|
| GET | `/root/`, `/admin/`, `/panel/` | Según consola | Root, administrador de empresa y portal legado |
|
||||||
|
|
||||||
**Módulos disponibles:**
|
**Estado de módulos y pendientes:**
|
||||||
|
|
||||||
| Módulo | Estado |
|
| Módulo / tarea | Estado |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Servidor API REST (Axum) | ✅ Completo y probado |
|
| API Rust y consola root | Implementadas y desplegadas; health HTTP verificado |
|
||||||
| Consola web admin (`/admin`) | ✅ Completa |
|
| Consola de empresa | Implementada; validar login y flujos de consola en producción |
|
||||||
| Consola web panel cliente (`/panel`) | ✅ Completa |
|
| Portal `/panel/` | Legado por token de integración |
|
||||||
| `cloud_sync_core` (librería Rust) — crypto, uploader, backup_engine, restore | ✅ Completo |
|
| `cloud-sync-clients` | Proyecto separado, MCP id 21; harness aislado upload/list/download/restore pasó contra `192.168.10.136:3000` el 2026-10-17; Windows pendiente de validación funcional |
|
||||||
| Agente server-to-server (`cloud-sync-clients`) — archivos y dumps | ✅ Base Linux; UI local :3001; harness local/remoto; MCP ID 21; Windows pendiente de validación real |
|
| Autenticación dual | Diseño aprobado; falta cerrar proveedor/login, tokens y registro/revocación de instalaciones |
|
||||||
| Script de despliegue configurable | ✅ Preparado para `192.168.10.136` |
|
| Uploads reanudables y cola durable | Objetivo 10, pendiente de diseño e implementación |
|
||||||
| App desktop (Tauri — Windows/Linux/macOS) | ⏳ Pendiente |
|
| Restauración automatizada y verificada | Objetivo 11; el harness validó un ciclo básico remoto, la automatización de archivos/dumps sigue pendiente |
|
||||||
| SDK Flutter (uniffi-rs → Dart FFI) | ⏳ Pendiente |
|
| HTTPS público | Pendiente confirmar dominio y configurar la entrada aprobada |
|
||||||
| SDK Java (uniffi-rs → JNI) | ⏳ Pendiente |
|
| Bitácora WebControl | Objetivo 8; bloqueada hasta confirmar `app_origen` y obtener `x-api-key` autorizada |
|
||||||
| Deploy en `192.168.10.136` | ✅ Ejecutado; smoke test HTTP aprobado |
|
| App de escritorio Tauri / SDK Flutter / SDK Java | Pendientes |
|
||||||
| HTTPS público detrás de Apache | ⏳ Pendiente de configurar y verificar |
|
|
||||||
| Bitácora centralizada de errores | ⏳ Pendiente de clave, reportador y consulta operativa |
|
|
||||||
| Autenticación dual para apps y servicios | ⏳ Diseño aprobado; login Cloud, sesiones cortas y credencial por instalación pendientes |
|
|
||||||
|
|
||||||
**Integración con el ecosistema:**
|
**Integración y separación de sistemas:**
|
||||||
- **SIGMAC** (Java ERP, CT 141): SDK Java pendiente — backup automático de archivos y BD de las empresas clientes
|
- **MCP de Infraestructura** (`192.168.10.160:3006`): inventario y reglas operativas. Ficha de servicio id 12; no es proxy de errores de runtime.
|
||||||
- **sigmac_app** (Flutter, `.72`): SDK Flutter pendiente — backup de datos del app sin intervención del usuario
|
- **Feedback MCP:** el 2026-10-17 se reportó MISSING id 24 porque el inventario no contiene una entidad de hardware para `192.168.10.136`. No registrar un CT o ubicación hasta confirmarlos.
|
||||||
- **MCP de Infraestructura** (`192.168.10.160:3006`): inventario actualizado; no es proxy de la bitácora de errores
|
- **API-SIGMA-WEBCONTROL** (`https://api.siax-system.net/api/errores`): destino separado para errores de runtime. No hay `x-api-key` autorizada ni `app_origen` confirmado en este proyecto; la ausencia de consulta no significa ausencia de errores.
|
||||||
- **API-SIGMA-WEBCONTROL**: canal previsto para reportar/consultar errores de runtime cuando exista una clave autorizada
|
- **Reglas de despliegue:** no se devolvieron reglas activas globales ni para `cloud-sync`; no inferir dominio, DNS, ruta pública o contenedor.
|
||||||
- **Autenticación Cloud**: diseño aprobado para login de apps con access/refresh token y credencial protegida por instalación en servicios automáticos; implementación pendiente
|
- **Autenticación Cloud:** la implementación dual sigue pendiente de cerrar el contrato con el proveedor autorizado. No persistir contraseñas Cloud en cloud-sync.
|
||||||
- **Apache**: VirtualHost HTTPS pendiente
|
|
||||||
|
|
||||||
**Dónde buscar más información:** `siax-amd:/home/pablinux/Projects/Rust/cloud monitor sinc/agents.md`; cliente server-to-server: `/home/pablinux/Projects/Rust/cloud-sync-clients/agents.md`
|
**Dónde buscar más información:** `siax-amd:/home/pablinux/Projects/Rust/cloud monitor sinc/agents.md`; cliente server-to-server: `/home/pablinux/Projects/Rust/cloud-sync-clients/agents.md`
|
||||||
|
|
||||||
|
|||||||
@@ -36,3 +36,9 @@
|
|||||||
| 2026-10-13 | Sesión Guest con auto-eliminación obligatoria en 24h (física y BD) | Worker periódico de purga en segundo plano verificado con tests |
|
| 2026-10-13 | Sesión Guest con auto-eliminación obligatoria en 24h (física y BD) | Worker periódico de purga en segundo plano verificado con tests |
|
||||||
| 2026-10-13 | Cuentas registradas (Google y WebControl) con almacenamiento permanente | Cuentas no-guest exentas de borrado a las 24h |
|
| 2026-10-13 | Cuentas registradas (Google y WebControl) con almacenamiento permanente | Cuentas no-guest exentas de borrado a las 24h |
|
||||||
| 2026-10-13 | Panel / lista de "Mis Archivos" para usuarios registrados con opción de borrado | Interfaz `/mis-archivos` con métricas de visitas y eliminación |
|
| 2026-10-13 | Panel / lista de "Mis Archivos" para usuarios registrados con opción de borrado | Interfaz `/mis-archivos` con métricas de visitas y eliminación |
|
||||||
|
| 2026-10-14 | Scripts de despliegue estándar (`desplegar.sh`, `init.sh`) y servicio para SIAX Monitor | Compilación Go estática, scp, servers.conf y systemd |
|
||||||
|
| 2026-10-17 | Registro oficial en MCP Infraestructura (ID 34) | Ficha técnica completa registrada en MariaDB bajo categoría application |
|
||||||
|
| 2026-10-17 | Registro en centro de comunicaciones de agentes (sigma-robot) | Identidad `Agente drop.sigmac.app` registrada vía REST; credenciales en `.env` |
|
||||||
|
| 2026-10-17 | Sincronización de normas y archivos base del ecosistema | Creación de `id_agente.md`, `reglas.md`, `tareas.md` y espejo de `infraestructura.md` |
|
||||||
|
| 2026-10-19 | Despliegue en producción en CT 132 con SIAX Monitor (:8080) | Servicio `siax-app-DROP_SIGMAC_APP.service` operativo en puerto 2008 |
|
||||||
|
| 2026-10-20 | Puesta en producción pública vía HTTPS en drop.sigmac.app | Proxy Apache con SSL operativo, URLs y QR dinámicos con protocolo HTTPS verificado |
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ app_compartir/
|
|||||||
│ ├── objetivos.md
|
│ ├── objetivos.md
|
||||||
│ ├── proyecto_errores.md
|
│ ├── proyecto_errores.md
|
||||||
│ ├── proyecto_estructura.md
|
│ ├── proyecto_estructura.md
|
||||||
│ └── proyecto_memoria.md
|
│ ├── proyecto_memoria.md
|
||||||
|
│ ├── reglas.md
|
||||||
|
│ └── tareas.md
|
||||||
├── internal/
|
├── internal/
|
||||||
│ ├── auth/
|
│ ├── auth/
|
||||||
│ │ └── auth.go # Manejo de sesiones, WebControl y Google OAuth2
|
│ │ └── auth.go # Manejo de sesiones, WebControl y Google OAuth2
|
||||||
@@ -32,6 +34,11 @@ app_compartir/
|
|||||||
│ └── storage/
|
│ └── storage/
|
||||||
│ └── storage.go # Slugs aleatorios, sniffing MIME y guardado en disco
|
│ └── storage.go # Slugs aleatorios, sniffing MIME y guardado en disco
|
||||||
├── web/
|
├── web/
|
||||||
|
│ ├── static/ # Iconos, favicons y recursos estáticos de la marca
|
||||||
|
│ │ ├── favicon.ico # Icono multirresolución para navegadores
|
||||||
|
│ │ ├── favicon.svg # Icono vectorial escalable y moderno
|
||||||
|
│ │ ├── icon-192.png # Icono PWA / accesos directos móviles
|
||||||
|
│ │ └── icon-512.png # Icono alta resolución / splash screen
|
||||||
│ └── templates/
|
│ └── templates/
|
||||||
│ ├── base.html # Layout común con Tailwind CSS y FontAwesome
|
│ ├── base.html # Layout común con Tailwind CSS y FontAwesome
|
||||||
│ ├── expired.html # Mensaje informativo de archivo guest caducado (24h)
|
│ ├── expired.html # Mensaje informativo de archivo guest caducado (24h)
|
||||||
@@ -42,8 +49,10 @@ app_compartir/
|
|||||||
├── .env.example
|
├── .env.example
|
||||||
├── .gitignore
|
├── .gitignore
|
||||||
├── desplegar.sh # Script de compilación y despliegue (máquina dev)
|
├── desplegar.sh # Script de compilación y despliegue (máquina dev)
|
||||||
|
├── id_agente.md # Identidad oficial del agente en el ecosistema
|
||||||
├── init.sh # Script ejecutor local en el servidor
|
├── init.sh # Script ejecutor local en el servidor
|
||||||
├── servers.conf.example # Plantilla de credenciales SSH para despliegue
|
├── servers.conf.example # Plantilla de credenciales SSH para despliegue
|
||||||
|
├── siax-app-DROP_SIGMAC_APP.service # Definición de servicio systemd para SIAX Monitor
|
||||||
├── go.mod
|
├── go.mod
|
||||||
├── go.sum
|
├── go.sum
|
||||||
├── main.go # Punto de entrada y servidor HTTP Go
|
├── main.go # Punto de entrada y servidor HTTP Go
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
| 2026-10-13 | Generación de QR nativa en Go vía `/qr/{id}` | Se utiliza `github.com/skip2/go-qrcode` para servir el QR directamente en formato PNG (256x256), garantizando compatibilidad total con cualquier cámara de smartphone sin depender de scripts cliente pesados. |
|
| 2026-10-13 | Generación de QR nativa en Go vía `/qr/{id}` | Se utiliza `github.com/skip2/go-qrcode` para servir el QR directamente en formato PNG (256x256), garantizando compatibilidad total con cualquier cámara de smartphone sin depender de scripts cliente pesados. |
|
||||||
| 2026-10-13 | Soporte doble de login: WebControl corporativo + Google OAuth | Brinda flexibilidad tanto para colaboradores del ecosistema Telcotronics (vía `api.siax-system.net`) como para usuarios externos con Google. |
|
| 2026-10-13 | Soporte doble de login: WebControl corporativo + Google OAuth | Brinda flexibilidad tanto para colaboradores del ecosistema Telcotronics (vía `api.siax-system.net`) como para usuarios externos con Google. |
|
||||||
| 2026-10-14 | Adopción de la marca y dominio `drop.sigmac.app` | Nombre conciso, memorable y alineado con los servicios y subdominios del ecosistema SIGMA (sigmac.app). |
|
| 2026-10-14 | Adopción de la marca y dominio `drop.sigmac.app` | Nombre conciso, memorable y alineado con los servicios y subdominios del ecosistema SIGMA (sigmac.app). |
|
||||||
|
| 2026-10-17 | Registro oficial en MCP Infraestructura (ID 34) e identidad de agente | Se vinculó el proyecto al inventario central MariaDB vía API REST (:3007), asignando ID 34 bajo categoría application, y se creó `id_agente.md` según el estándar de identidad del ecosistema. |
|
||||||
|
| 2026-10-19 | Despliegue en producción en CT 132 con supervisión SIAX Monitor | Se desplegó en `server-webapps`. Registrado en SIAX Monitor (:8080) como `siax-app-DROP_SIGMAC_APP.service`, habilitando reinicio atómico vía API sin sudo. |
|
||||||
|
| 2026-10-20 | Reasignación de puerto a 2008 para liberar 2006 a audio_control | Se acordó con pablinux liberar el puerto 2006 para `audio_control` (correlativo a SoundWave en 2005) y trasladar `drop.sigmac.app` al puerto 2008 en CT 132 y MCP Infraestructura (ID 34). |
|
||||||
|
| 2026-10-20 | Activación de dominio público https://drop.sigmac.app | Pablinux configuró el proxy reverso Apache en CT 150 con SSL Let's Encrypt. Se optimizó `getBaseURL()` para forzar HTTPS en los enlaces y códigos QR generados cuando el Host es `drop.sigmac.app`. |
|
||||||
|
| 2026-10-20 | Identidad visual e iconos multi-formato (SVG, ICO, PNG) | Se diseñó un icono SVG escalable con flecha bidireccional y pulsos de transferencia en colores esmeralda/cian sobre contenedor squircle grafito oscuro. Se generaron versiones PNG (64, 192, 512 px) e ICO multi-resolución, enlazados en el navbar y servidos nativamente en `/favicon.ico` y `/favicon.svg`. |
|
||||||
|
| 2026-10-21 | Blindaje de seguridad y protección contra ejecución de scripts (RCE/XSS/MIME) | Se auditaron riesgos de subida de scripts (.php, .phtml, .sh, .html, .svg). Se neutralizó la detección de MIME impidiendo que scripts se sirvan como ejecutables o HTML activo (Stored XSS). Se implementó Sandbox CSP, nosniff, DENY de frames en /raw/, permisos estrictos 0640 sin +x en disco, .htaccess preventivo en uploads/, flag Secure en cookies HTTPS, límite de 5 MB en share-text y sanitización reforzada contra path traversal multiplataforma. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -27,7 +33,9 @@ Versión funcional completa y validada:
|
|||||||
- Visor universal adaptativo con vista previa multimedia, lector de PDF y código con botón de copia.
|
- Visor universal adaptativo con vista previa multimedia, lector de PDF y código con botón de copia.
|
||||||
- Purga automática cada 10 minutos para archivos guest > 24h (test automatizado superado en `main_test.go`).
|
- Purga automática cada 10 minutos para archivos guest > 24h (test automatizado superado en `main_test.go`).
|
||||||
- Cuentas registradas con almacenamiento permanente y panel `/mis-archivos`.
|
- Cuentas registradas con almacenamiento permanente y panel `/mis-archivos`.
|
||||||
- Documentación e integración de infraestructura sincronizada con el MCP y global `infraestructura.md`.
|
- Registrado oficialmente en el MCP de Infraestructura (ID 34) y sincronizado con espejo local `.agente/infraestructura.md`.
|
||||||
|
- Desplegado y ejecutándose activamente en producción (CT 132 puerto 2008) supervisado por SIAX Monitor.
|
||||||
|
- Enrutamiento público operativo al 100% en `https://drop.sigmac.app` con SSL.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -40,6 +48,14 @@ Versión funcional completa y validada:
|
|||||||
- [x] Implementar auth con WebControl (`api.siax-system.net`) y Google OAuth2
|
- [x] Implementar auth con WebControl (`api.siax-system.net`) y Google OAuth2
|
||||||
- [x] Implementar panel "Mis Archivos" para usuarios autenticados
|
- [x] Implementar panel "Mis Archivos" para usuarios autenticados
|
||||||
- [x] Sincronizar sección de infraestructura en global e internal para MCP
|
- [x] Sincronizar sección de infraestructura en global e internal para MCP
|
||||||
|
- [x] Crear scripts de despliegue estándar (`desplegar.sh`, `init.sh`, `servers.conf.example`)
|
||||||
|
- [x] Integrar servicio systemd para SIAX Monitor (`siax-app-DROP_SIGMAC_APP.service`)
|
||||||
|
- [x] Soporte para proxy reverso dinámico Apache (`X-Forwarded-Proto`, `Host`) en puerto 2008
|
||||||
|
- [x] Registrar proyecto en MCP de Infraestructura (ID 34) y crear `id_agente.md` oficial
|
||||||
|
- [x] Desplegar en producción en `server-webapps` (CT 132, :2008) y registrar en SIAX Monitor (:8080)
|
||||||
|
- [x] Crear icono SVG/PNG/ICO e integrarlo en la cabecera, navbar y servidor HTTP en producción
|
||||||
|
- [x] Auditar y blindar seguridad contra RCE, Stored XSS, MIME sniffing, Path Traversal y DoS en subidas y raw
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# reglas.md — Reglas operativas y técnicas de app_compartir (drop.sigmac.app)
|
||||||
|
|
||||||
|
> Normas inviolables que todo agente o desarrollador debe respetar al interactuar con este proyecto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Identidad del Agente
|
||||||
|
- Mantener siempre el archivo [`id_agente.md`](../id_agente.md) en la raíz del repositorio según la norma del ecosistema.
|
||||||
|
- **No inventar nombres ni identificadores de agente**: El nombre oficial y el ID numérico son potestad exclusiva de la confirmación emitida por `sigma-robot` y autorizada por `pablinux`.
|
||||||
|
- Al inicio de cada sesión ("carga proyecto"), leer `id_agente.md` y `agents.md` para conocer el estado y credenciales.
|
||||||
|
- Nunca commitear tokens, contraseñas ni códigos secretos (`SIGMA_AGENTE_SOLICITUD_ID`, `SIGMA_AGENTE_CODIGO`, `SIGMA_AGENTE_TOKEN`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Infraestructura y MCP
|
||||||
|
- **Prohibido asumir o inventar IPs, puertos o servidores**:
|
||||||
|
- Servidor de producción asignado: `server-webapps` CT 132 (`192.168.10.160`).
|
||||||
|
- Puerto de producción interno: `2008` (puerto local de desarrollo: `8085`).
|
||||||
|
- Dominio público: `https://drop.sigmac.app` (Reverse Proxy Apache con SSL/TLS Let's Encrypt).
|
||||||
|
- Servicio systemd: `siax-app-DROP_SIGMAC_APP.service` (gestionado y supervisado por SIAX Monitor en el puerto `8080`).
|
||||||
|
- Base de datos: SQLite en modo WAL (`data/compartir.db`) sin dependencias de MySQL ni MongoDB.
|
||||||
|
- Registro central MCP: Ficha técnica registrada en MariaDB con **ID 34** (categoría `application`, tipo `web`).
|
||||||
|
- El archivo [`.agente/infraestructura.md`](infraestructura.md) es un espejo local de solo lectura del documento maestro `/home/pablinux/Projects/infraestructura.md`. Nunca modificarlo de forma independiente sin sincronizar con el maestro.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Reglas de Negocio y Ciclo de Vida de Archivos
|
||||||
|
- **Política de Retención Estricta**:
|
||||||
|
- **Invitados (Guest)**: Cero fricción (sin login requerido). Todo archivo, nota o código subido como invitado tiene una vida útil máxima e inmutable de **24 horas**. El worker en segundo plano (`cleaner.go`) purga periódicamente tanto el archivo físico en disco como su fila en SQLite.
|
||||||
|
- **Usuarios Registrados (WebControl / Google OAuth)**: Disponen de almacenamiento **permanente**. Sus archivos nunca expiran ni son tocados por el limpiador automático. Tienen gestión completa desde `/mis-archivos`.
|
||||||
|
- **Generación de Código QR**:
|
||||||
|
- Se genera de forma nativa en backend Go vía `github.com/skip2/go-qrcode` en endpoint `/qr/{id}` como imagen PNG. Cero dependencias pesadas de librerías JS cliente para la generación del código QR.
|
||||||
|
- **Visor Universal Embebido**:
|
||||||
|
- El visor `/v/{id}` debe renderizar vista previa directa según el MIME type (imágenes con modal zoom, reproductor HTML5 de video/audio, visualizador embebido de PDF, y código formateado con botón de copia con un clic).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Comandos de Contexto del Ecosistema
|
||||||
|
- **"carga proyecto"**: Leer `agents.md`, `id_agente.md` y la carpeta `.agente/` para absorber el contexto, reglas, objetivos y memoria técnica antes de ejecutar modificaciones.
|
||||||
|
- **"actualizar proyecto"**: Ejecutar tests automatizados (`go test -v ./...`), realizar commit estructurado de los cambios pendientes, actualizar `agents.md`, `README.md` y `.agente/proyecto_estructura.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Seguridad y Dependencias
|
||||||
|
- Compilar binarios de producción sin CGO (`CGO_ENABLED=0 GOOS=linux GOARCH=amd64`), utilizando la implementación pura en Go de SQLite (`modernc.org/sqlite`).
|
||||||
|
- Mantener las rutas de almacenamiento dentro de `DATA_DIR` y utilizar nombres generados criptográficamente (slugs no predecibles) para prevenir ataques de Path Traversal.
|
||||||
|
- Toda sesión web debe utilizar cookies seguras HTTP-only protegidas por `SESSION_SECRET`.
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# tareas.md — Control de tareas de app_compartir (drop.sigmac.app)
|
||||||
|
|
||||||
|
> Registro ágil de tareas completadas, en progreso y backlog priorizado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Tareas Completadas
|
||||||
|
|
||||||
|
- [x] **Base de Datos y Esquema:** SQLite DAL (`modernc.org/sqlite`) con tablas `shares`, `users`, `sessions` en modo WAL.
|
||||||
|
- [x] **Subida de Archivos y Texto:** Endpoints `/api/upload` (multipart hasta 500 MB) y `/api/share-text` (notas rápidas y código).
|
||||||
|
- [x] **Visor Universal Adaptativo (`/v/{id}`):** Vista previa nativa para imágenes con zoom, video HTML5, audio HTML5, lector de PDF embebido y visualizador de código con botón de copia.
|
||||||
|
- [x] **Generador de QR Nativo:** Endpoint `/qr/{id}` con salida PNG (256x256) generado en Go nativo vía `github.com/skip2/go-qrcode`.
|
||||||
|
- [x] **Worker de Purga Automática 24h:** Goroutine en segundo plano (`cleaner.go`) que elimina archivos físicos y filas de BD vencidas de usuarios Guest.
|
||||||
|
- [x] **Autenticación Dual:** Login corporativo con Telcotronics WebControl (`api.siax-system.net`) y Google OAuth2.
|
||||||
|
- [x] **Panel "Mis Archivos":** Interfaz `/mis-archivos` para usuarios autenticados con almacenamiento permanente, estadísticas y borrado manual.
|
||||||
|
- [x] **Scripts de Despliegue:** `desplegar.sh` (compilación Linux amd64, transferencia scp/rsync) e `init.sh` (ejecutor en servidor).
|
||||||
|
- [x] **Integración con SIAX Monitor:** Servicio systemd `siax-app-DROP_SIGMAC_APP.service` para supervisión en puerto 2008 / 8080.
|
||||||
|
- [x] **Soporte de Reverse Proxy:** Manejo dinámico de encabezados Apache (`X-Forwarded-Proto`, `Host`, `X-Forwarded-For`).
|
||||||
|
- [x] **Pruebas Automatizadas:** Suite de tests unitarios y de integración en `main_test.go` verificando la retención 24h vs permanencia.
|
||||||
|
- [x] **Registro en MCP de Infraestructura:** Registrado oficialmente con **ID 34** en MariaDB (`http://192.168.10.160:3007/api/services/34`).
|
||||||
|
- [x] **Espejo de Infraestructura:** Sincronizado `.agente/infraestructura.md` con `/home/pablinux/Projects/infraestructura.md`.
|
||||||
|
- [x] **Identidad del Agente:** Creación de `id_agente.md` en la raíz del repositorio.
|
||||||
|
- [x] **Centro de Comunicaciones sigma-robot:** Autorizado formalmente por Pablinux como **Agente ID 86** (`Agente drop.sigmac.app`). Token definitivo resguardado en `.env` (modo 600).
|
||||||
|
- [x] **Despliegue a Producción (CT 132 server-webapps):** Desplegado y ejecutándose activamente como `siax-app-DROP_SIGMAC_APP.service` supervisado por SIAX Monitor (`:8080`), escuchando en puerto interno `2008` (dejando el 2006 reservado para `audio_control`). Subida atómica y reinicio automático operativo.
|
||||||
|
- [x] **Proxy Reverso Apache (SSL port 443 en CT 150):** Configurado por Pablinux y verificado operando al 100% en `https://drop.sigmac.app` (redirección 301 de HTTP a HTTPS, terminación SSL y proxy a `:2008`). Detección inteligente de HTTPS en enlaces generados y códigos QR.
|
||||||
|
- [x] **Identidad Visual e Iconos del Proyecto:** Creación de conjunto completo de iconos SVG vectoriales y ráster (SVG, ICO multi-resolución, PNG 64/192/512 px) con temática visual de transferencia (verde esmeralda / cian sobre grafito). Integrados en `web/templates/base.html` (meta tags, favicon, icono de marca en navbar) y expuestos vía `/favicon.ico`, `/favicon.svg` y `/static/*`. Desplegado en producción.
|
||||||
|
- [x] **Auditoría y Blindaje de Seguridad en Subidas (RCE / Stored XSS / MIME Sniffing):**
|
||||||
|
- Neutralización de scripts `.php`, `.phtml`, `.phar`, `.sh`, `.cgi` y archivos `.html` para que nunca se sirvan como ejecutables o código HTML activo.
|
||||||
|
- Implementación de cabeceras de Sandbox (`Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox`, `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`) en `/raw/{id}`.
|
||||||
|
- Middleware global de cabeceras de seguridad (`nosniff`, `SAMEORIGIN`, `strict-origin-when-cross-origin`, `Permissions-Policy`).
|
||||||
|
- Protección en disco con permisos `0640` (sin bit `+x`) y archivo preventivo `.htaccess` en `uploads/` (`Require all denied` para scripts).
|
||||||
|
- Sanitización anti-path traversal multiplataforma (eliminando `\` y secuencias `..`).
|
||||||
|
- Protección de cookies de sesión con flag `Secure: true` bajo HTTPS.
|
||||||
|
- Límite de 5 MB en endpoint `/api/share-text` contra DoS por memoria.
|
||||||
|
- Suite de pruebas de seguridad automatizada en `main_test.go` (`TestSecurityProtections`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 En Progreso / Inmediatas
|
||||||
|
|
||||||
|
- [ ] **Creación del Repositorio Git Remoto:**
|
||||||
|
- Crear repo en `git.telcotronics.net/pablinux/app_compartir.git`.
|
||||||
|
- Configurar `git remote add origin ...` y realizar primer push.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Backlog Priorizado
|
||||||
|
|
||||||
|
### Fase 2: Experiencia y Utilidades
|
||||||
|
- [ ] **Descarga Multi-archivo Comprimida (.zip):**
|
||||||
|
- Empaquetar múltiples archivos en un único archivo zip descargable al vuelo.
|
||||||
|
- [ ] **Protección por PIN / Contraseña opcional:**
|
||||||
|
- Permitir a quien comparte proteger el visor con una clave de 4-6 dígitos.
|
||||||
|
- [ ] **Modo de un solo uso (Burn after reading):**
|
||||||
|
- Auto-destrucción inmediata del archivo tras la primera descarga o visualización.
|
||||||
|
|
||||||
|
### Fase 3: Integraciones del Ecosistema
|
||||||
|
- [ ] **Notificaciones a sigma-robot:**
|
||||||
|
- Enviar eventos al bot o a Pablinux cuando se descargue un archivo relevante.
|
||||||
|
- [ ] **Widget publicitario app_marketing:**
|
||||||
|
- Integrar widget ligero de `ads.sigmac.app` opcional en la pantalla de descarga de invitados.
|
||||||
@@ -157,7 +157,16 @@ app_compartir/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏢 Ecosistema Telcotronics
|
## 🏢 Ecosistema Telcotronics e Infraestructura
|
||||||
|
|
||||||
Este proyecto forma parte del ecosistema Telcotronics y se encuentra registrado en la infraestructura compartida del MCP (`mcp.telcotronics.net` / `infraestructura.md`).
|
Este proyecto forma parte oficial del **Ecosistema Telcotronics** y se encuentra integrado con las siguientes especificaciones:
|
||||||
Para más detalles técnicos y reglas del agente, consultar [`agents.md`](agents.md).
|
|
||||||
|
| Entorno / Servicio | Detalle de Conexión |
|
||||||
|
|---|---|
|
||||||
|
| **Registro Central MCP** | Registrado en MariaDB como **Servicio ID 34** (Categoría: `application`) |
|
||||||
|
| **Centro de Agentes (sigma-robot)** | **Agente ID 86** (`Agente drop.sigmac.app`) activo y autorizado vía API REST |
|
||||||
|
| **Servidor de Producción** | `server-webapps` CT 132 (`192.168.10.160`), puerto interno `2008` |
|
||||||
|
| **Dominio y Proxy Apache** | `https://drop.sigmac.app` con terminación SSL/TLS Let's Encrypt (✅ Operativo) |
|
||||||
|
| **Supervisión de Procesos** | Servicio `siax-app-DROP_SIGMAC_APP.service` en [SIAX Monitor](http://192.168.10.160:8080) |
|
||||||
|
| **Identidad y Normas** | Consultar [`id_agente.md`](id_agente.md), [`agents.md`](agents.md) y [`.agente/reglas.md`](.agente/reglas.md) |
|
||||||
|
| **Espejo de Infraestructura** | Sincronizado en [`.agente/infraestructura.md`](.agente/infraestructura.md) desde `/home/pablinux/Projects/infraestructura.md` |
|
||||||
|
|||||||
@@ -74,10 +74,11 @@ Goroutine periódica (ticker) que consulta items donde `is_guest = true AND expi
|
|||||||
| Parámetro | Valor |
|
| Parámetro | Valor |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Dominio público | `drop.sigmac.app` |
|
| Dominio público | `drop.sigmac.app` |
|
||||||
| Puerto interno | `2006` (producción) / `8085` (local) |
|
| Puerto interno | `2008` (producción) / `8085` (local) |
|
||||||
| Servicio systemd | `app-compartir.service` |
|
| Servicio systemd | `siax-app-DROP_SIGMAC_APP.service` (gestionado por SIAX Monitor) |
|
||||||
| BD principal | SQLite local (`data/compartir.db`) |
|
| BD principal | SQLite local (`data/compartir.db`) |
|
||||||
| MCP ecosistema | `mcp.telcotronics.net` 🔧 en mantenimiento |
|
| MCP ID | `34` (registrado en `http://192.168.10.160:3007/api/services` / SSE `:3006`) |
|
||||||
|
| Centro de agentes (sigma-robot) | Activo como **Agente ID 86** (autorizado por pablinux vía API REST) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -92,6 +93,9 @@ Goroutine periódica (ticker) que consulta items donde `is_guest = true AND expi
|
|||||||
| `GOOGLE_CLIENT_ID` | Client ID de Google OAuth | (opcional) |
|
| `GOOGLE_CLIENT_ID` | Client ID de Google OAuth | (opcional) |
|
||||||
| `GOOGLE_CLIENT_SECRET` | Client Secret de Google OAuth | (opcional) |
|
| `GOOGLE_CLIENT_SECRET` | Client Secret de Google OAuth | (opcional) |
|
||||||
| `WEBCONTROL_API_URL` | URL de autenticación de WebControl | `https://api.siax-system.net/api/auth/login` |
|
| `WEBCONTROL_API_URL` | URL de autenticación de WebControl | `https://api.siax-system.net/api/auth/login` |
|
||||||
|
| `SIGMA_AGENTE_SOLICITUD_ID` | ID de solicitud en sigma-robot (nunca commitear) | (local `.env`) |
|
||||||
|
| `SIGMA_AGENTE_CODIGO` | Código secreto en sigma-robot (nunca commitear) | (local `.env`) |
|
||||||
|
| `SIGMA_AGENTE_TOKEN` | Token bearer permanente tras autorización (nunca commitear) | (local `.env`) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -113,6 +117,7 @@ go run main.go
|
|||||||
app_compartir/
|
app_compartir/
|
||||||
├── README.md
|
├── README.md
|
||||||
├── agents.md # ← LEER PRIMERO
|
├── agents.md # ← LEER PRIMERO
|
||||||
|
├── id_agente.md # Identidad del agente en el ecosistema
|
||||||
├── go.mod
|
├── go.mod
|
||||||
├── main.go
|
├── main.go
|
||||||
└── .agente/
|
└── .agente/
|
||||||
@@ -121,6 +126,8 @@ app_compartir/
|
|||||||
├── proyecto_memoria.md # Decisiones técnicas e historial
|
├── proyecto_memoria.md # Decisiones técnicas e historial
|
||||||
├── proyecto_errores.md # Registro de errores
|
├── proyecto_errores.md # Registro de errores
|
||||||
├── objetivos.md # Qué debe cumplirse (sin fecha)
|
├── objetivos.md # Qué debe cumplirse (sin fecha)
|
||||||
|
├── reglas.md # Normas operativas y técnicas inviolables
|
||||||
|
├── tareas.md # Control ágil de tareas y backlog
|
||||||
└── ideas.md # Posibilidades sin compromiso
|
└── ideas.md # Posibilidades sin compromiso
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+18
-7
@@ -53,7 +53,7 @@ compilar() {
|
|||||||
preparar_remoto() {
|
preparar_remoto() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "📂 Preparando estructura de directorios en $SERVIDOR:$REMOTE_DIR..."
|
echo "📂 Preparando estructura de directorios en $SERVIDOR:$REMOTE_DIR..."
|
||||||
remote "mkdir -p '$REMOTE_DIR/web/templates' '$REMOTE_DIR/data/uploads'"
|
remote "mkdir -p '$REMOTE_DIR/web/templates' '$REMOTE_DIR/web/static' '$REMOTE_DIR/data/uploads'"
|
||||||
echo "✅ Directorios listos"
|
echo "✅ Directorios listos"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,10 +63,13 @@ subir() {
|
|||||||
preparar_remoto
|
preparar_remoto
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🚀 Subiendo binario, plantillas y scripts al servidor..."
|
echo "🚀 Subiendo binario (atómico), plantillas y scripts al servidor..."
|
||||||
copy "$SCRIPT_DIR/app_compartir" "$SCRIPT_DIR/init.sh" "$SCRIPT_DIR/.env.example" "$SERVIDOR:$REMOTE_DIR/"
|
copy "$SCRIPT_DIR/app_compartir" "$SERVIDOR:$REMOTE_DIR/app_compartir.tmp"
|
||||||
|
remote "mv -f '$REMOTE_DIR/app_compartir.tmp' '$REMOTE_DIR/app_compartir' && chmod 0755 '$REMOTE_DIR/app_compartir'"
|
||||||
|
copy "$SCRIPT_DIR/init.sh" "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/siax-app-DROP_SIGMAC_APP.service" "$SERVIDOR:$REMOTE_DIR/"
|
||||||
copy "$SCRIPT_DIR/web/templates/"*.html "$SERVIDOR:$REMOTE_DIR/web/templates/"
|
copy "$SCRIPT_DIR/web/templates/"*.html "$SERVIDOR:$REMOTE_DIR/web/templates/"
|
||||||
remote "chmod 0755 '$REMOTE_DIR/app_compartir' '$REMOTE_DIR/init.sh'"
|
copy "$SCRIPT_DIR/web/static/"* "$SERVIDOR:$REMOTE_DIR/web/static/"
|
||||||
|
remote "chmod 0755 '$REMOTE_DIR/init.sh'"
|
||||||
echo "✅ Archivos sincronizados en $REMOTE_DIR"
|
echo "✅ Archivos sincronizados en $REMOTE_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +88,7 @@ todo() {
|
|||||||
echo "=================================================="
|
echo "=================================================="
|
||||||
echo "🎉 ¡Despliegue completado con éxito!"
|
echo "🎉 ¡Despliegue completado con éxito!"
|
||||||
echo "🌐 Dominio público: https://drop.sigmac.app"
|
echo "🌐 Dominio público: https://drop.sigmac.app"
|
||||||
echo "🎯 Destino interno: http://$HOST:2006"
|
echo "🎯 Destino interno: http://$HOST:2008"
|
||||||
echo "=================================================="
|
echo "=================================================="
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,12 +97,20 @@ estado() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo "📊 Estado del servidor en $SERVIDOR:"
|
echo "📊 Estado del servidor en $SERVIDOR:"
|
||||||
remote "
|
remote "
|
||||||
if tmux has-session -t drop-sigmac-app 2>/dev/null; then
|
if curl -sI http://localhost:8080/api/apps 2>/dev/null | grep -q '200 OK'; then
|
||||||
|
echo '==> Estado en SIAX Monitor (:8080):'
|
||||||
|
curl -s http://localhost:8080/api/apps | grep -o '{\"name\":\"DROP_SIGMAC_APP\"[^}]*}' || true
|
||||||
|
echo ''
|
||||||
|
fi
|
||||||
|
if systemctl is-active --quiet siax-app-DROP_SIGMAC_APP.service 2>/dev/null; then
|
||||||
|
echo '==> Servicio systemd siax-app-DROP_SIGMAC_APP (SIAX Monitor) activo:'
|
||||||
|
journalctl -u siax-app-DROP_SIGMAC_APP.service -n 12 --no-pager -q
|
||||||
|
elif tmux has-session -t drop-sigmac-app 2>/dev/null; then
|
||||||
echo '==> Sesión tmux drop-sigmac-app activa:'
|
echo '==> Sesión tmux drop-sigmac-app activa:'
|
||||||
tmux capture-pane -pt drop-sigmac-app | tail -n 25
|
tmux capture-pane -pt drop-sigmac-app | tail -n 25
|
||||||
elif systemctl is-active --quiet drop-sigmac-app.service 2>/dev/null; then
|
elif systemctl is-active --quiet drop-sigmac-app.service 2>/dev/null; then
|
||||||
echo '==> Servicio systemd drop-sigmac-app activo:'
|
echo '==> Servicio systemd drop-sigmac-app activo:'
|
||||||
systemctl status drop-sigmac-app.service --no-pager
|
journalctl -u drop-sigmac-app.service -n 12 --no-pager -q
|
||||||
else
|
else
|
||||||
echo '⚠️ No se detectó proceso activo en tmux ni systemd.'
|
echo '⚠️ No se detectó proceso activo en tmux ni systemd.'
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Identidad del agente
|
||||||
|
|
||||||
|
- Proyecto: app_compartir
|
||||||
|
- Nombre asignado en sigma-robot: Agente drop.sigmac.app
|
||||||
|
- ID de agente en sigma-robot: 86
|
||||||
|
- Estado: activo
|
||||||
|
- Autorizado por: pablinux
|
||||||
|
- Canal: API REST (`https://sigma-bot.telcotronics.com/ecosistema/v1/agentes`)
|
||||||
|
- Credenciales: Guardadas de forma segura en `.env` local (`SIGMA_AGENTE_TOKEN`); nunca en Git ni en mensajes.
|
||||||
@@ -13,8 +13,8 @@ if [[ ! -f "$SCRIPT_DIR/.env" ]]; then
|
|||||||
if [[ -f "$SCRIPT_DIR/.env.example" ]]; then
|
if [[ -f "$SCRIPT_DIR/.env.example" ]]; then
|
||||||
echo "==> Creando .env inicial desde .env.example..."
|
echo "==> Creando .env inicial desde .env.example..."
|
||||||
cp "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/.env"
|
cp "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/.env"
|
||||||
# Configurar puerto de producción 2006 y URL por defecto
|
# Configurar puerto de producción 2008 y URL por defecto
|
||||||
sed -i 's/^PORT=.*/PORT=2006/' "$SCRIPT_DIR/.env"
|
sed -i 's/^PORT=.*/PORT=2008/' "$SCRIPT_DIR/.env"
|
||||||
sed -i 's|^BASE_URL=.*|BASE_URL=https://drop.sigmac.app|' "$SCRIPT_DIR/.env"
|
sed -i 's|^BASE_URL=.*|BASE_URL=https://drop.sigmac.app|' "$SCRIPT_DIR/.env"
|
||||||
else
|
else
|
||||||
echo "⚠️ Advertencia: No se encontró .env ni .env.example."
|
echo "⚠️ Advertencia: No se encontró .env ni .env.example."
|
||||||
@@ -27,11 +27,31 @@ mkdir -p "$SCRIPT_DIR/data/uploads"
|
|||||||
# 3. Permisos de ejecución al binario
|
# 3. Permisos de ejecución al binario
|
||||||
chmod +x "$SCRIPT_DIR/app_compartir"
|
chmod +x "$SCRIPT_DIR/app_compartir"
|
||||||
|
|
||||||
# 4. Modo de arranque: Si existe servicio systemd, usarlo; de lo contrario, sesión tmux
|
# 4. Modo de arranque: SIAX Monitor API, systemd o tmux
|
||||||
if systemctl is-active --quiet drop-sigmac-app.service 2>/dev/null || [ -f /etc/systemd/system/drop-sigmac-app.service ]; then
|
if curl -sI http://localhost:8080/api/apps 2>/dev/null | grep -q "200 OK"; then
|
||||||
echo "==> Reiniciando servicio systemd 'drop-sigmac-app.service'..."
|
echo "==> Reiniciando DROP_SIGMAC_APP vía API de SIAX Monitor (:8080)..."
|
||||||
sudo systemctl restart drop-sigmac-app.service
|
RESP=$(curl -s -X POST http://localhost:8080/api/apps/DROP_SIGMAC_APP/restart)
|
||||||
sudo systemctl status drop-sigmac-app.service --no-pager
|
echo " Respuesta: $RESP"
|
||||||
|
if echo "$RESP" | grep -q '"success":true'; then
|
||||||
|
echo "✅ Servicio reiniciado exitosamente por SIAX Monitor."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVICE_NAME=""
|
||||||
|
if systemctl is-active --quiet siax-app-DROP_SIGMAC_APP.service 2>/dev/null || [ -f /etc/systemd/system/siax-app-DROP_SIGMAC_APP.service ]; then
|
||||||
|
SERVICE_NAME="siax-app-DROP_SIGMAC_APP.service"
|
||||||
|
elif systemctl is-active --quiet drop-sigmac-app.service 2>/dev/null || [ -f /etc/systemd/system/drop-sigmac-app.service ]; then
|
||||||
|
SERVICE_NAME="drop-sigmac-app.service"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$SERVICE_NAME" ]]; then
|
||||||
|
echo "==> Reiniciando servicio systemd '$SERVICE_NAME'..."
|
||||||
|
if sudo -n true 2>/dev/null; then
|
||||||
|
sudo systemctl restart "$SERVICE_NAME"
|
||||||
|
else
|
||||||
|
systemctl restart "$SERVICE_NAME" 2>/dev/null || true
|
||||||
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,21 @@ func (a *AuthService) GenerateToken(length int) string {
|
|||||||
return hex.EncodeToString(bytes)
|
return hex.EncodeToString(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AuthService) isSecureRequest(r *http.Request) bool {
|
||||||
|
if r.TLS != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.EqualFold(r.Header.Get("X-Forwarded-Proto"), "https") ||
|
||||||
|
strings.EqualFold(r.Header.Get("X-Forwarded-Ssl"), "on") ||
|
||||||
|
r.Header.Get("X-Forwarded-Port") == "443" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(a.cfg.BaseURL, "https://") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AuthService) GetOrCreateSession(w http.ResponseWriter, r *http.Request) *models.Session {
|
func (a *AuthService) GetOrCreateSession(w http.ResponseWriter, r *http.Request) *models.Session {
|
||||||
cookie, err := r.Cookie(CookieSessionName)
|
cookie, err := r.Cookie(CookieSessionName)
|
||||||
if err == nil && cookie.Value != "" {
|
if err == nil && cookie.Value != "" {
|
||||||
@@ -93,6 +108,7 @@ func (a *AuthService) GetOrCreateSession(w http.ResponseWriter, r *http.Request)
|
|||||||
Path: "/",
|
Path: "/",
|
||||||
Expires: expiresAt,
|
Expires: expiresAt,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
Secure: a.isSecureRequest(r),
|
||||||
SameSite: http.SameSiteLaxMode,
|
SameSite: http.SameSiteLaxMode,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -124,6 +140,7 @@ func (a *AuthService) AttachUserToSession(w http.ResponseWriter, r *http.Request
|
|||||||
Path: "/",
|
Path: "/",
|
||||||
Expires: expiresAt,
|
Expires: expiresAt,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
Secure: a.isSecureRequest(r),
|
||||||
SameSite: http.SameSiteLaxMode,
|
SameSite: http.SameSiteLaxMode,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -142,6 +159,8 @@ func (a *AuthService) Logout(w http.ResponseWriter, r *http.Request) {
|
|||||||
Expires: time.Unix(0, 0),
|
Expires: time.Unix(0, 0),
|
||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
Secure: a.isSecureRequest(r),
|
||||||
|
SameSite: http.SameSiteLaxMode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,10 @@ type PageData struct {
|
|||||||
|
|
||||||
func (h *Handlers) getBaseURL(r *http.Request) string {
|
func (h *Handlers) getBaseURL(r *http.Request) string {
|
||||||
proto := "http"
|
proto := "http"
|
||||||
if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" {
|
if r.TLS != nil ||
|
||||||
|
strings.EqualFold(r.Header.Get("X-Forwarded-Proto"), "https") ||
|
||||||
|
strings.EqualFold(r.Header.Get("X-Forwarded-Ssl"), "on") ||
|
||||||
|
r.Header.Get("X-Forwarded-Port") == "443" {
|
||||||
proto = "https"
|
proto = "https"
|
||||||
}
|
}
|
||||||
host := r.Host
|
host := r.Host
|
||||||
@@ -80,6 +83,9 @@ func (h *Handlers) getBaseURL(r *http.Request) string {
|
|||||||
host = r.Header.Get("X-Forwarded-Host")
|
host = r.Header.Get("X-Forwarded-Host")
|
||||||
}
|
}
|
||||||
if host != "" {
|
if host != "" {
|
||||||
|
if strings.HasPrefix(h.cfg.BaseURL, "https://") && strings.Contains(h.cfg.BaseURL, host) {
|
||||||
|
proto = "https"
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s://%s", proto, host)
|
return fmt.Sprintf("%s://%s", proto, host)
|
||||||
}
|
}
|
||||||
return h.cfg.BaseURL
|
return h.cfg.BaseURL
|
||||||
@@ -205,12 +211,15 @@ func (h *Handlers) ShareText(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Límite de 5 MB para evitar DoS por memoria
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 5<<20)
|
||||||
|
|
||||||
var req struct {
|
var req struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || strings.TrimSpace(req.Text) == "" {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || strings.TrimSpace(req.Text) == "" {
|
||||||
http.Error(w, "Contenido de texto requerido", http.StatusBadRequest)
|
http.Error(w, "Contenido de texto requerido o tamaño excesivo (máx 5 MB)", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,6 +325,17 @@ func (h *Handlers) View(w http.ResponseWriter, r *http.Request) {
|
|||||||
_ = h.db.IncrementViews(id)
|
_ = h.db.IncrementViews(id)
|
||||||
share.ViewsCount++
|
share.ViewsCount++
|
||||||
|
|
||||||
|
// Si es un archivo de texto/código y no tiene TextContent, leer hasta 512 KB para vista previa segura
|
||||||
|
isText := strings.HasPrefix(share.MimeType, "text/") || share.Type == "text"
|
||||||
|
if isText && share.TextContent == "" && share.StoredPath != "" {
|
||||||
|
fullPath := filepath.Join(h.cfg.UploadDir, share.StoredPath)
|
||||||
|
if fi, err := os.Stat(fullPath); err == nil && fi.Size() <= 512*1024 {
|
||||||
|
if content, err := os.ReadFile(fullPath); err == nil {
|
||||||
|
share.TextContent = string(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
session := h.auth.GetOrCreateSession(w, r)
|
session := h.auth.GetOrCreateSession(w, r)
|
||||||
data := PageData{
|
data := PageData{
|
||||||
Session: session,
|
Session: session,
|
||||||
@@ -325,7 +345,7 @@ func (h *Handlers) View(w http.ResponseWriter, r *http.Request) {
|
|||||||
IsVideo: strings.HasPrefix(share.MimeType, "video/"),
|
IsVideo: strings.HasPrefix(share.MimeType, "video/"),
|
||||||
IsAudio: strings.HasPrefix(share.MimeType, "audio/"),
|
IsAudio: strings.HasPrefix(share.MimeType, "audio/"),
|
||||||
IsPDF: share.MimeType == "application/pdf",
|
IsPDF: share.MimeType == "application/pdf",
|
||||||
IsText: strings.HasPrefix(share.MimeType, "text/") || share.Type == "text",
|
IsText: isText,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := h.templates["viewer.html"]
|
tmpl := h.templates["viewer.html"]
|
||||||
@@ -345,6 +365,35 @@ func (h *Handlers) renderExpired(w http.ResponseWriter, r *http.Request) {
|
|||||||
_ = tmpl.ExecuteTemplate(w, "base.html", data)
|
_ = tmpl.ExecuteTemplate(w, "base.html", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSafeInlineMime(mimeType string) bool {
|
||||||
|
switch mimeType {
|
||||||
|
case "image/png", "image/jpeg", "image/gif", "image/webp", "image/bmp", "image/x-icon":
|
||||||
|
return true
|
||||||
|
case "audio/mpeg", "audio/wav", "audio/ogg", "audio/webm", "audio/aac", "audio/flac":
|
||||||
|
return true
|
||||||
|
case "video/mp4", "video/webm", "video/ogg":
|
||||||
|
return true
|
||||||
|
case "application/pdf":
|
||||||
|
return true
|
||||||
|
case "text/plain", "text/plain; charset=utf-8":
|
||||||
|
return true
|
||||||
|
case "image/svg+xml":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeHeaderFilename(name string) string {
|
||||||
|
cleaned := strings.ReplaceAll(name, "\r", "")
|
||||||
|
cleaned = strings.ReplaceAll(cleaned, "\n", "")
|
||||||
|
cleaned = strings.ReplaceAll(cleaned, "\"", "_")
|
||||||
|
if strings.TrimSpace(cleaned) == "" {
|
||||||
|
cleaned = "archivo.bin"
|
||||||
|
}
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handlers) Download(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) Download(w http.ResponseWriter, r *http.Request) {
|
||||||
id := strings.TrimPrefix(r.URL.Path, "/d/")
|
id := strings.TrimPrefix(r.URL.Path, "/d/")
|
||||||
if id == "" {
|
if id == "" {
|
||||||
@@ -365,9 +414,12 @@ func (h *Handlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
_ = h.db.IncrementDownloads(id)
|
_ = h.db.IncrementDownloads(id)
|
||||||
|
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
safeTitle := sanitizeHeaderFilename(share.Title)
|
||||||
|
|
||||||
if share.Type == "text" {
|
if share.Type == "text" {
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", share.Title+".txt"))
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", safeTitle+".txt"))
|
||||||
_, _ = w.Write([]byte(share.TextContent))
|
_, _ = w.Write([]byte(share.TextContent))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -378,7 +430,7 @@ func (h *Handlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", share.Title))
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", safeTitle))
|
||||||
http.ServeFile(w, r, fullPath)
|
http.ServeFile(w, r, fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,8 +452,14 @@ func (h *Handlers) Raw(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cabeceras de seguridad estrictas contra XSS y MIME-sniffing
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
|
||||||
|
|
||||||
if share.Type == "text" {
|
if share.Type == "text" {
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
w.Header().Set("Content-Disposition", "inline")
|
||||||
_, _ = w.Write([]byte(share.TextContent))
|
_, _ = w.Write([]byte(share.TextContent))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -412,8 +470,21 @@ func (h *Handlers) Raw(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", share.MimeType)
|
// Neutralizar cualquier intento de servir HTML, PHP o scripts ejecutables inline
|
||||||
|
mimeType := share.MimeType
|
||||||
|
if strings.HasPrefix(mimeType, "text/html") || strings.Contains(mimeType, "php") || strings.Contains(mimeType, "javascript") {
|
||||||
|
mimeType = "text/plain; charset=utf-8"
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", mimeType)
|
||||||
|
|
||||||
|
if isSafeInlineMime(mimeType) {
|
||||||
w.Header().Set("Content-Disposition", "inline")
|
w.Header().Set("Content-Disposition", "inline")
|
||||||
|
} else {
|
||||||
|
// Archivos ejecutables, binarios o desconocidos se fuerzan a descarga
|
||||||
|
safeTitle := sanitizeHeaderFilename(share.Title)
|
||||||
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", safeTitle))
|
||||||
|
}
|
||||||
|
|
||||||
http.ServeFile(w, r, fullPath)
|
http.ServeFile(w, r, fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+63
-28
@@ -28,8 +28,19 @@ func GenerateSlug(length int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SanitizeFilename(name string) string {
|
func SanitizeFilename(name string) string {
|
||||||
base := filepath.Base(name)
|
// Normalizar separadores de ruta (incluyendo Windows \)
|
||||||
|
normalized := strings.ReplaceAll(name, "\\", "/")
|
||||||
|
base := filepath.Base(normalized)
|
||||||
|
|
||||||
|
// Eliminar secuencias '..' para evitar cualquier ambigüedad
|
||||||
|
for strings.Contains(base, "..") {
|
||||||
|
base = strings.ReplaceAll(base, "..", ".")
|
||||||
|
}
|
||||||
|
|
||||||
sanitized := safeFilenameRegex.ReplaceAllString(base, "_")
|
sanitized := safeFilenameRegex.ReplaceAllString(base, "_")
|
||||||
|
// Evitar archivos ocultos o nombres que empiecen por punto
|
||||||
|
sanitized = strings.TrimLeft(sanitized, "._-")
|
||||||
|
|
||||||
if len(sanitized) > 100 {
|
if len(sanitized) > 100 {
|
||||||
ext := filepath.Ext(sanitized)
|
ext := filepath.Ext(sanitized)
|
||||||
sanitized = sanitized[:100-len(ext)] + ext
|
sanitized = sanitized[:100-len(ext)] + ext
|
||||||
@@ -42,25 +53,19 @@ func SanitizeFilename(name string) string {
|
|||||||
|
|
||||||
func DetectMimeType(filename string, reader io.ReadSeeker) string {
|
func DetectMimeType(filename string, reader io.ReadSeeker) string {
|
||||||
ext := strings.ToLower(filepath.Ext(filename))
|
ext := strings.ToLower(filepath.Ext(filename))
|
||||||
if m := mime.TypeByExtension(ext); m != "" {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sniff first 512 bytes
|
// 1. Extensiones peligrosas de scripts, código y plantillas: NUNCA ejecutables como text/html o php
|
||||||
if reader != nil {
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
n, err := reader.Read(buf)
|
|
||||||
_, _ = reader.Seek(0, io.SeekStart)
|
|
||||||
if err == nil && n > 0 {
|
|
||||||
detected := http.DetectContentType(buf[:n])
|
|
||||||
if detected != "application/octet-stream" {
|
|
||||||
return detected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Known fallback extensions
|
|
||||||
switch ext {
|
switch ext {
|
||||||
|
case ".html", ".htm", ".shtml", ".xhtml", ".hta":
|
||||||
|
return "text/plain; charset=utf-8"
|
||||||
|
case ".php", ".phtml", ".php3", ".php4", ".php5", ".pht", ".phar", ".inc", ".cgi", ".pl", ".py", ".sh", ".bash", ".sql":
|
||||||
|
return "text/plain; charset=utf-8"
|
||||||
|
case ".js", ".mjs", ".ts", ".jsx", ".tsx":
|
||||||
|
return "text/plain; charset=utf-8"
|
||||||
|
case ".txt", ".log", ".env", ".ini", ".conf", ".yaml", ".yml", ".md", ".go", ".rs", ".java", ".c", ".cpp", ".h", ".css":
|
||||||
|
return "text/plain; charset=utf-8"
|
||||||
|
case ".json":
|
||||||
|
return "application/json"
|
||||||
case ".pdf":
|
case ".pdf":
|
||||||
return "application/pdf"
|
return "application/pdf"
|
||||||
case ".mp4":
|
case ".mp4":
|
||||||
@@ -83,29 +88,59 @@ func DetectMimeType(filename string, reader io.ReadSeeker) string {
|
|||||||
return "image/webp"
|
return "image/webp"
|
||||||
case ".svg":
|
case ".svg":
|
||||||
return "image/svg+xml"
|
return "image/svg+xml"
|
||||||
case ".txt", ".log", ".env":
|
}
|
||||||
|
|
||||||
|
// 2. Consulta MIME del sistema para otras extensiones
|
||||||
|
if m := mime.TypeByExtension(ext); m != "" {
|
||||||
|
// Doble verificación: neutralizar si el sistema operativo clasifica como HTML o PHP
|
||||||
|
if strings.HasPrefix(m, "text/html") || strings.Contains(m, "php") || strings.Contains(m, "javascript") {
|
||||||
return "text/plain; charset=utf-8"
|
return "text/plain; charset=utf-8"
|
||||||
case ".json":
|
}
|
||||||
return "application/json"
|
return m
|
||||||
case ".js":
|
}
|
||||||
return "application/javascript"
|
|
||||||
case ".go", ".py", ".rs", ".java", ".c", ".cpp", ".sh", ".sql", ".html", ".css", ".md":
|
// 3. Sniff de primeros 512 bytes
|
||||||
|
if reader != nil {
|
||||||
|
buf := make([]byte, 512)
|
||||||
|
n, err := reader.Read(buf)
|
||||||
|
_, _ = reader.Seek(0, io.SeekStart)
|
||||||
|
if err == nil && n > 0 {
|
||||||
|
detected := http.DetectContentType(buf[:n])
|
||||||
|
if detected != "application/octet-stream" {
|
||||||
|
if strings.HasPrefix(detected, "text/html") {
|
||||||
return "text/plain; charset=utf-8"
|
return "text/plain; charset=utf-8"
|
||||||
default:
|
}
|
||||||
return "application/octet-stream"
|
return detected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "application/octet-stream"
|
||||||
|
}
|
||||||
|
|
||||||
func SaveFile(uploadDir, slug, originalFilename string, src io.Reader) (string, int64, error) {
|
func SaveFile(uploadDir, slug, originalFilename string, src io.Reader) (string, int64, error) {
|
||||||
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
if err := os.MkdirAll(uploadDir, 0750); err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crear archivo preventivo .htaccess para bloquear cualquier motor de script si Apache apuntara aquí
|
||||||
|
htaccessPath := filepath.Join(uploadDir, ".htaccess")
|
||||||
|
if _, err := os.Stat(htaccessPath); os.IsNotExist(err) {
|
||||||
|
htaccessContent := `# Seguridad drop.sigmac.app: Desactivar ejecución de scripts
|
||||||
|
Options -ExecCGI -Indexes
|
||||||
|
<FilesMatch "\.(php|phtml|phar|pl|py|cgi|sh|bash)$">
|
||||||
|
Require all denied
|
||||||
|
</FilesMatch>
|
||||||
|
`
|
||||||
|
_ = os.WriteFile(htaccessPath, []byte(htaccessContent), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
safeName := SanitizeFilename(originalFilename)
|
safeName := SanitizeFilename(originalFilename)
|
||||||
storedFilename := slug + "_" + safeName
|
storedFilename := slug + "_" + safeName
|
||||||
destPath := filepath.Join(uploadDir, storedFilename)
|
destPath := filepath.Join(uploadDir, storedFilename)
|
||||||
|
|
||||||
dest, err := os.Create(destPath)
|
// Crear con permisos 0640 (sin flag +x ejecutable)
|
||||||
|
dest, err := os.OpenFile(destPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0640)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,17 @@ func main() {
|
|||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
// Archivos estáticos e iconos
|
||||||
|
fs := http.FileServer(http.Dir("web/static"))
|
||||||
|
mux.Handle("GET /static/", http.StripPrefix("/static/", fs))
|
||||||
|
mux.HandleFunc("GET /favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFile(w, r, "web/static/favicon.ico")
|
||||||
|
})
|
||||||
|
mux.HandleFunc("GET /favicon.svg", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "image/svg+xml")
|
||||||
|
http.ServeFile(w, r, "web/static/favicon.svg")
|
||||||
|
})
|
||||||
|
|
||||||
// Rutas Web
|
// Rutas Web
|
||||||
mux.HandleFunc("GET /", h.Index)
|
mux.HandleFunc("GET /", h.Index)
|
||||||
mux.HandleFunc("GET /v/{id}", h.View)
|
mux.HandleFunc("GET /v/{id}", h.View)
|
||||||
@@ -80,7 +91,7 @@ func main() {
|
|||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: ":" + cfg.Port,
|
Addr: ":" + cfg.Port,
|
||||||
Handler: mux,
|
Handler: securityHeadersMiddleware(mux),
|
||||||
ReadTimeout: 120 * time.Second,
|
ReadTimeout: 120 * time.Second,
|
||||||
WriteTimeout: 120 * time.Second,
|
WriteTimeout: 120 * time.Second,
|
||||||
IdleTimeout: 120 * time.Second,
|
IdleTimeout: 120 * time.Second,
|
||||||
@@ -109,3 +120,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.Println("👋 Servidor apagado correctamente.")
|
log.Println("👋 Servidor apagado correctamente.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// securityHeadersMiddleware inyecta cabeceras de protección defensiva en todas las respuestas
|
||||||
|
func securityHeadersMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
|
||||||
|
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||||||
|
w.Header().Set("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"app_compartir/internal/cleaner"
|
"app_compartir/internal/cleaner"
|
||||||
"app_compartir/internal/db"
|
"app_compartir/internal/db"
|
||||||
"app_compartir/internal/models"
|
"app_compartir/internal/models"
|
||||||
|
"app_compartir/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGuestExpirationAndRegisteredPermanence(t *testing.T) {
|
func TestGuestExpirationAndRegisteredPermanence(t *testing.T) {
|
||||||
@@ -132,3 +134,78 @@ func TestGuestExpirationAndRegisteredPermanence(t *testing.T) {
|
|||||||
|
|
||||||
t.Log("✅ Test completado con éxito: Solo los datos de invitado caducan en 24h; los de usuarios registrados son permanentes.")
|
t.Log("✅ Test completado con éxito: Solo los datos de invitado caducan en 24h; los de usuarios registrados son permanentes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSecurityProtections(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "test_security_*")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creando tmpDir: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
uploadDir := filepath.Join(tmpDir, "uploads")
|
||||||
|
|
||||||
|
// 1. Probar Sanitización de Nombres (Path Traversal)
|
||||||
|
traversalTests := []struct {
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"../../../../etc/passwd", "etc_passwd"},
|
||||||
|
{"..\\..\\windows\\system32\\cmd.exe", "cmd.exe"},
|
||||||
|
{"test/../../shell.php", "shell.php"},
|
||||||
|
{"../../../.htaccess", "_.htaccess"},
|
||||||
|
}
|
||||||
|
for _, tc := range traversalTests {
|
||||||
|
sanitized := storage.SanitizeFilename(tc.input)
|
||||||
|
if strings.Contains(sanitized, "/") || strings.Contains(sanitized, "\\") || strings.Contains(sanitized, "..") {
|
||||||
|
t.Fatalf("Fallo en sanitización de path traversal para %s: resultado=%s", tc.input, sanitized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Probar Detección de MIME Types Peligrosos (Neutralización de XSS y Scripts)
|
||||||
|
dangerousMimes := []struct {
|
||||||
|
filename string
|
||||||
|
forbidden string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"shell.php", "application/x-httpd-php", "text/plain; charset=utf-8"},
|
||||||
|
{"exploit.phtml", "text/html", "text/plain; charset=utf-8"},
|
||||||
|
{"malicious.html", "text/html", "text/plain; charset=utf-8"},
|
||||||
|
{"test.xhtml", "text/html", "text/plain; charset=utf-8"},
|
||||||
|
{"script.sh", "application/x-sh", "text/plain; charset=utf-8"},
|
||||||
|
}
|
||||||
|
for _, tc := range dangerousMimes {
|
||||||
|
m := storage.DetectMimeType(tc.filename, nil)
|
||||||
|
if m == tc.forbidden || strings.HasPrefix(m, "text/html") {
|
||||||
|
t.Fatalf("MIME peligroso no neutralizado para %s: obtenido %s (prohibido %s)", tc.filename, m, tc.forbidden)
|
||||||
|
}
|
||||||
|
if m != tc.expected {
|
||||||
|
t.Fatalf("MIME esperado %s para %s, obtenido %s", tc.expected, tc.filename, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Probar Guardado Seguro en Disco (Permisos no ejecutables y .htaccess preventivo)
|
||||||
|
sampleContent := strings.NewReader("<?php echo 'hacked'; ?>")
|
||||||
|
storedFile, _, err := storage.SaveFile(uploadDir, "secslug", "shell.php", sampleContent)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error guardando archivo seguro: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar permisos en disco (NO ejecutable)
|
||||||
|
destPath := filepath.Join(uploadDir, storedFile)
|
||||||
|
fi, err := os.Stat(destPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error comprobando archivo guardado: %v", err)
|
||||||
|
}
|
||||||
|
mode := fi.Mode().Perm()
|
||||||
|
if mode&0111 != 0 {
|
||||||
|
t.Fatalf("El archivo guardado tiene permisos de ejecución: %o", mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar existencia de .htaccess preventivo
|
||||||
|
htaccessPath := filepath.Join(uploadDir, ".htaccess")
|
||||||
|
if _, err := os.Stat(htaccessPath); os.IsNotExist(err) {
|
||||||
|
t.Fatalf(".htaccess preventivo no fue creado en uploadDir")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("✅ Test completado con éxito: Protecciones de seguridad activas (sin path traversal, scripts neutralizados a texto plano y archivos no ejecutables).")
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=SIAX App: drop.sigmac.app (Plataforma de Transferencia y Visor Rápido de Archivos)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=user_apps
|
||||||
|
WorkingDirectory=/home/user_apps/apps/drop_sigmac_app
|
||||||
|
ExecStart=/home/user_apps/apps/drop_sigmac_app/app_compartir
|
||||||
|
EnvironmentFile=/home/user_apps/apps/drop_sigmac_app/.env
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5s
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,75 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%">
|
||||||
|
<defs>
|
||||||
|
<!-- Background Gradient -->
|
||||||
|
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#020617"/>
|
||||||
|
<stop offset="50%" stop-color="#0f172a"/>
|
||||||
|
<stop offset="100%" stop-color="#030712"/>
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<!-- Border Glow Gradient -->
|
||||||
|
<linearGradient id="borderGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#10b981"/>
|
||||||
|
<stop offset="50%" stop-color="#06b6d4"/>
|
||||||
|
<stop offset="100%" stop-color="#3b82f6"/>
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<!-- Main Drop Gradient -->
|
||||||
|
<linearGradient id="dropGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#4ade80"/>
|
||||||
|
<stop offset="40%" stop-color="#22c55e"/>
|
||||||
|
<stop offset="80%" stop-color="#06b6d4"/>
|
||||||
|
<stop offset="100%" stop-color="#0284c7"/>
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<!-- Inner Core Gradient -->
|
||||||
|
<linearGradient id="coreGrad" x1="0%" y1="100%" x2="0%" y2="0%">
|
||||||
|
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.95"/>
|
||||||
|
<stop offset="100%" stop-color="#ecfdf5" stop-opacity="0.75"/>
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<!-- Glow Filter -->
|
||||||
|
<filter id="neonGlow" x="-20%" y="-20%" width="140%" height="140%">
|
||||||
|
<feGaussianBlur stdDeviation="14" result="blur"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="blur"/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<filter id="softShadow" x="-10%" y="-10%" width="120%" height="120%">
|
||||||
|
<feDropShadow dx="0" dy="8" stdDeviation="12" flood-color="#000000" flood-opacity="0.6"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Base Icon Container (Squircle) -->
|
||||||
|
<rect x="16" y="16" width="480" height="480" rx="112" fill="url(#bgGrad)" stroke="url(#borderGrad)" stroke-width="10" filter="url(#softShadow)"/>
|
||||||
|
|
||||||
|
<!-- Radial Glow Behind the Symbol -->
|
||||||
|
<circle cx="256" cy="256" r="140" fill="#22c55e" opacity="0.12" filter="url(#neonGlow)"/>
|
||||||
|
|
||||||
|
<!-- Wireless Transfer Signal Arches (Top Right) -->
|
||||||
|
<path d="M 320 120 A 70 70 0 0 1 380 180" fill="none" stroke="#38bdf8" stroke-width="12" stroke-linecap="round" opacity="0.75"/>
|
||||||
|
<path d="M 345 95 A 105 105 0 0 1 425 175" fill="none" stroke="#22c55e" stroke-width="12" stroke-linecap="round" opacity="0.45"/>
|
||||||
|
|
||||||
|
<!-- Main Teardrop / Fast Drop Silhouette -->
|
||||||
|
<!-- Smooth teardrop geometry combining drop + speed -->
|
||||||
|
<path d="M 256 90
|
||||||
|
C 256 90, 375 220, 375 320
|
||||||
|
A 119 119 0 0 1 137 320
|
||||||
|
C 137 220, 256 90, 256 90 Z"
|
||||||
|
fill="url(#dropGrad)"
|
||||||
|
filter="url(#neonGlow)"/>
|
||||||
|
|
||||||
|
<!-- Negative Space Inner Arrows (Bidirectional Fast Transfer) -->
|
||||||
|
<g fill="url(#coreGrad)">
|
||||||
|
<!-- Upward Transfer Arrow Head -->
|
||||||
|
<path d="M 256 160 L 298 215 L 274 215 L 274 275 L 238 275 L 238 215 L 214 215 Z"/>
|
||||||
|
|
||||||
|
<!-- Downward Transfer Chevron / Base Anchor -->
|
||||||
|
<path d="M 220 305 L 256 348 L 292 305 L 280 295 L 256 324 L 232 295 Z" opacity="0.9"/>
|
||||||
|
|
||||||
|
<!-- Energy Transfer Dot -->
|
||||||
|
<circle cx="256" cy="385" r="9" fill="#ffffff" opacity="0.95"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -4,6 +4,11 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{block "title" .}}drop.sigmac.app — Pasa archivos y texto al instante{{end}}</title>
|
<title>{{block "title" .}}drop.sigmac.app — Pasa archivos y texto al instante{{end}}</title>
|
||||||
|
<!-- Favicon & Touch Icons -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
|
<link rel="alternate icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
<link rel="apple-touch-icon" href="/static/icon-192.png">
|
||||||
|
|
||||||
<!-- Tailwind CSS CDN -->
|
<!-- Tailwind CSS CDN -->
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -44,9 +49,7 @@
|
|||||||
<header class="border-b border-slate-800 bg-slate-900/80 backdrop-blur sticky top-0 z-50">
|
<header class="border-b border-slate-800 bg-slate-900/80 backdrop-blur sticky top-0 z-50">
|
||||||
<div class="max-w-6xl mx-auto px-4 h-16 flex items-center justify-between">
|
<div class="max-w-6xl mx-auto px-4 h-16 flex items-center justify-between">
|
||||||
<a href="/" class="flex items-center gap-2.5 font-bold text-lg tracking-tight hover:opacity-90 transition">
|
<a href="/" class="flex items-center gap-2.5 font-bold text-lg tracking-tight hover:opacity-90 transition">
|
||||||
<span class="w-9 h-9 rounded-xl bg-gradient-to-tr from-brand-600 to-emerald-400 flex items-center justify-center text-slate-950 font-black shadow-lg shadow-brand-500/20">
|
<img src="/favicon.svg" alt="Logo" class="w-9 h-9 rounded-xl shadow-lg shadow-brand-500/20 object-contain">
|
||||||
<i class="fa-solid fa-cloud-arrow-up text-base"></i>
|
|
||||||
</span>
|
|
||||||
<span class="text-white">drop<span class="text-brand-500">.sigmac.app</span></span>
|
<span class="text-white">drop<span class="text-brand-500">.sigmac.app</span></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user