- Asociar entornos locales con activos y conservar su trazabilidad. - Registrar la cuenta de acceso del mapeo con historial de cambios. - Calcular cobertura documental específica sin alterar la cobertura general. - Añadir migraciones, validaciones, endpoints protegidos, pruebas y documentación.
313 lines
18 KiB
Markdown
313 lines
18 KiB
Markdown
# Recursos compartidos SMB — pasos 3.1 y 3.2
|
||
|
||
## Identidad y alcance
|
||
|
||
Una carpeta SMB es un Resource existente especializado, con el mismo UUID. Se reutilizan
|
||
`template:shared_folder`, `field:unc`, `field:server` y `field:description`. El nombre visible
|
||
es independiente de la UNC. La capacidad del recurso es `network:smb-share` y solo se
|
||
expone cuando existe una proyección válida; la clasificación de plantilla por stockKey
|
||
identifica el tipo esperado, no convierte un recurso antiguo en SMB.
|
||
|
||
No se incorporan publicadores, relaciones Asset–Resource, cuentas nuevas, resolutores
|
||
de relaciones ni modificaciones de WorkProfile o FolderMapping. Los accesos reutilizan
|
||
el modelo de permisos, necesidades y concesiones existente.
|
||
|
||
## Dirección y normalización
|
||
|
||
`field:unc` es la única entrada editable. La presentación conserva mayúsculas, Unicode
|
||
y un separador final introducido por el usuario, después del trim exterior ya utilizado
|
||
por Resource. La clave de comparación elimina un único separador final y pliega solo
|
||
ASCII A–Z. No aplica NFC/NFKC, eliminación de tildes ni case folding Unicode.
|
||
|
||
Se admiten hosts NetBIOS, DNS e IPv4 como nombres documentales, comparticiones
|
||
administrativas como C$, Unicode y espacios interiores en nombres de carpetas.
|
||
No se resuelve DNS ni se identifica el publicador por el host o la IP.
|
||
|
||
Se rechazan rutas locales, URL, host/compartición vacíos, separadores repetidos,
|
||
segmentos `.` y `..`, caracteres de control, Unicode mal formado, nombres de segmento
|
||
con espacios exteriores o punto final, caracteres estructuralmente incompatibles,
|
||
UNC extendidas de dispositivo y direcciones con más de 255 unidades UTF-16.
|
||
IPv6 con dos puntos, URI SMB y hosts terminados en punto quedan fuera de v1.
|
||
|
||
La comparación conservadora considera equivalentes `\\NAS\Docs` y `\\nas\docs`, pero
|
||
no equipara `\\nas\Docs` con `\\files.empresa.local\Docs`, ni formas Unicode distintas.
|
||
Las variantes Unicode no ASCII pueden permanecer distintas aunque un servidor concreto
|
||
las trate como equivalentes: no inventamos una política de cotejo universal.
|
||
|
||
### Raíz y subcarpetas
|
||
|
||
Se permiten rutas profundas por compatibilidad con el modelo UNC existente de Companies.
|
||
El contrato distingue `host`, `shareName` y `relativePath`; una ruta relativa vacía indica
|
||
la raíz de la compartición. Se recomienda registrar la raíz cuando se inventaría una
|
||
compartición y utilizar rutas más profundas en FolderMapping, pero no se invalidan
|
||
registros por contener subcarpetas. La unicidad corresponde a la dirección completa;
|
||
una raíz y una subcarpeta son direcciones distintas.
|
||
|
||
## Persistencia e integridad
|
||
|
||
La migración idempotente `migrateNetworkShares` crea `resource_network_shares`, sin
|
||
rellenar registros anteriores. Se ejecuta después de las migraciones de Resources y
|
||
Dominios, cuyos índices compuestos de raíz y plantilla reutiliza.
|
||
|
||
```sql
|
||
resourceId CHAR(36) BINARY PRIMARY KEY
|
||
companyId CHAR(36) BINARY NOT NULL
|
||
templateId CHAR(36) BINARY NOT NULL
|
||
templateKey VARCHAR(100) NOT NULL -- fijo: template:shared_folder
|
||
protocol VARCHAR(10) NOT NULL -- fijo: smb
|
||
normalizedAddress VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL
|
||
activeAddress VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL
|
||
UNIQUE (companyId, activeAddress)
|
||
UNIQUE (resourceId, companyId)
|
||
FOREIGN KEY (resourceId, companyId, templateId)
|
||
REFERENCES resources(id, companyId, templateId)
|
||
FOREIGN KEY (templateId, templateKey)
|
||
REFERENCES resource_definitions(id, stockKey)
|
||
```
|
||
|
||
CHECK restringe protocolo, plantilla, dirección no vacía y coincidencia binaria entre
|
||
clave activa y normalizada cuando no es null. El código mantiene `activeAddress` igual a
|
||
la dirección canónica para activos y null para inactivos. MariaDB garantiza la unicidad
|
||
de las claves activas, incluso bajo concurrencia; no se depende de un SELECT previo.
|
||
El cotejo binario conserva diferencias Unicode y acentos.
|
||
|
||
La proyección no es otra entidad principal ni otro editor. No duplica dirección de
|
||
presentación, host o shareName: se obtienen del campo UNC validado de Resource.
|
||
|
||
## Transacciones, historial y ciclo de vida
|
||
|
||
El guardado existente valida nuevas carpetas y carpetas ya especializadas. Dentro de
|
||
una transacción escribe Resource, revisiones y proyección SMB. Un conflicto de índice se
|
||
traduce a `SMB_ADDRESS_EXISTS` y provoca rollback de todas esas escrituras, también de
|
||
la proyección access si existía. Una UNC inválida produce `INVALID_SMB_ADDRESS`.
|
||
|
||
Cambiar UNC o nombre conserva UUID y revisiones. Un guardado idéntico no añade revisión.
|
||
Modificar solo notas/nombre/presentación no actualiza innecesariamente la proyección si
|
||
la dirección canónica y el estado no cambian.
|
||
|
||
Desactivar conserva Resource, proyección e historial y libera la clave activa.
|
||
Reactivar vuelve a adquirirla; un conflicto mantiene el recurso inactivo sin una revisión
|
||
parcial. Las direcciones inactivas se pueden reutilizar.
|
||
|
||
Eliminar utiliza las protecciones anteriores y comprueba también referencias a la
|
||
proyección SMB. Si existen referencias actuales o históricas externas, devuelve
|
||
`RESOURCE_IN_USE`. Sin referencias, elimina los datos propios, incluida la proyección,
|
||
en la transacción existente. No conserva un historial separado tras un borrado físico
|
||
explícito: se mantiene la semántica anterior de Resource.
|
||
|
||
## Catálogo
|
||
|
||
Renombrar plantilla/campos, cambiar etiquetas, iconos, secciones u orden no elimina la
|
||
capacidad. `template:shared_folder` debe conservar el campo de serie `field:unc`; quitarlo
|
||
produce `SMB_FIELD_REQUIRED`. El mecanismo anterior ya impide cambiar el tipo del campo.
|
||
No se modifica automáticamente la obligatoriedad del catálogo: el validador especializado
|
||
exige UNC para altas SMB aunque la característica siga figurando como opcional.
|
||
|
||
Deshabilitar disponibilidad por compañía impide altas nuevas, sin desespecializar los
|
||
registros existentes. No existe una operación ordinaria de borrado físico de plantillas;
|
||
las FK protegen la plantilla de los recursos especializados. La sincronización de
|
||
especificaciones conserva las características y añade revisiones mediante el mecanismo
|
||
existente; no altera la dirección ni la proyección por cambios de presentación.
|
||
|
||
## Registros anteriores y diagnóstico
|
||
|
||
Los registros previos **no se convierten automáticamente**, incluso si su UNC es válida,
|
||
al reiniciar, editar, activar/desactivar o sincronizar la plantilla. La distinción se
|
||
basa en la presencia de la proyección persistida, no en fechas aproximadas.
|
||
|
||
`GET /api/resources/:companyId/network-share-diagnostics`, con la autenticación existente,
|
||
no acepta parámetros adicionales y devuelve solo identificadores y clasificación:
|
||
|
||
- `classification`: `shared-folder` cierta por stockKey, o `legacy-candidate` heurística.
|
||
- `addressStatus`: `valid`, `invalid` o `missing`.
|
||
- `duplicate`: dirección canónica compartida por candidatos activos de esa compañía.
|
||
- `managed`: ya tiene proyección SMB.
|
||
- `requiresConversion`: todavía no tiene esa proyección.
|
||
|
||
La heurística para `template:access` busca una referencia o característica con prefijo
|
||
UNC. No usa nombres visibles, no declara capacidad y puede necesitar revisión humana.
|
||
No descubre carpetas escondidas en cualquier texto libre ni interpreta nombres de tipos
|
||
de acceso. El diagnóstico informa duplicados también entre candidatos, pero la restricción
|
||
SQL solo se aplica a recursos especializados; resolver legados queda fuera de 3.1 y 3.2.
|
||
|
||
El diagnóstico usa el inventario autorizado ya existente, no escribe ni incluye notas,
|
||
credenciales o características completas. No modifica plantilla, integración ni historial,
|
||
y no fusiona, elimina o sustituye recursos.
|
||
|
||
## Contratos públicos y siguientes fases
|
||
|
||
ResourceDTO incluye `networkShare` opcional con capacidad, protocolo, dirección principal,
|
||
clave canónica, host, nombre compartido y ruta relativa. Es solo respuesta, no entrada.
|
||
|
||
`@metis/resources/services` exporta `INetworkShareReader` y `NetworkShareReference`.
|
||
`resources.services.networkShares(transaction).find(companyId, resourceId)` devuelve
|
||
Maybe de la referencia mínima con UUID, compañía y estado; comprueba la especialización
|
||
y la dirección persistida y bloquea la lectura para los futuros comandos de publicación.
|
||
El consumidor debe autenticar y autorizar el comando antes de usar el lector interno.
|
||
No expone repositorios ni introduce dependencias de Resources hacia Assets o Companies.
|
||
|
||
La integración access sigue teniendo contexto fijo. Una carpeta histórica sin integración
|
||
no puede incorporarla mediante edición ordinaria. Las nuevas carpetas reciben integración
|
||
automática según el paso 3.2 descrito más abajo; no se incorpora conversión histórica ni
|
||
se crean cuentas o una segunda ficha editable.
|
||
`field:server` sigue siendo documental y no se resuelve contra Assets.
|
||
|
||
Una modificación de UNC en un recurso integrado actualiza su proyección access mediante
|
||
el flujo existente. No reescribe UNC, letra ni historial de FolderMapping; una ruta vieja
|
||
sigue siendo detectable por las reglas de disponibilidad anteriores. `permissionsCovered`
|
||
no cambia y continúa siendo cobertura documental, no comprobación real de permisos.
|
||
|
||
## Validación
|
||
|
||
- Resources: normalizador, equivalencias/no equivalencias, catálogo y diagnóstico.
|
||
- MariaDB: alta, edición, rollback, estados, concurrencia, FK, unicidad, lector, borrado,
|
||
legado, access, dominios, recursos ordinarios, FolderMapping y contrato HTTP autenticado.
|
||
- Integración dedicada: `pnpm --filter @metis/server test:network-shares`.
|
||
- La prueba exige NODE_ENV=test y DB_NAME=metis_desk_test antes de conectar; limpia solo
|
||
UUID de fixture y tablas auxiliares de referencias creadas por esa ejecución.
|
||
|
||
Las incidencias previas de dos textos de Companies y TS2375 en el calendario vendor
|
||
permanecen fuera del alcance. Los resultados de validación de esta entrega se informan
|
||
al finalizar, sin atribuir a esta fase pruebas que no se hayan ejecutado.
|
||
|
||
### Resultados del paso 3.1
|
||
|
||
- Suite completa de Resources: 78/78.
|
||
- Integración combinada Dominios 2.1–2.4 y SMB 3.1: 27/27 casos nativos, solo
|
||
metis_desk_test. Incluye 11 subcasos SMB y su contenedor.
|
||
- Typecheck de Resources, Assets, Companies, servidor y web: correcto.
|
||
- Biome y ESLint de los archivos afectados: correctos; build del servidor y
|
||
`git diff --check`: correctos.
|
||
- Suite completa Companies: 171/173; se reproducen únicamente los dos fallos previos
|
||
de texto en companies-page.test.tsx. No se modifican esas pruebas.
|
||
- Typecheck directo de vendor: reproduce TS2375 en calendar.tsx:165. No se modifica
|
||
packages/shadcn-ui ni se oculta el error.
|
||
|
||
Comandos utilizados (PNPM requiere desactivar su verificación automática de dependencias
|
||
antes de ejecutar en este entorno):
|
||
|
||
```bash
|
||
pnpm --config.verify-deps-before-run=false --filter @metis/resources --filter @metis/assets --filter @metis/companies --filter @metis/server --filter @metis/web typecheck
|
||
pnpm --config.verify-deps-before-run=false --filter @metis/server build
|
||
pnpm --config.verify-deps-before-run=false --filter @metis/server test:network-shares
|
||
```
|
||
|
||
Las suites completas de Resources y Companies se ejecutaron con todos los archivos de
|
||
sus scripts existentes, añadiendo `--test-isolation=none` para obtener los casos nativos.
|
||
La integración combinada se ejecutó con ese mismo flag y `--test-concurrency=1` sobre
|
||
los cinco archivos directory-domains, asset-directory-memberships, typed-relationships,
|
||
windows-domains-ui y network-shares. Biome y ESLint se limitaron a los archivos afectados.
|
||
|
||
## Paso 3.2 — integración automática con AccessWorkspace
|
||
|
||
Esta sección actualiza el comportamiento de las **nuevas altas** descrito para 3.1.
|
||
El paso 3.2 no incorpora conversión, adopción ni migración de recursos anteriores.
|
||
Cualquier adopción histórica requerirá un encargo independiente.
|
||
|
||
### Alta y contexto
|
||
|
||
El servidor establece `integration.kind=access`, referencia `field:unc` y fija
|
||
`systemId=null` y `environmentId=null`. No deduce autenticación del host UNC, dominio
|
||
ni publicador. Un cliente que suministra buzón, otro campo/tipo o un contexto incompatible
|
||
recibe `SMB_ACCESS_INCOMPATIBLE`; omitir integración activa la configuración automática.
|
||
|
||
El tipo configurable de AccessWorkspace se obtiene por el UUID ya utilizado por otra
|
||
carpeta de esa plantilla en la compañía. Así, renombrar el tipo no cambia las referencias.
|
||
Para la primera carpeta, Companies reutiliza un tipo visible llamado «Carpeta compartida
|
||
SMB» o crea ese tipo local usando el agregado y su auditoría habituales. Esta etiqueta
|
||
solo selecciona un valor por defecto de catálogo: la capacidad SMB sigue dependiendo de
|
||
stockKey y de la especialización, nunca del nombre. No se crean permisos, cuentas ni grupos.
|
||
Un tipo existente inactivo no se reactiva silenciosamente.
|
||
|
||
No se añade una migración de esquema ni una clasificación paralela. El tipo se conserva
|
||
por sus referencias UUID existentes; después de eliminar todas las carpetas, una primera
|
||
alta vuelve a aplicar la selección de catálogo descrita.
|
||
|
||
### Puerto transaccional y ownership
|
||
|
||
`IResourceAccessWriter`, exportado por `@metis/resources/services`, dispone de:
|
||
|
||
- `ensureSmbType`: prepara el tipo de acceso para una nueva carpeta dentro de la transacción.
|
||
- `project`: recibe valores e identidades de Resource y sus revisiones, sin exponer su
|
||
repositorio ni acceder al agregado de Companies desde Resources.
|
||
|
||
Companies implementa el puerto con `ResourceAccessWriter`, reutiliza `writeAccessRow` y
|
||
conserva el UUID de Resource y los UUID, actor, fecha y orden de sus revisiones. La
|
||
composición inyecta `companies.resourceAccess(transaction)` en Resources siguiendo
|
||
el patrón de DirectoryContextWriter. Resources no importa Companies: no aparece un ciclo.
|
||
La escritura de proyecciones access se ha trasladado de resource-compatibility a su módulo
|
||
propietario. Se conservan las consultas de referencias y las protecciones de eliminación
|
||
existentes, sin refactorizar globalmente AccessWorkspace.
|
||
|
||
### Flujo atómico
|
||
|
||
1. Bloquear catálogo de Resources, catálogo de accesos y compañía en el orden compartido.
|
||
2. Solo para una nueva carpeta, preparar el tipo configurable dentro de la transacción.
|
||
3. Validar datos y establecer integración en el servidor.
|
||
4. Escribir Resource y sus revisiones.
|
||
5. Escribir la proyección SMB y comprobar unicidad.
|
||
6. Proyectar valores y revisiones en AccessWorkspace.
|
||
7. Confirmar; cualquier fallo revierte también el tipo creado y ambas proyecciones.
|
||
|
||
`ACCESS_PROJECTION_UNAVAILABLE` indica que no se puede preparar el acceso; no se guarda
|
||
una nueva carpeta a medias si falta el participante de Companies. Las composiciones
|
||
que escriban recursos con integración access deben inyectar ese participante.
|
||
|
||
Renombrar, editar características/UNC y activar/desactivar reutiliza la misma proyección.
|
||
No se cierran necesidades, concesiones ni configuraciones al desactivar. Las referencias
|
||
actuales o históricas de acceso siguen impidiendo el borrado físico de Resource.
|
||
|
||
### Interfaz y utilización
|
||
|
||
El formulario existente muestra ayuda traducida sobre acceso automático, configurado o
|
||
histórico. No ofrece selectores incompatibles de sistema, entorno o tipo para SMB.
|
||
El navegador sigue editando únicamente Resource; los endpoints antiguos de escritura
|
||
AccessItem(resource) continúan rechazados por `RESOURCE_MOVED`.
|
||
|
||
Cuentas y accesos permite crear permisos configurables, necesidades y concesiones para
|
||
la carpeta, con las reglas existentes. Una misma carpeta puede recibir una concesión
|
||
a un grupo AD y otra a una cuenta local NAS. La cobertura es documental: no autentica,
|
||
comprueba ACL ni demuestra permisos efectivos SMB/NTFS.
|
||
|
||
WorkProfile y FolderMapping conservan su implementación, referencias, UNC registrada,
|
||
letra de unidad, historial y cálculo de cobertura. El cambio de dirección del recurso
|
||
no reescribe mapeos anteriores: sigue siendo detectable como desactualización.
|
||
|
||
### Históricos y límites
|
||
|
||
El alta automática se decide por una operación nueva, no por la presencia de una UNC ni
|
||
por una fecha. Leer, reiniciar, editar o activar una carpeta antigua sin integración
|
||
no añade AccessItem ni cambia su contexto. También se conservan carpetas especializadas
|
||
en 3.1 sin integración, y recursos anteriores bajo template:access.
|
||
|
||
No hay publicadores (3.3), identidades locales vinculadas a activos ni cambios de
|
||
FolderMapping (3.4), pantallas de administración de carpetas (3.5), LDAP, credenciales,
|
||
grupos anidados ni permisos efectivos. No se amplía el diagnóstico histórico.
|
||
|
||
Prueba dedicada: `pnpm --filter @metis/server test:smb-access`, exclusivamente contra
|
||
metis_desk_test. Comprueba integración, rechazos, sincronización, tipo renombrado, concesiones
|
||
AD/NAS, cobertura, borrado, rollback tras escribir accesos y ausencia de adopción histórica.
|
||
|
||
### Verificación del paso 3.2
|
||
|
||
- Resources completo: 80/80, incluidos contrato automático, rechazo de contexto y ayuda es/en.
|
||
- MariaDB: 35/35 en la ejecución combinada de Dominios 2.1–2.4, SMB 3.1 y SMB Access 3.2.
|
||
Solo metis_desk_test; incluye concesiones directas y por grupo, cobertura, rollback real
|
||
después de escribir proyecciones, contexto renombrado y registros anteriores sin adopción.
|
||
- Typechecks de Resources, Assets, Companies, servidor y web: correctos.
|
||
- Biome, ESLint de los archivos afectados, build del servidor y git diff --check: correctos.
|
||
- Grafo de los 13 paquetes del workspace: sin ciclos.
|
||
- Companies completo: 171/173; permanecen las dos expectativas previas de texto en
|
||
companies-page.test.tsx. Las pruebas de accesos incluidas en esa suite pasan.
|
||
- Se reproduce TS2375 en packages/shadcn-ui/src/components/calendar.tsx:165; vendor intacto.
|
||
|
||
La integración combinada usa NODE_ENV=test, DB_NAME=metis_desk_test,
|
||
--test-isolation=none y --test-concurrency=1. Las comprobaciones completas de paquetes
|
||
mantienen todos los archivos de sus scripts y añaden --test-isolation=none para obtener
|
||
los casos del runner nativo. Este paso no añade migraciones, conversiones históricas ni
|
||
pruebas de interacción en navegador; el cambio mínimo del formulario se verifica mediante SSR.
|
||
|
||
## Publicador documental (paso 3.3)
|
||
|
||
La relación especializada, sus comandos e historial, las restricciones SQL y las consultas tipadas se describen en [publicación de recursos compartidos SMB](publicacion-de-recursos-compartidos-smb.md). Assets es su propietario y consume el lector SMB público de Resources; esta relación no modifica la integración de accesos descrita en este documento.
|