@rmdes/indiekit-endpoint-posts 1.0.0-beta.42 → 1.0.0-beta.43

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/index.js CHANGED
@@ -6,6 +6,7 @@ import express from "express";
6
6
 
7
7
  import { deleteController } from "./lib/controllers/delete.js";
8
8
  import { editController } from "./lib/controllers/edit.js";
9
+ import { purgeAllController } from "./lib/controllers/purge-all.js";
9
10
  import { purgeController } from "./lib/controllers/purge.js";
10
11
  import { formController } from "./lib/controllers/form.js";
11
12
  import { newController } from "./lib/controllers/new.js";
@@ -47,6 +48,9 @@ export default class PostsEndpoint {
47
48
 
48
49
  router.get("/edit", editController.get);
49
50
 
51
+ router.get("/purge-deleted", purgeAllController.get);
52
+ router.post("/purge-deleted", purgeAllController.post);
53
+
50
54
  router.get("/new", newController.get);
51
55
  router.post("/new", validate.new, newController.post);
52
56
 
@@ -111,6 +111,14 @@ export const postsController = async (request, response, next) => {
111
111
  text: response.locals.__("posts.create.action"),
112
112
  }
113
113
  : {},
114
+ scope && checkScope(scope, "delete") && status === "deleted" && total > 0
115
+ ? {
116
+ classes: "actions__link--warning",
117
+ href: path.join(request.baseUrl, "/purge-deleted"),
118
+ icon: "delete",
119
+ text: response.locals.__("posts.purgeAll.action"),
120
+ }
121
+ : {},
114
122
  ],
115
123
  cursor,
116
124
  posts,
@@ -0,0 +1,68 @@
1
+ import { checkScope } from "@indiekit/endpoint-micropub/lib/scope.js";
2
+
3
+ import { getPosts, purgeAllDeletedPosts } from "../utils.js";
4
+
5
+ export const purgeAllController = {
6
+ /**
7
+ * Confirm purge of all deleted posts
8
+ * @type {import("express").RequestHandler}
9
+ */
10
+ async get(request, response) {
11
+ const { application } = request.app.locals;
12
+ const { scope } = request.session;
13
+
14
+ if (!scope || !checkScope(scope, "delete")) {
15
+ return response.redirect(request.baseUrl);
16
+ }
17
+
18
+ // Count deleted posts for the confirmation message
19
+ const { total } = await getPosts(application, { status: "deleted" });
20
+
21
+ if (total === 0) {
22
+ const message = encodeURIComponent(
23
+ response.locals.__("posts.purgeAll.none"),
24
+ );
25
+ return response.redirect(`${request.baseUrl}?success=${message}`);
26
+ }
27
+
28
+ response.render("post-purge-all", {
29
+ title: response.locals.__("posts.purgeAll.title"),
30
+ deletedCount: total,
31
+ postsPath: request.baseUrl,
32
+ });
33
+ },
34
+
35
+ /**
36
+ * Permanently delete all soft-deleted posts
37
+ * @type {import("express").RequestHandler}
38
+ */
39
+ async post(request, response) {
40
+ const { application } = request.app.locals;
41
+ const { scope } = request.session;
42
+
43
+ if (!scope || !checkScope(scope, "delete")) {
44
+ return response.redirect(request.baseUrl);
45
+ }
46
+
47
+ try {
48
+ const deletedCount = await purgeAllDeletedPosts(application);
49
+
50
+ const message = encodeURIComponent(
51
+ response.locals.__("posts.purgeAll.success", deletedCount),
52
+ );
53
+ response.redirect(`${request.baseUrl}?success=${message}`);
54
+ } catch (error) {
55
+ const { total } = await getPosts(application, {
56
+ status: "deleted",
57
+ });
58
+
59
+ response.status(error.status || 500);
60
+ response.render("post-purge-all", {
61
+ title: response.locals.__("posts.purgeAll.title"),
62
+ deletedCount: total,
63
+ postsPath: request.baseUrl,
64
+ error,
65
+ });
66
+ }
67
+ },
68
+ };
package/lib/utils.js CHANGED
@@ -307,6 +307,24 @@ export const purgePost = async (uid, application) => {
307
307
  return result.deletedCount > 0;
308
308
  };
309
309
 
310
+ /**
311
+ * Permanently delete all soft-deleted posts from MongoDB
312
+ * @param {object} application - Application object with collections
313
+ * @returns {Promise<number>} Number of posts deleted
314
+ */
315
+ export const purgeAllDeletedPosts = async (application) => {
316
+ const postsCollection = application?.collections?.get("posts");
317
+ if (!postsCollection) {
318
+ return 0;
319
+ }
320
+
321
+ const result = await postsCollection.deleteMany({
322
+ "properties.deleted": { $exists: true },
323
+ });
324
+
325
+ return result.deletedCount;
326
+ };
327
+
310
328
  /**
311
329
  * Query database for post data by Micropub URL
312
330
  * @param {string} url - Micropub URL (e.g. https://example.com/articles/2026/02/13/slug)
package/locales/en.json CHANGED
@@ -62,6 +62,15 @@
62
62
  "cancel": "No – return to posts",
63
63
  "success": "Post permanently deleted"
64
64
  },
65
+ "purgeAll": {
66
+ "action": "Purge all deleted",
67
+ "title": "Permanently delete all deleted posts?",
68
+ "warning": "This will permanently remove **%s deleted post(s)** from the database. This action cannot be undone.",
69
+ "submit": "I'm sure - permanently delete all",
70
+ "cancel": "No - return to posts",
71
+ "success": "Permanently deleted %s post(s)",
72
+ "none": "No deleted posts to purge"
73
+ },
65
74
  "form": {
66
75
  "advancedOptions": "Advanced options",
67
76
  "back": "Change post type",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-posts",
3
- "version": "1.0.0-beta.42",
3
+ "version": "1.0.0-beta.43",
4
4
  "description": "Post management endpoint for Indiekit with syndicate form fix. View posts published by your Micropub endpoint and publish new posts to it.",
5
5
  "keywords": [
6
6
  "indiekit",
@@ -0,0 +1,20 @@
1
+ {% extends "form.njk" %}
2
+
3
+ {% block form %}
4
+ {{ heading({
5
+ text: title
6
+ }) }}
7
+
8
+ {{ prose({
9
+ text: __("posts.purgeAll.warning", deletedCount)
10
+ }) }}
11
+
12
+ {{ button({
13
+ classes: "button--warning",
14
+ text: __("posts.purgeAll.submit")
15
+ }) }}
16
+
17
+ {{ prose({
18
+ text: "[" + __("posts.purgeAll.cancel") + "](" + postsPath + ")"
19
+ }) }}
20
+ {% endblock %}