# MaxEditor — SaaS Companion App: Plan de Implementación
**Fecha:** Abril 2026 | **Plataforma v1:** Windows | **Pagos:** Stripe

---

## Visión del sistema

El usuario descarga e instala un pequeño programa (`MaxEditor Setup.exe`).
Al ejecutarlo:
1. Se instalan dependencias locales (FFmpeg, Python runtime embebido)
2. Se levanta un servidor local en `https://localhost:7432`
3. Se abre el browser en `altoque.tv/maxeditor`
4. El usuario hace login → el server verifica suscripción activa → carga el editor
5. Todo el procesamiento de video (export, probe, crop) ocurre en su máquina
6. El server nunca recibe archivos de video

---

## Módulos del sistema

### A. Companion App (local, en la máquina del cliente)
### B. Auth & Subscriptions (en altoque.tv)
### C. UI Adapter (cambios al frontend existente)
### D. Local API Bridge (reemplaza llamadas al backend Django actual)

---

## MÓDULO A — Companion App

### A1. Estructura del proceso

```
MaxEditorCompanion.exe
  ├── Al iniciar:
  │   ├── Verifica FFmpeg (descarga si falta)
  │   ├── Verifica token JWT local (archivo encriptado en AppData)
  │   ├── Levanta FastAPI en https://localhost:7432
  │   └── Abre browser en altoque.tv/maxeditor
  ├── Mientras corre:
  │   ├── Tray icon (systray) con estado y opción de cerrar
  │   └── Acepta requests del frontend via localhost
  └── Al cerrar:
      └── Mata el servidor local
```

### A2. Endpoints locales que expone la companion app

| Endpoint | Método | Descripción |
|---|---|---|
| `/health` | GET | Confirma que la app está corriendo (el frontend lo consulta al cargar) |
| `/auth/token` | GET/POST | Leer/guardar JWT local encriptado |
| `/ffmpeg/probe` | POST | Recibe path de archivo, retorna metadata (duración, FPS, resolución) |
| `/ffmpeg/export` | POST | Recibe instrucciones de export, corre FFmpeg, retorna progreso via SSE |
| `/ffmpeg/cancel` | POST | Cancela export en curso |
| `/file/pick` | POST | Abre diálogo nativo de selección de archivo, retorna path |
| `/file/save-dialog` | POST | Abre diálogo nativo "guardar como", retorna path destino |
| `/settings/get` | GET | Lee configuración local (API keys, paths, preferencias) |
| `/settings/set` | POST | Guarda configuración local encriptada |
| `/version` | GET | Versión de la companion app (para actualizaciones) |

### A3. Seguridad del servidor local

- El servidor local genera un **token de sesión aleatorio de 32 bytes** al iniciar
- Lo escribe en `AppData\Local\MaxEditor\session.token`
- El browser lo lee via `altoque.tv/maxeditor` solo si la página fue lanzada por la app
- Cada request al localhost debe incluir `X-Companion-Token: <token>` en el header
- Sin ese token, el servidor local rechaza requests (previene que otras páginas accedan)

### A4. Certificado local HTTPS

- La companion genera un certificado autofirmado para `localhost` al primer inicio
- Lo instala en el store de certificados de Windows (requiere elevación una sola vez)
- Esto permite HTTPS → HTTPS sin "mixed content" errors en Chrome
- El cert se renueva automáticamente si expira (1 año de validez)

### A5. Almacenamiento local

```
AppData\Local\MaxEditor\
  ├── session.token          ← token de seguridad de la sesión actual
  ├── auth.json              ← JWT encriptado con DPAPI de Windows
  ├── settings.json          ← API keys y config, encriptado con DPAPI
  ├── ffmpeg\
  │   └── ffmpeg.exe         ← binario FFmpeg descargado automáticamente
  └── logs\
      └── companion.log      ← logs de la app
```

**Cifrado:** Se usa `win32crypt.CryptProtectData` (DPAPI) — el archivo solo puede ser
descifrado por el mismo usuario de Windows en la misma máquina. Sin contraseña extra.

### A6. Stack técnico de la companion app

| Componente | Tecnología |
|---|---|
| Servidor local | FastAPI + uvicorn |
| Proceso FFmpeg | `subprocess` + SSE para progreso |
| Tray icon | `pystray` + `Pillow` |
| Cert local | `cryptography` (pyca) |
| Cifrado local | `pywin32` (DPAPI) |
| Empaquetado | PyInstaller (onefile + windowed) |
| Instalador | Inno Setup (genera `MaxEditor Setup.exe`) |

### A7. Proceso de instalación para el usuario

