feat: Add button to empty trash (#6772)

Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
Hemachandar
2024-04-16 18:34:56 +05:30
committed by GitHub
parent a5d2752122
commit ef0fb74308
11 changed files with 244 additions and 18 deletions

View File

@@ -1,5 +1,5 @@
import uniq from "lodash/uniq";
import { QueryTypes } from "sequelize";
import { Op, QueryTypes } from "sequelize";
import Logger from "@server/logging/Logger";
import { Document, Attachment } from "@server/models";
import DeleteAttachmentTask from "@server/queues/tasks/DeleteAttachmentTask";
@@ -73,6 +73,21 @@ export default async function documentPermanentDeleter(documents: Document[]) {
);
}
const documentIds = documents.map((document) => document.id);
await Document.update(
{
parentDocumentId: null,
},
{
where: {
parentDocumentId: {
[Op.in]: documentIds,
},
},
paranoid: false,
}
);
return Document.scope("withDrafts").destroy({
where: {
id: documents.map((document) => document.id),

View File

@@ -18,6 +18,24 @@ exports[`#documents.delete should require authentication 1`] = `
}
`;
exports[`#documents.empty_trash should not allow non-admin users 1`] = `
{
"error": "authorization_error",
"message": "Admin role required",
"ok": false,
"status": 403,
}
`;
exports[`#documents.empty_trash should require authentication 1`] = `
{
"error": "authentication_required",
"message": "Authentication required",
"ok": false,
"status": 401,
}
`;
exports[`#documents.list should require authentication 1`] = `
{
"error": "authentication_required",

View File

@@ -4345,3 +4345,58 @@ describe("#documents.memberships", () => {
expect(body.data.users[0].id).toEqual(members[1].id);
});
});
describe("#documents.empty_trash", () => {
it("should require authentication", async () => {
const res = await server.post("/api/documents.empty_trash");
const body = await res.json();
expect(res.status).toEqual(401);
expect(body).toMatchSnapshot();
});
it("should allow admin users", async () => {
const user = await buildAdmin();
const res = await server.post("/api/documents.empty_trash", {
body: {
token: user.getJwtToken(),
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.success).toEqual(true);
});
it("should not allow non-admin users", async () => {
const user = await buildUser();
const res = await server.post("/api/documents.empty_trash", {
body: {
token: user.getJwtToken(),
},
});
const body = await res.json();
expect(res.status).toEqual(403);
expect(body).toMatchSnapshot();
});
it("should permanently delete documents", async () => {
const user = await buildAdmin();
const document = await buildDocument({
userId: user.id,
teamId: user.teamId,
});
await document.delete(user.id);
const res = await server.post("/api/documents.empty_trash", {
body: {
token: user.getJwtToken(),
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.success).toEqual(true);
const deletedDoc = await Document.findByPk(document.id, {
userId: user.id,
paranoid: false,
});
expect(deletedDoc).toBeNull();
});
});

View File

@@ -1213,17 +1213,6 @@ router.post(
});
authorize(user, "permanentDelete", document);
await Document.update(
{
parentDocumentId: null,
},
{
where: {
parentDocumentId: document.id,
},
paranoid: false,
}
);
await documentPermanentDeleter([document]);
await Event.create({
name: "documents.permanent_delete",
@@ -1701,4 +1690,55 @@ router.post(
}
);
router.post(
"documents.empty_trash",
auth({ role: UserRole.Admin }),
async (ctx: APIContext) => {
const { user } = ctx.state.auth;
const collectionIds = await user.collectionIds({
paranoid: false,
});
const collectionScope: Readonly<ScopeOptions> = {
method: ["withCollectionPermissions", user.id],
};
const documents = await Document.scope([
collectionScope,
"withDrafts",
]).findAll({
where: {
deletedAt: {
[Op.ne]: null,
},
[Op.or]: [
{
collectionId: {
[Op.in]: collectionIds,
},
},
{
createdById: user.id,
collectionId: {
[Op.is]: null,
},
},
],
},
paranoid: false,
});
await documentPermanentDeleter(documents);
await Event.create({
name: "documents.empty_trash",
teamId: user.teamId,
actorId: user.id,
ip: ctx.request.ip,
});
ctx.body = {
success: true,
};
}
);
export default router;