238 lines
13 KiB
Markdown
238 lines
13 KiB
Markdown
# Integración visual de dominios Windows · Paso 2.4
|
||
|
||
## Uso y responsabilidades
|
||
|
||
Los dominios se consultan en **Recursos**, dentro de su plantilla existente. La
|
||
identificación funcional es `resource.directory.capability === "directory:domain"`;
|
||
renombrar una plantilla no cambia la clasificación. La ficha conserva nombre,
|
||
características DNS/NetBIOS, estado, observaciones e historial propios de Resource.
|
||
|
||
Companies compone `CompanyResourceRelatedContent` mediante `relatedContent`, tanto
|
||
para el panel lateral como para el diálogo existente. El adaptador integra el
|
||
componente público `DomainDirectoryMemberships` de Assets y los enlaces de accesos.
|
||
Los recursos ordinarios mantienen su comportamiento y los buzones conservan
|
||
`MailboxRelations`. Resources no depende de Assets ni de implementaciones privadas
|
||
de Companies.
|
||
|
||
En la ficha del dominio hay **una única tabla paginada**, con columnas de activo,
|
||
plantilla, estado del activo, rol y vigencia de la pertenencia. Se adopta la alternativa
|
||
A: miembros y controladores comparten el cursor del proveedor; el resumen de
|
||
controladores indica expresamente que corresponde a la página visible. No hay
|
||
paginaciones independientes, filtrados que oculten miembros de otras páginas ni
|
||
contadores globales inferidos de veinte filas. La clasificación usa `roleCode`.
|
||
|
||
Cada nombre disponible enlaza al activo por su UUID. Para asociar o administrar una
|
||
pertenencia desde el dominio se abre el inventario o la ficha del activo. Los comandos
|
||
y sus diálogos permanecen en Assets; no se duplican en Resources.
|
||
|
||
La sección **Dominio Windows** del activo está integrada en `AssetDetail`, compartido
|
||
por página, diálogo y panel lateral. En el panel adopta un AccordionItem de la raíz
|
||
`multiple` existente. Permite:
|
||
|
||
- Ver dominio, DNS, rol y estado, y abrir el recurso mediante su UUID.
|
||
- Asociar un equipo compatible, eligiendo manualmente un dominio activo.
|
||
- Cambiar rol: solo los servidores ofrecen controlador; también se respeta la
|
||
clasificación técnica explícita de infraestructura del paso 2.2.
|
||
- Trasladar mediante **un único comando atómico** existente; el diálogo explica y
|
||
confirma el cierre de la pertenencia anterior al guardar.
|
||
- Finalizar con confirmación, conservando los datos y el historial.
|
||
- Consultar los periodos vigentes y finalizados mediante el proveedor tipado.
|
||
|
||
La tabla histórica muestra el rol y estado de cada periodo, no un timeline de cada
|
||
revisión intermedia del rol: el contrato tipado no proporciona fechas ni revisiones.
|
||
Las revisiones completas siguen conservadas y accesibles por la API especializada
|
||
del paso 2.2. No se inventan fechas, códigos de inventario ni metadatos ausentes.
|
||
|
||
## HTTP y servicios
|
||
|
||
Se añaden tres GET acotados bajo el prefijo ya existente:
|
||
|
||
| Ruta | Finalidad |
|
||
| --- | --- |
|
||
| `/api/asset-directory-memberships/:companyId/relationships` | Página del proveedor tipado y resúmenes del extremo relacionado por lote |
|
||
| `/api/asset-directory-memberships/:companyId/domains` | Opciones activas de Resources, búsqueda por nombre/DNS y cursor |
|
||
| `/api/asset-directory-memberships/:companyId/:assetId/eligibility` | Clasificación de un activo con la política existente |
|
||
|
||
La primera exige `typeId=assets:directory-membership`, `anchorType=assets:asset` o
|
||
`resources:resource`, `anchorId`, `direction`, `includeEnded=true|false` y
|
||
`pageSize=1..100`, más cursor opcional. La respuesta es
|
||
`{page: {items, nextCursor}, summaries}`; conserva `roleCode` y el orden de
|
||
correspondencia entre filas y resúmenes. El adaptador llama a
|
||
`runtime.relationships.memberships` y al resolutor propietario correspondiente.
|
||
No reconstruye las consultas del proveedor ni hidrata inventarios completos.
|
||
|
||
Las opciones se obtienen del servicio público `resources.services.directoryOptions`:
|
||
una proyección de Recursos y su tabla de dominios, limitada por compañía y estado,
|
||
con búsqueda parametrizada y `LIMIT 21` para devolver hasta veinte opciones y
|
||
continuación. El cursor de este selector es una identidad existente tratada como
|
||
opaca por la UI; no es el cursor HMAC de relaciones. Cambiar la búsqueda reinicia
|
||
su página. No se descarga el catálogo completo para filtrar dominios en el navegador.
|
||
|
||
La compatibilidad proyecta únicamente el activo solicitado, su plantilla y la
|
||
clasificación explícita de servidor de la última revisión de infraestructura.
|
||
Reutiliza `directoryMembershipEligibility`; la validación definitiva continúa en el
|
||
comando, incluyendo dominio activo, estado del activo, concurrencia y compañía.
|
||
|
||
Las escrituras siguen usando, sin alterar su contrato:
|
||
|
||
```text
|
||
POST /api/asset-directory-memberships/:companyId/:assetId
|
||
join | role | transfer | end
|
||
```
|
||
|
||
No hay un escritor genérico de relaciones, nueva tabla, migración o dependencia
|
||
externa en este paso. Los contratos del paquete común y los cursores HMAC del paso
|
||
2.3 permanecen intactos.
|
||
|
||
## Sesión, errores y actualización
|
||
|
||
Las rutas nuevas usan el middleware público de autenticación. Derivan usuario y
|
||
`authVersion` de la sesión y obtienen un contexto con la fábrica existente, sin
|
||
copiar su credencial. La compañía se valida antes de leer datos propietarios; cada
|
||
servicio conserva la autorización del contexto. No se acepta `operatorId` en query
|
||
o body de estas lecturas. Los tres GET se registran de forma que no intercepten los
|
||
comandos anteriores.
|
||
|
||
Errores tipados: `INVALID_QUERY` → 400, `FORBIDDEN` → 403 y `UNAVAILABLE` → 404;
|
||
la ausencia de sesión produce 401 mediante el middleware existente. Los errores
|
||
inesperados siguen el diagnóstico central del servidor. Las referencias ajenas no
|
||
revelan nombres. Sigue existiendo acceso general de operadores autenticados a las
|
||
compañías atendidas, **sin ACL granular por compañía**.
|
||
|
||
El cliente valida las respuestas mediante schemas públicos y utiliza `csrfFetch`
|
||
para las escrituras. Muestra carga, vacío real, error y datos disponibles de forma
|
||
diferenciada, conserva errores funcionales y no presenta guardados optimistas.
|
||
Tras éxito o conflicto invalida exclusivamente las consultas de pertenencias de
|
||
esa compañía, que incluyen ambos extremos e historial; ante errores también marca
|
||
como obsoletas sus opciones de dominio para no reutilizar un dominio retirado. El formulario solo se cierra
|
||
tras confirmar el éxito y actualizar las consultas.
|
||
|
||
El diálogo captura el UUID de la pertenencia al abrir la operación. Un refresco
|
||
concurrente no lo sustituye por el de un periodo nuevo: el backend puede rechazar
|
||
la operación obsoleta. No se realizan finalización y alta separadas para trasladar.
|
||
|
||
Los listados de relaciones actualizan cada sesenta segundos en primer plano mediante
|
||
`useListQuery`. El navegador conserva una pila de cursores opacos para anterior y
|
||
siguiente. «Primera página» reinicia la consulta inicial y sus datos, recuperándose
|
||
de cursores invalidados por un reinicio del servidor. No se fingen páginas totales
|
||
ni se reordena localmente una sola página del servidor como si fuera el conjunto.
|
||
|
||
## Cuentas y grupos
|
||
|
||
Los enlaces abren el `AccessWorkspace` existente con:
|
||
|
||
```text
|
||
/companies/:companyId/access?kind=account&environmentId=:environmentId
|
||
/companies/:companyId/access?kind=group&environmentId=:environmentId
|
||
```
|
||
|
||
`environmentId` procede de la asociación estructurada del dominio. Las rutas validan
|
||
UUID, parámetros repetidos y ámbito permitido. El listado existente combina el
|
||
filtro con búsqueda y tipo, indica el entorno y permite quitarlo. Cambiar a una
|
||
sección incompatible retira el filtro; modificarlo reinicia la paginación.
|
||
No se copian cuentas/grupos ni se añaden formularios AD, consultas SQL privadas o
|
||
permisos del directorio como autorizaciones de Metis.
|
||
|
||
## Componentes y archivos
|
||
|
||
Creados:
|
||
|
||
- `packages/rdx-ui/src/components/cursor-pagination.tsx`: navegación genérica sin
|
||
dependencia de entidades, DTO o consultas.
|
||
- `modules/assets/src/common/directory-membership-read.ts`: contratos HTTP acotados.
|
||
- `modules/resources/src/common/directory-domain-options.ts`: contrato del selector.
|
||
- `modules/assets/src/api/infrastructure/persistence/directory-membership-eligibility-reader.ts`.
|
||
- `modules/resources/src/api/infrastructure/persistence/directory-domain-options-reader.ts`.
|
||
- `apps/server/src/infrastructure/http/directory-relationship.routes.ts` y
|
||
`apps/server/src/windows-domains-ui.integration.test.ts`.
|
||
- En `modules/assets/src/web/directory-memberships/`: API y query keys, hooks de
|
||
consultas/mutación, secciones de activo y dominio, tabla de página, selector de
|
||
dominios y diálogos de edición/finalización. Cada componente principal tiene su
|
||
fichero; las operaciones están separadas en `detail`, `manage`, `select` y `shared`.
|
||
- `modules/companies/src/web/resources/detail/ui/company-resource-related-content.tsx`.
|
||
- Predicado y prueba de filtro de entorno, junto al listado de AccessWorkspace.
|
||
|
||
Modificados principalmente:
|
||
|
||
- Composition roots de Assets/Resources y runtime HTTP del servidor: inyección y rutas.
|
||
- `AssetDetail`, `AssetSelectionPanel`, `ResourceDetailDialog` y `CompanySection`:
|
||
composición contextual y altura estable del diálogo de recurso.
|
||
- Ruta, controlador y listado de AccessWorkspace, con sus pruebas de navegación.
|
||
- `ConfirmDeleteDialog`: opción `layout="stable"` utilizada por la confirmación nueva;
|
||
los consumidores anteriores conservan su presentación.
|
||
- Exports públicos, traducciones es/en, manifiestos de pruebas, fixture compartido y
|
||
documentación. No se modifica código vendor ni se revierten los pasos anteriores.
|
||
|
||
Reutilizados: `DetailPanel`, `DetailSection`, Accordion `multiple`, `EmptyState`,
|
||
`EntityStatus`, Table/Button/Input de shadcn/ui, `FormDialog`, `FormBody`, `FormHelp`,
|
||
`FormActions`, `FormLabel`, `RequiredIndicator`, `ConfirmDeleteDialog`, `BrowserLink`,
|
||
TanStack Query y las operaciones públicas anteriores. No se usa AssetPickerDialog:
|
||
la gestión se inicia desde el activo seleccionado y no necesita descargar otro
|
||
inventario. El selector de dominios tiene búsqueda remota y selección mediante
|
||
botones accesibles, sin añadir paginación local sobre su página remota.
|
||
|
||
## Verificación y límites
|
||
|
||
Las suites usan Node y `tsx` con `--test-isolation=none`, conservando todos los
|
||
ficheros de cada script. La integración solo utiliza `metis_desk_test`, datos con
|
||
UUID propios y limpieza por compañía, sin tocar bases de desarrollo o producción.
|
||
|
||
- Assets: 126/126; Resources: 53/53; RDX UI: 52/52.
|
||
- Companies: 171/173; pasan las nuevas pruebas de ruta y filtrado. Persisten las dos
|
||
expectativas previas de texto en `companies-page.test.tsx:180` y `:210`.
|
||
- Integración conjunta de pasos 2.1–2.4: 15/15. Incluye consultas directas/inversas,
|
||
roles, cursor ajeno/inválido, límites, sesión, compañía inexistente, datos ajenos,
|
||
CSRF, asociación, cambio de rol, traslado, finalización repetida y conflictos.
|
||
El selector se comprueba con más de veinte dominios, búsqueda y exclusión de
|
||
dominios inactivos y de otra compañía.
|
||
- Se comprueban renderizado es/en, enlaces UUID, estados, ausencia de filtraciones
|
||
de nombres no disponibles y transporte de un único comando atómico con CSRF.
|
||
- Typecheck correcto en Assets, Resources, Companies, servidor, web y RDX UI.
|
||
- Biome y ESLint correctos en los 41 ficheros TypeScript del paso; Biome también
|
||
comprueba tres manifiestos y cuatro ficheros de traducciones.
|
||
- Builds de servidor y web correctos; `git diff --check` sin incidencias.
|
||
- Grafo workspace verificado: 13 paquetes, sin ciclos.
|
||
- Se ha reproducido TS2375 en el fichero vendor sin modificar
|
||
`packages/shadcn-ui/src/components/calendar.tsx:165`. No se corrige aquí.
|
||
- El build web conserva el aviso de chunks mayores de 500 kB.
|
||
|
||
No se ha realizado una sesión interactiva de navegador: el entorno no dispone de
|
||
herramienta de navegador ni ejecutable Chromium. Los diálogos usan el foco inicial
|
||
de búsqueda y la restitución de foco de FormDialog/Base UI; los tests SSR no
|
||
sustituyen una comprobación manual de teclado, viewport móvil y tema oscuro.
|
||
|
||
Los cursores HMAC siguen siendo por instancia y sin instantánea entre peticiones.
|
||
No hay sincronización LDAP, automatización AD ni gestión SMB. La siguiente fase
|
||
puede reutilizar contratos, resolución por lotes y navegación por cursor, pero las
|
||
reglas y comandos de SMB deberán seguir en su propietario; no están implementados.
|
||
|
||
|
||
### Comandos de verificación
|
||
|
||
Desde la raíz, usando Node 24 y el PNPM fijado:
|
||
|
||
```bash
|
||
pnpm --config.verify-deps-before-run=false --filter @metis/assets --filter @metis/resources --filter @metis/companies --filter @metis/server --filter @metis/web --filter @repo/rdx-ui typecheck
|
||
pnpm --config.verify-deps-before-run=false --filter @metis/server test:windows-domains-ui
|
||
pnpm --config.verify-deps-before-run=false --filter @metis/server build
|
||
pnpm --config.verify-deps-before-run=false --filter @metis/web build
|
||
git diff --check
|
||
```
|
||
|
||
La integración conjunta se ejecutó desde `apps/server`:
|
||
|
||
```bash
|
||
DB_NAME=metis_desk_test NODE_ENV=test node --env-file=../../.env --import tsx \
|
||
--test --test-isolation=none --test-concurrency=1 \
|
||
src/directory-domains.integration.test.ts \
|
||
src/asset-directory-memberships.integration.test.ts \
|
||
src/typed-relationships.integration.test.ts \
|
||
src/windows-domains-ui.integration.test.ts
|
||
```
|
||
|
||
Las suites completas de Assets, Resources, Companies y RDX UI se ejecutaron con
|
||
sus listas originales de ficheros más los casos nuevos, añadiendo
|
||
`--test-isolation=none` al runner para enumerar los casos reales. No se eliminaron
|
||
pruebas ni se cambiaron las expectativas previas fallidas. No se afirma que los
|
||
checks globales del monorepo estén verdes.
|