13 KiB
Servidor Personal tzzr.net — Actualización Repositorio GitHub
Contexto
Servidor personal autoalojado en Hostinger VPS (Ubuntu 22.04, 8GB RAM, 100GB SSD). Función: máquina personal del usuario, punto de entrada privado al ecosistema.
IP: 72.62.1.113
Dominio: tzzr.net
Hostname: box.tzzr.net
Arquitectura Actual
[Cloudflare DNS]
│
[72.62.1.113]
│
┌─────────────────┴─────────────────┐
│ │
[Mail-in-a-Box] [Docker Stack]
Puertos 25,443,993 Puertos internos
│ │
┌────┴────┐ ┌───────────────┼───────────────┐
│ │ │ │ │ │ │
Email Nextcloud PostgreSQL NocoDB Shlink Addy Vaultwarden
(box) (/cloud) 5432 8081 8083 8084 8085
│
FileBrowser
8082
Servicios Docker
| Servicio | Puerto | Subdominio | Imagen |
|---|---|---|---|
| PostgreSQL 15 | 5432 | (interno) | postgres:15-alpine |
| NocoDB | 8081 | db.tzzr.net | nocodb/nocodb:latest |
| FileBrowser | 8082 | files.tzzr.net | filebrowser/filebrowser:latest |
| Shlink | 8083 | s.tzzr.net | shlinkio/shlink:stable |
| Addy.io | 8084 | alias.tzzr.net | anonaddy/anonaddy:latest |
| Redis | 6379 | (interno) | redis:alpine |
| Vaultwarden | 8085 | pass.tzzr.net | vaultwarden/server:latest |
Tarea: Actualizar Repositorio GitHub
Estructura de Archivos del Repositorio
tzzr-server/
├── README.md # Documentación principal
├── docker-compose.yml # Stack de servicios
├── init.sql # Schema PostgreSQL completo
├── nginx/
│ └── services.conf # Configuración reverse proxy
├── scripts/
│ ├── backup.sh # Script de backup diario
│ ├── mail-processor.py # Procesar correos → mail_registry
│ └── hst-sync.py # Sincronización etiquetas HST
├── dns/
│ └── tzzr_net.zone # Registros DNS para Cloudflare
└── docs/
├── INSTALACION.md # Guía de instalación desde cero
├── CLONACION.md # Guía para clonar a nuevo usuario
└── CREDENCIALES.template # Plantilla de credenciales (sin valores reales)
Schema PostgreSQL (init.sql)
Tablas de Etiquetas HST
Crear 4 tablas con estructura idéntica:
-- hst_tags_sistema: Sync desde tzrtech.org (grupo: hst)
-- hst_tags_empresa: Sync desde servidor empresa (grupo: emp)
-- hst_tags_usuario: Etiquetas personales (grupo: hsu)
-- hst_tags_proyecto: Etiquetas de proyecto (grupo: pjt)
CREATE TABLE IF NOT EXISTS hst_tags_sistema (
id SERIAL PRIMARY KEY,
h_maestro VARCHAR(64) UNIQUE, -- SHA-256, identificador único
codigo VARCHAR(50) NOT NULL, -- Código corto memorable
nombre VARCHAR(100) NOT NULL, -- Nombre legible
nombre_en VARCHAR(100), -- Nombre en inglés (opcional)
descripcion TEXT,
imagen_url TEXT, -- URL externa a la imagen (no blob)
color VARCHAR(7), -- Color hex (#RRGGBB)
grupo VARCHAR(10) DEFAULT 'hst', -- hst, emp, hsu, pjt
padre_h_maestro VARCHAR(64), -- Jerarquía: referencia al padre
activo BOOLEAN DEFAULT true,
metadata JSONB, -- Datos adicionales flexibles
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
synced_at TIMESTAMP -- Última sincronización (para hst/emp)
);
-- Repetir estructura para: hst_tags_empresa, hst_tags_usuario, hst_tags_proyecto
-- Cambiar DEFAULT de 'grupo' según corresponda: 'emp', 'hsu', 'pjt'
Índices requeridos:
CREATE INDEX idx_hst_sistema_h_maestro ON hst_tags_sistema(h_maestro);
CREATE INDEX idx_hst_sistema_codigo ON hst_tags_sistema(codigo);
CREATE INDEX idx_hst_sistema_grupo ON hst_tags_sistema(grupo);
CREATE INDEX idx_hst_sistema_padre ON hst_tags_sistema(padre_h_maestro);
Tabla mail_registry
CREATE TABLE IF NOT EXISTS mail_registry (
id SERIAL PRIMARY KEY,
message_id VARCHAR(255) UNIQUE, -- Message-ID del correo
-- Direcciones
from_address VARCHAR(255) NOT NULL,
to_address VARCHAR(255) NOT NULL,
cc_address TEXT, -- Puede ser múltiple
reply_to VARCHAR(255),
-- Contenido
subject TEXT,
body_preview TEXT, -- Primeras 500 palabras
-- Fechas
date_sent TIMESTAMP,
date_received TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- Adjuntos
has_attachments BOOLEAN DEFAULT false,
attachment_count INTEGER DEFAULT 0,
attachment_paths JSONB, -- Array de rutas en FileBrowser
-- Clasificación
direction VARCHAR(10) NOT NULL, -- 'inbound' o 'outbound'
folder VARCHAR(100) DEFAULT 'INBOX',
is_spam BOOLEAN DEFAULT false,
spam_score DECIMAL(5,2),
read_status BOOLEAN DEFAULT false,
-- Etiquetado
tags JSONB, -- Array de h_maestro
-- Procesamiento IA
processed_by_ia BOOLEAN DEFAULT false,
ia_classification JSONB, -- Resultado del clasificador
ia_processed_at TIMESTAMP,
-- Metadata
size_bytes INTEGER,
headers JSONB, -- Headers relevantes
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Índices
CREATE INDEX idx_mail_registry_message_id ON mail_registry(message_id);
CREATE INDEX idx_mail_registry_from ON mail_registry(from_address);
CREATE INDEX idx_mail_registry_to ON mail_registry(to_address);
CREATE INDEX idx_mail_registry_date ON mail_registry(date_received);
CREATE INDEX idx_mail_registry_direction ON mail_registry(direction);
CREATE INDEX idx_mail_registry_tags ON mail_registry USING GIN(tags);
-- Constraint para direction
ALTER TABLE mail_registry
ADD CONSTRAINT chk_direction CHECK (direction IN ('inbound', 'outbound'));
Tabla log_personal
CREATE TABLE IF NOT EXISTS log_personal (
id SERIAL PRIMARY KEY,
tipo VARCHAR(50) NOT NULL, -- nota, recordatorio, evento, etc.
titulo VARCHAR(255),
descripcion TEXT,
fecha_evento TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
tags JSONB, -- Array de h_maestro
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_log_personal_tipo ON log_personal(tipo);
CREATE INDEX idx_log_personal_fecha ON log_personal(fecha_evento);
CREATE INDEX idx_log_personal_tags ON log_personal USING GIN(tags);
Tabla log_sistema
CREATE TABLE IF NOT EXISTS log_sistema (
id SERIAL PRIMARY KEY,
tipo_evento VARCHAR(50) NOT NULL, -- Tipo de evento (ver enum abajo)
nivel VARCHAR(20) NOT NULL DEFAULT 'INFO', -- INFO, WARNING, ERROR, DEBUG
servicio VARCHAR(100), -- Servicio origen
mensaje TEXT NOT NULL,
detalles JSONB, -- Datos estructurados del evento
-- Contexto
ip_origen VARCHAR(45),
usuario VARCHAR(100),
trace_id VARCHAR(64), -- Para correlacionar eventos
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tipos de evento permitidos
COMMENT ON COLUMN log_sistema.tipo_evento IS
'Valores: mail_received, mail_sent, file_uploaded, file_downloaded,
login, logout, api_call, sync_hst, sync_emp, backup_started,
backup_completed, backup_failed, error, warning, service_start, service_stop';
CREATE INDEX idx_log_sistema_tipo ON log_sistema(tipo_evento);
CREATE INDEX idx_log_sistema_nivel ON log_sistema(nivel);
CREATE INDEX idx_log_sistema_fecha ON log_sistema(created_at);
CREATE INDEX idx_log_sistema_servicio ON log_sistema(servicio);
CREATE INDEX idx_log_sistema_trace ON log_sistema(trace_id);
Tablas Auxiliares
-- Aliases de correo (tracking de Addy.io)
CREATE TABLE IF NOT EXISTS mail_aliases (
id SERIAL PRIMARY KEY,
alias VARCHAR(255) UNIQUE NOT NULL,
destino VARCHAR(255) NOT NULL,
descripcion TEXT,
activo BOOLEAN DEFAULT true,
contador_uso INTEGER DEFAULT 0,
ultimo_uso TIMESTAMP,
tags JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- URLs acortadas (tracking adicional a Shlink)
CREATE TABLE IF NOT EXISTS short_urls (
id SERIAL PRIMARY KEY,
short_code VARCHAR(50) UNIQUE NOT NULL,
url_original TEXT NOT NULL,
titulo VARCHAR(255),
descripcion TEXT,
visitas INTEGER DEFAULT 0,
activo BOOLEAN DEFAULT true,
fecha_expiracion TIMESTAMP,
tags JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Script mail-processor.py
Script Python que escucha nuevos correos y los registra en mail_registry.
Requisitos funcionales:
- Conectar a Dovecot via IMAP (localhost:993)
- Escuchar eventos IDLE para nuevos correos
- Por cada correo nuevo:
- Extraer headers y metadatos
- Generar body_preview (primeras 500 palabras, texto plano)
- Si hay adjuntos: guardar en FileBrowser, registrar rutas en attachment_paths
- Insertar registro en mail_registry
- Insertar evento en log_sistema (tipo: mail_received)
- Manejar reconexión automática
- Logging a stdout (para docker logs)
Dependencias:
imapclient
psycopg2-binary
python-dotenv
Ejecutar como servicio systemd o contenedor Docker.
Script hst-sync.py
Script Python para sincronizar etiquetas HST desde servidor externo.
Requisitos funcionales:
- Conectar a API de tzrtech.org (endpoint y auth por definir)
- Obtener lista de etiquetas con h_maestro
- Comparar con hst_tags_sistema local
- UPDATE si h_maestro existe y hay cambios
- INSERT si h_maestro no existe
- Marcar synced_at en registros actualizados
- Registrar evento en log_sistema (tipo: sync_hst)
Ejecutar via cron cada 24h.
Script backup.sh
Actualizar el script existente para incluir:
- Dump PostgreSQL completo
- Volumen vaultwarden_data
- Configuración de servicios
- Volumen filebrowser_data
- Rotación: 7 días
- Log de cada backup en log_sistema (via psql o curl a API)
Configuración Nginx (services.conf)
Asegurar que incluye todos los servicios:
- db.tzzr.net → localhost:8081 (NocoDB)
- files.tzzr.net → localhost:8082 (FileBrowser)
- s.tzzr.net → localhost:8083 (Shlink)
- alias.tzzr.net → localhost:8084 (Addy.io)
- pass.tzzr.net → localhost:8085 (Vaultwarden, con WebSocket)
Todos con:
- SSL usando certificados de Mail-in-a-Box
- Headers de seguridad estándar
- ACME challenge para renovación SSL
README.md
Incluir:
- Descripción del proyecto (servidor personal, no sistema completo)
- Arquitectura (diagrama ASCII)
- Servicios y URLs
- Requisitos previos (VPS, dominio, Cloudflare)
- Instalación rápida
- Estructura de archivos
- Comandos útiles
- Backups y restauración
- Seguridad (puertos, firewall, SSL)
- Licencia
NO incluir credenciales reales en el repositorio.
Entregables
docker-compose.yml— Actualizado con todos los serviciosinit.sql— Schema completo con las 4 tablas HST + mail_registry + logsnginx/services.conf— Configuración reverse proxy completascripts/backup.sh— Actualizado con vaultwardenscripts/mail-processor.py— Nuevo: procesar correosscripts/hst-sync.py— Nuevo: sincronizar etiquetasdns/tzzr_net.zone— Archivo de zona DNSREADME.md— Documentación principaldocs/INSTALACION.md— Guía paso a pasodocs/CLONACION.md— Guía para replicar a nuevo usuariodocs/CREDENCIALES.template— Plantilla sin valores reales.gitignore— Excluir .env, credenciales, backups.env.example— Variables de entorno requeridas
Variables de Entorno (.env.example)
# PostgreSQL
POSTGRES_USER=tzzr
POSTGRES_PASSWORD=
POSTGRES_DB=tzzr
# NocoDB
NC_AUTH_JWT_SECRET=
# Addy.io
ANONADDY_DOMAIN=tzzr.net
ANONADDY_SECRET=
APP_KEY=
# Vaultwarden
VAULTWARDEN_DOMAIN=https://pass.tzzr.net
VAULTWARDEN_ADMIN_TOKEN=
# Shlink
SHLINK_DOMAIN=s.tzzr.net
# Mail processor
IMAP_HOST=localhost
IMAP_USER=
IMAP_PASSWORD=
# HST Sync (por definir)
HST_API_URL=
HST_API_TOKEN=
Notas para Code
- No crear servicios nuevos — Solo documentar y estructurar lo existente
- No incluir credenciales reales — Usar placeholders o .env.example
- Mantener compatibilidad — El servidor ya está funcionando
- Schema SQL debe ser idempotente — Usar IF NOT EXISTS
- Scripts deben tener logging — Para debugging via docker logs
- Documentación en español — Usuario hispanohablante
Repositorio Destino
Por confirmar con el usuario. Puede ser:
- Nuevo repositorio
- Repositorio existente a actualizar
Documento generado: Diciembre 2024