```
1. Descarga MaxEditor_Setup.exe desde altoque.tv/download
2. Ejecuta → UAC prompt (solo para instalar cert y escribir en Program Files)
3. Instalador:
   a. Copia companion app a C:\Program Files\MaxEditor\
   b. Descarga FFmpeg (~80MB) durante instalación
   c. Instala certificado de localhost
   d. Crea acceso directo en Desktop y en Startup (autostart opcional)
4. Lanza app automáticamente al terminar
5. Browser abre en altoque.tv/maxeditor/login
```

### A8. Actualizaciones

- Al iniciar, la app llama a `altoque.tv/api/companion/version-check`
- Si hay versión nueva: muestra notificación en el tray
- El usuario descarga manualmente el nuevo instalador (v1 — sin auto-update)
- v2: implementar Squirrel o NSIS auto-update

---

## MÓDULO B — Auth & Subscriptions (en tu servidor)

### B1. Flujo de autenticación completo

```
[Usuario abre browser en altoque.tv/maxeditor]
        ↓
[Frontend consulta companion: GET https://localhost:7432/health]
   → Si falla: muestra "Instala MaxEditor companion app" con link de descarga
   → Si OK: continúa
        ↓
[Frontend consulta companion: GET https://localhost:7432/auth/token]
   → Si hay JWT válido y no expirado: carga editor directo
   → Si no hay o expiró: muestra página de login
        ↓
[Usuario ingresa email + password]
        ↓
[POST altoque.tv/api/auth/login]
   → Verifica credenciales
   → Verifica suscripción activa en Stripe
   → Retorna JWT con: user_id, email, plan, expiry (24h), companion_required: true
        ↓
[Frontend envía JWT a companion: POST https://localhost:7432/auth/token]
[Companion lo cifra con DPAPI y lo guarda en AppData]
        ↓
[Editor carga]
```

### B2. Verificación de suscripción en cada sesión

- El JWT dura **24 horas** (renovación silenciosa si la suscripción sigue activa)
- Al renovar: el frontend llama a `altoque.tv/api/auth/refresh`
- El server verifica en Stripe que la suscripción no fue cancelada/pausada
- Si la suscripción expiró: el editor muestra modal "Tu suscripción ha vencido" con link a billing

### B3. Endpoints en tu servidor

| Endpoint | Descripción |
|---|---|
| `POST /api/auth/register` | Registro nuevo usuario |
| `POST /api/auth/login` | Login → retorna JWT |
| `POST /api/auth/refresh` | Renueva JWT si suscripción sigue activa |
| `POST /api/auth/logout` | Invalida sesión server-side |
| `GET /api/auth/me` | Datos del usuario actual |
| `POST /api/billing/create-checkout` | Crea sesión de Stripe Checkout |
| `POST /api/billing/portal` | Link al portal de Stripe (gestionar suscripción) |
| `POST /api/billing/webhook` | Webhook de Stripe (activar/cancelar subs) |
| `GET /api/companion/version-check` | Versión más reciente de la companion app |

### B4. Modelo de datos (nuevo, independiente del editor actual)

```python
# Tabla: users
- id (UUID)
- email (unique)
- password_hash (bcrypt)
- created_at
- stripe_customer_id

# Tabla: subscriptions
- id
- user_id (FK)
- stripe_subscription_id
- plan (basic | pro | lifetime)
- status (active | past_due | canceled | trialing)
- current_period_end
- created_at
- updated_at
```

### B5. Planes sugeridos

| Plan | Precio sugerido | Límites |
|---|---|---|
| Trial | Gratis 7 días | Funcionalidad completa |
| Pro | $29/mes o $199/año | Sin límites |
| Lifetime | $499 pago único | Sin límites, sin renovación |

---

## MÓDULO C — UI Adapter (cambios al frontend existente)

### C1. Detección de modo

El frontend detecta si está corriendo con companion app o en modo dev local:

```javascript
// Al iniciar la app
const COMPANION_URL = 'https://localhost:7432'

async function detectMode() {
  try {
    const res = await fetch(`${COMPANION_URL}/health`, { timeout: 2000 })
    const data = await res.json()
    return { mode: 'companion', token: data.session_token }
  } catch {
    return { mode: 'dev' } // modo desarrollo: todo sigue igual
  }
}
```

### C2. Pantalla de login (nueva, solo en modo companion)

- Si no hay JWT válido → mostrar `LoginPage` antes de cargar el editor
- Diseño: logo + email/password + botón login + link "crear cuenta" + link "olvidé contraseña"
- Si companion no está corriendo → mostrar `CompanionRequiredPage` con botón de descarga

### C3. Reemplazos de endpoints

En modo companion, las llamadas de export/FFmpeg que hoy van al backend Django
se redirigen al companion local:

```
HOY:   POST /api/export/render    → Django → FFmpeg en el server
NUEVO: POST https://localhost:7432/ffmpeg/export  → FFmpeg en la máquina del usuario
```

Esta es la parte de mayor trabajo técnico. Requiere un **adapter layer** en el frontend
que decida a dónde mandar cada llamada según el modo detectado.

