@live-change/comment-service 0.8.35

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/comment.js ADDED
@@ -0,0 +1,217 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+
4
+ import definition from './definition.js'
5
+ const config = definition.config
6
+
7
+ import { encodeDate } from '@live-change/uid'
8
+
9
+ const {
10
+ contentObject = false,
11
+ commentProperties = {},
12
+ adminRoles = ['administrator', 'admin'],
13
+ moderatorRoles = ['administrator', 'admin', 'moderator'],
14
+ editExpire = 1000 * 60 * 60 * 15 // 15 minutes
15
+ } = config
16
+ const {
17
+ readAccessControl,
18
+ createAccessControl,
19
+ updateAccessControl,
20
+ deleteAccessControl,
21
+ } = config
22
+ const {
23
+ readAccess = readAccessControl ? undefined
24
+ : () => true,
25
+ createAccess = createAccessControl ? undefined
26
+ : () => true,
27
+ updateAccess = updateAccessControl ? undefined
28
+ : ({ comment }, { client, visibilityTest }) => {
29
+ if(visibilityTest) return true
30
+ if(client.roles.some(role => adminRoles.includes(role))) return true
31
+ if(!editExpire || Date.now() - new Date(comment.createdAt).getDate() > editExpire) return false
32
+ if(comment.authorType === 'session_Session' && comment.author === client.session) return true
33
+ if(comment.authorType === 'user_User' && comment.author === client.user) return true
34
+ return false
35
+ },
36
+ deleteAccess = (params, { client }) =>
37
+ client.roles.some(role => adminRoles.includes(role)) || client.roles.some(role => moderatorRoles.includes(role))
38
+ } = config
39
+
40
+ const Comment = definition.model({
41
+ name: 'Comment',
42
+ itemOfAny: {
43
+ to: 'cause',
44
+ readAccess,
45
+ createAccess,
46
+ updateAccess,
47
+ deleteAccess,
48
+ readAccessControl,
49
+ createAccessControl,
50
+ updateAccessControl,
51
+ deleteAccessControl
52
+ },
53
+ properties: {
54
+ content: contentObject ? {
55
+ type: Object, // TODO: prosemirror based validation
56
+ validation: ['nonEmpty']
57
+ } : {
58
+ type: String,
59
+ validation: ['nonEmpty']
60
+ },
61
+ createdAt: {
62
+ default: () => new Date()
63
+ },
64
+ updatedAt: {
65
+ updated: () => new Date()
66
+ },
67
+ ...commentProperties
68
+ },
69
+ saveAuthor: true,
70
+ saveUpdater: true,
71
+ indexes: {
72
+ byAuthor: {
73
+ property: ['authorType', 'author']
74
+ },
75
+ byRoot: {
76
+ function: async function(input, output, { tableName }) {
77
+ async function findRoot(object){
78
+ let current = object
79
+ while(current) {
80
+ if(current.causeType !== tableName) return `"${current.causeType}":"${current.cause}"`
81
+ current = await input.table(tableName).object(current.cause).get()
82
+ }
83
+ }
84
+ await input.table(tableName).onChange(async (obj, oldObj) => {
85
+ const id = obj?.id || oldObj?.id
86
+ const root = obj ? await findRoot(obj) : []
87
+ const oldRoot = oldObj ? await findRoot(oldObj) : []
88
+ //console.log("ROOT", id, oldRoot, '=>', root)
89
+ if(root !== oldRoot) {
90
+ if(oldRoot) {
91
+ await output.change(null, { id: `${oldRoot}_${id}`, to: id })
92
+ }
93
+ if(root) {
94
+ await output.change({ id: `${root}_${id}`, to: id }, null)
95
+ }
96
+ }
97
+ })
98
+ },
99
+ parameters: {
100
+ tableName: definition.name + '_Comment'
101
+ }
102
+ },
103
+ byRootAndDates: {
104
+ function: async function(input, output, { tableName }) {
105
+ async function findRoot(object) {
106
+ let current = object
107
+ const dates = []
108
+ while(current) {
109
+ if(current.causeType !== tableName)
110
+ return `"${current.causeType}":"${current.cause}:${dates.join('-')}"`
111
+ current = await input.table(tableName).object(current.cause).get()
112
+ if(current.causeType === tableName) {
113
+ dates.push(encodeDate(current.createdAt))
114
+ }
115
+ }
116
+ }
117
+ await input.table(tableName).onChange(async (obj, oldObj) => {
118
+ const id = obj?.id || oldObj?.id
119
+ const root = obj ? await findRoot(obj) : []
120
+ const oldRoot = oldObj ? await findRoot(oldObj) : []
121
+ //console.log("ROOT", id, oldRoot, '=>', root)
122
+ if(root !== oldRoot) {
123
+ if(oldRoot) {
124
+ await output.change(null, { id: `${oldRoot}_${id}`, to: id })
125
+ }
126
+ if(root) {
127
+ await output.change({ id: `${root}_${id}`, to: id }, null)
128
+ }
129
+ }
130
+ })
131
+ },
132
+ parameters: {
133
+ tableName: definition.name + '_Comment'
134
+ }
135
+ }
136
+ }
137
+ })
138
+
139
+ definition.view({
140
+ name: 'commentsByAuthor',
141
+ internal: true,
142
+ properties: {
143
+ authorType: {
144
+ type: String
145
+ },
146
+ author: {
147
+ type: String
148
+ },
149
+ ...App.rangeProperties
150
+ },
151
+ returns: {
152
+ type: Array,
153
+ of: {
154
+ type: Comment
155
+ }
156
+ },
157
+ async daoPath(props, { client }) {
158
+ const range = App.extractRange(props)
159
+ if(range.limit === undefined) range.limit = 256
160
+ return Comment.indexRangePath('byAuthor', [authorType, author], { })
161
+ }
162
+ })
163
+
164
+ definition.view({
165
+ name: 'commentsByRoot',
166
+ properties: {
167
+ rootType: {
168
+ type: String,
169
+ validation: ['nonEmpty']
170
+ },
171
+ root: {
172
+ type: String,
173
+ validation: ['nonEmpty']
174
+ },
175
+ ...App.rangeProperties
176
+ },
177
+ returns: {
178
+ type: Array,
179
+ of: {
180
+ type: Comment
181
+ }
182
+ },
183
+ async daoPath(props) {
184
+ const { rootType, root } = props
185
+ const range = App.extractRange(props)
186
+ if(!range.limit) range.limit = 256
187
+ return Comment.indexRangePath('byRoot', [rootType, root], range)
188
+ }
189
+ })
190
+
191
+ definition.view({
192
+ name: 'commentsByRootGroupedByDates',
193
+ properties: {
194
+ rootType: {
195
+ type: String,
196
+ validation: ['nonEmpty']
197
+ },
198
+ root: {
199
+ type: String,
200
+ validation: ['nonEmpty']
201
+ },
202
+ ...App.rangeProperties
203
+ },
204
+ returns: {
205
+ type: Array,
206
+ of: {
207
+ type: Comment
208
+ }
209
+ },
210
+ async daoPath(props) {
211
+ const { rootType, root } = props
212
+ const range = App.extractRange(props)
213
+ if(!range.limit) range.limit = 256
214
+ return Comment.indexRangePath('byRootAndDates', [rootType, root], range)
215
+ }
216
+ })
217
+
package/definition.js ADDED
@@ -0,0 +1,12 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+
4
+ import relationsPlugin from '@live-change/relations-plugin'
5
+ import accessControlService from '@live-change/access-control-service'
6
+
7
+ const definition = app.createServiceDefinition({
8
+ name: "comment",
9
+ use: [ relationsPlugin, accessControlService ]
10
+ })
11
+
12
+ export default definition
package/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+
4
+ import definition from './definition.js'
5
+
6
+ import './comment.js'
7
+
8
+ definition.beforeStart(() => {
9
+ /// TODO: restart stopped tasks
10
+ })
11
+
12
+ export default definition
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@live-change/comment-service",
3
+ "version": "0.8.35",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "NODE_ENV=test tape tests/*"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/live-change/live-change-stack.git"
12
+ },
13
+ "license": "MIT",
14
+ "bugs": {
15
+ "url": "https://github.com/live-change/live-change-stack/issues"
16
+ },
17
+ "homepage": "https://github.com/live-change/live-change-stack",
18
+ "author": {
19
+ "email": "michal@laszczewski.pl",
20
+ "name": "Michał Łaszczewski",
21
+ "url": "https://www.viamage.com/"
22
+ },
23
+ "type": "module",
24
+ "dependencies": {
25
+ "@live-change/framework": "^0.8.35",
26
+ "@live-change/relations-plugin": "^0.8.35"
27
+ },
28
+ "gitHead": "90fbb746dc7270895daf17b437ca48c0b0a01c01"
29
+ }