- Added `removeAccessAccount` function to handle account deletion in the database, ensuring no external references exist. - Created `deleteAccount` API function to facilitate account deletion requests from the client. - Developed `AccountDeleteDialog` component for user confirmation before account deletion, integrating with the delete API. - Introduced resource image handling with validation for PNG uploads, including UI components for image selection and preview. - Implemented resource icon template evolution logic to maintain icon bindings without overwriting existing fields. - Added scripts for auditing account deletion and usage checks, ensuring robust testing of the new features.
304 lines
8.8 KiB
TypeScript
304 lines
8.8 KiB
TypeScript
import { UniqueID } from "@repo/rdx-ddd";
|
||
|
||
import { BUILTIN_RESOURCE_TEMPLATE_ICONS } from "../../common/resource-template-icons";
|
||
|
||
import { evolveOperatingSystemTemplate } from "./evolve-operating-system-template";
|
||
import { evolveResourceIconTemplates } from "./evolve-resource-icon-templates";
|
||
import type { ResourceCatalog } from "./resource-catalog.aggregate";
|
||
import type { ResourceDefinitionSpec, ResourceFieldType } from "./resource-definition";
|
||
import { ResourceDefinitionValues } from "./value-objects/resource-definition-values.vo";
|
||
|
||
const FIELDS: readonly [string, string, ResourceFieldType, string, (readonly string[])?][] = [
|
||
[
|
||
"os_family",
|
||
"Familia del sistema operativo",
|
||
"text",
|
||
"Familia comercial del producto. Ejemplo: Windows Server.",
|
||
],
|
||
[
|
||
"os_product_version",
|
||
"Versión principal del producto",
|
||
"text",
|
||
"Versión comercial, no la versión instalada. Ejemplo: 2022 o 24.04 LTS.",
|
||
],
|
||
[
|
||
"os_edition_state",
|
||
"Estado de edición",
|
||
"choice",
|
||
"Distingue edición conocida, no aplicable y desconocida. Ejemplo: known para Pro.",
|
||
["known", "not_applicable", "unknown"],
|
||
],
|
||
[
|
||
"email",
|
||
"Dirección de correo",
|
||
"text",
|
||
"Indica la dirección completa del buzón. Ejemplo: soporte@empresa.es.",
|
||
],
|
||
[
|
||
"provider",
|
||
"Proveedor",
|
||
"text",
|
||
"Indica el proveedor o fabricante del servicio. Ejemplo: Microsoft 365.",
|
||
],
|
||
[
|
||
"context",
|
||
"Contexto",
|
||
"text",
|
||
"Aclara el uso, área o persona a la que está destinado. Ejemplo: facturación.",
|
||
],
|
||
["unc", "Ruta UNC", "text", "Usa la ruta de red completa. Ejemplo: \\servidor\\contabilidad."],
|
||
[
|
||
"server",
|
||
"Servidor (referencia documental)",
|
||
"text",
|
||
"Anota el servidor que publica el recurso, si se conoce. Ejemplo: FS-01.",
|
||
],
|
||
[
|
||
"description",
|
||
"Descripción",
|
||
"text",
|
||
"Añade los detalles necesarios para identificar o utilizar el recurso. Ejemplo: acceso solo para administración.",
|
||
],
|
||
[
|
||
"url",
|
||
"URL",
|
||
"text",
|
||
"Incluye la dirección web de acceso o de documentación. Ejemplo: https://portal.empresa.es.",
|
||
],
|
||
[
|
||
"holder",
|
||
"Titular",
|
||
"text",
|
||
"Indica la persona u organización titular del certificado. Ejemplo: Empresa S. L.",
|
||
],
|
||
[
|
||
"issuer",
|
||
"Emisor",
|
||
"text",
|
||
"Indica la entidad que emitió el certificado. Ejemplo: Let's Encrypt.",
|
||
],
|
||
[
|
||
"expires",
|
||
"Caducidad",
|
||
"date",
|
||
"Indica la fecha en la que deja de ser válido. Ejemplo: 2027-06-30.",
|
||
],
|
||
[
|
||
"fingerprint",
|
||
"Huella digital",
|
||
"text",
|
||
"Copia la huella o identificador único del certificado. Ejemplo: SHA-256 A1:B2:C3:…",
|
||
],
|
||
[
|
||
"purpose",
|
||
"Finalidad",
|
||
"text",
|
||
"Explica qué servicio, proceso o equipo utiliza este recurso. Ejemplo: firma de facturas.",
|
||
],
|
||
[
|
||
"reference",
|
||
"Referencia",
|
||
"text",
|
||
"Registra el identificador, número de contrato o referencia del proveedor. Ejemplo: contrato MS-2026-014.",
|
||
],
|
||
[
|
||
"software_product",
|
||
"Producto de software",
|
||
"text",
|
||
"Indica el nombre comercial del producto licenciado. Ejemplo: Microsoft 365 Business Premium.",
|
||
],
|
||
[
|
||
"software_edition",
|
||
"Edición o plan",
|
||
"text",
|
||
"Indica la edición, nivel o plan contratado. Ejemplo: Enterprise E3.",
|
||
],
|
||
[
|
||
"license_model",
|
||
"Modalidad de licencia",
|
||
"choice",
|
||
"Selecciona cómo se adquiere o mantiene la licencia. Ejemplo: Suscripción.",
|
||
["Suscripción", "Perpetua", "OEM", "Otra"],
|
||
],
|
||
[
|
||
"license_metric",
|
||
"Criterio de licenciamiento",
|
||
"choice",
|
||
"Selecciona la unidad con la que se calcula la cobertura contratada. Ejemplo: Por usuario.",
|
||
["Por usuario", "Por dispositivo", "Por servidor", "Concurrente", "Corporativa", "Otro"],
|
||
],
|
||
[
|
||
"license_quantity",
|
||
"Cantidad contratada",
|
||
"number",
|
||
"Indica el número de licencias o unidades cubiertas. Ejemplo: 25.",
|
||
],
|
||
[
|
||
"renews",
|
||
"Fecha de renovación",
|
||
"date",
|
||
"Indica la próxima fecha prevista de renovación. Ejemplo: 2027-01-01.",
|
||
],
|
||
[
|
||
"auto_renew",
|
||
"Renovación automática",
|
||
"boolean",
|
||
"Marca si la renovación se realiza automáticamente. Ejemplo: Sí si el proveedor carga el siguiente periodo.",
|
||
],
|
||
[
|
||
"domain_dns",
|
||
"Nombre DNS",
|
||
"text",
|
||
"Nombre DNS del dominio. Ejemplo: empresa.local. IDN: introducir el nombre ASCII punycode.",
|
||
],
|
||
[
|
||
"domain_netbios",
|
||
"Nombre NetBIOS",
|
||
"text",
|
||
"Nombre corto del dominio, opcional, hasta 15 caracteres. Ejemplo: EMPRESA.",
|
||
],
|
||
[
|
||
"icon",
|
||
"Icono",
|
||
"image",
|
||
"Imagen PNG estática de hasta 512 × 512 píxeles y 2 MiB. Representa el producto. Ejemplo: icono de la aplicación.",
|
||
],
|
||
];
|
||
const TEMPLATES: readonly [
|
||
keyof typeof BUILTIN_RESOURCE_TEMPLATE_ICONS,
|
||
string,
|
||
readonly string[],
|
||
][] = [
|
||
["mailbox", "Buzón de correo", ["email", "provider", "context"]],
|
||
["access", "Recurso de acceso", ["reference"]],
|
||
["shared_folder", "Carpeta compartida", ["unc", "server", "description"]],
|
||
["support", "Herramienta de asistencia remota", ["provider", "url", "description"]],
|
||
[
|
||
"os",
|
||
"Sistema operativo",
|
||
[
|
||
"provider",
|
||
"os_family",
|
||
"os_product_version",
|
||
"os_edition_state",
|
||
"software_edition",
|
||
"description",
|
||
"url",
|
||
"icon",
|
||
],
|
||
],
|
||
["application", "Aplicación", ["provider", "url", "description", "icon"]],
|
||
["certificate", "Certificado digital", ["holder", "issuer", "expires", "fingerprint", "purpose"]],
|
||
[
|
||
"software_license",
|
||
"Licencia de software",
|
||
[
|
||
"software_product",
|
||
"software_edition",
|
||
"provider",
|
||
"license_model",
|
||
"license_metric",
|
||
"license_quantity",
|
||
"reference",
|
||
"expires",
|
||
"renews",
|
||
"auto_renew",
|
||
"description",
|
||
"icon",
|
||
],
|
||
],
|
||
["windows_domain", "Dominio Windows", ["domain_dns", "domain_netbios"]],
|
||
];
|
||
/** Solo alimenta definiciones; no crea recursos ni migra registros anteriores. */
|
||
const SECTION_FIELDS: readonly [string, readonly string[]][] = [
|
||
["Identificación", ["holder", "issuer", "fingerprint"]],
|
||
["Producto", ["software_product", "software_edition", "provider"]],
|
||
["Contratación", ["license_model", "license_metric", "license_quantity", "reference"]],
|
||
["Vigencia y renovación", ["expires", "renews", "auto_renew"]],
|
||
["Uso y observaciones", ["purpose", "description"]],
|
||
];
|
||
|
||
export function seedResourceCatalog(catalog: ResourceCatalog) {
|
||
const evolveIcons = !catalog.definitions.some(
|
||
(item) => item.stockKey.getOrUndefined() === "field:icon"
|
||
);
|
||
const evolveOs = !catalog.definitions.some(
|
||
(item) => item.stockKey.getOrUndefined() === "field:os_edition_state"
|
||
);
|
||
let current = catalog;
|
||
|
||
const add = (key: string, input: ResourceDefinitionSpec) => {
|
||
const values = ResourceDefinitionValues.create(input);
|
||
|
||
if (!values.isSuccess) {
|
||
throw new Error("Definición inicial de recursos inválida.");
|
||
}
|
||
const result = current.seed(key, values.data);
|
||
|
||
if (!result.isSuccess) {
|
||
throw new Error("Catálogo inicial de recursos inconsistente.");
|
||
}
|
||
current = result.data;
|
||
};
|
||
|
||
for (const [key, name, type, description, choices] of FIELDS) {
|
||
add(`field:${key}`, {
|
||
kind: "field",
|
||
name,
|
||
description,
|
||
type,
|
||
unit: "",
|
||
choices: [...(choices ?? [])],
|
||
});
|
||
}
|
||
for (const [key, name, fields] of TEMPLATES) {
|
||
const sectionFields = fields;
|
||
const sections = (["certificate", "software_license"].includes(key) ? SECTION_FIELDS : [])
|
||
.filter(([, keys]) => keys.some((field) => sectionFields.includes(field)))
|
||
.map(([name, keys]) => ({
|
||
id: UniqueID.generateNewID().toPrimitive(),
|
||
name,
|
||
description: "",
|
||
keys,
|
||
}));
|
||
const references = fields.map((field) => {
|
||
const id = current.definitions
|
||
.find((item) => item.stockKey.getOrUndefined() === `field:${field}`)
|
||
?.id.toPrimitive();
|
||
|
||
if (!id) {
|
||
throw new Error("Campo inicial de recursos ausente.");
|
||
}
|
||
const sectionId = sections.find((section) => section.keys.includes(field))?.id;
|
||
|
||
return {
|
||
fieldId: id,
|
||
required: key === "windows_domain" && field === "domain_dns",
|
||
...(sectionId ? { sectionId } : {}),
|
||
};
|
||
});
|
||
|
||
add(`template:${key}`, {
|
||
kind: "template",
|
||
icon: BUILTIN_RESOURCE_TEMPLATE_ICONS[key],
|
||
...(key === "software_license"
|
||
? { usage: "license" as const }
|
||
: key === "support" || key === "os"
|
||
? { usage: key }
|
||
: {}),
|
||
name,
|
||
description: "",
|
||
fields: references,
|
||
...(sections.length
|
||
? { sections: sections.map(({ id, name, description }) => ({ id, name, description })) }
|
||
: {}),
|
||
});
|
||
}
|
||
|
||
if (evolveOs) {
|
||
current = evolveOperatingSystemTemplate(current);
|
||
}
|
||
|
||
return evolveIcons ? evolveResourceIconTemplates(current) : current;
|
||
}
|