# MaxEditor — Plan de Trabajo Detallado de Deploy y Seguridad

**Última actualización:** 25 abril 2026
**Subdominio destino:** `maxeditor.altoque.tv`
**Repositorio:** GitHub privado (a crear)
**Acceso al server:** SSH en HawkHost (cPanel)

> Documento operativo que se actualiza paso a paso. Cada bloque tiene **prerequisitos**,
> **acciones** y **criterio de "hecho"**. No avanzamos al siguiente sin completar el actual.

---

## Filosofía de seguridad

El sistema se construye con **3 anillos concéntricos** de protección:

```
┌─────────────────────────────────────┐
│  Anillo 1: SERVER (maxeditor.altoque.tv)         │
│  - Solo HTTPS (Let's Encrypt)                    │
│  - Firewall: solo 80, 443, SSH (puerto custom)   │
│  - SSH solo con clave (sin passwords)            │
│  - Django con SECRET_KEY rotable, DEBUG=False    │
│  - Rate limiting en /auth/login                  │
│  - CORS restrictivo: solo maxeditor.altoque.tv   │
│  - Headers: HSTS, CSP, X-Frame-Options           │
└─────────────────────────────────────┘
               ▲
               │
┌─────────────────────────────────────┐
│  Anillo 2: AUTH                                   │
│  - JWT con expiry corto (24h)                    │
│  - Refresh token rotativo                        │
│  - Verificación de suscripción en cada refresh   │
│  - Password hash bcrypt                          │
│  - Email verification (fase 5)                   │
│  - Captcha en signup público (fase 5)            │
└─────────────────────────────────────┘
               ▲
               │
┌─────────────────────────────────────┐
│  Anillo 3: CLIENT (companion app)                 │
│  - JWT cifrado con DPAPI                         │
│  - Token de sesión por instalación               │
│  - Detección de tampering (hash check)           │
│  - HTTPS-only para llamadas al server            │
└─────────────────────────────────────┘
```

---

## ETAPA A — Preparar repositorio + GitHub

**Objetivo:** Tener el código en un repo privado de GitHub listo para deploy continuo.

### A.1 Crear repo privado en GitHub

**Quién:** Tú
**Qué:**
- [ ] Crear cuenta GitHub si no tienes (o usar la existente)
- [ ] Crear repo privado: `maxeditor` (nombre sugerido)
- [ ] Settings → "Branch protection" en `main`: requiere PR (opcional, recomendado)

### A.2 Inicializar repo local + primer push

**Quién:** Yo (con tu confirmación)
**Acciones:**
- [ ] Verificar que existe `.gitignore` apropiado (excluir `.venv`, `node_modules`, `db.sqlite3`, `media/`, `__pycache__`, `.env`)
- [ ] `git init` (si aún no es repo) → `git add .` → `git commit`
- [ ] Conectar remote: `git remote add origin git@github.com:<TU_USER>/maxeditor.git`
- [ ] `git push -u origin main`

**Verificación:** El repo aparece en GitHub con todo el código.

### A.3 SSH key del server → GitHub

**Quién:** Yo (vía SSH)
**Acciones:**
- [ ] Conectarme por SSH a tu server HawkHost
- [ ] Generar par de llaves: `ssh-keygen -t ed25519 -C "hawkhost-maxeditor"`
- [ ] Copiar la pública (`.ssh/id_ed25519.pub`)
- [ ] **Tú:** agregarla en GitHub → Settings → Deploy keys del repo (read-only)
- [ ] Test: `ssh -T git@github.com` desde el server debe dar éxito

**Verificación:** Desde el server puedes hacer `git clone git@github.com:<user>/maxeditor.git` sin contraseña.

---

## ETAPA B — Configurar HawkHost / cPanel

**Objetivo:** Subdominio configurado, Python disponible, base de datos creada.

### B.1 Crear subdominio

**Quién:** Tú (en cPanel)
**Acciones:**
- [ ] cPanel → "Subdomains" → crear `maxeditor` apuntando a `/home/<user>/maxeditor.altoque.tv/`
- [ ] cPanel → "SSL/TLS Status" → emitir certificado Let's Encrypt para `maxeditor.altoque.tv`
- [ ] Verificar que `https://maxeditor.altoque.tv` retorna 200 (página vacía está OK)

### B.2 Setup Python en cPanel

**Quién:** Yo (vía SSH) o tú (vía cPanel UI)
**Acciones:**
- [ ] cPanel → "Setup Python App" → crear app:
  - Python version: 3.11 o 3.12 (la más alta disponible)
  - App root: `/home/<user>/apps/maxeditor`
  - App URL: `maxeditor.altoque.tv`
  - Startup file: `passenger_wsgi.py`