### C4. Gestión de API keys en el UI

Nueva sección en Settings del editor:
- Campo para OpenAI API key (si se usa para transcripción/IA)
- Campo para Whisper API key
- Botón "Guardar en mi máquina" → `POST https://localhost:7432/settings/set`
- Las keys nunca van a tu server

---

## MÓDULO D — Local API Bridge

### D1. Qué sigue en tu server vs qué se mueve al cliente

| Función | ¿Dónde en v1? | Notas |
|---|---|---|
| Auth + suscripciones | Tu server | Siempre |
| UI (React build) | Tu server | Siempre |
| Metadatos de proyectos | Tu server (o local en v2) | TBD |
| Transcripción (Whisper) | Cliente (con su API key) | Key del usuario |
| Llamadas IA (OpenAI) | Cliente (con su API key) | Key del usuario |
| FFmpeg export | Cliente | Core de la companion app |
| FFmpeg probe | Cliente | |
| Crop / preview | Cliente | |
| Almacenamiento de videos | Solo local (nunca sube) | Crítico |

---

## Timeline de fases

### Fase 1 — Auth + Subscriptions en el server (2-3 semanas)
- [ ] Nuevo módulo Django `accounts` con modelos User + Subscription
- [ ] Endpoints de auth (register, login, refresh, logout)
- [ ] Integración Stripe: checkout, webhook, portal
- [ ] Página de login en el frontend (modo companion)
- [ ] Página "companion requerida" con link de descarga

### Fase 2 — Companion App core (2-3 semanas)
- [ ] FastAPI + uvicorn con los endpoints básicos (`/health`, `/auth/token`)
- [ ] Generación e instalación de cert local
- [ ] DPAPI: cifrado/descifrado de auth.json y settings.json
- [ ] Tray icon con pystray
- [ ] Descarga automática de FFmpeg al instalar
- [ ] Integración con browser (abre altoque.tv al iniciar)

### Fase 3 — Endpoints FFmpeg locales (1-2 semanas)
- [ ] `/ffmpeg/probe` — metadata de archivos
- [ ] `/ffmpeg/export` — export completo con progreso SSE
- [ ] `/ffmpeg/cancel` — cancelación de export en curso
- [ ] Diálogos nativos de archivo (`/file/pick`, `/file/save-dialog`)

### Fase 4 — Adapter layer en el frontend (1-2 semanas)
- [ ] `useCompanionMode()` hook: detecta si companion está corriendo
- [ ] Adapter que redirige calls de export al companion
- [ ] Settings UI para API keys
- [ ] Manejo de errores: companion caído, token expirado, suscripción vencida

### Fase 5 — Empaquetado + Instalador (1 semana)
- [ ] PyInstaller: companion → `.exe` standalone
- [ ] Inno Setup: `MaxEditor Setup.exe` con FFmpeg incluido
- [ ] Firma de código (opcional para v1, necesario para evitar SmartScreen en v2)
- [ ] Página de descarga en altoque.tv/download

### Fase 6 — QA + Hardening (1 semana)
- [ ] Test en máquina limpia (sin Python, sin nada)
- [ ] Test de flujo completo: install → login → export → close
- [ ] Test de casos edge: companion caído mid-session, suscripción expirada, FFmpeg faltante
- [ ] Rate limiting en endpoints de auth (anti-brute force)

---

## Decisiones técnicas pendientes (a confirmar antes de implementar)

| Decisión | Opciones | Recomendación |
|---|---|---|
| ¿Proyectos guardados dónde? | Local en cliente / En tu server | Server en v1 (más simple, ya existe) |
| ¿Auto-start de companion con Windows? | Sí por defecto / Opcional | Opcional (menos invasivo) |
| ¿Trial sin tarjeta? | Sí / No | Sí (7 días, maximiza conversión) |
| ¿Una sola suscripción por usuario? | Sí / Multi-seat | Una en v1 |
| ¿Versión Mac en v2? | Sí / No | Sí (mismo código Python, diferente empaquetado) |

---

## Riesgos identificados

| Riesgo | Probabilidad | Mitigación |
|---|---|---|
| Antivirus bloquea companion app | Media | Firma de código en v1.1, documentar en FAQ |
| SmartScreen warning al instalar | Alta | Firma de código (EV cert ~$300/año) o instrucciones para usuarios |
| Usuario pierde su API key | Media | Exportar/importar settings en JSON |
| Companion no inicia (puerto ocupado) | Baja | Probar puertos alternativos (7433, 7434...) |
| Cert local falla en Edge/Firefox | Baja | Documentar y dar instrucciones, Edge usa mismo store que Chrome |

---

## Próximo paso inmediato

**Arrancar con Fase 1:** Crear el módulo `accounts` en Django con auth básica + Stripe.
Esto no requiere tocar nada del editor actual y puede desplegarse en paralelo.

