mseller docs

Arquitectura del stack

Cómo se comunican los componentes del stack mseller — UI web, apps móviles, integraciones externas y backend.

El stack mseller tiene varios componentes que se comunican sobre HTTP y Firebase. Cada API en mseller-api está hecha para un consumidor específico:

Roles de cada repo

mseller-api (.NET 10)

Tres APIs en una solución, una por tipo de consumidor:

  • Portal.Api (puerto 5186) — Endpoints administrativos para la UI web. Consumidor único: mseller-cloud (Next.js). Cubre catálogos, reportes, configuración del tenant, gestión de usuarios.
  • Consumo.Api (puerto 5188) — Endpoints para las apps móviles que usa la operación en campo:
    • mobile-seller (iOS, Swift) — vendedores tomando pedidos en ruta.
    • mseller-lite (React Native / Expo) — almacén: picking de pedidos y carga de camiones.
  • Ingestion.Api (puerto 5187) — Endpoints para integraciones de sistemas externos (SAP, otros ERPs, cargas batch). Recibe data de upstream y la sincroniza con Postgres y, cuando aplica, con Firestore.

Persistencia: PostgreSQL (multi-tenant por businessId — un filtro global en AppDbContext se asegura de que cada query solo vea la data del tenant del usuario autenticado).

Autenticación: Firebase Auth (verifica los tokens JWT que emite mseller-firebase, ya sea desde producción o desde el emulador local).

mseller-firebase (Cloud Functions)

  • Funciones HTTP/callable para sign-up, perfil de usuario, generación de PDFs, integraciones de email/WhatsApp.
  • Reglas de Firestore y Storage.
  • Configuración de los emuladores que el resto del stack usa localmente.

Las funciones están en TypeScript (Node 22) y se despliegan automáticamente al hacer merge a main vía GitHub Actions, autenticándose con una cuenta de servicio dedicada (github-deploy).

mseller-cloud (Next.js)

UI administrativa. Habla con:

  • mseller-api vía REST (variable de entorno TARGET).
  • mseller-firebase vía httpsCallable (Firebase Functions SDK).
  • Firestore directamente para algunas lecturas en tiempo real.

Estilo y componentes: MUI v5 + tema Materio. Estado: Redux Toolkit.

Apps móviles

Las apps móviles no están en el web stack — viven en repos separados y solo necesitas clonarlas si vas a tocarlas.

mobile-seller (iOS, Swift)

App nativa (Swift 5 + Objective-C) para los vendedores en ruta — toma de pedidos, cobros, facturación, inventario, y demás operaciones de campo. Consume Consumo.Api con Firebase Auth en el header Authorization: Bearer {token}. Para trabajar aquí necesitas macOS + Xcode 16.3 + CocoaPods.

mseller-lite (React Native / Expo)

App de almacén — flujo de preparación (picking de pedidos) y carga de camión. Conecta a Consumo.Api con axios y JWT de Firebase, igual que la iOS. Es online-only para los screens de O2D (no persiste esos flujos en AsyncStorage).

Integraciones externas

Sistemas como SAP y otros ERPs no usan el Portal ni el Consumo — empujan data al stack vía Ingestion.Api (cargas batch). La autenticación ahí es por API key (ApiKeyAuthFilter), no con JWT de Firebase: el sistema externo tiene una llave por tenant, no una sesión de usuario.

mseller-docs (este repo)

Documentación interna. Built con Fumadocs sobre Next.js, deployado en Vercel.

mseller-marketing-site

Sitio público de marketing en mseller.app — landing, features, pricing, etc. Es Next.js 14 + Tailwind, deployado en Vercel. No habla con mseller-api ni con Firebase: es estático/contenido público. Los CTAs ("Iniciar sesión", "Probar la app") apuntan a cloud.mseller.app.

Identidad y multi-tenancy

Cada business (tenant) tiene un UUID único compartido entre los tres sistemas:

  1. Firebase Auth — el usuario tiene un custom claim business: <id>.
  2. Firestore — colección business/<id> con la configuración del tenant.
  3. PostgreSQL — todas las tablas tenant-scope tienen una columna BusinessId filtrada automáticamente por AppDbContext.

Cuando los tres están en sincronía, todo funciona. Cuando se desfasan (por ejemplo, cuando se borra el usuario en Firebase pero queda data en Postgres), se generan tenants huérfanos.

Por esto el seed de desarrollo usa un businessId fijo: 00000000-0000-0000-0000-000000000001. Si reseteas el emulador y vuelves a sembrarlo, el ID es el mismo, así que cualquier data en Postgres asociada a ese tenant sigue siendo accesible.

Despliegue (alto nivel)

RepoEntornoCómo se despliega
mseller-apiAzure App Service (3 apps)GitHub Actions, post-merge a main, azure/webapps-deploy
mseller-firebaseFirebase (Cloud Functions, Firestore rules, Storage rules)GitHub Actions, firebase deploy --only functions
mseller-cloudVercelAuto-deploy en cada push a main (config en vercel.json)
mseller-marketing-siteVercelAuto-deploy en cada push a main
mseller-docsVercelAuto-deploy en cada push a main

Cada deploy usa una cuenta de servicio dedicada con permisos mínimos. No se guardan llaves en laptops de developers.

Dominios de producción

Lo que ven los clientes (y dónde apunta cada cosa cuando hablamos de producción, no del stack local). Cutover a Container Apps + custom domains terminó el 2026-05-15 — los hostnames de producción están en mseller.app con TLS gestionado por Azure (gratis, auto-renew).

DominioRepoQué sirve
mseller.appmseller-marketing-siteLanding público — features, pricing, "iniciar sesión".
cloud.mseller.appmseller-cloudPortal admin web (lo que usa el cliente día a día).
portal.api.mseller.appmseller-api · Portal.ApiAPI REST que consume cloud.mseller.app.
ingestion.api.mseller.appmseller-api · Ingestion.ApiAPI de ingesta — recibe payloads de integraciones (SAP, etc.).
consumo.api.mseller.appmseller-api · Consumo.ApiAPI consumida por la app móvil iOS.
storage.mseller.appFirebase / GCSCDN público de imágenes (logos de tenants, productos, etc.).
Cloud Functionsmseller-firebaseURL gestionada por Firebase (*.cloudfunctions.net o *.run.app). El cliente las llama vía httpsCallable, no por URL directa.

El stack de dev vive en el TLD mseller.dev (DNS en Cloudflare en vez del cPanel de prod), y cubre tanto los APIs como el portal admin:

DominioRepoQué sirve
cloud.mseller.devmseller-cloudPortal admin de dev (Vercel).
portal.api.mseller.devmseller-apiPortal.Api dev.
ingestion.api.mseller.devmseller-apiIngestion.Api dev.
consumo.api.mseller.devmseller-apiConsumo.Api dev (móvil iOS dev).

Cookies y sesiones no cruzan entre mseller.app y mseller.dev (son TLDs distintos), así que el SSO del portal de docs / prod cloud se mantiene aislado del portal de dev — intencional.

Las URLs internas de Azure (mseller-prod-portal-api.mangoocean-fef986ad.eastus2.azurecontainerapps.io, etc.) siguen funcionando pero no son las que tienen que usar los clientes — protegen contra lock-in al proveedor. Si alguna integración todavía tiene hardcoded esas URLs, migrala al hostname con dominio propio del entorno correspondiente (*.mseller.app para prod, *.mseller.dev para dev).

Detalles sobre cómo está implementado el DNS, cómo agregar un hostname nuevo, y troubleshooting de TLS: ver Despliegue → Dominios personalizados.

Lectura recomendada