- [ ] Activar virtualenv que cPanel crea automáticamente

### B.3 Crear base de datos PostgreSQL

**Quién:** Tú (en cPanel)
**Acciones:**
- [ ] cPanel → "PostgreSQL Databases" (o MySQL si HawkHost no tiene Postgres)
- [ ] Crear DB: `<user>_maxeditor`
- [ ] Crear usuario DB con password fuerte
- [ ] Asignar usuario a la DB con todos los privilegios
- [ ] **Anotar:** host, port, db_name, user, password (los necesitamos en `.env`)

### B.4 Variables de entorno seguras

**Quién:** Yo (vía SSH)
**Acciones:**
- [ ] Crear `/home/<user>/apps/maxeditor/.env` con:
  ```
  DJANGO_SECRET_KEY=<generado_aleatorio>
  DJANGO_DEBUG=False
  DJANGO_ALLOWED_HOSTS=maxeditor.altoque.tv
  DATABASE_URL=postgres://user:pass@host:port/dbname
  CORS_ALLOWED_ORIGINS=https://maxeditor.altoque.tv
  JWT_SIGNING_KEY=<generado_aleatorio_diferente>
  ```
- [ ] `chmod 600 .env` (solo el dueño puede leerlo)
- [ ] `.env` está en `.gitignore` — NUNCA se commitea

**Verificación:** `cat .env` muestra las variables, otros usuarios del server no pueden leerlo.

---

## ETAPA C — Sistema de auth + admin (lo que va al server)

**Objetivo:** Login funcional + admin Django para gestionar usuarios.

### C.1 Backend: app `accounts`

**Quién:** Yo
**Acciones:**
- [ ] Instalar `djangorestframework-simplejwt`, `django-cors-headers`, `python-decouple`
- [ ] Crear `backend/apps/accounts/`:
  - `models.py`: `User(AbstractUser)` con `email` único, `Subscription(plan, status, current_period_end)`
  - `serializers.py`: `RegisterSerializer`, `LoginSerializer`, `UserSerializer`
  - `views.py`: register, login, refresh, logout, me, change_password
  - `urls.py`: rutas bajo `/api/auth/`
  - `admin.py`: registrar User y Subscription en admin Django
- [ ] `settings.py`:
  - `AUTH_USER_MODEL = 'accounts.User'`
  - `INSTALLED_APPS += ['accounts', 'rest_framework', 'rest_framework_simplejwt']`
  - Configuración SimpleJWT (24h access, 30d refresh, rotation True)
- [ ] Migración inicial
- [ ] Comando: `python manage.py create_admin --email <e> --password <p>`

### C.2 Asignar `owner` a proyectos existentes

**Quién:** Yo
**Acciones:**
- [ ] Migración: agregar `Project.owner = ForeignKey(User, null=True)`
- [ ] Migración de datos: asignar todos al primer admin
- [ ] Migración: hacer `owner` not-null
- [ ] Filtrar querysets por `owner=request.user` en views editor (en una segunda pasada, no romper nada todavía)

### C.3 Frontend: páginas de auth

**Quién:** Yo
**Acciones:**
- [ ] `frontend/src/api/client.js`:
  - Axios instance con `baseURL`
  - Interceptor: agrega `Authorization: Bearer <token>` desde localStorage
  - Interceptor de respuesta: si 401 → intenta refresh, si falla → logout y redirect
- [ ] `frontend/src/pages/LoginPage.jsx`: form email + password
- [ ] `frontend/src/pages/RegisterPage.jsx`: opcional en v1 (solo admin crea usuarios)
- [ ] `frontend/src/components/ProtectedRoute.jsx`: HOC que redirige si no hay token
- [ ] `frontend/src/contexts/AuthContext.jsx`: estado global de usuario + métodos login/logout
- [ ] `App.jsx`: envolver rutas en `<AuthProvider>` y `<ProtectedRoute>`

**Verificación local:**
- Backend en `runserver`, frontend en dev mode
- Visitar `localhost:5173` → redirige a `/login`
- Login con admin creado → entra al editor
- Refresh de página mantiene sesión

### C.4 Tests de auth

**Quién:** Yo
**Acciones:**
- [ ] Test: register crea User + Subscription en estado `active` (lifetime para office)
- [ ] Test: login retorna access+refresh
- [ ] Test: refresh invalida el anterior (rotation)
- [ ] Test: 401 sin token, 200 con token válido
- [ ] Test: token expirado retorna 401

