mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-22 16:55:44 +00:00
Merge pull request #3122 from fosrl/button-to-rebuild-association
Add button to rebuid cache
This commit is contained in:
@@ -782,3 +782,9 @@ authenticated.get(
|
|||||||
verifyClientAccess,
|
verifyClientAccess,
|
||||||
client.verifyClientAssociationsCache
|
client.verifyClientAssociationsCache
|
||||||
);
|
);
|
||||||
|
|
||||||
|
authenticated.post(
|
||||||
|
"/client/:clientId/rebuild-associations-cache",
|
||||||
|
verifyClientAccess,
|
||||||
|
client.rebuildClientAssociationsCacheRoute
|
||||||
|
);
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ export * from "./updateClient";
|
|||||||
export * from "./getClient";
|
export * from "./getClient";
|
||||||
export * from "./createUserClient";
|
export * from "./createUserClient";
|
||||||
export * from "./verifyClientAssociationsCache";
|
export * from "./verifyClientAssociationsCache";
|
||||||
|
export * from "./rebuildClientAssociationsCacheRoute";
|
||||||
|
|||||||
81
server/routers/client/rebuildClientAssociationsCacheRoute.ts
Normal file
81
server/routers/client/rebuildClientAssociationsCacheRoute.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { db } from "@server/db";
|
||||||
|
import { clients } from "@server/db";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import response from "@server/lib/response";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
import { rebuildClientAssociationsFromClient } from "@server/lib/rebuildClientAssociations";
|
||||||
|
|
||||||
|
const paramsSchema = z.strictObject({
|
||||||
|
clientId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/client/{clientId}/rebuild-associations-cache",
|
||||||
|
description:
|
||||||
|
"Rebuild the client's site/site-resource association cache based on current permissions.",
|
||||||
|
tags: [OpenAPITags.Client],
|
||||||
|
request: {
|
||||||
|
params: paramsSchema
|
||||||
|
},
|
||||||
|
responses: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function rebuildClientAssociationsCacheRoute(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
const parsedParams = paramsSchema.safeParse(req.params);
|
||||||
|
if (!parsedParams.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedParams.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { clientId } = parsedParams.data;
|
||||||
|
|
||||||
|
const [client] = await db
|
||||||
|
.select()
|
||||||
|
.from(clients)
|
||||||
|
.where(eq(clients.clientId, clientId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
`Client with ID ${clientId} not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await rebuildClientAssociationsFromClient(client);
|
||||||
|
|
||||||
|
return response(res, {
|
||||||
|
data: null,
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Client association cache rebuilt successfully",
|
||||||
|
status: HttpCode.OK
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to rebuild client association cache"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -165,6 +165,34 @@ export default function GeneralPage() {
|
|||||||
actualSiteIds: number[];
|
actualSiteIds: number[];
|
||||||
}>(null);
|
}>(null);
|
||||||
const [isCheckingCache, setIsCheckingCache] = useState(false);
|
const [isCheckingCache, setIsCheckingCache] = useState(false);
|
||||||
|
const [isRebuildingCache, setIsRebuildingCache] = useState(false);
|
||||||
|
|
||||||
|
const handleRebuildCache = async () => {
|
||||||
|
if (!client.clientId) return;
|
||||||
|
setIsRebuildingCache(true);
|
||||||
|
try {
|
||||||
|
await api.post(
|
||||||
|
`/client/${client.clientId}/rebuild-associations-cache`
|
||||||
|
);
|
||||||
|
// Re-verify after rebuild so the result refreshes
|
||||||
|
const res = await api.get(
|
||||||
|
`/client/${client.clientId}/verify-associations-cache`
|
||||||
|
);
|
||||||
|
setCacheCheck(res.data.data);
|
||||||
|
toast({
|
||||||
|
title: "Cache rebuilt",
|
||||||
|
description: "Association cache rebuilt successfully."
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Rebuild failed",
|
||||||
|
description: formatAxiosError(e, "Failed to rebuild cache")
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsRebuildingCache(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleVerifyCache = async () => {
|
const handleVerifyCache = async () => {
|
||||||
if (!client.clientId) return;
|
if (!client.clientId) return;
|
||||||
@@ -904,7 +932,7 @@ export default function GeneralPage() {
|
|||||||
Cache is consistent
|
Cache is consistent
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-1">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center gap-1 font-semibold">
|
<div className="flex items-center gap-1 font-semibold">
|
||||||
<XCircle className="h-3 w-3" />
|
<XCircle className="h-3 w-3" />
|
||||||
Cache is INCONSISTENT
|
Cache is INCONSISTENT
|
||||||
@@ -929,6 +957,16 @@ export default function GeneralPage() {
|
|||||||
Extra sites: [
|
Extra sites: [
|
||||||
{cacheCheck.extraSiteIds.join(", ")}]
|
{cacheCheck.extraSiteIds.join(", ")}]
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleRebuildCache}
|
||||||
|
disabled={isRebuildingCache}
|
||||||
|
className="mt-1 text-xs underline font-semibold disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isRebuildingCache
|
||||||
|
? "Rebuilding…"
|
||||||
|
: "Rebuild cache now"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user