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.
|