MEMORIA FUNCIONAL — SocialCutter
Producto: SocialCutter — Procesador de imágenes para redes sociales
URL live: https://socialcutter.theboomer.dev
Frontend: code/frontend/src/App.tsx (+ components/ImageUploader, PlatformSelector, ProcessingOptions, Results, CreditsDisplay, QuotaBadge, ApiKeysManager, pages/, services/socialcutter.service.ts)
Backend: FastAPI (code/src/main.py + src/api/routes/{process,credits,history,apikeys,auth}.py); GridFS/MongoDB; auth Clerk
Fuente: Documento generado a partir del código real del frontend y del backend.
1. Introducción
SocialCutter redimensiona y optimiza una imagen de origen para múltiples formatos de redes sociales en una sola operación. El usuario sube una imagen (URL o archivo), selecciona una o varias combinaciones plataforma+formato (con vista previa del ratio de aspecto), ajusta calidad/formato de salida/color de fondo y obtiene una o varias imágenes listas para descargar (individualmente o en ZIP). Incluye autenticación Clerk, sistema de créditos por destino + cuota diaria, historial con las imágenes procesadas, API keys, facturación Stripe y enlace a la documentación de la API (/docs).
UI bilingüe ES/EN (default es) y tema dark/light/system (default dark), persistidos en localStorage.
2. Tipos de usuario
| Tipo | Acceso |
|---|---|
| Usuario anónimo | Puede procesar 1 imagen/día (localStorage anon_processes + límite IP en backend). Tras el primer proceso se abre el UpgradeDialog. |
| Usuario registrado (Free) | 3 procesos/día (plan free). Badge de cuota usados/límite + CreditsDisplay de créditos diarios. |
| Usuario Pro / Enterprise | Límites mayores según plan (stripe-api); extra_rate para excesos con cargo; API keys disponibles (el gestor de API keys no está restringido a Enterprise en la UI actual). |
Regla clave: cada destino (plataforma+formato) consume 1 crédito. Un proceso con 4 destinos cuesta 4 créditos del límite diario (el backend valida y descuenta dest_count créditos).
3. Funcionalidades
F-101 Autenticación con Clerk
- Descripción: Login/registro modal de Clerk (Google SSO). Token JWT en
localStorage('clerk_token'), enviado comoAuthorization: Bearer. - Flujo: Botón "Iniciar sesión" (header) → modal Clerk →
useClerkToken()→ UI autenticada (CreditsDisplay + QuotaBadge + badge "Gratis" + UserButton). - Validaciones: Billing y Profile requieren token (pantalla "Inicia sesión..." si no).
afterSignOutUrl="/".
F-102 Selección de imagen (URL o archivo)
- Descripción: Componente
ImageUploadercon dos modos de origen. - Modo URL: radio "URL" → input
https://example.com/image.jpg+ botón "Use URL" → preview conmax-h-48; si la imagen falla al cargar (onError) se limpia el valor. - Modo Base64/File: radio "Base64 / File" → zona drag & drop (borde punteado, highlight
border-primaryal arrastrar) o botón "Select File" (accept="image/*"); el archivo se lee conFileReader.readAsDataURLy se marcaimageType='base64'. Preview de la imagen cargada + texto "Image loaded". - Validaciones: solo se aceptan archivos con
file.type.startsWith('image/')en el drop.
F-103 Selección de plataformas y formatos
- Descripción: Componente
PlatformSelector. 6 plataformas con iconos SVG y color de marca: Instagram, Facebook, X/Twitter, LinkedIn, YouTube, TikTok. - Flujo: 1) Clic en plataforma (botón coloreado, highlight con el color de marca) → 2) se muestran las tarjetas de formato de esa plataforma (2 columnas) con preview visual del ratio de aspecto (SVG), nombre del formato, resolución y ratio → 3) clic en una tarjeta la añade/quita de los destinos (toggle) → 4) bajo la cuadrícula, chips de "Selected (N)" con icono de plataforma, formato, resolución y X para quitar; contador de créditos "⚡ N credit(s)".
- Formatos disponibles (idénticos en frontend y backend):
| Plataforma | Formatos (resolución, ratio) |
|---|---|
| post 1080×1080 (1:1) · story 1080×1920 (9:16) · landscape 1080×566 (1.91:1) | |
| post 1200×630 (1.91:1) · story 1080×1920 (9:16) · cover 820×312 (2.63:1) | |
| X/Twitter | post 1200×675 (16:9) · header 1500×500 (3:1) |
| post 1200×627 (1.91:1) · cover 1128×191 (5.9:1) | |
| YouTube | thumbnail 1280×720 (16:9) · banner 2560×1440 (16:9) |
| TikTok | cover 1080×1920 (9:16) |
- Validaciones: cada destino se crea con
fit_mode: 'cover'fijo; al menos 1 destino es obligatorio para procesar (si no: error "Please select at least one platform/format").
F-104 Opciones de procesamiento
- Descripción: Modal
ProcessingOptions(botón punteado "Processing Options — Quality · Format · Background"). - Controles:
- Quality: slider 1–100 (default 85), labels "Smaller file" / "Better quality".
- Output Format: WebP (default) | PNG | JPEG.
- Background Color: color picker nativo + input hex (default
#000000). - Botón "Done" aplica y cierra.
- Detalle UX: el backdrop no cierra el modal mientras el color picker nativo está abierto (muestra aviso "Close the color picker before closing this dialog").
F-105 Procesar imagen
- Descripción: Acción principal. Botón full-height con gradiente "Procesar Imagen" (icono Wand2).
- Flujo:
1. Validación previa: imagen obligatoria ("Please select an image") y ≥1 destino ("Please select at least one platform/format").
2.
POST {API_BASE}/api/v1/images/processcon body{ source: {type: 'url'|'base64', value}, destinations: [{platform, format, fit_mode}], options: {quality, format, background_color} }+ Bearer si hay sesión. 3. Éxito →data.outputs→ sección Results. Anónimo:incrementAnonProcesses()→ UpgradeDialog. Autenticado:incrementTodayUsage()(y el backend descuenta créditos). 4. Error → banner rojo condata.detail. - Estados del botón: disabled (gris, cursor not-allowed) sin imagen o sin destinos; spinner "Procesando..." durante
loading. - Backend: valida origen, plataforma,
fit_mode(cover/contain/fill/stretch) y formato (png/jpg/jpeg/webp/gif); comprueba y descuenta 1 crédito por destino (_check_credits→ 429 si insuficiente: "Not enough credits. You have X remaining but Y are required."); procesa con Pillow; guarda outputs en GridFS; registra en historial.
F-106 Resultados y descarga
- Descripción: Sección "Resultados" con las imágenes procesadas.
- UI por resultado: preview (aspect-square, object-contain), nombre "Plataforma Formato", resolución
W × H, tamaño (formateado B/KB/MB), botón "Download" (abre en nueva pestaña, filename{platform}_{format}.{ext}). - Descargar todo (ZIP): si hay >1 resultado, botón "Download All (ZIP)" → descarga cada output vía fetch, empaqueta con JSZip (import dinámico) y genera
socialcutter-images.zip.
F-107 Créditos (CreditsDisplay)
- Descripción: Badge del header (solo autenticados) con
⚡ restantes/total(ej.2/3). - Flujo:
GET /api/v1/credits→{remaining, total, used}. Siremaining <= 1pasa a ámbar y muestra "Más créditos". - Interacción: clic → vista Billing.
F-108 Quota badge (cuota diaria)
- Descripción: Badge
usados/límite(ej.2/3) + código de plan, igual patrón que ThumbnailGen (localStorage('usage_log')+plan.limits.daily_captionsvía stripe-api). Clic → Billing si el plan es free.
F-109 Historial
- Descripción: Página "Historial" con las imágenes procesadas del usuario.
- Flujo:
GET /api/v1/history(Bearer) →{items: [{id, source_type, destinations, outputs, metadata, created_at}]}. - UI: buscador "Buscar por plataforma..." (filtra por
destinations[].platform), tarjetas con fecha (locale es/en), etiquetasource_type, grid de miniaturas de outputs (hover → overlay "Descargar" que abre el archivo), cada miniatura con "plataforma · formato" y "WxH · tamaño". - Estados: loading (spinner + "Cargando..."), error (banner), vacío ("Aún no has procesado imágenes").
F-110 API keys
- Descripción: Componente
ApiKeysManager(en Perfil). Crear, copiar y revocar claves API para acceso programático. - Flujo: botón "Crear Clave API" (nombre) →
POST /api/v1/apikeys→ se añade a la lista → copiar (feedback "¡Copiado!") o revocar (DELETE /api/v1/apikeys/{id}) con icono papelera. - Backend:
GET/POST /api/v1/apikeys,DELETE /api/v1/apikeys/{id}; previewkey_previewylast_used.
F-111 Facturación (Billing) y Perfil
- Descripción: Mismo patrón tentpole que ThumbnailGen: "Tu Plan" (suscripción, estado, renovación, importe), "Planes de Precios" (
PlanCardcon límites: thumbnails diarios, caracteres máx., idiomas, acceso API, modelo IA), "Gestionar facturación" (portal Stripe), paquetes de créditos, historial de facturas, bloqueo sin token. - Perfil: Información del usuario (Clerk), "Plan y Uso" (plan, límite diario, usados, restantes, créditos extra, acceso API) y Claves API (ApiKeysManager).
F-112 Enlace a documentación API
- Descripción: Enlace "API" (icono BookOpen) en la navegación →
/docs(documentación FastAPI/Scalar del backend, nueva pestaña).
4. Pantallas (wireframes textuales)
P1 — Header
┌──────────────────────────────────────────────────────────────────────────┐
│ [✨ SocialCutter ] Generator | Historial | Facturación | Perfil │
│ Procesador de imágenes │ API │
│ para redes sociales │ [⚡2/3] [2/3 ⬢free] [👤] [🌙☀️🖥] [ES▾]│
└──────────────────────────────────────────────────────────────────────────┘
- Sticky con backdrop-blur. Derecha autenticado: CreditsDisplay, QuotaBadge, badge "Gratis", UserButton, tema, idioma. Anónimo: "Iniciar sesión". Móvil: pestañas inferiores.
P2 — Generator (bento grid 5 columnas)
┌───────────────────────────┬───────────────────────────────────────────────┐
│ [Step 1] │ [Step 2] │
│ Seleccionar Imagen │ Elegir Plataformas │
│ (2 cols, min 25vh) │ (3 cols) │
│ │ [📸 Instagram][📘 Facebook][🐦 X/Twitter] │
│ ( ) URL ( ) Base64/File │ [💼 LinkedIn][▶ YouTube][🎵 TikTok] │
│ ┌───────────────────────┐ │ ┌──────────┐ ┌──────────┐ │
│ │ input URL + Use URL │ │ │ [ratio] │ │ [ratio] │ │
│ │ — o — │ │ │ post │ │ story │ │
│ │ zona drag&drop │ │ │ 1080×1080│ │ 1080×1920│ │
│ │ [Select File] │ │ │ 1:1 │ │ 9:16 │ │
│ └───────────────────────┘ │ └──────────┘ └──────────┘ │
│ (preview imagen cargada) │ Selected (2) ⚡ 2 credits │
│ │ [📸post×1080 ✕] [🎵cover×1080 ✕] │
├───────────────────────────┼───────────────────────────────────────────────┤
│ ┌───────────────────────┐ │ ┌───────────────────────────────────────────┐ │
│ │ Processing Options │ │ │ [✨ Procesar Imagen] │ │
│ │ Quality·Format·Bg │ │ │ (botón gradiente full-height) │ │
│ └───────────────────────┘ │ └───────────────────────────────────────────┘ │
└───────────────────────────┴───────────────────────────────────────────────┘
[Error banner rojo si error]
Resultados
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ [preview] │ │ [preview] │ │ ... │
│ Instagram post│ │ TikTok cover │ │ │
│ 1080 × 1080 │ │ 1080 × 1920 │ │ │
│ 245.3 KB │ │ 312.1 KB │ │ │
│ [Download] │ │ [Download] │ │ │
└───────────────┘ └───────────────┘ └───────────────┘
[⬇ Download All (ZIP)] (si >1 resultado)
P3 — Modal Processing Options
┌────────────────────────────────────────┐
│ ⚙ Processing Options [×] │
│ Calidad: 85% │
│ ────●────────────────── Smaller file │
│ Better quality│
│ Formato de salida: [WebP][PNG][JPEG] │
│ Color de fondo: [🟥][ #000000 ] │
│ (aviso si color picker abierto) │
│ [ Done ] │
└────────────────────────────────────────┘
P4 — Billing / Profile / History
- Billing: igual wireframe que ThumbnailGen (Tu Plan · Planes de Precio · Paquetes de Créditos · Historial de Facturas; bloqueo sin token).
- Profile: Información del Usuario · Plan y Uso (plan/límite/usados/restantes/créditos extra/acceso API) · Claves API (
ApiKeysManagersin gating enterprise). - History: buscador por plataforma + tarjetas con miniaturas y descarga hover.
P5 — UpgradeDialog (modal global)
┌───────────────────────────────┐
│ [×] │
│ ✨ │
│ Desbloquea mas procesos │
│ "Crea una cuenta gratis y │
│ obten 3 procesos al dia!" │
│ [G Iniciar sesion con Google]│
└───────────────────────────────┘
5. Flujos de trabajo
Flujo 1 — Anónimo procesa una imagen
- Step 1: pega una URL y pulsa "Use URL" (o arrastra un archivo).
- Step 2: elige Instagram → tarjeta "post" → aparece chip Selected (1) y "⚡ 1 credit".
- (Opcional) Abre Processing Options y ajusta calidad/formato/fondo → Done.
- "Procesar Imagen" → spinner → Resultados (1 output) → descarga.
- Se abre el UpgradeDialog ("Crea una cuenta gratis y obten 3 procesos al dia!").
Flujo 2 — Usuario registrado procesa multi-formato
- Login → header muestra
⚡ 3/3y QuotaBadge. - Selecciona 2 plataformas + 3 formatos (ej. IG post + IG story + TikTok cover) → "⚡ 3 credits".
- Procesa → 3 outputs → "Download All (ZIP)" descarga
socialcutter-images.zip. - El historial guarda el job (con
processing_time_ms); los créditos se descuentan (3). - Con 1 crédito restante, el badge pasa a ámbar con "Más créditos".
Flujo 3 — Cuota agotada
- Backend responde 429 (o "Not enough credits...") → banner de error → clic en CreditsDisplay/QuotaBadge → Billing → suscripción o paquete de créditos → nuevo límite.
6. Reglas de negocio
Créditos y cuotas
- 1 crédito = 1 destino procesado (
dest_count = len(destinations), mínimo 1). Se valida antes (check_credits) y se descuenta después de validar la petición (deduct_credit); sin saldo → HTTP 429. - Cuota diaria (stripe-api): anónimo 1/día por IP (frontend:
localStorage('anon_processes')), registrado free 3/día (plan.limits.daily_captions), Pro/Enterprise según plan;extra_rate > 0permite exceso con cargo. CreditsDisplaymuestra el resumen diario real del backend (/api/v1/credits: remaining/total/used).
Validaciones de entrada (backend)
- Origen:
typeurl o base64; url válida; base64 decodificable. - Plataforma: instagram, facebook, twitter, linkedin, youtube, tiktok.
- Formato de salida: png, jpg, jpeg, webp, gif.
fit_mode: cover, contain, fill, stretch (descripciones: "Fill area, crop excess" / "Fit within, add padding" / "Stretch to fill (may distort)" / "Force exact dimensions").- Tamaño máx. por formato definido en
platforms.py(ej. twitter header 2 MB, youtube thumbnail 2 MB, facebook 45 MB, resto 30 MB).
API (endpoints)
| Método | Endpoint | Uso |
|---|---|---|
| POST | /api/v1/images/process |
Procesar (source + destinations + options) → {outputs: [{platform, format, url, width, height, size_bytes}]} |
| POST | /api/v1/images/upload |
Procesar base64 (alternativa) |
| POST | /api/v1/images/upload/file |
Procesar multipart (file + destinations/options JSON) |
| POST | /api/v1/images/batch |
Procesar lote de imágenes (no usado por la UI actual) |
| GET | /api/v1/images/{id} |
Detalle de un job |
| GET | /api/v1/platforms / /api/v1/formats / /api/v1/fit-modes |
Catálogos |
| GET | /api/v1/credits |
Resumen de créditos |
| GET | /api/v1/history |
Historial de procesados |
| GET/POST/DELETE | /api/v1/apikeys[/{id}] |
Gestión API keys |
| GET/POST | /api/v1/auth/me, /api/v1/auth/sync |
Perfil y sync Clerk |
| GET | /api/v1/billing/pricing-plans · /summary · /invoices · /credit-packs |
Billing |
| POST | /api/v1/billing/create-checkout-session · /create-portal-session · /buy-credits |
Checkout/portal/créditos |
| GET | /api/v1/storage/{file_id} |
Servir archivos GridFS (Cache-Control 1 año) |
Notas técnicas
API_BASE=VITE_API_URL(en prodhttps://api.socialcutter.theboomer.dev) o proxy de Vite en dev.- Auth:
localStorage('clerk_token'), refresco víawindow.Clerk.session.getToken()(polling 10s). - Storage: MongoDB + GridFS;
X-Tentpole-Versionen cada respuesta.