Saltar al contenido principal
Versión: main (sin publicar)

Paquetes frontend @echotechs/*

Los cuatro paquetes @echotechs/* de la sección 3 del spec y el scaffolder create-echotechs-app de la 3.5 están implementados, viven en packages/ en la raíz de este repo (monorepo pnpm + Turborepo, spec 0), y fueron verificados end-to-end contra infraestructura real — login, multi-tenancy, subida de archivos y la pantalla de core-config, todo probado contra un reference-app real con Postgres, OpenFGA y MinIO levantados, no simulados. Ver Quickstart para correr exactamente esa verificación.

Dos bloqueos que existían antes de esto se resolvieron como parte del mismo trabajo:

  • springdoc-openapi (spec 1.8) ahora vive en core-web — expone /v3/api-docs y /docs, habilitados por defecto fuera del perfil prod (SpringdocDefaultsEnvironmentPostProcessor), con las rutas agregadas al permitAll de core-auth's SecurityFilterChain.
  • POST /api/authz/check en core-authz (CheckController) expone la decisión del motor de autorización por HTTP — nunca lanza 403, responde {"allowed": boolean} siempre, porque es una consulta para decidir qué renderizar, no una protección de endpoint.

También se agregó CORS opt-in en core-web (echotechs.web.cors.allowed-origins, vacío por defecto — cero impacto en proyectos existentes) para que un frontend en otro origen pueda mandar las cookies httpOnly de sesión.

@echotechs/api-client (spec 3.1)​

Generado con orval desde el OpenAPI real de platform-core (client: 'fetch', mode: 'tags-split'). Nada bajo src/generated/ se edita a mano — se regenera con:

curl http://localhost:8080/v3/api-docs -o packages/api-client/openapi.json
pnpm --filter @echotechs/api-client generate

src/client.ts (escrito a mano) es el custom mutator que orval invoca: agrega credentials: 'include' a cada request (las cookies de sesión son httpOnly, nunca localStorage) e implementa el interceptor de refresh — cualquier 401 fuera de /api/auth/login//api/auth/refresh dispara un POST /api/auth/refresh y reintenta una vez antes de propagar un ApiError.

Cubre los endpoints humanos de core-auth, el check de core-authz, core-storage y core-config — no incluye POST /api/auth/service-token (flujo service-to-service, no algo que un SPA de navegador llame) ni los endpoints propios de cada proyecto de dominio (ej. PurchaseRequest de reference-app), que quedan fuera del alcance de un paquete versionado compartido por todos los proyectos.

@echotechs/auth-web (spec 3.2)​

  • useAuth() — login, logout, logoutAll, y el session actual (SessionResponse: userId, organizationId, sessionId). No existe endpoint /me: al montar, <AuthProvider> llama POST /api/auth/refresh una vez para restaurar la sesión desde la cookie httpOnly; si falla, no hay sesión.
  • useTenant() — deriva organizationId del mismo contexto.
  • usePermission(relation, objectType, objectId) — llama POST /api/authz/check, cacheado por sessionId:relation:objectType:objectId para no repetir la misma pregunta dentro de una sesión. Resuelve allowed: false sin llamar a la API si no hay sesión.
  • <RequireAuth> — guard de cliente, redirige a /login si no hay sesión tras el intento de refresh inicial. Es UX, no el límite de seguridad real (eso sigue siendo core-auth/core-authz en cada request).
  • createAuthGuard() — helper para middleware.ts de Next.js App Router. Corre en el servidor, así que puede leer la presencia de la cookie de acceso (aunque sea httpOnly) — pero deliberadamente no decodifica ni verifica el JWT (necesitaría el secreto de firma en el edge). Es un atajo de UX para evitar el parpadeo de una página protegida antes del 401 inevitable, no la autorización real.

@echotechs/ui-kit (spec 3.3)​

Un componente real por categoría: Button, Modal, Table, Sidebar, KpiCard, Badge, con theming vía <ThemeProvider> (variables CSS, packages/ui-kit/src/styles.css trae los valores por defecto).

Incluye ConfigAdminPanel, la pantalla que la sección 1.9 del spec pide explícitamente para core-config ("tabla + modal, ya construidos"): lista GET /api/organizations/{orgId}/config, edita vía PUT .../config/{key}, borra overrides vía DELETE, con los controles de edición ocultos por completo (no solo deshabilitados) para quien usePermission('admin', 'organization', orgId) diga que no puede.

Bug real encontrado probando esto contra Postgres real, no H2

ConfigEntry.value es @Lob (columna TEXT, portable Postgres/Oracle). Leerlo o escribirlo fuera de una transacción explícita fallaba contra Postgres real con "Large Objects may not be used in auto-commit mode" — nunca se había detectado porque los tests de core-config sólo corrían contra H2. ConfigService ahora anota @Transactional/@Transactional(readOnly = true) en cada método público. Los tests con H2 (que sí pasaban antes) no lo hubieran agarrado — esto se encontró recién al levantar reference-app con infraestructura real como parte de esta verificación.

@echotechs/upload-widget (spec 3.4)​

<UploadWidget resourceType resourceId onUploaded onError /> — el flujo de tres pasos verificado contra MinIO real: POST /api/storage/files (initiate) → PUT directo a uploadUrl con el mismo Content-Type declarado → POST /api/storage/files/{fileId}/confirm. Si confirm responde 409 UPLOAD_NOT_FOUND (el PUT todavía no había terminado del lado de MinIO), reintenta la confirmación una vez con un backoff corto de 400ms antes de propagar el error.

create-echotechs-app (spec 3.5)​

npx @echotechs/create-echotechs-app mi-proyecto

CLI sin dependencias pesadas (node:util parseArgs, copia de directorio) que scaffoldea un proyecto Next.js App Router con los cuatro paquetes ya integrados:

  • /login funcional contra core-auth.
  • app/(app)/layout.tsx — <RequireAuth> + <Sidebar> con enlaces a /requests y /admin/config.
  • /requests — recurso de ejemplo genérico (tabla + alta + UploadWidget), parametrizado por NEXT_PUBLIC_RESOURCE_PATH/NEXT_PUBLIC_RESOURCE_TYPE — swapear esos dos valores por el endpoint REST de tu propia entidad de dominio es literalmente lo que un proyecto nuevo hace el primer día.
  • /admin/config — ConfigAdminPanel, funciona contra cualquier backend platform-core sin configuración adicional.
  • middleware.ts — createAuthGuard() ya cableado.
  • docker-compose.yml con los mismos cuatro servicios (Postgres, OpenFGA, MinIO, Mailpit) que este quickstart usa por separado.

Probado de punta a punta: create-echotechs-app scaffoldeó un proyecto real, compiló (next build) y corrió (next start) contra un reference-app real, y un usuario real hizo login, creó un recurso, subió un archivo a MinIO, y editó core-config — ver Quickstart. En esa primera vuelta los paquetes se consumieron por pnpm link (Nexus solo alojaba artefactos Maven todavía); ahora los cinco paquetes (los cuatro más @echotechs/create-echotechs-app) están publicados de verdad en el mismo Nexus, en un repositorio npm-hosted nuevo — verificado instalándolos con npm install desde un proyecto externo real, sin ningún link/workspace de por medio. Igual que Maven, la lectura también requiere credenciales (el acceso anónimo está apagado a nivel de toda la instancia), así que un .npmrc con usuario/password de Nexus es necesario antes de pnpm install — create-echotechs-app genera un .npmrc.example con el formato exacto.

Testing frontend (spec 4.5)​

  • Vitest + React Testing Library por paquete — 44 tests entre los cuatro paquetes más el CLI.
  • Playwright (packages/create-echotechs-app/e2e/login-and-resource.spec.ts) contra el proyecto scaffoldeado real: redirección del middleware sin sesión, login válido/inválido, persistencia de sesión tras reload (refresh silencioso), logout, subida de archivo real contra MinIO, y la pantalla de admin de core-config.

No integrado todavía a GitHub Actions (.github/workflows/) — sigue el mismo patrón que .github/workflows/test.yml cuando se agregue, pero queda fuera de este trabajo.