strapi-plugin-recycle-bin 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +77 -3
- package/dist/admin/App-C4C5Xm58.mjs +133 -0
- package/dist/admin/App-CVuyP4MA.js +133 -0
- package/dist/admin/en-Dsektncp.mjs +6 -0
- package/dist/admin/en-tJ9b64pl.js +6 -0
- package/dist/admin/index.js +69 -0
- package/dist/admin/index.mjs +69 -0
- package/dist/admin/src/components/Initializer.d.ts +5 -0
- package/dist/admin/src/components/PluginIcon.d.ts +2 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/pages/App.d.ts +2 -0
- package/dist/admin/src/pages/HomePage.d.ts +3 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/utils/getTranslation.d.ts +2 -0
- package/dist/server/index.js +258 -0
- package/dist/server/index.mjs +258 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +8 -0
- package/dist/server/src/content-types/index.d.ts +53 -0
- package/dist/server/src/content-types/recycle-bin-item/schema.json.d.ts +51 -0
- package/dist/server/src/controllers/index.d.ts +10 -0
- package/dist/server/src/controllers/recycle-bin.d.ts +9 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +110 -0
- package/dist/server/src/middlewares/index.d.ts +2 -0
- package/dist/server/src/middlewares/intercept-deletion.d.ts +2 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/admin.d.ts +12 -0
- package/dist/server/src/routes/index.d.ts +14 -0
- package/dist/server/src/services/archive.d.ts +7 -0
- package/dist/server/src/services/index.d.ts +16 -0
- package/dist/server/src/services/restore.d.ts +10 -0
- package/package.json +85 -4
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
+
const registerDeletionMiddleware = (strapi) => {
|
|
4
|
+
strapi.documents.use(async (context, next) => {
|
|
5
|
+
if (context.action === "delete") {
|
|
6
|
+
const uid = context.uid;
|
|
7
|
+
const documentId = context.params?.documentId;
|
|
8
|
+
const auth = strapi.requestContext?.get()?.state?.user;
|
|
9
|
+
if (uid && documentId) {
|
|
10
|
+
try {
|
|
11
|
+
await strapi.plugin("recycle-bin").service("archive").capture(uid, documentId, auth);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
strapi.log.error(`[recycle-bin] Archiving failed for ${uid} (${documentId}):`, err);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return next();
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const bootstrap = ({ strapi }) => {
|
|
21
|
+
registerDeletionMiddleware(strapi);
|
|
22
|
+
strapi.cron.add({
|
|
23
|
+
recycleBinRetentionJob: {
|
|
24
|
+
task: async () => {
|
|
25
|
+
const config2 = strapi.config.get("plugin::recycle-bin");
|
|
26
|
+
const days = config2.retentionDays || 30;
|
|
27
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
28
|
+
const deleted = await strapi.db.query("plugin::recycle-bin.recycle-bin-item").deleteMany({
|
|
29
|
+
where: {
|
|
30
|
+
deletedAt: { $lt: cutoff }
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
strapi.log.info(`[recycle-bin] Auto-purged ${deleted.count} expired item(s).`);
|
|
34
|
+
},
|
|
35
|
+
options: {
|
|
36
|
+
rule: "0 0 * * *"
|
|
37
|
+
// Midnight
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const destroy = ({ strapi }) => {
|
|
43
|
+
};
|
|
44
|
+
const register = ({ strapi }) => {
|
|
45
|
+
};
|
|
46
|
+
const config = {
|
|
47
|
+
default: {
|
|
48
|
+
retentionDays: 30,
|
|
49
|
+
excludedUids: [
|
|
50
|
+
"admin::user",
|
|
51
|
+
"admin::permission",
|
|
52
|
+
"admin::role",
|
|
53
|
+
"admin::api-token",
|
|
54
|
+
"admin::transfer-token",
|
|
55
|
+
"plugin::recycle-bin.recycle-bin-item",
|
|
56
|
+
"plugin::upload.file"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
validator() {
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const kind = "collectionType";
|
|
63
|
+
const collectionName = "recycle_bin_items";
|
|
64
|
+
const info = { "singularName": "recycle-bin-item", "pluralName": "recycle-bin-items", "displayName": "Recycle Bin Item" };
|
|
65
|
+
const options = { "draftAndPublish": false };
|
|
66
|
+
const pluginOptions = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
|
|
67
|
+
const attributes = { "entityUid": { "type": "string", "required": true }, "recordDocumentId": { "type": "string", "required": true, "index": true }, "title": { "type": "string" }, "payload": { "type": "json", "required": true }, "deletedBy": { "type": "relation", "relation": "oneToOne", "target": "admin::user" }, "deletedAt": { "type": "datetime", "required": true, "index": true } };
|
|
68
|
+
const schema = {
|
|
69
|
+
kind,
|
|
70
|
+
collectionName,
|
|
71
|
+
info,
|
|
72
|
+
options,
|
|
73
|
+
pluginOptions,
|
|
74
|
+
attributes
|
|
75
|
+
};
|
|
76
|
+
const contentTypes = {
|
|
77
|
+
"recycle-bin-item": { schema }
|
|
78
|
+
};
|
|
79
|
+
const recycleBin = ({ strapi }) => ({
|
|
80
|
+
async find(ctx) {
|
|
81
|
+
const { page = 1, pageSize = 10, search, entityUid } = ctx.query;
|
|
82
|
+
const pageNum = Math.max(1, parseInt(page, 10) || 1);
|
|
83
|
+
const pageSizeNum = Math.max(1, Math.min(100, parseInt(pageSize, 10) || 10));
|
|
84
|
+
const where = {};
|
|
85
|
+
if (typeof entityUid === "string" && entityUid.trim()) {
|
|
86
|
+
where.entityUid = entityUid.trim();
|
|
87
|
+
}
|
|
88
|
+
if (typeof search === "string" && search.trim()) {
|
|
89
|
+
where.title = { $containsi: search.trim() };
|
|
90
|
+
}
|
|
91
|
+
const [items, total] = await Promise.all([
|
|
92
|
+
strapi.db.query("plugin::recycle-bin.recycle-bin-item").findMany({
|
|
93
|
+
where,
|
|
94
|
+
populate: ["deletedBy"],
|
|
95
|
+
orderBy: { deletedAt: "desc" },
|
|
96
|
+
offset: (pageNum - 1) * pageSizeNum,
|
|
97
|
+
limit: pageSizeNum
|
|
98
|
+
}),
|
|
99
|
+
strapi.db.query("plugin::recycle-bin.recycle-bin-item").count({ where })
|
|
100
|
+
]);
|
|
101
|
+
ctx.body = {
|
|
102
|
+
data: items,
|
|
103
|
+
meta: {
|
|
104
|
+
pagination: {
|
|
105
|
+
page: pageNum,
|
|
106
|
+
pageSize: pageSizeNum,
|
|
107
|
+
pageCount: Math.ceil(total / pageSizeNum) || 1,
|
|
108
|
+
total
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
async restore(ctx) {
|
|
114
|
+
const { id } = ctx.params;
|
|
115
|
+
const numericId = parseInt(id, 10);
|
|
116
|
+
if (isNaN(numericId) || numericId <= 0) {
|
|
117
|
+
return ctx.badRequest("Invalid or missing item ID");
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
await strapi.plugin("recycle-bin").service("restore").restoreItem(numericId);
|
|
121
|
+
ctx.body = { data: { restored: true } };
|
|
122
|
+
} catch (err) {
|
|
123
|
+
if (err.message?.includes("not found")) {
|
|
124
|
+
return ctx.notFound(err.message);
|
|
125
|
+
}
|
|
126
|
+
return ctx.badRequest(err.message || "Failed to restore item");
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
async purge(ctx) {
|
|
130
|
+
const { id } = ctx.params;
|
|
131
|
+
const numericId = parseInt(id, 10);
|
|
132
|
+
if (isNaN(numericId) || numericId <= 0) {
|
|
133
|
+
return ctx.badRequest("Invalid or missing item ID");
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await strapi.plugin("recycle-bin").service("restore").purgeItem(numericId);
|
|
137
|
+
ctx.body = { data: { purged: true } };
|
|
138
|
+
} catch (err) {
|
|
139
|
+
return ctx.badRequest(err.message || "Failed to purge item");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const controllers = {
|
|
144
|
+
recycleBin
|
|
145
|
+
};
|
|
146
|
+
const middlewares = {};
|
|
147
|
+
const policies = {};
|
|
148
|
+
const admin = {
|
|
149
|
+
type: "admin",
|
|
150
|
+
routes: [
|
|
151
|
+
{
|
|
152
|
+
method: "GET",
|
|
153
|
+
path: "/items",
|
|
154
|
+
handler: "recycleBin.find",
|
|
155
|
+
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
method: "POST",
|
|
159
|
+
path: "/items/:id/restore",
|
|
160
|
+
handler: "recycleBin.restore",
|
|
161
|
+
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
method: "DELETE",
|
|
165
|
+
path: "/items/:id",
|
|
166
|
+
handler: "recycleBin.purge",
|
|
167
|
+
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
};
|
|
171
|
+
const routes = {
|
|
172
|
+
admin
|
|
173
|
+
};
|
|
174
|
+
const archive = ({ strapi }) => ({
|
|
175
|
+
async capture(uid, documentId, userContext) {
|
|
176
|
+
const config2 = strapi.config.get("plugin::recycle-bin");
|
|
177
|
+
if (config2?.excludedUids?.includes(uid)) return;
|
|
178
|
+
let entries = await strapi.documents(uid).findMany({
|
|
179
|
+
filters: { documentId },
|
|
180
|
+
populate: "*",
|
|
181
|
+
status: "draft"
|
|
182
|
+
});
|
|
183
|
+
if (!entries || entries.length === 0) {
|
|
184
|
+
entries = await strapi.documents(uid).findMany({
|
|
185
|
+
filters: { documentId },
|
|
186
|
+
populate: "*"
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (!entries || entries.length === 0) return;
|
|
190
|
+
const mainEntry = entries[0];
|
|
191
|
+
const title = mainEntry.title || mainEntry.name || mainEntry.subject || mainEntry.documentId || "Untitled Record";
|
|
192
|
+
await strapi.db.query("plugin::recycle-bin.recycle-bin-item").create({
|
|
193
|
+
data: {
|
|
194
|
+
entityUid: uid,
|
|
195
|
+
recordDocumentId: documentId,
|
|
196
|
+
title,
|
|
197
|
+
payload: entries,
|
|
198
|
+
deletedBy: userContext?.id || null,
|
|
199
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
const restore = ({ strapi }) => ({
|
|
205
|
+
async restoreItem(id) {
|
|
206
|
+
const item = await strapi.db.query("plugin::recycle-bin.recycle-bin-item").findOne({
|
|
207
|
+
where: { id }
|
|
208
|
+
});
|
|
209
|
+
if (!item) {
|
|
210
|
+
throw new Error("Item not found in recycle bin");
|
|
211
|
+
}
|
|
212
|
+
const { entityUid, payload } = item;
|
|
213
|
+
const model = strapi.contentType(entityUid);
|
|
214
|
+
if (!model) {
|
|
215
|
+
throw new Error(`Target content type ${entityUid} no longer exists.`);
|
|
216
|
+
}
|
|
217
|
+
const records = Array.isArray(payload) ? payload : [payload];
|
|
218
|
+
for (const record of records) {
|
|
219
|
+
const sanitizedData = { ...record };
|
|
220
|
+
delete sanitizedData.id;
|
|
221
|
+
delete sanitizedData.documentId;
|
|
222
|
+
delete sanitizedData.createdAt;
|
|
223
|
+
delete sanitizedData.updatedAt;
|
|
224
|
+
delete sanitizedData.publishedAt;
|
|
225
|
+
const createParams = { data: sanitizedData };
|
|
226
|
+
if (model.options?.draftAndPublish) {
|
|
227
|
+
createParams.status = record.publishedAt ? "published" : "draft";
|
|
228
|
+
}
|
|
229
|
+
await strapi.documents(entityUid).create(createParams);
|
|
230
|
+
}
|
|
231
|
+
await strapi.db.query("plugin::recycle-bin.recycle-bin-item").delete({
|
|
232
|
+
where: { id }
|
|
233
|
+
});
|
|
234
|
+
return { success: true };
|
|
235
|
+
},
|
|
236
|
+
async purgeItem(id) {
|
|
237
|
+
return strapi.db.query("plugin::recycle-bin.recycle-bin-item").delete({
|
|
238
|
+
where: { id }
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
const services = {
|
|
243
|
+
archive,
|
|
244
|
+
restore
|
|
245
|
+
};
|
|
246
|
+
const index = {
|
|
247
|
+
register,
|
|
248
|
+
bootstrap,
|
|
249
|
+
destroy,
|
|
250
|
+
config,
|
|
251
|
+
controllers,
|
|
252
|
+
routes,
|
|
253
|
+
services,
|
|
254
|
+
contentTypes,
|
|
255
|
+
policies,
|
|
256
|
+
middlewares
|
|
257
|
+
};
|
|
258
|
+
exports.default = index;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
const registerDeletionMiddleware = (strapi) => {
|
|
2
|
+
strapi.documents.use(async (context, next) => {
|
|
3
|
+
if (context.action === "delete") {
|
|
4
|
+
const uid = context.uid;
|
|
5
|
+
const documentId = context.params?.documentId;
|
|
6
|
+
const auth = strapi.requestContext?.get()?.state?.user;
|
|
7
|
+
if (uid && documentId) {
|
|
8
|
+
try {
|
|
9
|
+
await strapi.plugin("recycle-bin").service("archive").capture(uid, documentId, auth);
|
|
10
|
+
} catch (err) {
|
|
11
|
+
strapi.log.error(`[recycle-bin] Archiving failed for ${uid} (${documentId}):`, err);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return next();
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
const bootstrap = ({ strapi }) => {
|
|
19
|
+
registerDeletionMiddleware(strapi);
|
|
20
|
+
strapi.cron.add({
|
|
21
|
+
recycleBinRetentionJob: {
|
|
22
|
+
task: async () => {
|
|
23
|
+
const config2 = strapi.config.get("plugin::recycle-bin");
|
|
24
|
+
const days = config2.retentionDays || 30;
|
|
25
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
26
|
+
const deleted = await strapi.db.query("plugin::recycle-bin.recycle-bin-item").deleteMany({
|
|
27
|
+
where: {
|
|
28
|
+
deletedAt: { $lt: cutoff }
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
strapi.log.info(`[recycle-bin] Auto-purged ${deleted.count} expired item(s).`);
|
|
32
|
+
},
|
|
33
|
+
options: {
|
|
34
|
+
rule: "0 0 * * *"
|
|
35
|
+
// Midnight
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const destroy = ({ strapi }) => {
|
|
41
|
+
};
|
|
42
|
+
const register = ({ strapi }) => {
|
|
43
|
+
};
|
|
44
|
+
const config = {
|
|
45
|
+
default: {
|
|
46
|
+
retentionDays: 30,
|
|
47
|
+
excludedUids: [
|
|
48
|
+
"admin::user",
|
|
49
|
+
"admin::permission",
|
|
50
|
+
"admin::role",
|
|
51
|
+
"admin::api-token",
|
|
52
|
+
"admin::transfer-token",
|
|
53
|
+
"plugin::recycle-bin.recycle-bin-item",
|
|
54
|
+
"plugin::upload.file"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
validator() {
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const kind = "collectionType";
|
|
61
|
+
const collectionName = "recycle_bin_items";
|
|
62
|
+
const info = { "singularName": "recycle-bin-item", "pluralName": "recycle-bin-items", "displayName": "Recycle Bin Item" };
|
|
63
|
+
const options = { "draftAndPublish": false };
|
|
64
|
+
const pluginOptions = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
|
|
65
|
+
const attributes = { "entityUid": { "type": "string", "required": true }, "recordDocumentId": { "type": "string", "required": true, "index": true }, "title": { "type": "string" }, "payload": { "type": "json", "required": true }, "deletedBy": { "type": "relation", "relation": "oneToOne", "target": "admin::user" }, "deletedAt": { "type": "datetime", "required": true, "index": true } };
|
|
66
|
+
const schema = {
|
|
67
|
+
kind,
|
|
68
|
+
collectionName,
|
|
69
|
+
info,
|
|
70
|
+
options,
|
|
71
|
+
pluginOptions,
|
|
72
|
+
attributes
|
|
73
|
+
};
|
|
74
|
+
const contentTypes = {
|
|
75
|
+
"recycle-bin-item": { schema }
|
|
76
|
+
};
|
|
77
|
+
const recycleBin = ({ strapi }) => ({
|
|
78
|
+
async find(ctx) {
|
|
79
|
+
const { page = 1, pageSize = 10, search, entityUid } = ctx.query;
|
|
80
|
+
const pageNum = Math.max(1, parseInt(page, 10) || 1);
|
|
81
|
+
const pageSizeNum = Math.max(1, Math.min(100, parseInt(pageSize, 10) || 10));
|
|
82
|
+
const where = {};
|
|
83
|
+
if (typeof entityUid === "string" && entityUid.trim()) {
|
|
84
|
+
where.entityUid = entityUid.trim();
|
|
85
|
+
}
|
|
86
|
+
if (typeof search === "string" && search.trim()) {
|
|
87
|
+
where.title = { $containsi: search.trim() };
|
|
88
|
+
}
|
|
89
|
+
const [items, total] = await Promise.all([
|
|
90
|
+
strapi.db.query("plugin::recycle-bin.recycle-bin-item").findMany({
|
|
91
|
+
where,
|
|
92
|
+
populate: ["deletedBy"],
|
|
93
|
+
orderBy: { deletedAt: "desc" },
|
|
94
|
+
offset: (pageNum - 1) * pageSizeNum,
|
|
95
|
+
limit: pageSizeNum
|
|
96
|
+
}),
|
|
97
|
+
strapi.db.query("plugin::recycle-bin.recycle-bin-item").count({ where })
|
|
98
|
+
]);
|
|
99
|
+
ctx.body = {
|
|
100
|
+
data: items,
|
|
101
|
+
meta: {
|
|
102
|
+
pagination: {
|
|
103
|
+
page: pageNum,
|
|
104
|
+
pageSize: pageSizeNum,
|
|
105
|
+
pageCount: Math.ceil(total / pageSizeNum) || 1,
|
|
106
|
+
total
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
async restore(ctx) {
|
|
112
|
+
const { id } = ctx.params;
|
|
113
|
+
const numericId = parseInt(id, 10);
|
|
114
|
+
if (isNaN(numericId) || numericId <= 0) {
|
|
115
|
+
return ctx.badRequest("Invalid or missing item ID");
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
await strapi.plugin("recycle-bin").service("restore").restoreItem(numericId);
|
|
119
|
+
ctx.body = { data: { restored: true } };
|
|
120
|
+
} catch (err) {
|
|
121
|
+
if (err.message?.includes("not found")) {
|
|
122
|
+
return ctx.notFound(err.message);
|
|
123
|
+
}
|
|
124
|
+
return ctx.badRequest(err.message || "Failed to restore item");
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
async purge(ctx) {
|
|
128
|
+
const { id } = ctx.params;
|
|
129
|
+
const numericId = parseInt(id, 10);
|
|
130
|
+
if (isNaN(numericId) || numericId <= 0) {
|
|
131
|
+
return ctx.badRequest("Invalid or missing item ID");
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
await strapi.plugin("recycle-bin").service("restore").purgeItem(numericId);
|
|
135
|
+
ctx.body = { data: { purged: true } };
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return ctx.badRequest(err.message || "Failed to purge item");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
const controllers = {
|
|
142
|
+
recycleBin
|
|
143
|
+
};
|
|
144
|
+
const middlewares = {};
|
|
145
|
+
const policies = {};
|
|
146
|
+
const admin = {
|
|
147
|
+
type: "admin",
|
|
148
|
+
routes: [
|
|
149
|
+
{
|
|
150
|
+
method: "GET",
|
|
151
|
+
path: "/items",
|
|
152
|
+
handler: "recycleBin.find",
|
|
153
|
+
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
method: "POST",
|
|
157
|
+
path: "/items/:id/restore",
|
|
158
|
+
handler: "recycleBin.restore",
|
|
159
|
+
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
method: "DELETE",
|
|
163
|
+
path: "/items/:id",
|
|
164
|
+
handler: "recycleBin.purge",
|
|
165
|
+
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
};
|
|
169
|
+
const routes = {
|
|
170
|
+
admin
|
|
171
|
+
};
|
|
172
|
+
const archive = ({ strapi }) => ({
|
|
173
|
+
async capture(uid, documentId, userContext) {
|
|
174
|
+
const config2 = strapi.config.get("plugin::recycle-bin");
|
|
175
|
+
if (config2?.excludedUids?.includes(uid)) return;
|
|
176
|
+
let entries = await strapi.documents(uid).findMany({
|
|
177
|
+
filters: { documentId },
|
|
178
|
+
populate: "*",
|
|
179
|
+
status: "draft"
|
|
180
|
+
});
|
|
181
|
+
if (!entries || entries.length === 0) {
|
|
182
|
+
entries = await strapi.documents(uid).findMany({
|
|
183
|
+
filters: { documentId },
|
|
184
|
+
populate: "*"
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (!entries || entries.length === 0) return;
|
|
188
|
+
const mainEntry = entries[0];
|
|
189
|
+
const title = mainEntry.title || mainEntry.name || mainEntry.subject || mainEntry.documentId || "Untitled Record";
|
|
190
|
+
await strapi.db.query("plugin::recycle-bin.recycle-bin-item").create({
|
|
191
|
+
data: {
|
|
192
|
+
entityUid: uid,
|
|
193
|
+
recordDocumentId: documentId,
|
|
194
|
+
title,
|
|
195
|
+
payload: entries,
|
|
196
|
+
deletedBy: userContext?.id || null,
|
|
197
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
const restore = ({ strapi }) => ({
|
|
203
|
+
async restoreItem(id) {
|
|
204
|
+
const item = await strapi.db.query("plugin::recycle-bin.recycle-bin-item").findOne({
|
|
205
|
+
where: { id }
|
|
206
|
+
});
|
|
207
|
+
if (!item) {
|
|
208
|
+
throw new Error("Item not found in recycle bin");
|
|
209
|
+
}
|
|
210
|
+
const { entityUid, payload } = item;
|
|
211
|
+
const model = strapi.contentType(entityUid);
|
|
212
|
+
if (!model) {
|
|
213
|
+
throw new Error(`Target content type ${entityUid} no longer exists.`);
|
|
214
|
+
}
|
|
215
|
+
const records = Array.isArray(payload) ? payload : [payload];
|
|
216
|
+
for (const record of records) {
|
|
217
|
+
const sanitizedData = { ...record };
|
|
218
|
+
delete sanitizedData.id;
|
|
219
|
+
delete sanitizedData.documentId;
|
|
220
|
+
delete sanitizedData.createdAt;
|
|
221
|
+
delete sanitizedData.updatedAt;
|
|
222
|
+
delete sanitizedData.publishedAt;
|
|
223
|
+
const createParams = { data: sanitizedData };
|
|
224
|
+
if (model.options?.draftAndPublish) {
|
|
225
|
+
createParams.status = record.publishedAt ? "published" : "draft";
|
|
226
|
+
}
|
|
227
|
+
await strapi.documents(entityUid).create(createParams);
|
|
228
|
+
}
|
|
229
|
+
await strapi.db.query("plugin::recycle-bin.recycle-bin-item").delete({
|
|
230
|
+
where: { id }
|
|
231
|
+
});
|
|
232
|
+
return { success: true };
|
|
233
|
+
},
|
|
234
|
+
async purgeItem(id) {
|
|
235
|
+
return strapi.db.query("plugin::recycle-bin.recycle-bin-item").delete({
|
|
236
|
+
where: { id }
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
const services = {
|
|
241
|
+
archive,
|
|
242
|
+
restore
|
|
243
|
+
};
|
|
244
|
+
const index = {
|
|
245
|
+
register,
|
|
246
|
+
bootstrap,
|
|
247
|
+
destroy,
|
|
248
|
+
config,
|
|
249
|
+
controllers,
|
|
250
|
+
routes,
|
|
251
|
+
services,
|
|
252
|
+
contentTypes,
|
|
253
|
+
policies,
|
|
254
|
+
middlewares
|
|
255
|
+
};
|
|
256
|
+
export {
|
|
257
|
+
index as default
|
|
258
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
'recycle-bin-item': {
|
|
3
|
+
schema: {
|
|
4
|
+
kind: string;
|
|
5
|
+
collectionName: string;
|
|
6
|
+
info: {
|
|
7
|
+
singularName: string;
|
|
8
|
+
pluralName: string;
|
|
9
|
+
displayName: string;
|
|
10
|
+
};
|
|
11
|
+
options: {
|
|
12
|
+
draftAndPublish: boolean;
|
|
13
|
+
};
|
|
14
|
+
pluginOptions: {
|
|
15
|
+
"content-manager": {
|
|
16
|
+
visible: boolean;
|
|
17
|
+
};
|
|
18
|
+
"content-type-builder": {
|
|
19
|
+
visible: boolean;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
attributes: {
|
|
23
|
+
entityUid: {
|
|
24
|
+
type: string;
|
|
25
|
+
required: boolean;
|
|
26
|
+
};
|
|
27
|
+
recordDocumentId: {
|
|
28
|
+
type: string;
|
|
29
|
+
required: boolean;
|
|
30
|
+
index: boolean;
|
|
31
|
+
};
|
|
32
|
+
title: {
|
|
33
|
+
type: string;
|
|
34
|
+
};
|
|
35
|
+
payload: {
|
|
36
|
+
type: string;
|
|
37
|
+
required: boolean;
|
|
38
|
+
};
|
|
39
|
+
deletedBy: {
|
|
40
|
+
type: string;
|
|
41
|
+
relation: string;
|
|
42
|
+
target: string;
|
|
43
|
+
};
|
|
44
|
+
deletedAt: {
|
|
45
|
+
type: string;
|
|
46
|
+
required: boolean;
|
|
47
|
+
index: boolean;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export default _default;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
"kind": "collectionType",
|
|
3
|
+
"collectionName": "recycle_bin_items",
|
|
4
|
+
"info": {
|
|
5
|
+
"singularName": "recycle-bin-item",
|
|
6
|
+
"pluralName": "recycle-bin-items",
|
|
7
|
+
"displayName": "Recycle Bin Item"
|
|
8
|
+
},
|
|
9
|
+
"options": {
|
|
10
|
+
"draftAndPublish": false
|
|
11
|
+
},
|
|
12
|
+
"pluginOptions": {
|
|
13
|
+
"content-manager": {
|
|
14
|
+
"visible": false
|
|
15
|
+
},
|
|
16
|
+
"content-type-builder": {
|
|
17
|
+
"visible": false
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"attributes": {
|
|
21
|
+
"entityUid": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"required": true
|
|
24
|
+
},
|
|
25
|
+
"recordDocumentId": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"required": true,
|
|
28
|
+
"index": true
|
|
29
|
+
},
|
|
30
|
+
"title": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
"payload": {
|
|
34
|
+
"type": "json",
|
|
35
|
+
"required": true
|
|
36
|
+
},
|
|
37
|
+
"deletedBy": {
|
|
38
|
+
"type": "relation",
|
|
39
|
+
"relation": "oneToOne",
|
|
40
|
+
"target": "admin::user"
|
|
41
|
+
},
|
|
42
|
+
"deletedAt": {
|
|
43
|
+
"type": "datetime",
|
|
44
|
+
"required": true,
|
|
45
|
+
"index": true
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
;
|
|
50
|
+
|
|
51
|
+
export default _default;
|