feat(F-158): completed feature
This commit is contained in:
20
work/artifacts/F-158/architect.md
Normal file
20
work/artifacts/F-158/architect.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# F-158 — Diseño
|
||||
|
||||
## Decisión
|
||||
Separar explícitamente URL pública y URL privada:
|
||||
|
||||
- Navegador POS: siempre usa `/api` del mismo origen (`:3006`).
|
||||
- Route handler Next.js: usa `POS_BACKEND_URL`, server-only, con fallback `http://127.0.0.1:3000`.
|
||||
- No exponer ni incrustar `NEXT_PUBLIC_API_URL` en el bundle.
|
||||
|
||||
## Proxy
|
||||
- Construye destino desde el catch-all path y query string.
|
||||
- Reenvía Cookie, Content-Type y `x-terminal-id`.
|
||||
- Propaga status, Content-Type y todos los `Set-Cookie` del backend.
|
||||
- GET y POST cubren las operaciones actuales del cliente POS.
|
||||
|
||||
## Desarrollo LAN
|
||||
`allowedDevOrigins` incluye `192.168.18.93` y `localhost` para recursos Next/HMR.
|
||||
|
||||
## Seguridad
|
||||
La URL backend queda server-only. Los paths proceden del segmento catch-all y se concatenan contra una base fija, sin aceptar host suministrado por el cliente.
|
||||
14
work/artifacts/F-158/documenter.md
Normal file
14
work/artifacts/F-158/documenter.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# F-158 — Documentación
|
||||
|
||||
## Acceso LAN
|
||||
El TPV se abre en `http://192.168.18.93:3006`. Todas las llamadas del navegador permanecen en ese origen mediante `/api`; solo el servidor Next accede al backend en `127.0.0.1:3000`.
|
||||
|
||||
## Primera vinculación
|
||||
Después del login, un dispositivo sin terminal muestra la pantalla **Vincular terminal**. Debe introducir el código de 8 caracteres. La vinculación se guarda en una cookie HttpOnly durante un año.
|
||||
|
||||
## Entorno local actual
|
||||
- Terminal: `TPV Principal`
|
||||
- Código de vinculación: `B794401C`
|
||||
- Backend: puerto 3000
|
||||
- Admin: puerto 3004
|
||||
- POS: puerto 3006
|
||||
24
work/artifacts/F-158/implementer.md
Normal file
24
work/artifacts/F-158/implementer.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# F-158 — Implementer
|
||||
|
||||
## Implementación
|
||||
- El cliente POS usa exclusivamente `/api`, nunca `localhost:3000` desde el navegador.
|
||||
- El proxy Next usa `POS_BACKEND_URL` server-only con fallback `127.0.0.1:3000`.
|
||||
- El proxy reenvía sesión, terminal y Content-Type; propaga `Set-Cookie` del login.
|
||||
- La vinculación guarda `pos_terminal_id` como cookie HttpOnly durante un año y el proxy la traduce a `x-terminal-id`.
|
||||
- Middleware reconoce la cookie real `mdv_session` además de nombres legacy.
|
||||
- Añadida pantalla de vinculación inicial de 8 caracteres y estados de error/reintento.
|
||||
- Corregidos errores TypeScript previos del esqueleto POS (`NextLayout`, tipos de config/customer/catch).
|
||||
- `allowedDevOrigins` permite `192.168.18.93` y `localhost`.
|
||||
|
||||
## Evidencia
|
||||
- `cd project/apps/pos && npm run build`: PASS, 4 rutas + middleware.
|
||||
- `cd project/apps/pos && npx tsc --noEmit`: PASS.
|
||||
- `cd project && node_modules/.bin/tsc --noEmit`: PASS.
|
||||
- Login proxy: HTTP 200 + cookie `mdv_session`.
|
||||
- Bind proxy: HTTP 200 + cookie `pos_terminal_id`.
|
||||
- Config proxy autenticado/vinculado: HTTP 200 con store, terminal y métodos de pago.
|
||||
- Servicios persistentes: 3000, 3004 y 3006 escuchando.
|
||||
- `./scripts/verify.sh`: PASS.
|
||||
|
||||
## Configuración local
|
||||
Se creó `TPV Principal` para la tienda por defecto. Código de vinculación: `B794401C`.
|
||||
16
work/artifacts/F-158/leader-close.json
Normal file
16
work/artifacts/F-158/leader-close.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"feature_id": "F-158",
|
||||
"agent": "leader",
|
||||
"stage": "close",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "POS LAN usa proxy same-origin y completa login, vinculación y config sin localhost del navegador.",
|
||||
"checks": [
|
||||
{ "item": "Reviewer approved", "ok": true },
|
||||
{ "item": "Security approved", "ok": true },
|
||||
{ "item": "QA approved", "ok": true },
|
||||
{ "item": "POS build passed", "ok": true },
|
||||
{ "item": "Runtime acceptance passed", "ok": true },
|
||||
{ "item": "verify.sh passed", "ok": true }
|
||||
],
|
||||
"issues": []
|
||||
}
|
||||
16
work/artifacts/F-158/qa.json
Normal file
16
work/artifacts/F-158/qa.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"feature_id": "F-158",
|
||||
"agent": "qa",
|
||||
"stage": "qa_gate",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "Flujo LAN completo validado contra 192.168.18.93:3006.",
|
||||
"checks": [
|
||||
{ "item": "Login through POS proxy", "ok": true, "evidence": "HTTP 200 and mdv_session cookie" },
|
||||
{ "item": "Terminal bind through POS proxy", "ok": true, "evidence": "HTTP 200 and pos_terminal_id cookie" },
|
||||
{ "item": "POS config through proxy", "ok": true, "evidence": "HTTP 200 with store, terminal and payment methods" },
|
||||
{ "item": "No direct browser backend request", "ok": true, "evidence": "api-client uses /api" },
|
||||
{ "item": "Build and types", "ok": true, "evidence": "POS build and both TypeScript checks passed" },
|
||||
{ "item": "Runtime services", "ok": true, "evidence": "Ports 3000, 3004 and 3006 listening" }
|
||||
],
|
||||
"issues": []
|
||||
}
|
||||
15
work/artifacts/F-158/reviewer.json
Normal file
15
work/artifacts/F-158/reviewer.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"feature_id": "F-158",
|
||||
"agent": "reviewer",
|
||||
"stage": "review_gate",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "El POS elimina la dependencia de localhost en navegador y completa login, vinculación y config por proxy same-origin.",
|
||||
"checks": [
|
||||
{ "item": "No browser localhost URL", "ok": true, "evidence": "api-client base is /api; source search has no NEXT_PUBLIC_API_URL" },
|
||||
{ "item": "Proxy contract", "ok": true, "evidence": "Cookies, terminal header, status and content type forwarded" },
|
||||
{ "item": "Terminal persistence", "ok": true, "evidence": "HttpOnly pos_terminal_id cookie translated to x-terminal-id" },
|
||||
{ "item": "POS production build", "ok": true, "evidence": "Next.js 15 build passed" },
|
||||
{ "item": "Runtime flow", "ok": true, "evidence": "login 200, bind 200, config 200 through :3006" }
|
||||
],
|
||||
"issues": []
|
||||
}
|
||||
14
work/artifacts/F-158/security.json
Normal file
14
work/artifacts/F-158/security.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"feature_id": "F-158",
|
||||
"agent": "security",
|
||||
"stage": "security_gate",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "La URL backend queda server-only y las cookies de sesión/terminal se mantienen HttpOnly y same-origin.",
|
||||
"checks": [
|
||||
{ "item": "Backend URL not public", "ok": true, "evidence": "POS_BACKEND_URL is only read in server route handler" },
|
||||
{ "item": "Terminal cookie protection", "ok": true, "evidence": "HttpOnly, SameSite=Lax, Secure in production" },
|
||||
{ "item": "Fixed upstream host", "ok": true, "evidence": "Client controls path only; upstream base comes from trusted env" },
|
||||
{ "item": "No broad header forwarding", "ok": true, "evidence": "Only cookie, content-type and x-terminal-id forwarded" }
|
||||
],
|
||||
"issues": []
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
# Feature activa: F-157 — Reporting sections live on Reporting page
|
||||
# Feature activa: F-158 — POS use same-origin API proxy on LAN
|
||||
|
||||
## Problema
|
||||
El menú principal muestra Dashboard, Ventas y Productos de Reporting como entradas indentadas. El operador requiere una única entrada principal **Reporting**. Al abrirla, sus apartados deben aparecer dentro del área de Reporting, igual que las pestañas internas de Ajustes.
|
||||
El TPV abierto en `http://192.168.18.93:3006` ejecuta peticiones del navegador contra `http://localhost:3000`. En un cliente LAN, `localhost` apunta al propio cliente y produce `ERR_CONNECTION_REFUSED`.
|
||||
|
||||
## Alcance
|
||||
- El sidebar principal muestra solo `Reporting`.
|
||||
- `/reporting` abre por defecto el apartado Dashboard.
|
||||
- Dashboard, Ventas y Productos se muestran como navegación local dentro de Reporting.
|
||||
- La navegación local permanece visible en las páginas de los tres apartados.
|
||||
- No se cambian APIs ni permisos de Reporting.
|
||||
- El cliente POS usa rutas same-origin `/api/...`.
|
||||
- El route handler de Next.js reenvía las peticiones al backend privado (`127.0.0.1:3000`).
|
||||
- El proxy conserva cookies de sesión y `x-terminal-id`.
|
||||
- El login propaga `Set-Cookie` al navegador.
|
||||
- Next dev permite el origen LAN `192.168.18.93`.
|
||||
|
||||
## Aceptación
|
||||
1. No aparecen `/reporting/dashboard`, `/reporting/sales` ni `/reporting/products` en el menú principal.
|
||||
2. Al pulsar Reporting aparece una lista local con Dashboard, Ventas y Productos.
|
||||
3. El apartado activo queda visualmente marcado.
|
||||
4. El build del admin y `verify.sh` pasan.
|
||||
1. El navegador no solicita directamente `localhost:3000`.
|
||||
2. `GET /api/pos/config` llega al backend mediante el proxy.
|
||||
3. Login conserva la cookie de sesión.
|
||||
4. POS build, TypeScript y `verify.sh` pasan.
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
{
|
||||
"feature_id": "F-157",
|
||||
"feature_id": "F-158",
|
||||
"stage": "close",
|
||||
"agent": "leader",
|
||||
"action": "Close F-157 after approved gates",
|
||||
"action": "Close F-158 after LAN acceptance",
|
||||
"state": "running",
|
||||
"next_agent": "leader",
|
||||
"waiting_for": "Seleccionar una feature pending y actualizar este estado",
|
||||
"updated_at": "2026-08-22T15:42:24Z",
|
||||
"updated_at": "2026-08-22T15:50:42Z",
|
||||
"timeline": [
|
||||
{
|
||||
"ts": "2026-08-22T15:38:27Z",
|
||||
"ts": "2026-08-22T15:42:54Z",
|
||||
"agent": "leader",
|
||||
"stage": "intake",
|
||||
"state": "running",
|
||||
"message": "Define Reporting navigation as page-local sections"
|
||||
"message": "Define LAN-safe POS API flow"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-22T15:39:01Z",
|
||||
"ts": "2026-08-22T15:43:10Z",
|
||||
"agent": "architect",
|
||||
"stage": "design",
|
||||
"state": "running",
|
||||
"message": "Design page-local Reporting navigation"
|
||||
"message": "Design same-origin POS proxy and cookie forwarding"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-22T15:39:21Z",
|
||||
"ts": "2026-08-22T15:43:38Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "running",
|
||||
"message": "Move Reporting sections from sidebar into Reporting layout"
|
||||
"message": "Implement LAN-safe POS API proxy"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-22T15:41:38Z",
|
||||
"ts": "2026-08-22T15:49:47Z",
|
||||
"agent": "reviewer",
|
||||
"stage": "review_gate",
|
||||
"state": "running",
|
||||
"message": "Review Reporting navigation and build evidence"
|
||||
"message": "Review POS same-origin proxy and binding flow"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-22T15:41:52Z",
|
||||
"ts": "2026-08-22T15:49:59Z",
|
||||
"agent": "security",
|
||||
"stage": "security_gate",
|
||||
"state": "running",
|
||||
"message": "Check navigation RBAC and client URL safety"
|
||||
"message": "Audit POS proxy cookies, headers and backend URL"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-22T15:42:06Z",
|
||||
"ts": "2026-08-22T15:50:19Z",
|
||||
"agent": "qa",
|
||||
"stage": "qa_gate",
|
||||
"state": "running",
|
||||
"message": "Validate Reporting sidebar and local section acceptance"
|
||||
"message": "Run POS LAN login-bind-config acceptance"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-22T15:42:15Z",
|
||||
"ts": "2026-08-22T15:50:31Z",
|
||||
"agent": "documenter",
|
||||
"stage": "document",
|
||||
"state": "running",
|
||||
"message": "Document Reporting navigation behavior"
|
||||
"message": "Document POS LAN and terminal binding behavior"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-22T15:42:24Z",
|
||||
"ts": "2026-08-22T15:50:42Z",
|
||||
"agent": "leader",
|
||||
"stage": "close",
|
||||
"state": "running",
|
||||
"message": "Close F-157 after approved gates"
|
||||
"message": "Close F-158 after LAN acceptance"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user