@btst/stack 2.7.0 → 2.8.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/README.md +1 -0
- package/dist/packages/stack/src/plugins/blog/client/components/loading/post-navigation-skeleton.cjs +13 -0
- package/dist/packages/stack/src/plugins/blog/client/components/loading/post-navigation-skeleton.mjs +11 -0
- package/dist/packages/stack/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.cjs +17 -0
- package/dist/packages/stack/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.mjs +15 -0
- package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +18 -7
- package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +18 -7
- package/dist/packages/stack/src/plugins/blog/client/components/shared/post-navigation.cjs +48 -52
- package/dist/packages/stack/src/plugins/blog/client/components/shared/post-navigation.mjs +49 -53
- package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.cjs +34 -37
- package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.mjs +35 -38
- package/dist/packages/stack/src/plugins/blog/client/hooks/blog-hooks.cjs +4 -21
- package/dist/packages/stack/src/plugins/blog/client/hooks/blog-hooks.mjs +4 -21
- package/dist/packages/stack/src/plugins/comments/api/getters.cjs +284 -0
- package/dist/packages/stack/src/plugins/comments/api/getters.mjs +280 -0
- package/dist/packages/stack/src/plugins/comments/api/mutations.cjs +118 -0
- package/dist/packages/stack/src/plugins/comments/api/mutations.mjs +112 -0
- package/dist/packages/stack/src/plugins/comments/api/plugin.cjs +335 -0
- package/dist/packages/stack/src/plugins/comments/api/plugin.mjs +333 -0
- package/dist/packages/stack/src/plugins/comments/api/query-key-defs.cjs +60 -0
- package/dist/packages/stack/src/plugins/comments/api/query-key-defs.mjs +55 -0
- package/dist/packages/stack/src/plugins/comments/api/serializers.cjs +23 -0
- package/dist/packages/stack/src/plugins/comments/api/serializers.mjs +21 -0
- package/dist/packages/stack/src/plugins/comments/client/components/comment-count.cjs +46 -0
- package/dist/packages/stack/src/plugins/comments/client/components/comment-count.mjs +44 -0
- package/dist/packages/stack/src/plugins/comments/client/components/comment-form.cjs +86 -0
- package/dist/packages/stack/src/plugins/comments/client/components/comment-form.mjs +84 -0
- package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +540 -0
- package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +538 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.cjs +64 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.cjs +426 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.mjs +424 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.mjs +62 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.cjs +66 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.cjs +256 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.mjs +254 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.mjs +64 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.cjs +86 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.cjs +191 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.mjs +189 -0
- package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.mjs +84 -0
- package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.cjs +27 -0
- package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.mjs +25 -0
- package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.cjs +37 -0
- package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.mjs +35 -0
- package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +476 -0
- package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +464 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/comments-moderation.cjs +67 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/comments-moderation.mjs +65 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/comments-my.cjs +27 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/comments-my.mjs +25 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.cjs +30 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.mjs +28 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/index.cjs +13 -0
- package/dist/packages/stack/src/plugins/comments/client/localization/index.mjs +11 -0
- package/dist/packages/stack/src/plugins/comments/client/plugin.cjs +116 -0
- package/dist/packages/stack/src/plugins/comments/client/plugin.mjs +114 -0
- package/dist/packages/stack/src/plugins/comments/client/utils.cjs +41 -0
- package/dist/packages/stack/src/plugins/comments/client/utils.mjs +37 -0
- package/dist/packages/stack/src/plugins/comments/db.cjs +75 -0
- package/dist/packages/stack/src/plugins/comments/db.mjs +73 -0
- package/dist/packages/stack/src/plugins/comments/schemas.cjs +45 -0
- package/dist/packages/stack/src/plugins/comments/schemas.mjs +38 -0
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +0 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +0 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.cjs +39 -22
- package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.mjs +40 -23
- package/dist/packages/ui/src/components/avatar.mjs +1 -1
- package/dist/packages/ui/src/components/pagination-controls.cjs +64 -0
- package/dist/packages/ui/src/components/pagination-controls.mjs +62 -0
- package/dist/packages/ui/src/components/when-visible.cjs +39 -0
- package/dist/packages/ui/src/components/when-visible.mjs +37 -0
- package/dist/plugins/blog/client/hooks/index.d.cts +1 -1
- package/dist/plugins/blog/client/hooks/index.d.mts +1 -1
- package/dist/plugins/blog/client/hooks/index.d.ts +1 -1
- package/dist/plugins/blog/client/index.d.cts +24 -2
- package/dist/plugins/blog/client/index.d.mts +24 -2
- package/dist/plugins/blog/client/index.d.ts +24 -2
- package/dist/plugins/comments/api/index.cjs +21 -0
- package/dist/plugins/comments/api/index.d.cts +126 -0
- package/dist/plugins/comments/api/index.d.mts +126 -0
- package/dist/plugins/comments/api/index.d.ts +126 -0
- package/dist/plugins/comments/api/index.mjs +5 -0
- package/dist/plugins/comments/client/components/index.cjs +15 -0
- package/dist/plugins/comments/client/components/index.d.cts +125 -0
- package/dist/plugins/comments/client/components/index.d.mts +125 -0
- package/dist/plugins/comments/client/components/index.d.ts +125 -0
- package/dist/plugins/comments/client/components/index.mjs +5 -0
- package/dist/plugins/comments/client/hooks/index.cjs +17 -0
- package/dist/plugins/comments/client/hooks/index.d.cts +200 -0
- package/dist/plugins/comments/client/hooks/index.d.mts +200 -0
- package/dist/plugins/comments/client/hooks/index.d.ts +200 -0
- package/dist/plugins/comments/client/hooks/index.mjs +1 -0
- package/dist/plugins/comments/client/index.cjs +9 -0
- package/dist/plugins/comments/client/index.d.cts +262 -0
- package/dist/plugins/comments/client/index.d.mts +262 -0
- package/dist/plugins/comments/client/index.d.ts +262 -0
- package/dist/plugins/comments/client/index.mjs +2 -0
- package/dist/plugins/comments/client.css +2 -0
- package/dist/plugins/comments/query-keys.cjs +113 -0
- package/dist/plugins/comments/query-keys.d.cts +71 -0
- package/dist/plugins/comments/query-keys.d.mts +71 -0
- package/dist/plugins/comments/query-keys.d.ts +71 -0
- package/dist/plugins/comments/query-keys.mjs +111 -0
- package/dist/plugins/comments/style.css +15 -0
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
- package/dist/plugins/kanban/client/index.d.cts +1 -1
- package/dist/plugins/kanban/client/index.d.mts +1 -1
- package/dist/plugins/kanban/client/index.d.ts +1 -1
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/shared/{stack.FeaWkglm.d.ts → stack.BxFl46lB.d.cts} +24 -1
- package/dist/shared/stack.C-b3Sn8j.d.cts +142 -0
- package/dist/shared/stack.C-b3Sn8j.d.mts +142 -0
- package/dist/shared/stack.C-b3Sn8j.d.ts +142 -0
- package/dist/shared/stack.CJE9sAjV.d.ts +335 -0
- package/dist/shared/stack.CmHRdhl8.d.cts +335 -0
- package/dist/shared/{stack.CNLHlv7r.d.mts → stack.DOZ1EXjM.d.mts} +6 -12
- package/dist/shared/{stack.FeaWkglm.d.mts → stack.DRpeDS6X.d.ts} +24 -1
- package/dist/shared/{stack.CQAZwXhV.d.cts → stack.DX-tQ93o.d.cts} +6 -12
- package/dist/shared/stack.Dcz6636A.d.mts +335 -0
- package/dist/shared/{stack.FeaWkglm.d.cts → stack.Jb0kQDJC.d.mts} +24 -1
- package/dist/shared/stack.Ldfkr5b2.d.cts +112 -0
- package/dist/shared/stack.Ldfkr5b2.d.mts +112 -0
- package/dist/shared/stack.Ldfkr5b2.d.ts +112 -0
- package/dist/shared/{stack.D3BsrpAz.d.ts → stack.VF6FhyZw.d.ts} +6 -12
- package/package.json +67 -2
- package/src/plugins/blog/client/components/loading/post-navigation-skeleton.tsx +10 -0
- package/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.tsx +18 -0
- package/src/plugins/blog/client/components/pages/post-page.internal.tsx +23 -8
- package/src/plugins/blog/client/components/shared/post-navigation.tsx +0 -5
- package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +1 -5
- package/src/plugins/blog/client/hooks/blog-hooks.tsx +8 -33
- package/src/plugins/blog/client/overrides.ts +26 -1
- package/src/plugins/cms/client/components/shared/pagination.tsx +14 -42
- package/src/plugins/comments/api/getters.ts +444 -0
- package/src/plugins/comments/api/index.ts +21 -0
- package/src/plugins/comments/api/mutations.ts +206 -0
- package/src/plugins/comments/api/plugin.ts +628 -0
- package/src/plugins/comments/api/query-key-defs.ts +143 -0
- package/src/plugins/comments/api/serializers.ts +37 -0
- package/src/plugins/comments/client/components/comment-count.tsx +66 -0
- package/src/plugins/comments/client/components/comment-form.tsx +112 -0
- package/src/plugins/comments/client/components/comment-thread.tsx +799 -0
- package/src/plugins/comments/client/components/index.tsx +11 -0
- package/src/plugins/comments/client/components/pages/moderation-page.internal.tsx +550 -0
- package/src/plugins/comments/client/components/pages/moderation-page.tsx +70 -0
- package/src/plugins/comments/client/components/pages/my-comments-page.internal.tsx +367 -0
- package/src/plugins/comments/client/components/pages/my-comments-page.tsx +72 -0
- package/src/plugins/comments/client/components/pages/resource-comments-page.internal.tsx +225 -0
- package/src/plugins/comments/client/components/pages/resource-comments-page.tsx +97 -0
- package/src/plugins/comments/client/components/shared/page-wrapper.tsx +32 -0
- package/src/plugins/comments/client/components/shared/pagination.tsx +44 -0
- package/src/plugins/comments/client/hooks/index.tsx +13 -0
- package/src/plugins/comments/client/hooks/use-comments.tsx +717 -0
- package/src/plugins/comments/client/index.ts +14 -0
- package/src/plugins/comments/client/localization/comments-moderation.ts +75 -0
- package/src/plugins/comments/client/localization/comments-my.ts +32 -0
- package/src/plugins/comments/client/localization/comments-thread.ts +32 -0
- package/src/plugins/comments/client/localization/index.ts +11 -0
- package/src/plugins/comments/client/overrides.ts +164 -0
- package/src/plugins/comments/client/plugin.tsx +195 -0
- package/src/plugins/comments/client/utils.ts +67 -0
- package/src/plugins/comments/client.css +2 -0
- package/src/plugins/comments/db.ts +77 -0
- package/src/plugins/comments/query-keys.ts +189 -0
- package/src/plugins/comments/schemas.ts +72 -0
- package/src/plugins/comments/style.css +15 -0
- package/src/plugins/comments/types.ts +73 -0
- package/src/plugins/kanban/client/components/forms/task-form.tsx +0 -1
- package/src/plugins/kanban/client/components/pages/board-page.internal.tsx +46 -27
- package/src/plugins/kanban/client/overrides.ts +27 -1
- package/dist/shared/{stack.Rtcvl8sS.d.cts → stack.BOokfhZD.d.cts} +3 -3
- package/dist/shared/{stack.D4Cea8II.d.ts → stack.BvCR4-9H.d.ts} +3 -3
- package/dist/shared/{stack.HE_IvqV5.d.mts → stack.CWxAl9K3.d.mts} +3 -3
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function createComment(adapter, input) {
|
|
4
|
+
return adapter.create({
|
|
5
|
+
model: "comment",
|
|
6
|
+
data: {
|
|
7
|
+
resourceId: input.resourceId,
|
|
8
|
+
resourceType: input.resourceType,
|
|
9
|
+
parentId: input.parentId ?? null,
|
|
10
|
+
authorId: input.authorId,
|
|
11
|
+
body: input.body,
|
|
12
|
+
status: input.status ?? "pending",
|
|
13
|
+
likes: 0,
|
|
14
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
15
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async function updateComment(adapter, id, body) {
|
|
20
|
+
const existing = await adapter.findOne({
|
|
21
|
+
model: "comment",
|
|
22
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
23
|
+
});
|
|
24
|
+
if (!existing) return null;
|
|
25
|
+
return adapter.update({
|
|
26
|
+
model: "comment",
|
|
27
|
+
where: [{ field: "id", value: id, operator: "eq" }],
|
|
28
|
+
update: {
|
|
29
|
+
body,
|
|
30
|
+
editedAt: /* @__PURE__ */ new Date(),
|
|
31
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function updateCommentStatus(adapter, id, status) {
|
|
36
|
+
const existing = await adapter.findOne({
|
|
37
|
+
model: "comment",
|
|
38
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
39
|
+
});
|
|
40
|
+
if (!existing) return null;
|
|
41
|
+
return adapter.update({
|
|
42
|
+
model: "comment",
|
|
43
|
+
where: [{ field: "id", value: id, operator: "eq" }],
|
|
44
|
+
update: { status, updatedAt: /* @__PURE__ */ new Date() }
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async function deleteComment(adapter, id) {
|
|
48
|
+
const existing = await adapter.findOne({
|
|
49
|
+
model: "comment",
|
|
50
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
51
|
+
});
|
|
52
|
+
if (!existing) return false;
|
|
53
|
+
await adapter.transaction(async (tx) => {
|
|
54
|
+
await tx.delete({
|
|
55
|
+
model: "comment",
|
|
56
|
+
where: [{ field: "parentId", value: id, operator: "eq" }]
|
|
57
|
+
});
|
|
58
|
+
await tx.delete({
|
|
59
|
+
model: "comment",
|
|
60
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
async function toggleCommentLike(adapter, commentId, authorId) {
|
|
66
|
+
return adapter.transaction(async (tx) => {
|
|
67
|
+
const comment = await tx.findOne({
|
|
68
|
+
model: "comment",
|
|
69
|
+
where: [{ field: "id", value: commentId, operator: "eq" }]
|
|
70
|
+
});
|
|
71
|
+
if (!comment) {
|
|
72
|
+
throw new Error("Comment not found");
|
|
73
|
+
}
|
|
74
|
+
const existingLike = await tx.findOne({
|
|
75
|
+
model: "commentLike",
|
|
76
|
+
where: [
|
|
77
|
+
{ field: "commentId", value: commentId, operator: "eq" },
|
|
78
|
+
{ field: "authorId", value: authorId, operator: "eq" }
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
let newLikes;
|
|
82
|
+
let isLiked;
|
|
83
|
+
if (existingLike) {
|
|
84
|
+
await tx.delete({
|
|
85
|
+
model: "commentLike",
|
|
86
|
+
where: [
|
|
87
|
+
{ field: "commentId", value: commentId, operator: "eq" },
|
|
88
|
+
{ field: "authorId", value: authorId, operator: "eq" }
|
|
89
|
+
]
|
|
90
|
+
});
|
|
91
|
+
newLikes = Math.max(0, comment.likes - 1);
|
|
92
|
+
isLiked = false;
|
|
93
|
+
} else {
|
|
94
|
+
await tx.create({
|
|
95
|
+
model: "commentLike",
|
|
96
|
+
data: {
|
|
97
|
+
commentId,
|
|
98
|
+
authorId,
|
|
99
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
newLikes = comment.likes + 1;
|
|
103
|
+
isLiked = true;
|
|
104
|
+
}
|
|
105
|
+
await tx.update({
|
|
106
|
+
model: "comment",
|
|
107
|
+
where: [{ field: "id", value: commentId, operator: "eq" }],
|
|
108
|
+
update: { likes: newLikes, updatedAt: /* @__PURE__ */ new Date() }
|
|
109
|
+
});
|
|
110
|
+
return { likes: newLikes, isLiked };
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
exports.createComment = createComment;
|
|
115
|
+
exports.deleteComment = deleteComment;
|
|
116
|
+
exports.toggleCommentLike = toggleCommentLike;
|
|
117
|
+
exports.updateComment = updateComment;
|
|
118
|
+
exports.updateCommentStatus = updateCommentStatus;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
async function createComment(adapter, input) {
|
|
2
|
+
return adapter.create({
|
|
3
|
+
model: "comment",
|
|
4
|
+
data: {
|
|
5
|
+
resourceId: input.resourceId,
|
|
6
|
+
resourceType: input.resourceType,
|
|
7
|
+
parentId: input.parentId ?? null,
|
|
8
|
+
authorId: input.authorId,
|
|
9
|
+
body: input.body,
|
|
10
|
+
status: input.status ?? "pending",
|
|
11
|
+
likes: 0,
|
|
12
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
13
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async function updateComment(adapter, id, body) {
|
|
18
|
+
const existing = await adapter.findOne({
|
|
19
|
+
model: "comment",
|
|
20
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
21
|
+
});
|
|
22
|
+
if (!existing) return null;
|
|
23
|
+
return adapter.update({
|
|
24
|
+
model: "comment",
|
|
25
|
+
where: [{ field: "id", value: id, operator: "eq" }],
|
|
26
|
+
update: {
|
|
27
|
+
body,
|
|
28
|
+
editedAt: /* @__PURE__ */ new Date(),
|
|
29
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async function updateCommentStatus(adapter, id, status) {
|
|
34
|
+
const existing = await adapter.findOne({
|
|
35
|
+
model: "comment",
|
|
36
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
37
|
+
});
|
|
38
|
+
if (!existing) return null;
|
|
39
|
+
return adapter.update({
|
|
40
|
+
model: "comment",
|
|
41
|
+
where: [{ field: "id", value: id, operator: "eq" }],
|
|
42
|
+
update: { status, updatedAt: /* @__PURE__ */ new Date() }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function deleteComment(adapter, id) {
|
|
46
|
+
const existing = await adapter.findOne({
|
|
47
|
+
model: "comment",
|
|
48
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
49
|
+
});
|
|
50
|
+
if (!existing) return false;
|
|
51
|
+
await adapter.transaction(async (tx) => {
|
|
52
|
+
await tx.delete({
|
|
53
|
+
model: "comment",
|
|
54
|
+
where: [{ field: "parentId", value: id, operator: "eq" }]
|
|
55
|
+
});
|
|
56
|
+
await tx.delete({
|
|
57
|
+
model: "comment",
|
|
58
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
async function toggleCommentLike(adapter, commentId, authorId) {
|
|
64
|
+
return adapter.transaction(async (tx) => {
|
|
65
|
+
const comment = await tx.findOne({
|
|
66
|
+
model: "comment",
|
|
67
|
+
where: [{ field: "id", value: commentId, operator: "eq" }]
|
|
68
|
+
});
|
|
69
|
+
if (!comment) {
|
|
70
|
+
throw new Error("Comment not found");
|
|
71
|
+
}
|
|
72
|
+
const existingLike = await tx.findOne({
|
|
73
|
+
model: "commentLike",
|
|
74
|
+
where: [
|
|
75
|
+
{ field: "commentId", value: commentId, operator: "eq" },
|
|
76
|
+
{ field: "authorId", value: authorId, operator: "eq" }
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
let newLikes;
|
|
80
|
+
let isLiked;
|
|
81
|
+
if (existingLike) {
|
|
82
|
+
await tx.delete({
|
|
83
|
+
model: "commentLike",
|
|
84
|
+
where: [
|
|
85
|
+
{ field: "commentId", value: commentId, operator: "eq" },
|
|
86
|
+
{ field: "authorId", value: authorId, operator: "eq" }
|
|
87
|
+
]
|
|
88
|
+
});
|
|
89
|
+
newLikes = Math.max(0, comment.likes - 1);
|
|
90
|
+
isLiked = false;
|
|
91
|
+
} else {
|
|
92
|
+
await tx.create({
|
|
93
|
+
model: "commentLike",
|
|
94
|
+
data: {
|
|
95
|
+
commentId,
|
|
96
|
+
authorId,
|
|
97
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
newLikes = comment.likes + 1;
|
|
101
|
+
isLiked = true;
|
|
102
|
+
}
|
|
103
|
+
await tx.update({
|
|
104
|
+
model: "comment",
|
|
105
|
+
where: [{ field: "id", value: commentId, operator: "eq" }],
|
|
106
|
+
update: { likes: newLikes, updatedAt: /* @__PURE__ */ new Date() }
|
|
107
|
+
});
|
|
108
|
+
return { likes: newLikes, isLiked };
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { createComment, deleteComment, toggleCommentLike, updateComment, updateCommentStatus };
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const api = require('@btst/stack/plugins/api');
|
|
4
|
+
const z = require('zod');
|
|
5
|
+
const db = require('../db.cjs');
|
|
6
|
+
const schemas = require('../schemas.cjs');
|
|
7
|
+
const getters = require('./getters.cjs');
|
|
8
|
+
const mutations = require('./mutations.cjs');
|
|
9
|
+
const utils = require('../../utils.cjs');
|
|
10
|
+
|
|
11
|
+
const commentsBackendPlugin = (options) => {
|
|
12
|
+
const postingEnabled = options.allowPosting !== false;
|
|
13
|
+
const editingEnabled = options.allowEditing !== false;
|
|
14
|
+
const onBeforePost = options.allowPosting !== false ? options.onBeforePost : void 0;
|
|
15
|
+
const resolveCurrentUserId = options.allowPosting !== false ? options.resolveCurrentUserId : void 0;
|
|
16
|
+
return api.defineBackendPlugin({
|
|
17
|
+
name: "comments",
|
|
18
|
+
dbPlugin: db.commentsSchema,
|
|
19
|
+
api: (adapter) => ({
|
|
20
|
+
listComments: (params) => getters.listComments(adapter, params, options?.resolveUser),
|
|
21
|
+
getCommentById: (id, currentUserId) => getters.getCommentById(adapter, id, options?.resolveUser, currentUserId),
|
|
22
|
+
getCommentCount: (params) => getters.getCommentCount(adapter, params)
|
|
23
|
+
}),
|
|
24
|
+
routes: (adapter) => {
|
|
25
|
+
const listCommentsEndpoint = api.createEndpoint(
|
|
26
|
+
"/comments",
|
|
27
|
+
{
|
|
28
|
+
method: "GET",
|
|
29
|
+
query: schemas.CommentListQuerySchema
|
|
30
|
+
},
|
|
31
|
+
async (ctx) => {
|
|
32
|
+
const context = {
|
|
33
|
+
query: ctx.query,
|
|
34
|
+
request: ctx.request,
|
|
35
|
+
headers: ctx.headers
|
|
36
|
+
};
|
|
37
|
+
if (ctx.query.authorId) {
|
|
38
|
+
if (!options?.onBeforeListByAuthor) {
|
|
39
|
+
throw ctx.error(403, {
|
|
40
|
+
message: "Forbidden: authorId filter requires onBeforeListByAuthor hook"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
await utils.runHookWithShim(
|
|
44
|
+
() => options.onBeforeListByAuthor(
|
|
45
|
+
ctx.query.authorId,
|
|
46
|
+
ctx.query,
|
|
47
|
+
context
|
|
48
|
+
),
|
|
49
|
+
ctx.error,
|
|
50
|
+
"Forbidden: Cannot list comments for this author"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (ctx.query.status && ctx.query.status !== "approved") {
|
|
54
|
+
if (!options?.onBeforeList) {
|
|
55
|
+
throw ctx.error(403, {
|
|
56
|
+
message: "Forbidden: status filter requires authorization"
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
await utils.runHookWithShim(
|
|
60
|
+
() => options.onBeforeList(ctx.query, context),
|
|
61
|
+
ctx.error,
|
|
62
|
+
"Forbidden: Cannot list comments with this status filter"
|
|
63
|
+
);
|
|
64
|
+
} else if (options?.onBeforeList && !ctx.query.authorId) {
|
|
65
|
+
await utils.runHookWithShim(
|
|
66
|
+
() => options.onBeforeList(ctx.query, context),
|
|
67
|
+
ctx.error,
|
|
68
|
+
"Forbidden: Cannot list comments"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
let resolvedCurrentUserId;
|
|
72
|
+
if (resolveCurrentUserId) {
|
|
73
|
+
try {
|
|
74
|
+
const result = await resolveCurrentUserId(context);
|
|
75
|
+
resolvedCurrentUserId = result ?? void 0;
|
|
76
|
+
} catch {
|
|
77
|
+
resolvedCurrentUserId = void 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return await getters.listComments(
|
|
81
|
+
adapter,
|
|
82
|
+
{ ...ctx.query, currentUserId: resolvedCurrentUserId },
|
|
83
|
+
options?.resolveUser
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
const createCommentEndpoint = api.createEndpoint(
|
|
88
|
+
"/comments",
|
|
89
|
+
{
|
|
90
|
+
method: "POST",
|
|
91
|
+
body: schemas.createCommentSchema
|
|
92
|
+
},
|
|
93
|
+
async (ctx) => {
|
|
94
|
+
if (!postingEnabled) {
|
|
95
|
+
throw ctx.error(403, { message: "Posting comments is disabled" });
|
|
96
|
+
}
|
|
97
|
+
const context = {
|
|
98
|
+
body: ctx.body,
|
|
99
|
+
headers: ctx.headers
|
|
100
|
+
};
|
|
101
|
+
const { authorId } = await utils.runHookWithShim(
|
|
102
|
+
() => onBeforePost(ctx.body, context),
|
|
103
|
+
ctx.error,
|
|
104
|
+
"Unauthorized: Cannot post comment"
|
|
105
|
+
);
|
|
106
|
+
const status = options?.autoApprove ? "approved" : "pending";
|
|
107
|
+
const comment = await mutations.createComment(adapter, {
|
|
108
|
+
...ctx.body,
|
|
109
|
+
authorId,
|
|
110
|
+
status
|
|
111
|
+
});
|
|
112
|
+
if (options?.onAfterPost) {
|
|
113
|
+
await options.onAfterPost(comment, context);
|
|
114
|
+
}
|
|
115
|
+
const serialized = await getters.getCommentById(
|
|
116
|
+
adapter,
|
|
117
|
+
comment.id,
|
|
118
|
+
options?.resolveUser
|
|
119
|
+
);
|
|
120
|
+
if (!serialized) {
|
|
121
|
+
throw ctx.error(500, {
|
|
122
|
+
message: "Failed to retrieve created comment"
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return serialized;
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
const updateCommentEndpoint = api.createEndpoint(
|
|
129
|
+
"/comments/:id",
|
|
130
|
+
{
|
|
131
|
+
method: "PATCH",
|
|
132
|
+
body: schemas.updateCommentSchema
|
|
133
|
+
},
|
|
134
|
+
async (ctx) => {
|
|
135
|
+
if (!editingEnabled) {
|
|
136
|
+
throw ctx.error(403, { message: "Editing comments is disabled" });
|
|
137
|
+
}
|
|
138
|
+
const { id } = ctx.params;
|
|
139
|
+
const context = {
|
|
140
|
+
params: ctx.params,
|
|
141
|
+
body: ctx.body,
|
|
142
|
+
headers: ctx.headers
|
|
143
|
+
};
|
|
144
|
+
if (!options?.onBeforeEdit) {
|
|
145
|
+
throw ctx.error(403, {
|
|
146
|
+
message: "Forbidden: editing comments requires the onBeforeEdit hook"
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
await utils.runHookWithShim(
|
|
150
|
+
() => options.onBeforeEdit(id, { body: ctx.body.body }, context),
|
|
151
|
+
ctx.error,
|
|
152
|
+
"Unauthorized: Cannot edit comment"
|
|
153
|
+
);
|
|
154
|
+
const updated = await mutations.updateComment(adapter, id, ctx.body.body);
|
|
155
|
+
if (!updated) {
|
|
156
|
+
throw ctx.error(404, { message: "Comment not found" });
|
|
157
|
+
}
|
|
158
|
+
if (options?.onAfterEdit) {
|
|
159
|
+
await options.onAfterEdit(updated, context);
|
|
160
|
+
}
|
|
161
|
+
const serialized = await getters.getCommentById(
|
|
162
|
+
adapter,
|
|
163
|
+
updated.id,
|
|
164
|
+
options?.resolveUser
|
|
165
|
+
);
|
|
166
|
+
if (!serialized) {
|
|
167
|
+
throw ctx.error(500, {
|
|
168
|
+
message: "Failed to retrieve updated comment"
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return serialized;
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
const getCommentCountEndpoint = api.createEndpoint(
|
|
175
|
+
"/comments/count",
|
|
176
|
+
{
|
|
177
|
+
method: "GET",
|
|
178
|
+
query: schemas.CommentCountQuerySchema
|
|
179
|
+
},
|
|
180
|
+
async (ctx) => {
|
|
181
|
+
const context = {
|
|
182
|
+
query: ctx.query,
|
|
183
|
+
headers: ctx.headers
|
|
184
|
+
};
|
|
185
|
+
if (ctx.query.status && ctx.query.status !== "approved") {
|
|
186
|
+
if (!options?.onBeforeList) {
|
|
187
|
+
throw ctx.error(403, {
|
|
188
|
+
message: "Forbidden: status filter requires authorization"
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
await utils.runHookWithShim(
|
|
192
|
+
() => options.onBeforeList(
|
|
193
|
+
{ ...ctx.query, status: ctx.query.status },
|
|
194
|
+
context
|
|
195
|
+
),
|
|
196
|
+
ctx.error,
|
|
197
|
+
"Forbidden: Cannot count comments with this status filter"
|
|
198
|
+
);
|
|
199
|
+
} else if (options?.onBeforeList) {
|
|
200
|
+
await utils.runHookWithShim(
|
|
201
|
+
() => options.onBeforeList(
|
|
202
|
+
{ ...ctx.query, status: ctx.query.status },
|
|
203
|
+
context
|
|
204
|
+
),
|
|
205
|
+
ctx.error,
|
|
206
|
+
"Forbidden: Cannot count comments"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
const count = await getters.getCommentCount(adapter, ctx.query);
|
|
210
|
+
return { count };
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
const toggleLikeEndpoint = api.createEndpoint(
|
|
214
|
+
"/comments/:id/like",
|
|
215
|
+
{
|
|
216
|
+
method: "POST",
|
|
217
|
+
body: z.z.object({ authorId: z.z.string().min(1) })
|
|
218
|
+
},
|
|
219
|
+
async (ctx) => {
|
|
220
|
+
const { id } = ctx.params;
|
|
221
|
+
const context = {
|
|
222
|
+
params: ctx.params,
|
|
223
|
+
body: ctx.body,
|
|
224
|
+
headers: ctx.headers
|
|
225
|
+
};
|
|
226
|
+
if (!options?.onBeforeLike) {
|
|
227
|
+
throw ctx.error(403, {
|
|
228
|
+
message: "Forbidden: toggling likes requires the onBeforeLike hook"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
await utils.runHookWithShim(
|
|
232
|
+
() => options.onBeforeLike(id, ctx.body.authorId, context),
|
|
233
|
+
ctx.error,
|
|
234
|
+
"Unauthorized: Cannot like comment"
|
|
235
|
+
);
|
|
236
|
+
const result = await mutations.toggleCommentLike(
|
|
237
|
+
adapter,
|
|
238
|
+
id,
|
|
239
|
+
ctx.body.authorId
|
|
240
|
+
);
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
const updateStatusEndpoint = api.createEndpoint(
|
|
245
|
+
"/comments/:id/status",
|
|
246
|
+
{
|
|
247
|
+
method: "PATCH",
|
|
248
|
+
body: schemas.updateCommentStatusSchema
|
|
249
|
+
},
|
|
250
|
+
async (ctx) => {
|
|
251
|
+
const { id } = ctx.params;
|
|
252
|
+
const context = {
|
|
253
|
+
params: ctx.params,
|
|
254
|
+
body: ctx.body,
|
|
255
|
+
headers: ctx.headers
|
|
256
|
+
};
|
|
257
|
+
if (!options?.onBeforeStatusChange) {
|
|
258
|
+
throw ctx.error(403, {
|
|
259
|
+
message: "Forbidden: changing comment status requires the onBeforeStatusChange hook"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
await utils.runHookWithShim(
|
|
263
|
+
() => options.onBeforeStatusChange(id, ctx.body.status, context),
|
|
264
|
+
ctx.error,
|
|
265
|
+
"Unauthorized: Cannot change comment status"
|
|
266
|
+
);
|
|
267
|
+
const updated = await mutations.updateCommentStatus(
|
|
268
|
+
adapter,
|
|
269
|
+
id,
|
|
270
|
+
ctx.body.status
|
|
271
|
+
);
|
|
272
|
+
if (!updated) {
|
|
273
|
+
throw ctx.error(404, { message: "Comment not found" });
|
|
274
|
+
}
|
|
275
|
+
if (ctx.body.status === "approved" && options?.onAfterApprove) {
|
|
276
|
+
await options.onAfterApprove(updated, context);
|
|
277
|
+
}
|
|
278
|
+
const serialized = await getters.getCommentById(
|
|
279
|
+
adapter,
|
|
280
|
+
updated.id,
|
|
281
|
+
options?.resolveUser
|
|
282
|
+
);
|
|
283
|
+
if (!serialized) {
|
|
284
|
+
throw ctx.error(500, {
|
|
285
|
+
message: "Failed to retrieve updated comment"
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return serialized;
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
const deleteCommentEndpoint = api.createEndpoint(
|
|
292
|
+
"/comments/:id",
|
|
293
|
+
{
|
|
294
|
+
method: "DELETE"
|
|
295
|
+
},
|
|
296
|
+
async (ctx) => {
|
|
297
|
+
const { id } = ctx.params;
|
|
298
|
+
const context = {
|
|
299
|
+
params: ctx.params,
|
|
300
|
+
headers: ctx.headers
|
|
301
|
+
};
|
|
302
|
+
if (!options?.onBeforeDelete) {
|
|
303
|
+
throw ctx.error(403, {
|
|
304
|
+
message: "Forbidden: deleting comments requires the onBeforeDelete hook"
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
await utils.runHookWithShim(
|
|
308
|
+
() => options.onBeforeDelete(id, context),
|
|
309
|
+
ctx.error,
|
|
310
|
+
"Unauthorized: Cannot delete comment"
|
|
311
|
+
);
|
|
312
|
+
const deleted = await mutations.deleteComment(adapter, id);
|
|
313
|
+
if (!deleted) {
|
|
314
|
+
throw ctx.error(404, { message: "Comment not found" });
|
|
315
|
+
}
|
|
316
|
+
if (options?.onAfterDelete) {
|
|
317
|
+
await options.onAfterDelete(id, context);
|
|
318
|
+
}
|
|
319
|
+
return { success: true };
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
return {
|
|
323
|
+
listComments: listCommentsEndpoint,
|
|
324
|
+
...postingEnabled && { createComment: createCommentEndpoint },
|
|
325
|
+
...editingEnabled && { updateComment: updateCommentEndpoint },
|
|
326
|
+
getCommentCount: getCommentCountEndpoint,
|
|
327
|
+
toggleLike: toggleLikeEndpoint,
|
|
328
|
+
updateCommentStatus: updateStatusEndpoint,
|
|
329
|
+
deleteComment: deleteCommentEndpoint
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
exports.commentsBackendPlugin = commentsBackendPlugin;
|