---

## ETAPA D — Deploy continuo (SSH + GitHub)

**Objetivo:** Hacer un cambio aquí en VS Code, ejecutar un comando, y verlo en producción en `maxeditor.altoque.tv`.

### D.1 Primer clone en el server

**Quién:** Yo (vía SSH)
**Acciones:**
- [ ] `cd /home/<user>/apps/maxeditor`
- [ ] `git clone git@github.com:<user>/maxeditor.git .`
- [ ] `pip install -r backend/requirements-prod.txt`
- [ ] `cd backend && python manage.py migrate`
- [ ] `python manage.py collectstatic --noinput`
- [ ] `python manage.py create_admin --email <tu_email> --password <strong>`

### D.2 Configurar passenger_wsgi.py

**Quién:** Yo
**Acciones:**
- [ ] `passenger_wsgi.py` en root del app:
  ```python
  import sys, os
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'backend'))
  os.environ['DJANGO_SETTINGS_MODULE'] = 'config.settings_prod'
  from django.core.wsgi import get_wsgi_application
  application = get_wsgi_application()
  ```
- [ ] Reiniciar app desde cPanel
- [ ] Visitar `https://maxeditor.altoque.tv/api/auth/me` → debe retornar 401 (correcto: necesita auth)

### D.3 Build + deploy del frontend

**Quién:** Yo
**Acciones:**
- [ ] En frontend: `VITE_API_BASE_URL=https://maxeditor.altoque.tv npm run build`
- [ ] El `dist/` resultante se sube al server (vía git si committed, o SFTP)
- [ ] Configurar Apache `.htaccess` o nginx para servir `dist/` en `/` y proxy `/api/` al passenger
- [ ] Visitar `https://maxeditor.altoque.tv` → ve `LoginPage`

### D.4 Script de deploy: `deploy.sh`

**Quién:** Yo
**Acciones:**
- [ ] Crear script en server:
  ```bash
  #!/bin/bash
  set -e
  cd /home/<user>/apps/maxeditor
  git pull origin main
  cd backend
  source ../venv/bin/activate
  pip install -r requirements-prod.txt
  python manage.py migrate --noinput
  python manage.py collectstatic --noinput
  cd ../frontend
  npm install
  VITE_API_BASE_URL=https://maxeditor.altoque.tv npm run build
  cp -r dist/* ../public_html/
  touch /home/<user>/apps/maxeditor/tmp/restart.txt
  ```
- [ ] `chmod +x deploy.sh`

### D.5 Workflow de actualización desde VS Code

**A partir de aquí, mi flujo cuando hagas un cambio será:**

1. Edito archivos en VS Code
2. `git add . && git commit -m "..." && git push`
3. Conexión SSH al server: `ssh <user>@<host>`
4. Ejecutar: `./deploy.sh`
5. Listo: cambios en vivo

**Esto puede automatizarse después con:**
- GitHub Actions (push a `main` → SSH al server → ejecuta deploy.sh)
- Webhooks de cPanel
- Por ahora, manual es suficiente y más seguro

**Verificación:** Cambio una palabra en `LoginPage.jsx`, push, deploy, recargo browser, veo el cambio.

---

## ETAPA E — Companion app mínima (primera instalación local)

**Objetivo:** En tu PC, ejecutar un `MaxEditor.exe` que abra una ventana, cargue `maxeditor.altoque.tv`, te dejes loguear, y veas el editor funcionando online.

### E.1 Estructura del proyecto companion

**Quién:** Yo
**Acciones:**
- [ ] Crear directorio `companion/` en el repo
- [ ] `companion/pyproject.toml` con deps: `pywebview`, `pystray`, `pillow`, `cryptography`, `pywin32`
- [ ] `companion/src/maxeditor/main.py`:
  ```python
  import webview
  import threading
  from .tray import setup_tray

  def start():
      window = webview.create_window(
          'MaxEditor',
          'https://maxeditor.altoque.tv',
          width=1400, height=900,
          min_size=(1024, 700),
          frameless=False
      )
      threading.Thread(target=setup_tray, args=(window,), daemon=True).start()
      webview.start(debug=False)

  if __name__ == '__main__':
      start()
  ```
- [ ] `companion/src/maxeditor/tray.py`: icono en system tray con menú "Mostrar / Salir"

### E.2 Logo + recursos

**Quién:** Yo
**Acciones:**
- [ ] Convertir `media/logo.png` a `.ico` (multi-resolución 16/32/48/256)
- [ ] Splash screen opcional al iniciar (mientras carga la página)

