@rmdes/indiekit-endpoint-blogroll 1.0.8 → 1.0.10
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/lib/controllers/blogs.js
CHANGED
|
@@ -38,6 +38,9 @@ async function list(request, response) {
|
|
|
38
38
|
// Get unique categories for filter dropdown
|
|
39
39
|
const categories = [...new Set(blogs.map((b) => b.category).filter(Boolean))];
|
|
40
40
|
|
|
41
|
+
// Extract flash messages for native Indiekit notification banner
|
|
42
|
+
const flash = consumeFlashMessage(request);
|
|
43
|
+
|
|
41
44
|
response.render("blogroll-blogs", {
|
|
42
45
|
title: request.__("blogroll.blogs.title"),
|
|
43
46
|
blogs: filteredBlogs,
|
|
@@ -45,6 +48,7 @@ async function list(request, response) {
|
|
|
45
48
|
filterCategory: category,
|
|
46
49
|
filterStatus,
|
|
47
50
|
baseUrl: request.baseUrl,
|
|
51
|
+
...flash,
|
|
48
52
|
});
|
|
49
53
|
} catch (error) {
|
|
50
54
|
console.error("[Blogroll] Blogs list error:", error);
|
|
@@ -157,7 +161,17 @@ async function edit(request, response) {
|
|
|
157
161
|
return response.status(404).render("404");
|
|
158
162
|
}
|
|
159
163
|
|
|
160
|
-
const
|
|
164
|
+
const rawItems = await getItemsForBlog(application, blog._id, 10);
|
|
165
|
+
const items = rawItems.map((item) => ({
|
|
166
|
+
...item,
|
|
167
|
+
published:
|
|
168
|
+
item.published instanceof Date
|
|
169
|
+
? item.published.toISOString()
|
|
170
|
+
: item.published,
|
|
171
|
+
}));
|
|
172
|
+
|
|
173
|
+
// Extract flash messages for native Indiekit notification banner
|
|
174
|
+
const flash = consumeFlashMessage(request);
|
|
161
175
|
|
|
162
176
|
response.render("blogroll-blog-edit", {
|
|
163
177
|
title: request.__("blogroll.blogs.edit"),
|
|
@@ -165,6 +179,7 @@ async function edit(request, response) {
|
|
|
165
179
|
items,
|
|
166
180
|
isNew: false,
|
|
167
181
|
baseUrl: request.baseUrl,
|
|
182
|
+
...flash,
|
|
168
183
|
});
|
|
169
184
|
} catch (error) {
|
|
170
185
|
console.error("[Blogroll] Edit blog error:", error);
|
|
@@ -287,6 +302,21 @@ async function refresh(request, response) {
|
|
|
287
302
|
}
|
|
288
303
|
}
|
|
289
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Extract and clear flash messages from session
|
|
307
|
+
* Returns { success, error } for Indiekit's native notificationBanner
|
|
308
|
+
*/
|
|
309
|
+
function consumeFlashMessage(request) {
|
|
310
|
+
const result = {};
|
|
311
|
+
if (request.session?.messages?.length) {
|
|
312
|
+
const msg = request.session.messages[0];
|
|
313
|
+
if (msg.type === "success") result.success = msg.content;
|
|
314
|
+
else if (msg.type === "error" || msg.type === "warning") result.error = msg.content;
|
|
315
|
+
request.session.messages = null;
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
|
|
290
320
|
export const blogsController = {
|
|
291
321
|
list,
|
|
292
322
|
newForm,
|
package/lib/storage/blogs.js
CHANGED
|
@@ -25,7 +25,7 @@ export async function getBlogs(application, options = {}) {
|
|
|
25
25
|
const collection = getCollection(application);
|
|
26
26
|
const { category, sourceId, includeHidden = false, limit = 100, offset = 0 } = options;
|
|
27
27
|
|
|
28
|
-
const query = {};
|
|
28
|
+
const query = { status: { $ne: "deleted" } };
|
|
29
29
|
if (!includeHidden) query.hidden = { $ne: true };
|
|
30
30
|
if (category) query.category = category;
|
|
31
31
|
if (sourceId) query.sourceId = new ObjectId(sourceId);
|
|
@@ -48,7 +48,7 @@ export async function countBlogs(application, options = {}) {
|
|
|
48
48
|
const collection = getCollection(application);
|
|
49
49
|
const { category, includeHidden = false } = options;
|
|
50
50
|
|
|
51
|
-
const query = {};
|
|
51
|
+
const query = { status: { $ne: "deleted" } };
|
|
52
52
|
if (!includeHidden) query.hidden = { $ne: true };
|
|
53
53
|
if (category) query.category = category;
|
|
54
54
|
|
|
@@ -75,7 +75,7 @@ export async function getBlog(application, id) {
|
|
|
75
75
|
*/
|
|
76
76
|
export async function getBlogByFeedUrl(application, feedUrl) {
|
|
77
77
|
const collection = getCollection(application);
|
|
78
|
-
return collection.findOne({ feedUrl });
|
|
78
|
+
return collection.findOne({ feedUrl, status: { $ne: "deleted" } });
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
@@ -142,7 +142,8 @@ export async function updateBlog(application, id, data) {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
|
-
* Delete a blog and its items
|
|
145
|
+
* Delete a blog and its items (soft delete)
|
|
146
|
+
* Marks blog as deleted so sync won't recreate it.
|
|
146
147
|
* @param {object} application - Application instance
|
|
147
148
|
* @param {string|ObjectId} id - Blog ID
|
|
148
149
|
* @returns {Promise<boolean>} Success
|
|
@@ -154,9 +155,19 @@ export async function deleteBlog(application, id) {
|
|
|
154
155
|
// Delete items for this blog
|
|
155
156
|
await db.collection("blogrollItems").deleteMany({ blogId: objectId });
|
|
156
157
|
|
|
157
|
-
//
|
|
158
|
-
const result = await db.collection("blogrollBlogs").
|
|
159
|
-
|
|
158
|
+
// Soft delete: mark as deleted so upsertBlog won't recreate it
|
|
159
|
+
const result = await db.collection("blogrollBlogs").updateOne(
|
|
160
|
+
{ _id: objectId },
|
|
161
|
+
{
|
|
162
|
+
$set: {
|
|
163
|
+
status: "deleted",
|
|
164
|
+
hidden: true,
|
|
165
|
+
deletedAt: new Date(),
|
|
166
|
+
updatedAt: new Date(),
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
return result.modifiedCount > 0;
|
|
160
171
|
}
|
|
161
172
|
|
|
162
173
|
/**
|
|
@@ -204,6 +215,7 @@ export async function getBlogsDueForRefresh(application, maxAge = 60) {
|
|
|
204
215
|
return collection
|
|
205
216
|
.find({
|
|
206
217
|
hidden: { $ne: true },
|
|
218
|
+
status: { $ne: "deleted" },
|
|
207
219
|
$or: [{ lastFetchAt: null }, { lastFetchAt: { $lt: cutoff } }],
|
|
208
220
|
})
|
|
209
221
|
.toArray();
|
|
@@ -219,7 +231,7 @@ export async function getCategories(application) {
|
|
|
219
231
|
|
|
220
232
|
return collection
|
|
221
233
|
.aggregate([
|
|
222
|
-
{ $match: { hidden: { $ne: true }, category: { $ne: "" } } },
|
|
234
|
+
{ $match: { hidden: { $ne: true }, status: { $ne: "deleted" }, category: { $ne: "" } } },
|
|
223
235
|
{ $group: { _id: "$category", count: { $sum: 1 } } },
|
|
224
236
|
{ $sort: { _id: 1 } },
|
|
225
237
|
])
|
|
@@ -236,6 +248,15 @@ export async function upsertBlog(application, data) {
|
|
|
236
248
|
const collection = getCollection(application);
|
|
237
249
|
const now = new Date();
|
|
238
250
|
|
|
251
|
+
// Skip if a blog with this feedUrl was soft-deleted
|
|
252
|
+
const deleted = await collection.findOne({
|
|
253
|
+
feedUrl: data.feedUrl,
|
|
254
|
+
status: "deleted",
|
|
255
|
+
});
|
|
256
|
+
if (deleted) {
|
|
257
|
+
return { upserted: false, modified: false, skippedDeleted: true };
|
|
258
|
+
}
|
|
259
|
+
|
|
239
260
|
const filter = { feedUrl: data.feedUrl };
|
|
240
261
|
if (data.sourceId) {
|
|
241
262
|
filter.sourceId = new ObjectId(data.sourceId);
|
package/package.json
CHANGED
|
@@ -203,11 +203,7 @@
|
|
|
203
203
|
<h1 class="page-header__title">{{ title }}</h1>
|
|
204
204
|
</header>
|
|
205
205
|
|
|
206
|
-
{
|
|
207
|
-
<div class="notice notice--{{ message.type }}">
|
|
208
|
-
<p>{{ message.content }}</p>
|
|
209
|
-
</div>
|
|
210
|
-
{% endfor %}
|
|
206
|
+
{# Flash messages rendered by Indiekit's native notificationBanner via success/error template vars #}
|
|
211
207
|
|
|
212
208
|
<form method="post" action="{% if isNew %}{{ baseUrl }}/blogs{% else %}{{ baseUrl }}/blogs/{{ blog._id }}{% endif %}" class="br-form">
|
|
213
209
|
{% if isNew %}
|
package/views/blogroll-blogs.njk
CHANGED
|
@@ -112,10 +112,7 @@
|
|
|
112
112
|
<h1 class="page-header__title">{{ __("blogroll.blogs.title") }}</h1>
|
|
113
113
|
</header>
|
|
114
114
|
|
|
115
|
-
{
|
|
116
|
-
<div class="notice notice--{{ message.type }}">
|
|
117
|
-
<p>{{ message.content }}</p>
|
|
118
|
-
</div>
|
|
115
|
+
{# Flash messages are now rendered by Indiekit's native notificationBanner via success/error template vars #}
|
|
119
116
|
{% endfor %}
|
|
120
117
|
|
|
121
118
|
<div class="br-blogs">
|