@rpcbase/server 0.380.0 → 0.381.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/package.json +15 -72
- package/src/getDerivedKey.ts +20 -0
- package/src/hashPassword.ts +24 -0
- package/src/index.ts +3 -0
- package/src/initServer.ts +68 -0
- package/src/types/index.ts +7 -0
- package/src/types/session.d.ts +10 -0
- package/boot/server.js +0 -36
- package/boot/shared.js +0 -17
- package/boot/worker.js +0 -37
- package/constants/keys.ts +0 -1
- package/database.js +0 -96
- package/express/custom_cors.js +0 -80
- package/express/dev_save_coverage.js +0 -18
- package/express/index.js +0 -93
- package/express/setup_handlers.js +0 -49
- package/files.ts +0 -1
- package/firebase.js +0 -33
- package/get_object_id.ts +0 -39
- package/index.js +0 -17
- package/mailer/index.js +0 -31
- package/mongoose/index.ts +0 -16
- package/mongoose/plugins/disable_default_timestamps_plugin.ts +0 -5
- package/mongoose/plugins/disable_default_version_key_plugin.ts +0 -5
- package/mongoose/plugins/object_id_plugin.ts +0 -31
- package/openai.js +0 -10
- package/publish-output.txt +0 -0
- package/queue/dispatch_indexer_queue.js +0 -22
- package/queue/dispatch_worker_queue.js +0 -38
- package/queue/index.js +0 -110
- package/queue/register_queue_listener.js +0 -180
- package/redis.js +0 -2
- package/rts/index.js +0 -444
- package/search/constants.ts +0 -1
- package/search/ensure_index.ts +0 -53
- package/search/get_client.ts +0 -15
- package/search/index.ts +0 -3
- package/src/access-control/apply_policies.js +0 -104
- package/src/access-control/get_added_fields.js +0 -23
- package/src/access-control/get_policies.js +0 -29
- package/src/access-control/hooks/doc_pre_create.js +0 -26
- package/src/access-control/hooks/query_pre_delete.js +0 -30
- package/src/access-control/index.js +0 -6
- package/src/access-control/mongoose_plugin.js +0 -136
- package/src/api/index.js +0 -6
- package/src/api/stored-values/get_stored_values.js +0 -41
- package/src/api/stored-values/index.js +0 -8
- package/src/api/stored-values/set_stored_values.js +0 -31
- package/src/auth/check_session.js +0 -43
- package/src/auth/forgot_password_email.html +0 -515
- package/src/auth/get_account.js +0 -35
- package/src/auth/get_accounts.js +0 -42
- package/src/auth/index.js +0 -24
- package/src/auth/reset_password.js +0 -70
- package/src/auth/set_new_password.js +0 -63
- package/src/auth/set_new_password_email.html +0 -3
- package/src/auth/sign_in.js +0 -61
- package/src/auth/sign_out.js +0 -11
- package/src/auth/sign_up.js +0 -56
- package/src/client/client_router.js +0 -105
- package/src/files/constants.ts +0 -9
- package/src/files/finalize_file_upload.ts +0 -25
- package/src/files/helpers/get_grid_fs_bucket.ts +0 -20
- package/src/files/index.js +0 -5
- package/src/files/tasks/finalize_file_upload/apply_img_preview.ts +0 -49
- package/src/files/tasks/finalize_file_upload/constants.ts +0 -23
- package/src/files/tasks/finalize_file_upload/download_file.ts +0 -98
- package/src/files/tasks/finalize_file_upload/get_text_vectors.ts +0 -13
- package/src/files/tasks/finalize_file_upload/helpers/convert_pdf_to_png.ts +0 -34
- package/src/files/tasks/finalize_file_upload/helpers/exec.ts +0 -5
- package/src/files/tasks/finalize_file_upload/helpers/get_metadata.ts +0 -18
- package/src/files/tasks/finalize_file_upload/index.ts +0 -53
- package/src/files/tasks/finalize_file_upload/run_ocr.ts +0 -42
- package/src/files/tasks/index.ts +0 -6
- package/src/files/upload_chunk.ts +0 -83
- package/src/helpers/sim_test_inject.ts +0 -21
- package/src/models/Invite.js +0 -23
- package/src/models/Notification.js +0 -44
- package/src/models/Policy.ts +0 -13
- package/src/models/ResetPasswordToken.js +0 -14
- package/src/models/SearchHistory.ts +0 -22
- package/src/models/User.js +0 -42
- package/src/models/UserStoredValues.js +0 -18
- package/src/models/index.js +0 -7
- package/src/notitications/ack_notification.js +0 -26
- package/src/notitications/get_notifications.js +0 -39
- package/src/notitications/llt/README.md +0 -8
- package/src/notitications/llt/get_llts.js +0 -42
- package/src/notitications/set_seen.js +0 -26
- package/src/sessions/index.js +0 -27
- package/src/sessions/session_proxy_middleware.js +0 -18
- package/src/sessions/session_store_middleware.js +0 -106
- package/src/sessions/warning_proxy_middleware.js +0 -17
- package/src/tasks/index.js +0 -8
- package/src/tasks/index_item.js +0 -8
- package/store/index.js +0 -31
package/rts/index.js
DELETED
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
const Promise = require("bluebird")
|
|
3
|
-
const _remove = require("lodash/remove")
|
|
4
|
-
const _get = require("lodash/get")
|
|
5
|
-
const _set = require("lodash/set")
|
|
6
|
-
const {Server} = require("socket.io")
|
|
7
|
-
const sift = require("sift")
|
|
8
|
-
const debug = require("debug")
|
|
9
|
-
const colors = require("picocolors")
|
|
10
|
-
|
|
11
|
-
const mongoose = require("../mongoose")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const log = debug("rb:rts:server")
|
|
15
|
-
|
|
16
|
-
const is_production = process.env.NODE_ENV === "production"
|
|
17
|
-
|
|
18
|
-
const QUERY_MAX_LIMIT = 4096
|
|
19
|
-
|
|
20
|
-
// TODO: there are edge cases in dev where the client reconnects to the server
|
|
21
|
-
// but the server already has the query results and sends it multiple times
|
|
22
|
-
// the server should ensure it is only registering queries once
|
|
23
|
-
|
|
24
|
-
// TODO: does this even support multiple clients ?
|
|
25
|
-
// TODO: investigate multiple change streams event triggers per client
|
|
26
|
-
// TODO: add tracing
|
|
27
|
-
const _sockets = {}
|
|
28
|
-
const _handlers = []
|
|
29
|
-
const _change_streams = {}
|
|
30
|
-
// we index both queries and socket queries
|
|
31
|
-
// to avoid going through all queries when a socket disconnects
|
|
32
|
-
const _queries = {}
|
|
33
|
-
const _socket_queries = {}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const get_query_options = (ctx, options) => {
|
|
37
|
-
// https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()
|
|
38
|
-
const query_options = {
|
|
39
|
-
ctx,
|
|
40
|
-
is_client: true,
|
|
41
|
-
//
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (options.sort) {
|
|
45
|
-
query_options.sort = options.sort
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const limit = typeof options.limit === "number" ? Math.min(QUERY_MAX_LIMIT, Math.abs(options.limit)) : QUERY_MAX_LIMIT
|
|
49
|
-
query_options.limit = limit
|
|
50
|
-
|
|
51
|
-
return query_options
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// helper to send and update / refresh event to all subscribed queries
|
|
56
|
-
const dispatch_doc_change = (model, doc) => {
|
|
57
|
-
const model_name = model.modelName
|
|
58
|
-
const queries = _queries[model_name] || {}
|
|
59
|
-
|
|
60
|
-
// WARNING: INVESTIGATE: there are edge cases where you might want to delete an object
|
|
61
|
-
// that was matched, but now it's not anymore because it's been udpated or removed
|
|
62
|
-
// rts should still update the query and send a payload to the client
|
|
63
|
-
// we could use the new mongodb fullDocumentBeforeChange param in watch,
|
|
64
|
-
// but it doesn't seem to be guaranteed to be there
|
|
65
|
-
|
|
66
|
-
// filter queries that match the document
|
|
67
|
-
const target_queries = Object.keys(queries).filter((query_key) => {
|
|
68
|
-
const {query} = queries[query_key]
|
|
69
|
-
// console.log("query", query)
|
|
70
|
-
const test_fn = sift(query)
|
|
71
|
-
return test_fn(doc)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
// we don't need to log every missing txn id, it is expected
|
|
75
|
-
// if (!doc.__txn_id) {
|
|
76
|
-
// console.log(colors.yellow("Warning!"), "change on model:", model.modelName, "matching queries", target_queries, "did not include a valid", colors.yellow("__txn"), "field")
|
|
77
|
-
// }
|
|
78
|
-
|
|
79
|
-
// TODO: we should not re-run each query here!!!!
|
|
80
|
-
// dispatch change to target query clients
|
|
81
|
-
target_queries.forEach(async(query_key) => {
|
|
82
|
-
const {query, options, sockets} = queries[query_key]
|
|
83
|
-
|
|
84
|
-
// TODO: remove this
|
|
85
|
-
await Promise.map(sockets, async(socket_id) => {
|
|
86
|
-
const s = _sockets[socket_id]
|
|
87
|
-
// socket was destroyed
|
|
88
|
-
// TODO: remove socket id from _queries when destroyed
|
|
89
|
-
if (!s) {
|
|
90
|
-
log("NO SOCKET, RETURNING")
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
// console.log("dipatch change", query_key)
|
|
94
|
-
const ctx = {req: s.request}
|
|
95
|
-
|
|
96
|
-
if (!ctx.req.session.user_id) {
|
|
97
|
-
console.error("dispatch_doc_change::error", "no user id for client query")
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const query_options = get_query_options(ctx, options)
|
|
102
|
-
// TODO: do not redo query here, use document from change event
|
|
103
|
-
const query_promise = model.find(query, options.projection, query_options)
|
|
104
|
-
if (query_options.sort) {
|
|
105
|
-
query_promise.sort(query_options.sort)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
query_promise.limit(query_options.limit)
|
|
109
|
-
|
|
110
|
-
// WARNING: DANGER
|
|
111
|
-
// TODO: we should not be doing a read here, use doc from event
|
|
112
|
-
const data = await query_promise
|
|
113
|
-
|
|
114
|
-
const data_buf = JSON.stringify(data)
|
|
115
|
-
|
|
116
|
-
log("emit payload")
|
|
117
|
-
|
|
118
|
-
s.emit("query_payload", {
|
|
119
|
-
model_name,
|
|
120
|
-
query_key,
|
|
121
|
-
data_buf,
|
|
122
|
-
txn_id: doc.__txn_id,
|
|
123
|
-
})
|
|
124
|
-
}, {concurrency: 1024}) // TODO: tweak concurrency it should be infinite
|
|
125
|
-
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// mongoose middleware used to broadcast delete events to appropriate sockets
|
|
131
|
-
const mongoose_delete_plugin = (schema) => {
|
|
132
|
-
schema.pre("deleteOne", function(next) {
|
|
133
|
-
throw new Error("rts::deleteOne is not supported, use findOneAndDelete")
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
// TODO: add other delete operations
|
|
137
|
-
// https://mongoosejs.com/docs/queries.html
|
|
138
|
-
schema.post("findOneAndDelete", function(doc) {
|
|
139
|
-
dispatch_doc_change(this.model, doc)
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Mongoose transaction plugin to save txn on each doc
|
|
145
|
-
// WARNING: query middlewares are not supported yet
|
|
146
|
-
// https://mongoosejs.com/docs/middleware.html#types-of-middleware
|
|
147
|
-
const mongoose_txn_plugin = (schema, options) => {
|
|
148
|
-
// console.log("register TXN plugin on schema", schema)
|
|
149
|
-
// WARNING: possible middlewares here:
|
|
150
|
-
// https://mongoosejs.com/docs/middleware.html
|
|
151
|
-
// this function isn't async but could be?
|
|
152
|
-
const apply_txn = /* async */function(next, save_options) {
|
|
153
|
-
// TODO: keep an eye on this, this seems to happen for nested schemas that aren't models
|
|
154
|
-
if (!save_options) {
|
|
155
|
-
next()
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const {ctx} = save_options
|
|
160
|
-
if (!ctx) {
|
|
161
|
-
log(colors.yellow("Warning!"), "{ctx} not attached to save")
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const txn_id = _get(ctx, ["req", "headers", "rts-txn-id"])
|
|
165
|
-
|
|
166
|
-
if (!txn_id) {
|
|
167
|
-
log(colors.yellow("Warning!"), "mongoose save request is missing txn_id this will cause issues with rts-client")
|
|
168
|
-
} else {
|
|
169
|
-
this.set("__txn_id", txn_id)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
next()
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
schema.pre("save", apply_txn)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
// responds to a change stream "change" event
|
|
181
|
-
const get_dispatch_change_handler = (model_name) => async(change) => {
|
|
182
|
-
// const queries = _queries[model_name]
|
|
183
|
-
|
|
184
|
-
const model = mongoose.model(model_name)
|
|
185
|
-
|
|
186
|
-
let doc
|
|
187
|
-
if (["insert", "update"].includes(change.operationType)) {
|
|
188
|
-
// console.log("rts onChange", change)
|
|
189
|
-
doc = change.fullDocument
|
|
190
|
-
} else if (change.operationType === "delete") {
|
|
191
|
-
console.log("op is delete")
|
|
192
|
-
} else if (!["delete", "drop", "invalidate"].includes(change.operationType)) {
|
|
193
|
-
console.log("SKIPPING: unknown operation type", change.operationType, change)
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (!doc) {
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
dispatch_doc_change(model, doc)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const add_change_stream = (socket_id, {model_name, query, query_key, options}) => {
|
|
206
|
-
if (!model_name) throw new Error("empty model name")
|
|
207
|
-
|
|
208
|
-
if (!_change_streams[model_name]) {
|
|
209
|
-
log("rts: initializing change stream for model:", model_name)
|
|
210
|
-
let model
|
|
211
|
-
try {
|
|
212
|
-
model = mongoose.model(model_name)
|
|
213
|
-
} catch (err) {
|
|
214
|
-
console.error("add change stream:ERROR registering model:", model_name)
|
|
215
|
-
console.error(err)
|
|
216
|
-
return
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// TODO: fix this, this has been released
|
|
220
|
-
// TODO: implement delete operation fullDocument retrieve,
|
|
221
|
-
// when this is released https://jira.mongodb.org/browse/SERVER-36941
|
|
222
|
-
const emitter = model.watch({
|
|
223
|
-
fullDocument: "updateLookup",
|
|
224
|
-
fullDocumentBeforeChange: "whenAvailable",
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
emitter.on("change", get_dispatch_change_handler(model_name))
|
|
228
|
-
|
|
229
|
-
emitter.on("error", (err) => {
|
|
230
|
-
console.log("change listener emitter got error", err)
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
emitter.on("close", () => {
|
|
234
|
-
delete _change_streams[model_name]
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
_change_streams[model_name] = emitter
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// save query
|
|
241
|
-
if (!_queries[model_name]) {
|
|
242
|
-
_queries[model_name] = {}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (!_queries[model_name][query_key]) {
|
|
246
|
-
_queries[model_name][query_key] = {query, options, sockets: []}
|
|
247
|
-
}
|
|
248
|
-
if (!_queries[model_name][query_key].sockets.includes(socket_id)) {
|
|
249
|
-
_queries[model_name][query_key].sockets.push(socket_id)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// save socket_query
|
|
253
|
-
if (!_socket_queries[socket_id]) {
|
|
254
|
-
_socket_queries[socket_id] = {}
|
|
255
|
-
}
|
|
256
|
-
if (!_socket_queries[socket_id][model_name]) {
|
|
257
|
-
_socket_queries[socket_id][model_name] = {}
|
|
258
|
-
}
|
|
259
|
-
_socket_queries[socket_id][model_name][query_key] = true
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const run_query = async(socket_id, {model_name, query, query_key, options}) => {
|
|
264
|
-
// Check if the model is registered
|
|
265
|
-
if (!mongoose.models[model_name]) {
|
|
266
|
-
// throw new Error(`Model "${model_name}" is not registered.`)
|
|
267
|
-
console.error(colors.red("ERROR: model not registered"), model_name, "will skip query")
|
|
268
|
-
return
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const model = mongoose.model(model_name)
|
|
272
|
-
|
|
273
|
-
log("run_query", {model_name, query, query_key, options})
|
|
274
|
-
|
|
275
|
-
if (!query) throw new Error("run_query empty query")
|
|
276
|
-
|
|
277
|
-
const s = _sockets[socket_id]
|
|
278
|
-
const ctx = {req: s.request}
|
|
279
|
-
|
|
280
|
-
// log("initial run_query AUTH", ctx.req.session.user_id)
|
|
281
|
-
if (!ctx.req.session.user_id) {
|
|
282
|
-
console.warn("no user ID for client query")
|
|
283
|
-
s.emit("query_payload", {model_name, query_key, error: "error no user id"})
|
|
284
|
-
return
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const query_options = get_query_options(ctx, options)
|
|
288
|
-
// log("QUERY", query)
|
|
289
|
-
const query_promise = model.find(query, options.projection, query_options)
|
|
290
|
-
|
|
291
|
-
if (query_options.sort) {
|
|
292
|
-
query_promise.sort(query_options.sort)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (typeof query_options.limit === "number" && query_options.limit >= 0) {
|
|
296
|
-
query_promise.limit(query_options.limit)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const data = await query_promise
|
|
300
|
-
|
|
301
|
-
const data_buf = JSON.stringify(data)
|
|
302
|
-
|
|
303
|
-
s.emit("query_payload", {model_name, query_key, data_buf})
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const remove_query = (socket_id, {model_name, query, query_key}) => {
|
|
308
|
-
const sockets = _queries[model_name]?.[query_key]?.sockets || []
|
|
309
|
-
|
|
310
|
-
let new_sockets
|
|
311
|
-
if (sockets.includes(socket_id)) {
|
|
312
|
-
new_sockets = _remove(sockets, (s) => s === socket_id)
|
|
313
|
-
} else new_sockets = sockets
|
|
314
|
-
|
|
315
|
-
_set(_queries, `${model_name}.${query_key}.sockets`, new_sockets)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const disconnect_handler = (socket_id, reason) => {
|
|
319
|
-
if (reason !== "transport close") {
|
|
320
|
-
console.log("client disconnected, reason:", reason, "socket:", socket_id)
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const local_queries = _socket_queries[socket_id] || {}
|
|
324
|
-
|
|
325
|
-
// remove all queries matching that socket id
|
|
326
|
-
Object.keys(local_queries)
|
|
327
|
-
.forEach((model_name) => {
|
|
328
|
-
|
|
329
|
-
Object.keys(local_queries[model_name]).forEach((query_key) => {
|
|
330
|
-
const active_sockets = _queries[model_name]?.[query_key]?.sockets || []
|
|
331
|
-
|
|
332
|
-
const socket_index = active_sockets.indexOf(socket_id)
|
|
333
|
-
|
|
334
|
-
// remove the socket id from the array
|
|
335
|
-
if (socket_index > -1) {
|
|
336
|
-
// _queries[model_name][query_key].sockets.splice(socket_index, 1)
|
|
337
|
-
active_sockets.splice(socket_index, 1)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// remove query if there are no more listeners
|
|
341
|
-
if (active_sockets.length === 0) {
|
|
342
|
-
delete _queries[model_name]?.[query_key]
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// TODO: why the || {} here ?
|
|
346
|
-
// remove change stream if there are no more queries on this collection
|
|
347
|
-
if (Object.keys(_queries[model_name] || {}).length === 0) {
|
|
348
|
-
delete _queries[model_name]
|
|
349
|
-
_change_streams[model_name]?.close()
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
})
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
// remove socket
|
|
356
|
-
delete _sockets[socket_id]
|
|
357
|
-
delete _socket_queries[socket_id]
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const init_rts = async(server) => {
|
|
362
|
-
// register the delete plugin
|
|
363
|
-
mongoose.plugin(mongoose_delete_plugin)
|
|
364
|
-
// txn plugin
|
|
365
|
-
mongoose.plugin(mongoose_txn_plugin)
|
|
366
|
-
|
|
367
|
-
//
|
|
368
|
-
const io = new Server(server, {
|
|
369
|
-
transports: is_production ? ["websocket", "polling"] : ["websocket"],
|
|
370
|
-
allowUpgrades: true,
|
|
371
|
-
cors: {
|
|
372
|
-
// TODO: dynamic client ports
|
|
373
|
-
// TODO: check if is production
|
|
374
|
-
origin: `http://localhost:${process.env.CLIENT_PORT}`,
|
|
375
|
-
methods: ["GET", "POST"],
|
|
376
|
-
credentials: true,
|
|
377
|
-
},
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
//
|
|
381
|
-
const has_session_store = process.env.RB_SESSION_STORE === "yes"
|
|
382
|
-
|
|
383
|
-
if (has_session_store) {
|
|
384
|
-
const session_store_middleware = require("../src/sessions/session_store_middleware")
|
|
385
|
-
// socket io session middleware
|
|
386
|
-
io.use((socket, next) => {
|
|
387
|
-
session_store_middleware(socket.request, {}, next)
|
|
388
|
-
})
|
|
389
|
-
} else {
|
|
390
|
-
const session_proxy_middleware = require("../src/sessions/session_proxy_middleware")
|
|
391
|
-
// proxy middleware that checks the rb-tenant-id header
|
|
392
|
-
io.use((socket, next) => {
|
|
393
|
-
session_proxy_middleware(socket.request, {}, next)
|
|
394
|
-
})
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// io.on('connection', (socket) => {
|
|
398
|
-
// const session = socket.request.session;
|
|
399
|
-
// session.connections++;
|
|
400
|
-
// session.save();
|
|
401
|
-
// });
|
|
402
|
-
|
|
403
|
-
io.on("connection", (socket) => {
|
|
404
|
-
_sockets[socket.id] = socket
|
|
405
|
-
|
|
406
|
-
log("socket connected", socket.id)
|
|
407
|
-
|
|
408
|
-
// RTS handlers
|
|
409
|
-
socket.on("registerQuery", (payload) => {
|
|
410
|
-
add_change_stream(socket.id, payload)
|
|
411
|
-
})
|
|
412
|
-
|
|
413
|
-
socket.on("remove_query", (payload) => {
|
|
414
|
-
remove_query(socket.id, payload)
|
|
415
|
-
})
|
|
416
|
-
|
|
417
|
-
socket.on("run_query", (payload) => {
|
|
418
|
-
try {
|
|
419
|
-
run_query(socket.id, payload)
|
|
420
|
-
} catch (err) {
|
|
421
|
-
console.log("run_query caught error", err)
|
|
422
|
-
}
|
|
423
|
-
})
|
|
424
|
-
// !RTS handlers
|
|
425
|
-
|
|
426
|
-
// register_rts_handler: custom server generic handlers
|
|
427
|
-
const cleanup_fns = _handlers.map((handler_fn) => handler_fn(socket))
|
|
428
|
-
|
|
429
|
-
socket.on("disconnect", (reason) => {
|
|
430
|
-
disconnect_handler(socket.id, reason)
|
|
431
|
-
cleanup_fns
|
|
432
|
-
.filter((fn) => typeof fn === "function")
|
|
433
|
-
.forEach((fn) => fn())
|
|
434
|
-
})
|
|
435
|
-
})
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const register_rts_handler = (handler_fn) => {
|
|
440
|
-
_handlers.push(handler_fn)
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
module.exports = {init_rts, register_rts_handler}
|
package/search/constants.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_SEARCH_EMBEDDINGS_DIMENSIONS = 2048
|
package/search/ensure_index.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import debug from "debug"
|
|
2
|
-
import {get_client} from "./get_client"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const log = debug("rb:search:ensure_index")
|
|
6
|
-
|
|
7
|
-
export const ensure_index = async({
|
|
8
|
-
index_id,
|
|
9
|
-
mappings,
|
|
10
|
-
}: {
|
|
11
|
-
index_id: string;
|
|
12
|
-
mappings: any;
|
|
13
|
-
}) => {
|
|
14
|
-
const client = get_client()
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const exists = await client.indices.exists({index: index_id})
|
|
18
|
-
|
|
19
|
-
if (exists) {
|
|
20
|
-
await client.indices.putMapping({
|
|
21
|
-
index: index_id,
|
|
22
|
-
body: mappings,
|
|
23
|
-
})
|
|
24
|
-
log("updated index mappings", index_id)
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
} catch (err) {
|
|
28
|
-
console.log(
|
|
29
|
-
"Error checking for index existance",
|
|
30
|
-
JSON.stringify(err, null, 2),
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const res = await client.indices.create({
|
|
35
|
-
index: index_id,
|
|
36
|
-
body: {
|
|
37
|
-
mappings,
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
expect(res.acknowledged).toBe(true)
|
|
42
|
-
expect(res.index).toBe(index_id)
|
|
43
|
-
|
|
44
|
-
log("created index", index_id)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
console.log("ensure_index.ts")
|
|
48
|
-
|
|
49
|
-
// {
|
|
50
|
-
// dynamic: true,
|
|
51
|
-
// // date_detection: false,
|
|
52
|
-
// properties: mappings_properties,
|
|
53
|
-
// }
|
package/search/get_client.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {Client} from "@elastic/elasticsearch"
|
|
2
|
-
|
|
3
|
-
// const is_docker = require("@rpcbase/std/is_docker")
|
|
4
|
-
const is_docker = () => true
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const {SEARCH_INDEX_PORT} = process.env
|
|
8
|
-
// search indexer
|
|
9
|
-
const search_hostname = is_docker() ? "elasticsearch" : "127.0.0.1"
|
|
10
|
-
const indexer_cluster_url = `http://${search_hostname}:${SEARCH_INDEX_PORT}`
|
|
11
|
-
|
|
12
|
-
export const get_client = () => {
|
|
13
|
-
const es_client = new Client({node: indexer_cluster_url})
|
|
14
|
-
return es_client
|
|
15
|
-
}
|
package/search/index.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
const _get = require("lodash/get")
|
|
3
|
-
const colors = require("picocolors")
|
|
4
|
-
|
|
5
|
-
const get_policies = require("./get_policies")
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const user_types = ["owner", "user", "any"]
|
|
9
|
-
const perm_operations = ["create", "read", "update", "delete"]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// TODO: add LRU cache for policies
|
|
13
|
-
|
|
14
|
-
// Owner
|
|
15
|
-
// the creator of the document, or an user_id that is in the "owners" field
|
|
16
|
-
|
|
17
|
-
// User
|
|
18
|
-
// any authenticated user that belongs in this tenant
|
|
19
|
-
|
|
20
|
-
// Any
|
|
21
|
-
// most relaxed permission, any client can access the resource
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// given ac config, operation and document / query,
|
|
25
|
-
// check if the user has the permission to run this op
|
|
26
|
-
// if the user doesn't have permission, the system should throw an error
|
|
27
|
-
const apply_policies = async({collection_name, model_name, operation, fields, user_id, doc}) => {
|
|
28
|
-
// TODO: fix rm policy here
|
|
29
|
-
// const rule_target = _get(ac_config, `${model_name}.${operation}`)
|
|
30
|
-
const rule_target = ""
|
|
31
|
-
// console.log("rule target", rule_target)
|
|
32
|
-
|
|
33
|
-
const policies = await get_policies({collection_name, model_name, operation, fields, user_id, doc})
|
|
34
|
-
console.log("MODEL APPLY POLICIES", {collection_name, model_name, operation, fields, user_id, doc})
|
|
35
|
-
return
|
|
36
|
-
// console.log("apply_policies", `'${model_name}:${operation}:${rule_target}'`)
|
|
37
|
-
|
|
38
|
-
if (!rule_target) {
|
|
39
|
-
throw new Error(`undefined rule_target for '${model_name}:${operation}'`)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!user_types.includes(rule_target)) {
|
|
43
|
-
throw new Error(`expected rule_target '${model_name}:${operation}' to be in '${JSON.stringify(user_types)}', but got '${rule_target}'`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Create
|
|
47
|
-
if (operation === "create") {
|
|
48
|
-
if (["owner", "user"].includes(rule_target)) {
|
|
49
|
-
if (!user_id) return new Error("create expected authenticated user_id")
|
|
50
|
-
} else {
|
|
51
|
-
return false
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Read
|
|
55
|
-
else if (operation === "read") {
|
|
56
|
-
if (rule_target === "owner") {
|
|
57
|
-
if (!user_id) throw new Error("read::owner invalid user_id")
|
|
58
|
-
|
|
59
|
-
const conditions = doc.getQuery()
|
|
60
|
-
|
|
61
|
-
// TMP: warn if user supplied _owners, which is currently overwritten for ACL
|
|
62
|
-
if (conditions._owners) {
|
|
63
|
-
if (JSON.stringify(conditions._owners) !== `{"$in":["${user_id}"]}`) {
|
|
64
|
-
console.log(colors.yellow("Warning!"), model_name, "ACL for read is owner, but client is sending _owners in query, the field will be overwritten", "\nreceived conditions._owners:", JSON.stringify(conditions._owners))
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
doc.setQuery({
|
|
69
|
-
...conditions,
|
|
70
|
-
_owners: {$in: [user_id]}
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// Update
|
|
76
|
-
else if (operation === "update") {
|
|
77
|
-
if (rule_target === "owner") {
|
|
78
|
-
if (!doc._owners?.includes(user_id)) {
|
|
79
|
-
console.log("MODEL:", model_name)
|
|
80
|
-
const err = new Error("expected user_id to be in owners to update document")
|
|
81
|
-
console.log("failed to update doc:", doc)
|
|
82
|
-
return err
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
throw new Error(`unknown rule target '${rule_target}' for operation ${model_name}::update`)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// Delete
|
|
89
|
-
else if (operation === "delete") {
|
|
90
|
-
if (rule_target === "owner") {
|
|
91
|
-
if (!doc._owners.includes(user_id)) {
|
|
92
|
-
// TODO: add debug logging
|
|
93
|
-
return new Error("expected user_id to be in owners to delete document")
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
throw new Error(`unknown rule target '${rule_target}' for operation ${model_name}::delete`)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return false
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
module.exports = apply_policies
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
|
|
3
|
-
const get_added_fields = () => {
|
|
4
|
-
|
|
5
|
-
return {
|
|
6
|
-
_owners: {
|
|
7
|
-
type: Array,
|
|
8
|
-
of: String,
|
|
9
|
-
index: true,
|
|
10
|
-
required: true,
|
|
11
|
-
},
|
|
12
|
-
_viewers: [String],
|
|
13
|
-
_editors: [String],
|
|
14
|
-
_created_by: String,
|
|
15
|
-
_created_at: {
|
|
16
|
-
type: Date
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
module.exports = get_added_fields
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const Policy = require("../models/Policy")
|
|
2
|
-
|
|
3
|
-
const DEFAULT_POLICY = {
|
|
4
|
-
"create": "user",
|
|
5
|
-
"read": "owner",
|
|
6
|
-
"update": "owner",
|
|
7
|
-
"delete": "owner",
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const get_policies = async({collection_name, model_name, operation, user_id, doc}) => {
|
|
11
|
-
|
|
12
|
-
const policies = await Policy.find({
|
|
13
|
-
$or: [
|
|
14
|
-
{ collection_name, operations: { $in: [operation] }},
|
|
15
|
-
{ doc_id: doc._id, operations: { $in: [operation] }},
|
|
16
|
-
// TODO: add field level operations
|
|
17
|
-
]
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
if (!policies) {
|
|
21
|
-
return [DEFAULT_POLICY]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
console.log("POLICIES", policies)
|
|
25
|
-
|
|
26
|
-
return policies
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = get_policies
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
//
|
|
3
|
-
// const has_permission = require("../has_permission")
|
|
4
|
-
//
|
|
5
|
-
// const delay = (time) => new Promise((resolve) => setTimeout(resolve, time))
|
|
6
|
-
// // await delay(2000)
|
|
7
|
-
//
|
|
8
|
-
// module.exports = (schema) => async function(next) {
|
|
9
|
-
// const model_name = this.model.modelName
|
|
10
|
-
// const operation = "delete"
|
|
11
|
-
//
|
|
12
|
-
// if (this.op !== "findOneAndDelete") {
|
|
13
|
-
// throw new Error(`in pre_delete unknown operation: ${this.op}`)
|
|
14
|
-
// }
|
|
15
|
-
//
|
|
16
|
-
// const filter = this.getFilter()
|
|
17
|
-
// const doc = await this.model.findOne(filter)
|
|
18
|
-
//
|
|
19
|
-
// console.log("DELETE GOT DOC", doc)
|
|
20
|
-
// // console.log("THIS", this)
|
|
21
|
-
// // console.log("THIS filter", this.getFilter())
|
|
22
|
-
// // console.log("SCHEMA DEL", schema)
|
|
23
|
-
// // TODO: find a way to check if user can delete this document without reading it
|
|
24
|
-
// // console.log("delete got ac config", ac_config)
|
|
25
|
-
// next()
|
|
26
|
-
// }
|