@btst/stack 2.7.0 → 2.8.1

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.
Files changed (181) hide show
  1. package/README.md +1 -0
  2. package/dist/packages/stack/src/plugins/blog/client/components/loading/post-navigation-skeleton.cjs +13 -0
  3. package/dist/packages/stack/src/plugins/blog/client/components/loading/post-navigation-skeleton.mjs +11 -0
  4. package/dist/packages/stack/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.cjs +17 -0
  5. package/dist/packages/stack/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.mjs +15 -0
  6. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +18 -7
  7. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +18 -7
  8. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-navigation.cjs +48 -52
  9. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-navigation.mjs +49 -53
  10. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.cjs +34 -37
  11. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.mjs +35 -38
  12. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-hooks.cjs +4 -21
  13. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-hooks.mjs +4 -21
  14. package/dist/packages/stack/src/plugins/comments/api/getters.cjs +284 -0
  15. package/dist/packages/stack/src/plugins/comments/api/getters.mjs +280 -0
  16. package/dist/packages/stack/src/plugins/comments/api/mutations.cjs +118 -0
  17. package/dist/packages/stack/src/plugins/comments/api/mutations.mjs +112 -0
  18. package/dist/packages/stack/src/plugins/comments/api/plugin.cjs +335 -0
  19. package/dist/packages/stack/src/plugins/comments/api/plugin.mjs +333 -0
  20. package/dist/packages/stack/src/plugins/comments/api/query-key-defs.cjs +60 -0
  21. package/dist/packages/stack/src/plugins/comments/api/query-key-defs.mjs +55 -0
  22. package/dist/packages/stack/src/plugins/comments/api/serializers.cjs +23 -0
  23. package/dist/packages/stack/src/plugins/comments/api/serializers.mjs +21 -0
  24. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.cjs +46 -0
  25. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.mjs +44 -0
  26. package/dist/packages/stack/src/plugins/comments/client/components/comment-form.cjs +86 -0
  27. package/dist/packages/stack/src/plugins/comments/client/components/comment-form.mjs +84 -0
  28. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +540 -0
  29. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +538 -0
  30. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.cjs +64 -0
  31. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.cjs +426 -0
  32. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.mjs +424 -0
  33. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.mjs +62 -0
  34. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.cjs +66 -0
  35. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.cjs +256 -0
  36. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.mjs +254 -0
  37. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.mjs +64 -0
  38. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.cjs +86 -0
  39. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.cjs +191 -0
  40. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.mjs +189 -0
  41. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.mjs +84 -0
  42. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.cjs +27 -0
  43. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.mjs +25 -0
  44. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.cjs +37 -0
  45. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.mjs +35 -0
  46. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +476 -0
  47. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +464 -0
  48. package/dist/packages/stack/src/plugins/comments/client/localization/comments-moderation.cjs +67 -0
  49. package/dist/packages/stack/src/plugins/comments/client/localization/comments-moderation.mjs +65 -0
  50. package/dist/packages/stack/src/plugins/comments/client/localization/comments-my.cjs +27 -0
  51. package/dist/packages/stack/src/plugins/comments/client/localization/comments-my.mjs +25 -0
  52. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.cjs +30 -0
  53. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.mjs +28 -0
  54. package/dist/packages/stack/src/plugins/comments/client/localization/index.cjs +13 -0
  55. package/dist/packages/stack/src/plugins/comments/client/localization/index.mjs +11 -0
  56. package/dist/packages/stack/src/plugins/comments/client/plugin.cjs +116 -0
  57. package/dist/packages/stack/src/plugins/comments/client/plugin.mjs +114 -0
  58. package/dist/packages/stack/src/plugins/comments/client/utils.cjs +41 -0
  59. package/dist/packages/stack/src/plugins/comments/client/utils.mjs +37 -0
  60. package/dist/packages/stack/src/plugins/comments/db.cjs +75 -0
  61. package/dist/packages/stack/src/plugins/comments/db.mjs +73 -0
  62. package/dist/packages/stack/src/plugins/comments/schemas.cjs +45 -0
  63. package/dist/packages/stack/src/plugins/comments/schemas.mjs +38 -0
  64. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +0 -1
  65. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +0 -1
  66. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.cjs +39 -22
  67. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.mjs +40 -23
  68. package/dist/packages/ui/src/components/avatar.mjs +1 -1
  69. package/dist/packages/ui/src/components/pagination-controls.cjs +64 -0
  70. package/dist/packages/ui/src/components/pagination-controls.mjs +62 -0
  71. package/dist/packages/ui/src/components/when-visible.cjs +39 -0
  72. package/dist/packages/ui/src/components/when-visible.mjs +37 -0
  73. package/dist/plugins/blog/client/hooks/index.d.cts +1 -1
  74. package/dist/plugins/blog/client/hooks/index.d.mts +1 -1
  75. package/dist/plugins/blog/client/hooks/index.d.ts +1 -1
  76. package/dist/plugins/blog/client/index.d.cts +24 -2
  77. package/dist/plugins/blog/client/index.d.mts +24 -2
  78. package/dist/plugins/blog/client/index.d.ts +24 -2
  79. package/dist/plugins/comments/api/index.cjs +21 -0
  80. package/dist/plugins/comments/api/index.d.cts +126 -0
  81. package/dist/plugins/comments/api/index.d.mts +126 -0
  82. package/dist/plugins/comments/api/index.d.ts +126 -0
  83. package/dist/plugins/comments/api/index.mjs +5 -0
  84. package/dist/plugins/comments/client/components/index.cjs +15 -0
  85. package/dist/plugins/comments/client/components/index.d.cts +125 -0
  86. package/dist/plugins/comments/client/components/index.d.mts +125 -0
  87. package/dist/plugins/comments/client/components/index.d.ts +125 -0
  88. package/dist/plugins/comments/client/components/index.mjs +5 -0
  89. package/dist/plugins/comments/client/hooks/index.cjs +17 -0
  90. package/dist/plugins/comments/client/hooks/index.d.cts +200 -0
  91. package/dist/plugins/comments/client/hooks/index.d.mts +200 -0
  92. package/dist/plugins/comments/client/hooks/index.d.ts +200 -0
  93. package/dist/plugins/comments/client/hooks/index.mjs +1 -0
  94. package/dist/plugins/comments/client/index.cjs +9 -0
  95. package/dist/plugins/comments/client/index.d.cts +262 -0
  96. package/dist/plugins/comments/client/index.d.mts +262 -0
  97. package/dist/plugins/comments/client/index.d.ts +262 -0
  98. package/dist/plugins/comments/client/index.mjs +2 -0
  99. package/dist/plugins/comments/client.css +2 -0
  100. package/dist/plugins/comments/query-keys.cjs +113 -0
  101. package/dist/plugins/comments/query-keys.d.cts +71 -0
  102. package/dist/plugins/comments/query-keys.d.mts +71 -0
  103. package/dist/plugins/comments/query-keys.d.ts +71 -0
  104. package/dist/plugins/comments/query-keys.mjs +111 -0
  105. package/dist/plugins/comments/style.css +15 -0
  106. package/dist/plugins/kanban/api/index.d.cts +1 -1
  107. package/dist/plugins/kanban/api/index.d.mts +1 -1
  108. package/dist/plugins/kanban/api/index.d.ts +1 -1
  109. package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
  110. package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
  111. package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
  112. package/dist/plugins/kanban/client/index.d.cts +1 -1
  113. package/dist/plugins/kanban/client/index.d.mts +1 -1
  114. package/dist/plugins/kanban/client/index.d.ts +1 -1
  115. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  116. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  117. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  118. package/dist/shared/{stack.FeaWkglm.d.ts → stack.BxFl46lB.d.cts} +24 -1
  119. package/dist/shared/stack.C-b3Sn8j.d.cts +142 -0
  120. package/dist/shared/stack.C-b3Sn8j.d.mts +142 -0
  121. package/dist/shared/stack.C-b3Sn8j.d.ts +142 -0
  122. package/dist/shared/stack.CJE9sAjV.d.ts +335 -0
  123. package/dist/shared/stack.CmHRdhl8.d.cts +335 -0
  124. package/dist/shared/{stack.CNLHlv7r.d.mts → stack.DOZ1EXjM.d.mts} +6 -12
  125. package/dist/shared/{stack.FeaWkglm.d.mts → stack.DRpeDS6X.d.ts} +24 -1
  126. package/dist/shared/{stack.CQAZwXhV.d.cts → stack.DX-tQ93o.d.cts} +6 -12
  127. package/dist/shared/stack.Dcz6636A.d.mts +335 -0
  128. package/dist/shared/{stack.FeaWkglm.d.cts → stack.Jb0kQDJC.d.mts} +24 -1
  129. package/dist/shared/stack.Ldfkr5b2.d.cts +112 -0
  130. package/dist/shared/stack.Ldfkr5b2.d.mts +112 -0
  131. package/dist/shared/stack.Ldfkr5b2.d.ts +112 -0
  132. package/dist/shared/{stack.D3BsrpAz.d.ts → stack.VF6FhyZw.d.ts} +6 -12
  133. package/package.json +69 -4
  134. package/src/plugins/blog/client/components/loading/post-navigation-skeleton.tsx +10 -0
  135. package/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.tsx +18 -0
  136. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +23 -8
  137. package/src/plugins/blog/client/components/shared/post-navigation.tsx +0 -5
  138. package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +1 -5
  139. package/src/plugins/blog/client/hooks/blog-hooks.tsx +8 -33
  140. package/src/plugins/blog/client/overrides.ts +26 -1
  141. package/src/plugins/cms/client/components/shared/pagination.tsx +14 -42
  142. package/src/plugins/comments/api/getters.ts +444 -0
  143. package/src/plugins/comments/api/index.ts +21 -0
  144. package/src/plugins/comments/api/mutations.ts +206 -0
  145. package/src/plugins/comments/api/plugin.ts +628 -0
  146. package/src/plugins/comments/api/query-key-defs.ts +143 -0
  147. package/src/plugins/comments/api/serializers.ts +37 -0
  148. package/src/plugins/comments/client/components/comment-count.tsx +66 -0
  149. package/src/plugins/comments/client/components/comment-form.tsx +112 -0
  150. package/src/plugins/comments/client/components/comment-thread.tsx +799 -0
  151. package/src/plugins/comments/client/components/index.tsx +11 -0
  152. package/src/plugins/comments/client/components/pages/moderation-page.internal.tsx +550 -0
  153. package/src/plugins/comments/client/components/pages/moderation-page.tsx +70 -0
  154. package/src/plugins/comments/client/components/pages/my-comments-page.internal.tsx +367 -0
  155. package/src/plugins/comments/client/components/pages/my-comments-page.tsx +72 -0
  156. package/src/plugins/comments/client/components/pages/resource-comments-page.internal.tsx +225 -0
  157. package/src/plugins/comments/client/components/pages/resource-comments-page.tsx +97 -0
  158. package/src/plugins/comments/client/components/shared/page-wrapper.tsx +32 -0
  159. package/src/plugins/comments/client/components/shared/pagination.tsx +44 -0
  160. package/src/plugins/comments/client/hooks/index.tsx +13 -0
  161. package/src/plugins/comments/client/hooks/use-comments.tsx +717 -0
  162. package/src/plugins/comments/client/index.ts +14 -0
  163. package/src/plugins/comments/client/localization/comments-moderation.ts +75 -0
  164. package/src/plugins/comments/client/localization/comments-my.ts +32 -0
  165. package/src/plugins/comments/client/localization/comments-thread.ts +32 -0
  166. package/src/plugins/comments/client/localization/index.ts +11 -0
  167. package/src/plugins/comments/client/overrides.ts +164 -0
  168. package/src/plugins/comments/client/plugin.tsx +195 -0
  169. package/src/plugins/comments/client/utils.ts +67 -0
  170. package/src/plugins/comments/client.css +2 -0
  171. package/src/plugins/comments/db.ts +77 -0
  172. package/src/plugins/comments/query-keys.ts +189 -0
  173. package/src/plugins/comments/schemas.ts +72 -0
  174. package/src/plugins/comments/style.css +15 -0
  175. package/src/plugins/comments/types.ts +73 -0
  176. package/src/plugins/kanban/client/components/forms/task-form.tsx +0 -1
  177. package/src/plugins/kanban/client/components/pages/board-page.internal.tsx +46 -27
  178. package/src/plugins/kanban/client/overrides.ts +27 -1
  179. package/dist/shared/{stack.Rtcvl8sS.d.cts → stack.BOokfhZD.d.cts} +3 -3
  180. package/dist/shared/{stack.D4Cea8II.d.ts → stack.BvCR4-9H.d.ts} +3 -3
  181. 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;