### E.3 Build del .exe (sin instalador todavía)

**Quién:** Yo
**Acciones:**
- [ ] `companion/build.py` con PyInstaller:
  ```python
  PyInstaller.__main__.run([
      'src/maxeditor/main.py',
      '--name=MaxEditor',
      '--onefile',
      '--windowed',
      '--icon=resources/icon.ico',
      '--add-data=resources;resources',
      '--noupx',
  ])
  ```
- [ ] Output: `companion/dist/MaxEditor.exe` (~30–50 MB)

**Verificación:** Doble clic al `.exe` en tu PC → se abre una ventana con `maxeditor.altoque.tv` → puedes loguear → ves el editor.

⚠️ **Limitación de esta etapa:** El editor todavía piensa que está en un browser y va a hacer requests al backend Django del server. Para v1 oficina **eso está bien** — el procesamiento FFmpeg sigue en el server hasta la Etapa F.

### E.4 Instalador básico (Inno Setup)

**Quién:** Yo
**Acciones:**
- [ ] `companion/installer/installer.iss` con Inno Setup
- [ ] Output: `MaxEditor_Setup_v0.1.0.exe` (~30 MB)
- [ ] Instala en `C:\Program Files\MaxEditor\`
- [ ] Crea acceso directo en Desktop + Start Menu

**Verificación:** Otra PC en tu oficina → descarga `MaxEditor_Setup.exe` → instala → ejecuta → loguea → trabaja.

---

## ETAPA F — Migración FFmpeg al companion (futuro)

Esto es la etapa más larga del plan SaaS, ya está detallada en
[SAAS_WORKPLAN.md](SAAS_WORKPLAN.md) Fase 2. **No es bloqueante** para tener el sistema
funcionando online en oficina — se va haciendo endpoint por endpoint sin downtime.

---

## Resumen de credenciales que necesito de ti

Para arrancar la Etapa A y B, necesito que me proporciones:

1. **GitHub:**
   - [ ] Username de GitHub
   - [ ] Email asociado
   - [ ] (Te guío para crear el repo privado)

2. **HawkHost SSH:**
   - [ ] Hostname SSH (típicamente `<server>.hawkhost.com`)
   - [ ] Username de SSH (suele ser tu usuario de cPanel)
   - [ ] Puerto SSH (usualmente 22 o uno custom)
   - [ ] Método de auth: ¿password o llave SSH?

3. **HawkHost cPanel:**
   - [ ] URL de cPanel (ej. `https://server.hawkhost.com:2083`)
   - [ ] Username/password (los necesitas para crear DB y subdominio — yo te guío, no me los compartas, los usas tú)

4. **DNS de altoque.tv:**
   - [ ] ¿Lo manejas en HawkHost o en otro proveedor (Cloudflare, GoDaddy, Namecheap)?
   - Necesito esto para crear el subdominio `maxeditor.altoque.tv`

---

## Orden recomendado para hoy / esta semana

1. **HOY:** Tú me das los datos arriba (sin contraseñas — solo username, host, puerto). Decidimos juntos qué cuenta de GitHub usar.
2. **HOY/MAÑANA:** Creo la app `accounts` localmente y la pruebo aquí en tu PC. Login funcional contra `localhost`.
3. **DESPUÉS:** Conectamos al server, configuramos el subdominio, primer deploy, login funciona en `maxeditor.altoque.tv`.
4. **DESPUÉS:** Construimos el primer `MaxEditor.exe` que abre tu sitio en una ventana nativa.

---

## Reglas de oro de seguridad (siempre)

| Regla | Por qué |
|---|---|
| **Nunca commitear `.env`** | Contiene SECRET_KEY, DB password, JWT key |
| **Nunca usar `runserver` en producción** | Es solo dev, tiene fugas, sin auth headers correctos |
| **DEBUG=False en producción** | Si crashea, NO mostrar stacktrace al cliente |
| **Passwords solo bcrypt** | Django lo hace por defecto, no cambiar |
| **HTTPS obligatorio** | Cualquier auth sobre HTTP es robable trivialmente |
| **CORS estricto** | Solo `https://maxeditor.altoque.tv`, no `*` |
| **SECRET_KEY rotable** | Si se filtra, se cambia y se invalidan sesiones |
| **Logs sin info sensible** | No loguear passwords, tokens, ni datos personales |
| **Backups diarios** | cron job que dumpea Postgres a un folder protegido |

---

**→ Documento vivo. Actualizar al completar cada etapa.**
