- 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.
196 lines
5.9 KiB
TypeScript
196 lines
5.9 KiB
TypeScript
import type { Application, RequestHandler, Response } from "express";
|
|
import { Router } from "express";
|
|
import { z } from "zod/v4";
|
|
|
|
import {
|
|
InventoryParamsSchema,
|
|
ResourceParamsSchema,
|
|
SaveResourceRequestSchema,
|
|
SetResourceActiveRequestSchema,
|
|
} from "../../../common/inventory";
|
|
import { diagnoseNetworkShares } from "../../application/network-share-diagnostics";
|
|
import type { ResourceUseCases } from "../../application/resource.use-cases";
|
|
import { buildResourceResponse } from "../../application/resource-response.builder";
|
|
|
|
const LifecycleRequestSchema = z.discriminatedUnion("action", [
|
|
z.strictObject({ action: z.literal("status"), body: SetResourceActiveRequestSchema }),
|
|
z.strictObject({ action: z.literal("delete"), body: z.strictObject({}) }),
|
|
]);
|
|
|
|
function fail(response: Response, code: string) {
|
|
response
|
|
.status(code === "INVALID_REQUEST" ? 400 : code.endsWith("NOT_FOUND") ? 404 : 409)
|
|
.json({ code, requestId: response.getHeader("x-request-id") });
|
|
}
|
|
|
|
function sendResource(
|
|
response: Response,
|
|
result: Awaited<ReturnType<ResourceUseCases["setActive"]>>
|
|
) {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
|
|
return;
|
|
}
|
|
response.json({
|
|
resource: buildResourceResponse(
|
|
result.data.resource,
|
|
result.data.actorNames,
|
|
result.data.directory,
|
|
result.data.networkShare
|
|
),
|
|
});
|
|
}
|
|
|
|
function sendDeleted(response: Response, result: Awaited<ReturnType<ResourceUseCases["remove"]>>) {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
|
|
return;
|
|
}
|
|
response.json({ deleted: true });
|
|
}
|
|
|
|
export function registerResourceRoutes(
|
|
app: Application,
|
|
useCases: ResourceUseCases,
|
|
authenticate: RequestHandler
|
|
) {
|
|
const router = Router();
|
|
|
|
for (const action of ["status", "delete"] as const) {
|
|
router.post(`/:companyId/:id/${action}`, (request, response, next) => {
|
|
const params = ResourceParamsSchema.safeParse(request.params);
|
|
const actor = z
|
|
.object({ userId: z.uuid() })
|
|
.safeParse("session" in request ? request.session : undefined);
|
|
const body = LifecycleRequestSchema.safeParse({ action, body: request.body });
|
|
|
|
if (
|
|
!(
|
|
params.success &&
|
|
actor.success &&
|
|
body.success &&
|
|
z.strictObject({}).safeParse(request.query).success
|
|
)
|
|
) {
|
|
fail(response, "INVALID_REQUEST");
|
|
|
|
return;
|
|
}
|
|
if (body.data.action === "delete") {
|
|
useCases
|
|
.remove(params.data.companyId, params.data.id)
|
|
.then((result) => sendDeleted(response, result))
|
|
.catch(next);
|
|
|
|
return;
|
|
}
|
|
useCases
|
|
.setActive(params.data.companyId, params.data.id, body.data.body.active, actor.data.userId)
|
|
.then((result) => sendResource(response, result))
|
|
.catch(next);
|
|
});
|
|
}
|
|
|
|
router.get("/:companyId/network-share-diagnostics", (request, response, next) => {
|
|
const params = InventoryParamsSchema.safeParse(request.params);
|
|
|
|
if (!(params.success && z.strictObject({}).safeParse(request.query).success)) {
|
|
fail(response, "INVALID_REQUEST");
|
|
|
|
return;
|
|
}
|
|
useCases
|
|
.get(params.data.companyId)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
|
|
return;
|
|
}
|
|
response.json({ items: diagnoseNetworkShares(result.data) });
|
|
})
|
|
.catch(next);
|
|
});
|
|
|
|
router.get("/:companyId", (request, response, next) => {
|
|
const params = InventoryParamsSchema.safeParse(request.params);
|
|
|
|
const query = z.strictObject({ environmentId: z.uuid().optional() }).safeParse(request.query);
|
|
|
|
if (!(params.success && query.success)) {
|
|
fail(response, "INVALID_REQUEST");
|
|
|
|
return;
|
|
}
|
|
useCases
|
|
.get(params.data.companyId)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
|
|
return;
|
|
}
|
|
response.json({
|
|
references: result.data.references,
|
|
resources: result.data.resources
|
|
.filter(
|
|
(resource) =>
|
|
!query.data.environmentId ||
|
|
result.data.directoryDomains?.get(resource.id.toPrimitive())?.environmentId ===
|
|
query.data.environmentId.toLowerCase()
|
|
)
|
|
.map((resource) =>
|
|
buildResourceResponse(
|
|
resource,
|
|
result.data.actorNames,
|
|
result.data.directoryDomains?.get(resource.id.toPrimitive()),
|
|
result.data.networkShares?.get(resource.id.toPrimitive())
|
|
)
|
|
),
|
|
});
|
|
})
|
|
.catch(next);
|
|
});
|
|
for (const path of ["/:companyId", "/:companyId/:id/update"]) {
|
|
router.post(path, (request, response, next) => {
|
|
const params = (
|
|
path.endsWith("update") ? ResourceParamsSchema : InventoryParamsSchema
|
|
).safeParse(request.params);
|
|
const body = SaveResourceRequestSchema.safeParse(request.body);
|
|
const actor = z
|
|
.object({ userId: z.uuid() })
|
|
.safeParse("session" in request ? request.session : undefined);
|
|
|
|
if (!(params.success && body.success && actor.success)) {
|
|
fail(response, "INVALID_REQUEST");
|
|
|
|
return;
|
|
}
|
|
const id =
|
|
"id" in params.data && typeof params.data.id === "string" ? params.data.id : undefined;
|
|
|
|
useCases
|
|
.save(params.data.companyId, id, body.data.values, actor.data.userId)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
|
|
return;
|
|
}
|
|
response.status(id ? 200 : 201).json({
|
|
resource: buildResourceResponse(
|
|
result.data.resource,
|
|
result.data.actorNames,
|
|
result.data.directory,
|
|
result.data.networkShare
|
|
),
|
|
});
|
|
})
|
|
.catch(next);
|
|
});
|
|
}
|
|
app.use("/api/resources", authenticate, router);
|
|
}
|