13 KiB
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:
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:
/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.tsyapps/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 endetail,manage,selectyshared. 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,ResourceDetailDialogyCompanySection: 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ónlayout="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:180y: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 --checksin 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:
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:
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.