{"id":39548158,"url":"https://github.com/adcondev/ticket-daemon","last_synced_at":"2026-02-23T23:21:40.549Z","repository":{"id":332218962,"uuid":"1130641934","full_name":"adcondev/ticket-daemon","owner":"adcondev","description":"Ticket Daemon is a Windows Service that bridges Web POS applications with physical thermal printers via WebSocket.  It receives JSON print documents following the Poster format, queues them for serial execution, and sends ESC/POS commands to thermal printers through the Windows Print Spooler.","archived":false,"fork":false,"pushed_at":"2026-01-12T22:27:37.000Z","size":44,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-13T02:45:34.974Z","etag":null,"topics":["daemon","escpos","escpos-printer","go","installer","point-of-sale","printer","ticketing-system","tui"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adcondev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-08T19:50:55.000Z","updated_at":"2026-01-12T22:27:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/adcondev/ticket-daemon","commit_stats":null,"previous_names":["adcondev/ticket-daemon"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/adcondev/ticket-daemon","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcondev%2Fticket-daemon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcondev%2Fticket-daemon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcondev%2Fticket-daemon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcondev%2Fticket-daemon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adcondev","download_url":"https://codeload.github.com/adcondev/ticket-daemon/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adcondev%2Fticket-daemon/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28532053,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["daemon","escpos","escpos-printer","go","installer","point-of-sale","printer","ticketing-system","tui"],"created_at":"2026-01-18T06:49:56.562Z","updated_at":"2026-02-23T23:21:40.528Z","avatar_url":"https://github.com/adcondev.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🎫 Ticket Daemon\n\n# Ticket Service Daemon\n\n[![codecov](https://codecov.io/gh/adcondev/ticket-daemon/branch/main/graph/badge.svg)](https://codecov.io/gh/adcondev/ticket-daemon)\n[![CI](https://github.com/adcondev/ticket-daemon/actions/workflows/ci.yml/badge.svg)](https://github.com/adcondev/ticket-daemon/actions/workflows/ci.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/adcondev/ticket-daemon)](https://goreportcard.com/report/github.com/adcondev/ticket-daemon)\n![Language](https://img.shields.io/badge/Go-1.24+-00ADD8?style=flat\u0026logo=go\u0026logoColor=white)\n![Platform](https://img.shields.io/badge/Platform-Windows-0078D6?style=flat\u0026logo=windows\u0026logoColor=white)\n![License](https://img.shields.io/badge/License-MIT-green?style=flat)\n![WebSocket](https://img.shields.io/badge/Protocol-WebSocket-purple?style=flat)\n\n**Ticket Daemon** es un Servicio de Windows diseñado para entornos de producción retail. Actúa como un middleware\nrobusto que conecta aplicaciones Web POS con impresoras térmicas físicas mediante WebSocket.\n\nEl servicio gestiona la concurrencia de multiples terminales, encola trabajos para garantizar el orden de impresión y\nutiliza la librería **Poster** como motor de renderizado ESC/POS.\n\n## ✨ Características Principales\n\n- 🔌 **Servidor WebSocket** de alto rendimiento (puerto 8766 por defecto).\n- 🛡️ **Protección de Backpressure**: Cola con buffer (100 slots) y rechazo inmediato si se satura.\n- 🖨️ **Servicio Nativo Windows**: Integración completa con SCM (Service Control Manager).\n- 📝 **Logging Estructurado**: Rotación automática de archivos (5 MB) para mantenimiento cero.\n- 🖨️ **Motor Poster**: Soporte avanzado para texto, códigos de barras, QR e imágenes.\n- 🔐 **Seguridad Dual**: Protección de Dashboard mediante Login, validación de Token y limitador de peticiones para\n  trabajos de impresión vía API.\n\n---\n\n## 🏗️ Arquitectura del Sistema\n\n### Estructura de Componentes\n\nEl siguiente diagrama ilustra como el servicio envuelve los servidores HTTP/WS y coordina el flujo hacia el hardware.\n\n```mermaid\ngraph TD\n    classDef go fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000;\n    classDef data fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000;\n    classDef hw fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000;\n\n    subgraph Host[\"Host del Servicio Windows\"]\n        direction TB\n        Service[Wrapper del Servicio]:::go --\u003e|Init/Start| HTTP[Servidor HTTP]:::go\n        Service --\u003e|Start/Stop| Worker[Worker de Impresion]:::go\n        HTTP --\u003e|/ws| WSServer[Handler WebSocket]:::go\n    end\n\n    subgraph Flow[\"Flujo de Datos\"]\n        direction TB\n        Client[Cliente Web POS]:::data \u003c--\u003e|JSON Messages| WSServer\n        WSServer --\u003e|Push Job| Queue[Canal Buffer 100]:::data\n        Queue --\u003e|Pop Job| Worker\n    end\n\n    subgraph Hardware[\"Integracion de Hardware\"]\n        direction TB\n        Worker --\u003e|Execute| PosterLib[Libreria Poster]:::hw\n        PosterLib --\u003e|Bytes ESC/POS| Spooler[Spooler de Windows]:::hw\n        Spooler --\u003e|USB/Serial/LPT| Printer[Impresora Termica]:::hw\n    end\n```\n\n### Modelo de Concurrencia (Fan-In)\n\nEl sistema utiliza un patron de **Fan-In** con un `Select` no bloqueante. Esto permite manejar multiples conexiones\nsimultáneas sin bloquear el hilo principal si la impresora es lenta.\n\n```mermaid\ngraph TB\n    classDef client fill: #e8f5e9, stroke: #2e7d32, stroke-width: 2px;\n    classDef logic fill: #fff9c4, stroke: #fbc02d, stroke-width: 2px;\n    classDef crit fill: #ffebee, stroke: #c62828, stroke-width: 2px;\n    classDef core fill: #e3f2fd, stroke: #1565c0, stroke-width: 2px;\n    subgraph Clients[\"Capa HTTP/WS Concurrente\"]\n        C1[Cliente POS 1]:::client --\u003e H1[Goroutine Handler 1]:::core\n        C2[Cliente POS 2]:::client --\u003e H2[Goroutine Handler 2]:::core\n        C3[Cliente POS 3]:::client --\u003e H3[Goroutine Handler 3]:::core\n    end\n\n    subgraph Sync[\"Sincronizacion\"]\n        direction TB\n        H1 \u0026 H2 \u0026 H3 --\u003e Select{Select Non-blocking}:::logic\n        Select -- \" Default Lleno \" --\u003e Overflow[Error: Cola Llena]:::crit\n        Select -- \" Case Send \" --\u003e Channel[Canal cap=100]:::core\n    end\n\n    subgraph Process[\"Procesamiento Serial\"]\n        Channel --\u003e WLoop[Worker Loop]:::core\n        WLoop --\u003e Mutex[Poster Executor]:::core\n        Mutex --\u003e Hardware[Hardware Fisico]:::crit\n    end\n```\n\n### Ciclo de Vida del Mensaje\n\n```mermaid\nsequenceDiagram\n    participant C as Cliente Web\n    participant H as WS Handler\n    participant Q as Cola Canal\n    participant W as Worker\n    participant P as Poster Engine\n    Note over C, H: Conexion establecida ws://...\n    C -\u003e\u003e H: {\"tipo\":\"ticket\", \"datos\":{...}}\n\n    rect rgb(240, 248, 255)\n        Note right of H: server.go\n        H -\u003e\u003e H: Validar JSON\n\n        alt Cola Llena (Select Default)\n            H --\u003e\u003e C: {\"tipo\":\"error\", \"mensaje\":\"Cola llena, reintente\"}\n        else Encolado Exitoso\n            H -\u003e\u003e Q: Push PrintJob\n            H --\u003e\u003e C: {\"tipo\":\"ack\", \"status\":\"queued\", \"pos\": 5}\n        end\n    end\n\n    rect rgb(255, 248, 240)\n        Note right of W: processor.go\n        Q -\u003e\u003e W: Pop PrintJob\n        W -\u003e\u003e P: Execute(Document)\n\n        alt Exito\n            P --\u003e\u003e W: nil\n            W -\u003e\u003e H: NotifyClient(Success)\n            H --\u003e\u003e C: {\"tipo\":\"result\", \"status\":\"success\"}\n        else Error\n            P --\u003e\u003e W: error\n            W -\u003e\u003e H: NotifyClient(Error)\n            H --\u003e\u003e C: {\"tipo\":\"result\", \"status\":\"error\", \"mensaje\":\"... \"}\n        end\n    end\n```\n\n---\n\n## 📡 Protocolo WebSocket\n\n### Endpoints\n\n| Endpoint                       | Descripcion            |\n|--------------------------------|------------------------|\n| `ws://localhost:8766/ws`       | Conexion WebSocket     |\n| `http://localhost:8766/health` | Health check (JSON)    |\n| `http://localhost:8766/`       | Cliente de prueba HTML |\n\n### Tipos de Mensaje\n\n| Direccion | `tipo`         | Descripcion                  |\n|-----------|----------------|------------------------------|\n| C -\u003e S    | `ticket`       | Enviar trabajo de impresion  |\n| C -\u003e S    | `status`       | Solicitar estado de la cola  |\n| C -\u003e S    | `ping`         | Ping al servidor             |\n| C -\u003e S    | `get_printers` | Listar impresoras instaladas |\n| S -\u003e C    | `ack`          | Trabajo aceptado y encolado  |\n| S -\u003e C    | `result`       | Trabajo completado/fallido   |\n| S -\u003e C    | `error`        | Error de validacion/cola     |\n| S -\u003e C    | `printers`     | Lista de impresoras          |\n\n### Ejemplo de Payload\n\n```json\n{\n  \"tipo\": \"ticket\",\n  \"id\": \"pos1-20260109-001\",\n  \"datos\": {\n    \"version\": \"1.0\",\n    \"profile\": {\n      \"model\": \"80mm EC-PM-80250\",\n      \"paper_width\": 80\n    },\n    \"commands\": [\n      {\n        \"type\": \"text\",\n        \"data\": {\n          \"content\": {\n            \"text\": \"TICKET DE PRUEBA\",\n            \"align\": \"center\",\n            \"content_style\": {\n              \"bold\": true,\n              \"size\": \"2x2\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"cut\",\n        \"data\": {\n          \"mode\": \"partial\"\n        }\n      }\n    ]\n  }\n}\n```\n\n### Descubrimiento de Impresoras\n\nEl servicio detecta automáticamente las impresoras instaladas en Windows al iniciar y expone esta información via\nWebSocket y HTTP.\n\n**Mensaje WebSocket:**\n\nPetición para obtener impresoras:\n\n```json\n{\n  \"tipo\": \"get_printers\"\n}\n```\n\nRespuesta del servidor:\n\n```json\n{\n  \"tipo\": \"printers\",\n  \"status\": \"ok\",\n  \"printers\": [\n    {\n      \"name\": \"58mm PT-210\",\n      \"port\": \"USB001\",\n      \"driver\": \"Generic / Text Only\",\n      \"status\": \"ready\",\n      \"is_default\": true,\n      \"is_virtual\": false,\n      \"printer_type\": \"thermal\"\n    }\n  ],\n  \"summary\": {\n    \"status\": \"ok\",\n    \"detected_count\": 5,\n    \"thermal_count\": 1,\n    \"default_name\": \"58mm PT-210\"\n  }\n}\n```\n\n**Health Check (`/health`):**\n\n```json\n{\n  \"status\": \"ok\",\n  \"printers\": {\n    \"status\": \"ok\",\n    \"detected_count\": 5,\n    \"thermal_count\": 1,\n    \"default_name\": \"58mm PT-210\"\n  }\n  // ... other fields\n}\n```\n\n| Estado Printers | Significado                                    |\n|-----------------|------------------------------------------------|\n| `ok`            | Al menos una impresora térmica detectada       |\n| `warning`       | Hay impresoras físicas pero ninguna es térmica |\n| `error`         | No hay impresoras físicas instaladas           |\n\n\u003e **Nota:** El estado `ready` refleja el último estado conocido del Windows Spooler. Para impresoras USB/Serial, esto\n\u003e puede no reflejar si están físicamente conectadas en tiempo real.\n\n---\n\n## 🔐 Configuración de Seguridad (Build-Time)\n\nEste servicio no usa archivos de configuración externos por seguridad. Las credenciales se inyectan al compilar:\n\n1. **Dashboard Password:** Requiere un hash Bcrypt en base64.\n2. **Auth Token:** Token simple para validar los WebSockets.\n\nVer `Taskfile.yml` para ejemplos de compilación.\n\n---\n\n## ⚙️ Configuración y Compilación\n\nEste servicio no utiliza archivos de configuración en tiempo de ejecución (`.env` o `.yaml`) por seguridad. Las\ncredenciales se inyectan directamente en el binario durante la compilación.\n\n### Variables de Build (LDFLAGS)\n\nLas variables residen en el paquete `github.com/adcondev/ticket-daemon/internal/config`:\n\n| Variable Go        | Descripción                                   | Ejemplo de Valor                    |\n|--------------------|-----------------------------------------------|-------------------------------------|\n| `AuthToken`        | Token para validar trabajos vía WebSocket     | `\"mi-token-secreto\"`                |\n| `PasswordHashB64`  | Hash Bcrypt (base64) para acceso al Dashboard | `\"Jd8a...\"` (generado externamente) |\n| `BuildEnvironment` | Define timeouts y comportamiento de logs      | `\"local\"` o `\"remote\"`              |\n\n### Ejemplo de Compilación Manual\n\nPara generar un binario seguro:\n\n```powershell\n# 1. Generar hash del password (usando herramienta auxiliar o externa)\n# ...\n\n# 2. Compilar inyectando variables\ngo build -ldflags \"-s -w \\\n  -X github.com/adcondev/ticket-daemon/internal/config.AuthToken=mi-token-secreto \n  -X '[github.com/adcondev/ticket-daemon/internal/config.PasswordHashB64=HASH_BASE64_AQUI](https://github.com/adcondev/ticket-daemon/' `\n  -X '[github.com/adcondev/ticket-daemon/internal/config.BuildEnvironment=local](https://github.com/adcondev/ticket-dae'\" `\n  -o TicketServicio.exe ./cmd/TicketServicio\n\n```\n\n---\n\n## 🚀 Inicio Rápido\n\n### Prerrequisitos\n\n- **Go 1.24+**\n- **Task** (go-task) - [Instalación](https://taskfile.dev/installation/)\n- Windows 10/11 o Windows Server\n\n### Comandos Comunes (con Task)\n\n```powershell\n# Ver todos los comandos disponibles\ntask\n\n# Compilar y ejecutar en modo consola (desarrollo)\ntask run\n\n# Compilar ejecutable standalone (doble-clic para ejecutar)\ntask build-console\n\n# Instalar como Servicio de Windows (requiere Admin)\ntask install\n\n# Ver logs en tiempo real\ntask logs\n\n# Abrir dashboard de diagnostico\ntask open\n\n# Verificar estado del servicio\ntask status\n```\n\n### Ejecutable Standalone (Sin Task)\n\nSi prefieres distribuir solo el `.exe`:\n\n```powershell\n# 1. Compilar\ntask build-console\n\n# 2. El ejecutable queda en: \n#    bin/TicketDaemon_Console.exe\n\n# 3. Doble-clic para ejecutar, o desde terminal:\n.\\bin\\TicketDaemon_Console.exe\n\n# 4. Abrir navegador en: http://localhost:8766\n```\n\n---\n\n## 📂 Estructura del Proyecto\n\n```\nticket-daemon/\n├── api/\n│   └── v1/\n│       ├── DOCUMENT_V1.md        # Especificacion formato documento\n│       ├── WEBSOCKET_V1.md       # Especificacion protocolo WebSocket\n│       ├── document.schema.json  # JSON Schema del documento\n│       └── websocket.schema.json # JSON Schema mensajes WS\n│\n├── cmd/\n│   └── TicketServicio/\n│       └── ticket_servicio.go    # Punto de entrada (main)\n│\n├── examples/\n│   └── json/                     # Ejemplos de documentos JSON\n│       ├── showcase.json         # Demo de todos los comandos\n│       └── table_showcase.json   # Ejemplos de tablas\n│\n├── internal/\n│   ├── assets/\n│   │   ├── embed.go              # Go embed para archivos web\n│   │   └── web/                  # Dashboard HTML/CSS/JS embebido\n│   │\n│   ├── daemon/\n│   │   ├── program.go            # Wrapper svc.Service y Configuracion\n│   │   ├── logger.go             # Logging filtrado con rotacion\n│   │   ├── daemon_types.go       # Tipos de respuesta Health\n│   │   └── printer_discovery.go  # Descubrimiento de impresoras Windows\n│   │\n│   ├── printer/\n│   │   └── printer_types.go      # Tipos compartidos de impresora\n│   │\n│   ├── server/\n│   │   ├── server.go             # Logica WebSocket y Cola (Select)\n│   │   └── clients.go            # Registro Thread-Safe de clientes\n│   │\n│   └── worker/\n│       └── processor.go          # Integracion con libreria Poster\n│\n├── go.mod\n├── Taskfile.yml                  # Automatizacion de tareas\n├── README.md\n└── LEARNING.md                   # Resumen tecnico para portfolio\n```\n\n---\n\n## 📝 Logs y Auditoria\n\nLos logs se escriben en `%PROGRAMDATA%` y rotan automáticamente al superar 5 MB.\n\n| Ambiente     | Ruta Tipica                                                              |\n|--------------|--------------------------------------------------------------------------|\n| **`remote`** | `C:\\ProgramData\\R2k_TicketServicio_Remote\\R2k_TicketServicio_Remote.log` |\n| **`local`**  | `C:\\ProgramData\\TicketServicioTest_Local\\TicketServicioTest_Local.log`   |\n\n### Ver Logs\n\n```powershell\n# Ultimas 100 lineas\ntask logs\n\n# O directamente:\nGet-Content \"C:\\ProgramData\\TicketServicioTest_Local\\TicketServicioTest_Local.log\" -Tail 100 -Wait\n```\n\n---\n\n## 🔧 Solución de Problemas\n\n### El servicio no inicia\n\n```powershell\n# Verificar estado\nsc query TicketServicioTest\n\n# Ver logs de error\ntask logs\n\n# Reinstalar\ntask uninstall\ntask install\n```\n\n### No se puede conectar por WebSocket\n\n1. Verificar que el servicio esté corriendo: `task status`\n2. Verificar firewall para puerto 8766\n3. Probar health check: `task health`\n\n### La impresora no imprime\n\n1. Verificar nombre exacto en `profile.model` (debe coincidir con Windows)\n2. Verificar que Print Spooler este activo: `Get-Service Spooler`\n3. Probar impresión directa desde Windows\n\n---\n\n## 📄 Licencia\n\nMIT © adcondev - RED 2000\n\n---\n\n## 📖 Documentación de la API\n\nEste proyecto incluye documentación detallada del protocolo y formato de documentos:\n\n| Documento                                             | Descripción                                          |\n|-------------------------------------------------------|------------------------------------------------------|\n| [DOCUMENT_V1.md](api/v1/DOCUMENT_V1.md)               | Especificación del formato de documento de impresión |\n| [WEBSOCKET_V1.md](api/v1/WEBSOCKET_V1.md)             | Especificación del protocolo WebSocket               |\n| [document.schema.json](api/v1/document.schema.json)   | JSON Schema para validación de documentos            |\n| [websocket.schema.json](api/v1/websocket.schema.json) | JSON Schema para mensajes WebSocket                  |\n\n### Ejemplos de Uso\n\nLa carpeta `examples/json/` contiene documentos de ejemplo listos para usar:\n\n- `showcase.json` - Demostración de todos los tipos de comandos\n- `table_showcase.json` - Ejemplos de tablas con diferentes configuraciones\n- `ticket_service_test.json` - Ticket completo con impuestos y QR\n\n---\n\n## 🔗 Recursos Relacionados\n\n- [Poster Library](https://github.com/adcondev/poster) - Motor de impresión ESC/POS\n- [Task - Automatización](https://taskfile.dev/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadcondev%2Fticket-daemon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadcondev%2Fticket-daemon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadcondev%2Fticket-daemon/lists"}