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-docsy/docs, habilitados por defecto fuera del perfilprod(SpringdocDefaultsEnvironmentPostProcessor), con las rutas agregadas alpermitAlldecore-auth'sSecurityFilterChain. POST /api/authz/checkencore-authz(CheckController) exponeFgaService.check()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 elsessionactual (SessionResponse:userId,organizationId,sessionId). No existe endpoint/me: al montar,<AuthProvider>llamaPOST /api/auth/refreshuna vez para restaurar la sesión desde la cookiehttpOnly; si falla, no hay sesión.useTenant()— derivaorganizationIddel mismo contexto.usePermission(relation, objectType, objectId)— llamaPOST /api/authz/check, cacheado porsessionId:relation:objectType:objectIdpara no repetir la misma pregunta dentro de una sesión. Resuelveallowed: falsesin llamar a la API si no hay sesión.<RequireAuth>— guard de cliente, redirige a/loginsi no hay sesión tras el intento de refresh inicial. Es UX, no el límite de seguridad real (eso sigue siendocore-auth/core-authzen cada request).createAuthGuard()— helper paramiddleware.tsde Next.js App Router. Corre en el servidor, así que puede leer la presencia de la cookie de acceso (aunque seahttpOnly) — 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.
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 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:
/loginfuncional contracore-auth.app/(app)/layout.tsx—<RequireAuth>+<Sidebar>con enlaces a/requestsy/admin/config./requests— recurso de ejemplo genérico (tabla + alta +UploadWidget), parametrizado porNEXT_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.ymlcon 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 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 decore-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.