feat(cms): improve catalog, editorial recovery and operations
This commit is contained in:
1 parent
2fead134e6
commit
c389c3893d
122 files changed
+8137
-703
No files matched your search
@@ -7,6 +7,7 @@ const mocks = vi.hoisted(() => ({
|
||||
guard: vi.fn(),
|
||||
create: vi.fn(),
|
||||
list: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
after: vi.fn(),
|
||||
ping: vi.fn(),
|
||||
source: vi.fn(),
|
||||
@@ -25,15 +26,17 @@ vi.mock("@/lib/redis", () => ({ redis: { ping: mocks.ping } }));
|
||||
vi.mock("@/lib/services/furni-job-worker", () => ({
|
||||
drainFurnitureImports: vi.fn(),
|
||||
}));
|
||||
vi.mock("@/lib/services/furni-job-store", () => ({
|
||||
vi.mock("@/lib/services/furni-job-store", async (original) => ({
|
||||
...(await original<typeof import("@/lib/services/furni-job-store")>()),
|
||||
ImportJobStore: class {
|
||||
create = mocks.create;
|
||||
list = mocks.list;
|
||||
requestCancellation = mocks.cancel;
|
||||
},
|
||||
}));
|
||||
vi.mock("@/lib/services/clone-sources", () => ({ getSource: mocks.source }));
|
||||
|
||||
import { GET, POST } from "./route";
|
||||
import { GET, PATCH, POST } from "./route";
|
||||
|
||||
const item = {
|
||||
id: 1,
|
||||
@@ -106,11 +109,23 @@ it("rejects a missing configured source", async () => {
|
||||
).status,
|
||||
).toBe(404);
|
||||
});
|
||||
it("only returns the current operators history", async () => {
|
||||
mocks.list.mockResolvedValue([
|
||||
{ id: "a", userId: 7 },
|
||||
{ id: "b", userId: 9 },
|
||||
]);
|
||||
it("requests bounded owner-scoped history from the store", async () => {
|
||||
mocks.list.mockResolvedValue([{ id: "a", userId: 7 }]);
|
||||
const response = await GET(request({}), ctx);
|
||||
expect(mocks.list).toHaveBeenCalledWith({ userId: 7, limit: 30 });
|
||||
expect((await response.json()).jobs).toEqual([{ id: "a", userId: 7 }]);
|
||||
});
|
||||
it("requests cancellation with the authenticated owner", async () => {
|
||||
const id = randomUUID();
|
||||
mocks.cancel.mockResolvedValue({ id, cancelRequested: true });
|
||||
const response = await PATCH(request({ id }), ctx);
|
||||
expect(response.status).toBe(200);
|
||||
expect(mocks.cancel).toHaveBeenCalledWith(id, 7);
|
||||
});
|
||||
it("does not reveal other owners' jobs", async () => {
|
||||
mocks.cancel.mockResolvedValue(null);
|
||||
expect((await PATCH(request({ id: randomUUID() }), ctx)).status).toBe(404);
|
||||
});
|
||||
it("rejects unsafe cancellation IDs", async () => {
|
||||
expect((await PATCH(request({ id: "../other" }), ctx)).status).toBe(400);
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import { validateClassnames } from "@/lib/furni/studio-inspection";
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { getSource } from "@/lib/services/clone-sources";
|
||||
import { ImportJobStore } from "@/lib/services/furni-job-store";
|
||||
import { ImportJobStore, validJobId } from "@/lib/services/furni-job-store";
|
||||
import { drainFurnitureImports } from "@/lib/services/furni-job-worker";
|
||||
|
||||
const schema = z.object({
|
||||
@@ -38,10 +38,10 @@ export const GET = withAdmin(
|
||||
{ permission: PERMS.ASSETS_IMPORT },
|
||||
async (_request, ctx) => {
|
||||
after(drainFurnitureImports);
|
||||
const jobs = (await new ImportJobStore().list())
|
||||
.filter((job) => job.userId === ctx.session.user.id)
|
||||
.reverse()
|
||||
.slice(0, 30);
|
||||
const jobs = await new ImportJobStore().list({
|
||||
userId: ctx.session.user.id,
|
||||
limit: 30,
|
||||
});
|
||||
return apiOk({ jobs });
|
||||
},
|
||||
);
|
||||
@@ -82,3 +82,18 @@ export const POST = withAdmin(
|
||||
return apiOk({ job });
|
||||
},
|
||||
);
|
||||
|
||||
export const PATCH = withAdmin(
|
||||
{ permission: PERMS.ASSETS_IMPORT },
|
||||
async (request, ctx) => {
|
||||
const body = await request.json().catch(() => null);
|
||||
if (!validJobId(body?.id)) return apiError("Invalid import ID", 400);
|
||||
const job = await new ImportJobStore().requestCancellation(
|
||||
body.id,
|
||||
ctx.session.user.id,
|
||||
);
|
||||
if (!job) return apiError("Import job not found", 404);
|
||||
after(drainFurnitureImports);
|
||||
return apiOk({ job });
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user