statebus 7.0.2 → 7.0.5
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/{clients.js → client-library.js} +310 -281
- package/extras/tests.js +110 -106
- package/package.json +3 -4
- package/{servers.js → server-library.js} +341 -247
- package/statebus.js +132 -76
|
@@ -37,7 +37,7 @@ function set_options (bus, options) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function import_server (bus, options)
|
|
40
|
+
function import_server (bus, make_statebus, options)
|
|
41
41
|
{ var extra_methods = {
|
|
42
42
|
|
|
43
43
|
serve: function serve (options) {
|
|
@@ -76,23 +76,6 @@ function import_server (bus, options)
|
|
|
76
76
|
try { bus.http.use(require('compression')())
|
|
77
77
|
console.log('Enabled http compression!') } catch (e) {}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
// Initialize new clients with an id. We put the client id on
|
|
81
|
-
// req.client, and also in a cookie for the browser to see.
|
|
82
|
-
if (bus.options.client)
|
|
83
|
-
bus.express.use(function (req, res, next) {
|
|
84
|
-
req.client = require('cookie').parse(req.headers.cookie || '').client
|
|
85
|
-
if (!req.client) {
|
|
86
|
-
req.client = (Math.random().toString(36).substring(2)
|
|
87
|
-
+ Math.random().toString(36).substring(2)
|
|
88
|
-
+ Math.random().toString(36).substring(2))
|
|
89
|
-
|
|
90
|
-
res.setHeader('Set-Cookie', 'client=' + req.client
|
|
91
|
-
+ '; Expires=21 Oct 2025 00:0:00 GMT;')
|
|
92
|
-
}
|
|
93
|
-
next()
|
|
94
|
-
})
|
|
95
|
-
|
|
96
79
|
// Initialize file sync
|
|
97
80
|
; (bus.options.sync_files || []).forEach( x => {
|
|
98
81
|
if (require('fs').existsSync(x.fs_path || x.state_path))
|
|
@@ -122,17 +105,17 @@ function import_server (bus, options)
|
|
|
122
105
|
// Now handle backup handlers
|
|
123
106
|
if (count === 0) {
|
|
124
107
|
|
|
125
|
-
// A set to master without a
|
|
108
|
+
// A set to master without a setter defined should be
|
|
126
109
|
// automatically reflected to all clients.
|
|
127
|
-
if (method === '
|
|
110
|
+
if (method === 'setter') {
|
|
128
111
|
bus.set.fire(arg, t)
|
|
129
112
|
bus.route(arg.key, 'on_set_sync', arg, t)
|
|
130
113
|
count++
|
|
131
114
|
}
|
|
132
115
|
|
|
133
|
-
// A get to master without a
|
|
116
|
+
// A get to master without a getter defined should go to
|
|
134
117
|
// the database, if we have one.
|
|
135
|
-
if (method === '
|
|
118
|
+
if (method === 'getter') {
|
|
136
119
|
bus.db_get(key)
|
|
137
120
|
// This basically renders the count useless-- you probably
|
|
138
121
|
// don't want to wrap this route() with another route().
|
|
@@ -165,30 +148,40 @@ function import_server (bus, options)
|
|
|
165
148
|
|| req.method === 'OPTIONS')
|
|
166
149
|
free_the_cors(req, res)
|
|
167
150
|
|
|
151
|
+
// Initialize new clients with an id. We put the client id on
|
|
152
|
+
// req.client, and also in a cookie for the browser to see.
|
|
153
|
+
req.peer = //req.headers.peer ||
|
|
154
|
+
require('cookie').parse(req.headers.cookie || '').peer
|
|
155
|
+
if (!req.peer) {
|
|
156
|
+
req.peer = (Math.random().toString(36).substring(2)
|
|
157
|
+
+ Math.random().toString(36).substring(2)
|
|
158
|
+
+ Math.random().toString(36).substring(2))
|
|
159
|
+
|
|
160
|
+
res.setHeader('Set-Cookie', 'peer=' + req.peer
|
|
161
|
+
+ '; Expires=21 Oct 2055 00:0:00 GMT;')
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Handle the GET or PUT request!
|
|
168
165
|
if (req.method === 'GET') {
|
|
166
|
+
var key = req.path.substr(1)
|
|
169
167
|
|
|
170
168
|
// Make a temporary client bus
|
|
171
169
|
var cbus = bus.bus_for_http_client(req, res)
|
|
172
|
-
var cb
|
|
173
|
-
function end_it_all () {
|
|
174
|
-
cbus.forget(key, cb)
|
|
175
|
-
cbus.delete_bus()
|
|
176
|
-
res.end()
|
|
177
|
-
}
|
|
178
170
|
|
|
179
171
|
braidify(req, res)
|
|
180
172
|
if (req.subscribe)
|
|
181
|
-
res.startSubscription({ onClose:
|
|
173
|
+
res.startSubscription({ onClose: end_subscription })
|
|
182
174
|
else
|
|
183
175
|
res.statusCode = 200
|
|
184
176
|
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
var key = req.path.substr(1)
|
|
188
|
-
cbus.get(key, cb = (o) => {
|
|
189
|
-
var body = to_http_body(o)
|
|
177
|
+
// We only return JSON
|
|
178
|
+
res.setHeader('Content-Type', 'application/json')
|
|
190
179
|
|
|
191
|
-
|
|
180
|
+
// console.log('http_in: doing cbus.get(', key, ')')
|
|
181
|
+
|
|
182
|
+
var send_to_client = (o, t) => {
|
|
183
|
+
// console.trace('http_in: Sending update of', key)
|
|
184
|
+
var body = to_http_body(o)
|
|
192
185
|
|
|
193
186
|
// Note: if body === undefined, we need to send the
|
|
194
187
|
// equivalent of a 404. This is missing in braid spec:
|
|
@@ -197,14 +190,35 @@ function import_server (bus, options)
|
|
|
197
190
|
|
|
198
191
|
// And shut down the connection if there's no subscription
|
|
199
192
|
if (!req.subscribe)
|
|
200
|
-
|
|
201
|
-
}
|
|
193
|
+
end_subscription()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function end_subscription () {
|
|
197
|
+
cbus.forget(key, send_to_client)
|
|
198
|
+
res.end()
|
|
199
|
+
cbus.http_unsubscribe(req)
|
|
200
|
+
|
|
201
|
+
let peer = req.headers.peer
|
|
202
|
+
if (cbus.http_callbacks)
|
|
203
|
+
delete cbus.http_callbacks[JSON.stringify({peer, key})]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// If we have a peer id, then register this callback at that peer,
|
|
207
|
+
// so it can avoid getting its own PUT versions echoed back to
|
|
208
|
+
// itself.
|
|
209
|
+
if (req.headers.peer) {
|
|
210
|
+
if (!cbus.http_callbacks)
|
|
211
|
+
cbus.http_callbacks = {}
|
|
212
|
+
let peer = req.headers.peer
|
|
213
|
+
cbus.http_callbacks[JSON.stringify({peer, key})]
|
|
214
|
+
= send_to_client
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// And issue the get!
|
|
218
|
+
cbus.get(key, send_to_client)
|
|
202
219
|
}
|
|
203
220
|
|
|
204
221
|
else if (req.method === 'PUT') {
|
|
205
|
-
// Make a temporary client bus
|
|
206
|
-
var cbus = bus.bus_for_http_client(req, res)
|
|
207
|
-
|
|
208
222
|
// Maybe the new state is expressed in patches
|
|
209
223
|
if (req.headers.patches) {
|
|
210
224
|
console.error("We don't actually handle PUT patches yet")
|
|
@@ -231,9 +245,31 @@ function import_server (bus, options)
|
|
|
231
245
|
res.end()
|
|
232
246
|
return
|
|
233
247
|
}
|
|
234
|
-
|
|
248
|
+
|
|
249
|
+
// Make a temporary client bus
|
|
250
|
+
var cbus = bus.bus_for_http_client(req, res)
|
|
251
|
+
var t = {
|
|
252
|
+
version: req.version || cbus.new_version(),
|
|
253
|
+
parents: req.parents
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// If the peer declares itself, let's remember it so that
|
|
257
|
+
// we don't send it echoes
|
|
258
|
+
if (req.headers.peer) {
|
|
259
|
+
let peer = req.headers.peer,
|
|
260
|
+
key = obj.key
|
|
261
|
+
var cb = cbus.http_callbacks[
|
|
262
|
+
JSON.stringify({peer, key})
|
|
263
|
+
]
|
|
264
|
+
if (cb) cb.has_seen(cbus, key, t.version)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Now send the version to the bus!
|
|
268
|
+
cbus.set(obj, t)
|
|
269
|
+
|
|
235
270
|
res.statusCode = 200
|
|
236
271
|
res.end()
|
|
272
|
+
cbus.http_unsubscribe(req)
|
|
237
273
|
})
|
|
238
274
|
}
|
|
239
275
|
}
|
|
@@ -243,16 +279,39 @@ function import_server (bus, options)
|
|
|
243
279
|
|
|
244
280
|
bus_for_http_client: function (req, res) {
|
|
245
281
|
var bus = this
|
|
246
|
-
if (!bus.bus_for_http_client.counter)
|
|
282
|
+
if (!bus.bus_for_http_client.counter) {
|
|
247
283
|
bus.bus_for_http_client.counter = 0
|
|
248
|
-
|
|
284
|
+
bus.bus_for_http_client.busses = {}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// If this client has a peer ID, and we've got a bus for that peer,
|
|
288
|
+
// then re-use it!
|
|
289
|
+
if (req.peer && bus.bus_for_http_client.busses[req.peer]) {
|
|
290
|
+
var cbus = bus.bus_for_http_client.busses[req.peer]
|
|
291
|
+
cbus.client_ip = req.connection.remoteAddress
|
|
292
|
+
cbus.num_subscriptions++
|
|
293
|
+
return cbus
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Otherwise, create a new bus
|
|
297
|
+
var cbus = make_statebus()
|
|
249
298
|
cbus.label = 'client_http' + bus.bus_for_http_client.counter++
|
|
250
299
|
cbus.master = bus
|
|
300
|
+
cbus.num_subscriptions = 1
|
|
301
|
+
cbus.http_unsubscribe = (req) => {
|
|
302
|
+
cbus.num_subscriptions--
|
|
303
|
+
if (cbus.num_subscriptions === 0) {
|
|
304
|
+
// log('Last subscription! Killing client bus!')
|
|
305
|
+
cbus.delete_bus()
|
|
306
|
+
delete bus.bus_for_http_client.busses[req.peer]
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
bus.bus_for_http_client.busses[req.peer] = cbus
|
|
251
310
|
|
|
252
|
-
//
|
|
311
|
+
// And log into it as the client
|
|
253
312
|
cbus.serves_auth({remoteAddress: req.connection.remoteAddress}, bus)
|
|
254
313
|
bus.options.client(cbus)
|
|
255
|
-
cbus.set({key: 'current_user', client: req.
|
|
314
|
+
cbus.set({key: 'current_user', val: {client: req.peer}})
|
|
256
315
|
return cbus
|
|
257
316
|
},
|
|
258
317
|
|
|
@@ -355,7 +414,7 @@ function import_server (bus, options)
|
|
|
355
414
|
// var client_busses = {} // XXX work in progress
|
|
356
415
|
var log = master.log
|
|
357
416
|
if (client_bus_func) {
|
|
358
|
-
master.set({key: 'connections'}) // Clean out old sessions
|
|
417
|
+
master.set({key: 'connections', val: {}}) // Clean out old sessions
|
|
359
418
|
var connections = master.get('connections')
|
|
360
419
|
}
|
|
361
420
|
var s = require('sockjs').createServer({
|
|
@@ -373,11 +432,11 @@ function import_server (bus, options)
|
|
|
373
432
|
// - When disconnecting, decrement the number, and if it gets
|
|
374
433
|
// to zero, delete the client bus.
|
|
375
434
|
|
|
376
|
-
connections[conn.id] = {client: conn.id, // client is deprecated
|
|
377
|
-
|
|
435
|
+
connections.val[conn.id] = {client: conn.id, // client is deprecated
|
|
436
|
+
id: conn.id}
|
|
378
437
|
master.set(connections)
|
|
379
438
|
|
|
380
|
-
var client =
|
|
439
|
+
var client = make_statebus()
|
|
381
440
|
client.label = 'client' + client_num++
|
|
382
441
|
master.label = master.label || 'master'
|
|
383
442
|
client.master = master
|
|
@@ -486,7 +545,7 @@ function import_server (bus, options)
|
|
|
486
545
|
for (var key in our_gets_in)
|
|
487
546
|
client.forget(key, sockjs_pubber)
|
|
488
547
|
if (client_bus_func) {
|
|
489
|
-
delete connections[conn.id]; master.set(connections)
|
|
548
|
+
delete connections.val[conn.id]; master.set(connections)
|
|
490
549
|
master.delete('connection/' + conn.id)
|
|
491
550
|
client.delete_bus()
|
|
492
551
|
}
|
|
@@ -496,7 +555,7 @@ function import_server (bus, options)
|
|
|
496
555
|
if (client_bus_func && !master.options.__secure) {
|
|
497
556
|
|
|
498
557
|
// A connection
|
|
499
|
-
client('connection/*').
|
|
558
|
+
client('connection/*').getter = function (key, star) {
|
|
500
559
|
var result = bus.clone(master.get(key))
|
|
501
560
|
if (master.options.connections.include_users && result.user)
|
|
502
561
|
result.user = client.get(result.user.key)
|
|
@@ -504,7 +563,7 @@ function import_server (bus, options)
|
|
|
504
563
|
result.client = star // Deprecated. Delete this line in v7.
|
|
505
564
|
return result
|
|
506
565
|
}
|
|
507
|
-
client('connection/*').
|
|
566
|
+
client('connection/*').setter = function (o, star, t) {
|
|
508
567
|
// Check permissions before editing
|
|
509
568
|
if (star !== conn.id && !master.options.connections.edit_others) {
|
|
510
569
|
t.abort()
|
|
@@ -516,7 +575,7 @@ function import_server (bus, options)
|
|
|
516
575
|
}
|
|
517
576
|
|
|
518
577
|
// Your connection
|
|
519
|
-
client('connection').
|
|
578
|
+
client('connection').getter = function () {
|
|
520
579
|
// subscribe to changes in authentication
|
|
521
580
|
client.get('current_user')
|
|
522
581
|
|
|
@@ -524,22 +583,22 @@ function import_server (bus, options)
|
|
|
524
583
|
delete result.key
|
|
525
584
|
return result
|
|
526
585
|
}
|
|
527
|
-
client('connection').
|
|
586
|
+
client('connection').setter = function (o) {
|
|
528
587
|
o = client.clone(o)
|
|
529
588
|
o.key = 'connection/' + conn.id
|
|
530
589
|
client.set(o)
|
|
531
590
|
}
|
|
532
591
|
|
|
533
592
|
// All connections
|
|
534
|
-
client('connections').
|
|
535
|
-
client('connections').
|
|
593
|
+
client('connections').setter = function noop (t) {t.abort()}
|
|
594
|
+
client('connections').getter = function () {
|
|
536
595
|
var result = []
|
|
537
|
-
var conns = master.get('connections')
|
|
596
|
+
var conns = master.get('connections').val
|
|
538
597
|
for (var connid in conns)
|
|
539
598
|
if (connid !== 'key')
|
|
540
599
|
result.push(client.get('connection/' + connid))
|
|
541
600
|
|
|
542
|
-
return {
|
|
601
|
+
return {val: result}
|
|
543
602
|
}
|
|
544
603
|
}
|
|
545
604
|
})
|
|
@@ -555,13 +614,6 @@ function import_server (bus, options)
|
|
|
555
614
|
WebSocket = require('websocket').w3cwebsocket
|
|
556
615
|
return new WebSocket(url + '/' + bus.options.websocket_path + '/websocket')
|
|
557
616
|
},
|
|
558
|
-
client_creds: function client_creds (server_url) {
|
|
559
|
-
// Right now the server just creates a different random id each time
|
|
560
|
-
// it connects.
|
|
561
|
-
return {clientid: (Math.random().toString(36).substring(2)
|
|
562
|
-
+ Math.random().toString(36).substring(2)
|
|
563
|
-
+ Math.random().toString(36).substring(2))}
|
|
564
|
-
},
|
|
565
617
|
|
|
566
618
|
file_store: (function () {
|
|
567
619
|
// Make a database
|
|
@@ -643,7 +695,7 @@ function import_server (bus, options)
|
|
|
643
695
|
}
|
|
644
696
|
on_set.priority = true
|
|
645
697
|
bus(prefix).on_set = on_set
|
|
646
|
-
bus(prefix).
|
|
698
|
+
bus(prefix).deleter = function (key) {
|
|
647
699
|
delete db[key]
|
|
648
700
|
if (active) save_later()
|
|
649
701
|
}
|
|
@@ -709,7 +761,7 @@ function import_server (bus, options)
|
|
|
709
761
|
return decodeURIComponent(k.replace('%2E', '.'))
|
|
710
762
|
}
|
|
711
763
|
|
|
712
|
-
bus(prefix).
|
|
764
|
+
bus(prefix).getter = function (key, t) {
|
|
713
765
|
firebase_ref.child(encode_firebase_key(key)).on('value', function (x) {
|
|
714
766
|
t.done(x.val() || {})
|
|
715
767
|
}, function (err) { t.abort() })
|
|
@@ -719,19 +771,19 @@ function import_server (bus, options)
|
|
|
719
771
|
firebase_ref.child(encode_firebase_key(o.key)).set(o)
|
|
720
772
|
}
|
|
721
773
|
|
|
722
|
-
// bus(prefix).
|
|
774
|
+
// bus(prefix).setter = function (o, t) {
|
|
723
775
|
// firebase_ref.child(encode_firebase_key(o.key)).set(o, (err) => {
|
|
724
776
|
// err ? t.abort() : t.done()
|
|
725
777
|
// })
|
|
726
778
|
// }
|
|
727
779
|
|
|
728
|
-
bus(prefix).
|
|
780
|
+
bus(prefix).deleter = function (key, t) {
|
|
729
781
|
firebase_ref.child(encode_firebase_key(key)).set(null, (err) => {
|
|
730
782
|
err ? t.abort() : t.done()
|
|
731
783
|
})
|
|
732
784
|
}
|
|
733
785
|
|
|
734
|
-
bus(prefix).
|
|
786
|
+
bus(prefix).forgetter = function (key, t) {
|
|
735
787
|
firebase_ref.child(encode_firebase_key(key)).off()
|
|
736
788
|
}
|
|
737
789
|
},
|
|
@@ -823,7 +875,7 @@ function import_server (bus, options)
|
|
|
823
875
|
: o))
|
|
824
876
|
|
|
825
877
|
// Publish this to the bus
|
|
826
|
-
bus.set.fire(obj, {
|
|
878
|
+
bus.set.fire(obj, {getter: true})
|
|
827
879
|
})
|
|
828
880
|
}
|
|
829
881
|
|
|
@@ -852,13 +904,13 @@ function import_server (bus, options)
|
|
|
852
904
|
if (opts.set_sync) {
|
|
853
905
|
var old_route = bus.route
|
|
854
906
|
bus.route = function (key, method, arg, t) {
|
|
855
|
-
if (method === '
|
|
907
|
+
if (method === 'setter') on_set(arg)
|
|
856
908
|
return old_route(key, method, arg, t)
|
|
857
909
|
}
|
|
858
910
|
} else
|
|
859
911
|
bus(prefix).on_set_sync = on_set
|
|
860
912
|
|
|
861
|
-
bus(prefix).
|
|
913
|
+
bus(prefix).deleter = function (key) {
|
|
862
914
|
if (opts.use_transactions && !open_transaction){
|
|
863
915
|
console.time('save db')
|
|
864
916
|
db.prepare('BEGIN TRANSACTION').run()
|
|
@@ -956,7 +1008,7 @@ function import_server (bus, options)
|
|
|
956
1008
|
}
|
|
957
1009
|
pg_set.priority = true
|
|
958
1010
|
bus(opts.prefix).on_set = pg_set
|
|
959
|
-
bus(opts.prefix).
|
|
1011
|
+
bus(opts.prefix).deleter = function (key) {
|
|
960
1012
|
db.query('delete from store where key = $1', [key])
|
|
961
1013
|
}
|
|
962
1014
|
|
|
@@ -1016,7 +1068,7 @@ function import_server (bus, options)
|
|
|
1016
1068
|
bus.usage_log_nots = nots
|
|
1017
1069
|
|
|
1018
1070
|
// Aggregate all accesses by day, to get daily active users
|
|
1019
|
-
bus('usage').
|
|
1071
|
+
bus('usage').getter = () => {
|
|
1020
1072
|
bus.get('time/' + refresh_interval)
|
|
1021
1073
|
var days = []
|
|
1022
1074
|
var last_day
|
|
@@ -1055,7 +1107,7 @@ function import_server (bus, options)
|
|
|
1055
1107
|
return {_: days}
|
|
1056
1108
|
}
|
|
1057
1109
|
|
|
1058
|
-
bus('recent_hits/*').
|
|
1110
|
+
bus('recent_hits/*').getter = (rest) => {
|
|
1059
1111
|
bus.get('time/' + refresh_interval)
|
|
1060
1112
|
var result = []
|
|
1061
1113
|
for (var row of db.prepare('select * from usage where '
|
|
@@ -1071,7 +1123,7 @@ function import_server (bus, options)
|
|
|
1071
1123
|
return {_: result}
|
|
1072
1124
|
}
|
|
1073
1125
|
|
|
1074
|
-
bus('recent_referers/*').
|
|
1126
|
+
bus('recent_referers/*').getter = (rest) => {
|
|
1075
1127
|
bus.get('time/' + refresh_interval)
|
|
1076
1128
|
var result = []
|
|
1077
1129
|
for (var row of db.prepare('select * from usage where '
|
|
@@ -1120,7 +1172,7 @@ function import_server (bus, options)
|
|
|
1120
1172
|
return times
|
|
1121
1173
|
}
|
|
1122
1174
|
|
|
1123
|
-
bus('socket_load_times').
|
|
1175
|
+
bus('socket_load_times').getter = () => {
|
|
1124
1176
|
return {_: sock_open_times()}
|
|
1125
1177
|
}
|
|
1126
1178
|
},
|
|
@@ -1132,21 +1184,21 @@ function import_server (bus, options)
|
|
|
1132
1184
|
},
|
|
1133
1185
|
|
|
1134
1186
|
time () {
|
|
1135
|
-
if (bus('time*').
|
|
1187
|
+
if (bus('time*').getter.length > 0)
|
|
1136
1188
|
// Then it's already installed
|
|
1137
1189
|
return
|
|
1138
1190
|
|
|
1139
1191
|
// Time ticker
|
|
1140
1192
|
var timeouts = {}
|
|
1141
1193
|
bus('time*', {
|
|
1142
|
-
|
|
1194
|
+
getter: (key, rest, t) => {
|
|
1143
1195
|
if (key === 'time') rest = '/1000'
|
|
1144
1196
|
timeout = parseInt(rest.substr(1))
|
|
1145
1197
|
function f () { bus.set.fire({key: key, val: Date.now()}) }
|
|
1146
1198
|
timeouts[key] = setInterval(f, timeout)
|
|
1147
1199
|
f()
|
|
1148
1200
|
},
|
|
1149
|
-
|
|
1201
|
+
forgetter: (key, rest) => {
|
|
1150
1202
|
if (key === 'time') key = 'time/1000'
|
|
1151
1203
|
clearTimeout(timeouts[key])
|
|
1152
1204
|
}
|
|
@@ -1217,8 +1269,8 @@ function import_server (bus, options)
|
|
|
1217
1269
|
// - Is there security hole if users have a ? or a / in their name?
|
|
1218
1270
|
// - Make the master.posts/ state not require /
|
|
1219
1271
|
// - Make standard url tools for optional slashes, and ? query params
|
|
1220
|
-
// - Should a e.g. client
|
|
1221
|
-
// - e.g. if master('posts*').
|
|
1272
|
+
// - Should a e.g. client setter abort if it calls set that aborts?
|
|
1273
|
+
// - e.g. if master('posts*').setter aborts
|
|
1222
1274
|
|
|
1223
1275
|
// Helpers
|
|
1224
1276
|
function get_posts (args) {
|
|
@@ -1273,15 +1325,15 @@ function import_server (bus, options)
|
|
|
1273
1325
|
// }
|
|
1274
1326
|
|
|
1275
1327
|
// Define state on master
|
|
1276
|
-
if (master('posts_for/*').
|
|
1328
|
+
if (master('posts_for/*').getter.length === 0) {
|
|
1277
1329
|
// Get posts for each user
|
|
1278
|
-
master('posts_for/*').
|
|
1330
|
+
master('posts_for/*').getter = (json) => {
|
|
1279
1331
|
watch_for_dirt('posts-for/' + json.for)
|
|
1280
1332
|
return {_: get_posts(json)}
|
|
1281
1333
|
}
|
|
1282
1334
|
// Saving any post will dirty the list for all users mentioned in
|
|
1283
1335
|
// the post
|
|
1284
|
-
master('post/*').
|
|
1336
|
+
master('post/*').setter = (old, New, t) => {
|
|
1285
1337
|
// To do: diff the cc, to, and from lists, and only dirty
|
|
1286
1338
|
// posts_for people who have been changed
|
|
1287
1339
|
|
|
@@ -1307,40 +1359,40 @@ function import_server (bus, options)
|
|
|
1307
1359
|
}
|
|
1308
1360
|
|
|
1309
1361
|
function user_addy (u) {
|
|
1362
|
+
console.error('fix this! not ported to v7 links')
|
|
1310
1363
|
return u.name + '@' + opts.domain
|
|
1311
1364
|
}
|
|
1312
1365
|
function current_addy () {
|
|
1313
1366
|
var c = client.get('current_user')
|
|
1314
|
-
|
|
1367
|
+
console.error('fix this! not ported to v7 links')
|
|
1368
|
+
return c.val.logged_in ? user_addy(c.val.user) : 'public'
|
|
1315
1369
|
}
|
|
1316
1370
|
function is_author (post) {
|
|
1317
1371
|
var from = post._.from
|
|
1318
|
-
var c = client.get('current_user')
|
|
1319
1372
|
return from.includes(current_addy())
|
|
1320
1373
|
}
|
|
1321
1374
|
function can_see (post) {
|
|
1322
|
-
|
|
1375
|
+
console.error('fix this! not ported to v7')
|
|
1323
1376
|
var allowed = post._.to.concat(post._.cc).concat(post._.from)
|
|
1324
1377
|
return allowed.includes(current_addy()) || allowed.includes('public')
|
|
1325
1378
|
}
|
|
1326
|
-
var drtbus = master//
|
|
1379
|
+
var drtbus = master//make_statebus()
|
|
1327
1380
|
function dirty (key) { drtbus.set({key: 'dirty-'+key, n: Math.random()}) }
|
|
1328
1381
|
function watch_for_dirt (key) { drtbus.get('dirty-'+key) }
|
|
1329
1382
|
|
|
1330
|
-
client('current_email').
|
|
1383
|
+
client('current_email').getter = () => {
|
|
1331
1384
|
return {_: current_addy()}
|
|
1332
1385
|
}
|
|
1333
1386
|
|
|
1334
1387
|
// Define state on client
|
|
1335
|
-
client('posts*').
|
|
1388
|
+
client('posts*').getter = (k, rest) => {
|
|
1336
1389
|
var args = bus.parse(rest.substr(1))
|
|
1337
|
-
var c = client.get('current_user')
|
|
1338
1390
|
args.for = current_addy()
|
|
1339
1391
|
var e = master.get('posts_for/' + JSON.stringify(args))
|
|
1340
1392
|
return {_: master.clone(e._).map(x=>client.get(x.key))}
|
|
1341
1393
|
}
|
|
1342
1394
|
|
|
1343
|
-
client('post/*').
|
|
1395
|
+
client('post/*').getter = (k) => {
|
|
1344
1396
|
var e = master.clone(master.get(k))
|
|
1345
1397
|
if (!e._) return {}
|
|
1346
1398
|
if (!can_see(e))
|
|
@@ -1352,7 +1404,7 @@ function import_server (bus, options)
|
|
|
1352
1404
|
return e
|
|
1353
1405
|
}
|
|
1354
1406
|
|
|
1355
|
-
client('post/*').
|
|
1407
|
+
client('post/*').setter = (o, t) => {
|
|
1356
1408
|
if (!(client.validate(o, {key: 'string',
|
|
1357
1409
|
_: {to: 'array',
|
|
1358
1410
|
cc: 'array',
|
|
@@ -1387,7 +1439,7 @@ function import_server (bus, options)
|
|
|
1387
1439
|
t.done()
|
|
1388
1440
|
}
|
|
1389
1441
|
|
|
1390
|
-
client('post/*').
|
|
1442
|
+
client('post/*').deleter = (key, o, t) => {
|
|
1391
1443
|
// Validate
|
|
1392
1444
|
if (!is_author(o)) {
|
|
1393
1445
|
console.error('User', current_addy(),
|
|
@@ -1411,14 +1463,14 @@ function import_server (bus, options)
|
|
|
1411
1463
|
t.done()
|
|
1412
1464
|
}
|
|
1413
1465
|
|
|
1414
|
-
client('friends').
|
|
1415
|
-
return {_: (master.get('users').
|
|
1466
|
+
client('friends').getter = t => {
|
|
1467
|
+
return {_: (master.get('users').val||[])
|
|
1416
1468
|
.map(u=>client.get('email/' + user_addy(u)))
|
|
1417
1469
|
.concat([client.get('email/public')])
|
|
1418
1470
|
}
|
|
1419
1471
|
}
|
|
1420
1472
|
|
|
1421
|
-
client('email/*').
|
|
1473
|
+
client('email/*').getter = (rest) => {
|
|
1422
1474
|
var m = rest.match(email_regex)
|
|
1423
1475
|
var result = {address: rest}
|
|
1424
1476
|
if (rest === 'public') {
|
|
@@ -1441,7 +1493,7 @@ function import_server (bus, options)
|
|
|
1441
1493
|
|
|
1442
1494
|
sqlite_query_server: function sqlite_query_server (db) {
|
|
1443
1495
|
var get = bus.get
|
|
1444
|
-
bus('table_columns/*').
|
|
1496
|
+
bus('table_columns/*').getter =
|
|
1445
1497
|
function get_table_columns (key, rest) {
|
|
1446
1498
|
if (typeof key !== 'string')
|
|
1447
1499
|
console.log(handlers.hash)
|
|
@@ -1464,7 +1516,7 @@ function import_server (bus, options)
|
|
|
1464
1516
|
}
|
|
1465
1517
|
|
|
1466
1518
|
|
|
1467
|
-
bus('table_foreign_keys/*').
|
|
1519
|
+
bus('table_foreign_keys/*').getter =
|
|
1468
1520
|
function table_foreign_keys (key, rest) {
|
|
1469
1521
|
var table_name = rest
|
|
1470
1522
|
var foreign_keys = get('sql/PRAGMA foreign_key_list(' + table_name + ')').rows
|
|
@@ -1476,7 +1528,7 @@ function import_server (bus, options)
|
|
|
1476
1528
|
return result
|
|
1477
1529
|
}
|
|
1478
1530
|
|
|
1479
|
-
bus('sql/*').
|
|
1531
|
+
bus('sql/*').getter =
|
|
1480
1532
|
function sql (key, rest) {
|
|
1481
1533
|
get('time/60000')
|
|
1482
1534
|
var query = rest
|
|
@@ -1553,7 +1605,7 @@ function import_server (bus, options)
|
|
|
1553
1605
|
// ************************
|
|
1554
1606
|
|
|
1555
1607
|
// Getting the whole table, or a single row
|
|
1556
|
-
bus(table_name + '*').
|
|
1608
|
+
bus(table_name + '*').getter = function (key, rest) {
|
|
1557
1609
|
if (rest === '')
|
|
1558
1610
|
// Return the whole table
|
|
1559
1611
|
return render_table()
|
|
@@ -1573,7 +1625,7 @@ function import_server (bus, options)
|
|
|
1573
1625
|
}
|
|
1574
1626
|
|
|
1575
1627
|
// Saving a row
|
|
1576
|
-
bus(table_name + '/*').
|
|
1628
|
+
bus(table_name + '/*').setter = function (obj, rest) {
|
|
1577
1629
|
var columns = table_columns.columns
|
|
1578
1630
|
var key = remapped_keys.keys[obj.key] || obj.key
|
|
1579
1631
|
|
|
@@ -1599,7 +1651,7 @@ function import_server (bus, options)
|
|
|
1599
1651
|
}
|
|
1600
1652
|
|
|
1601
1653
|
// Inserting a new row
|
|
1602
|
-
bus('new/' + table_name + '/*').
|
|
1654
|
+
bus('new/' + table_name + '/*').setter = function (obj) {
|
|
1603
1655
|
var columns = table_columns.columns
|
|
1604
1656
|
var stmt = ('insert into ' + table_name + ' (' + columns.join(',')
|
|
1605
1657
|
+ ') values (' + new Array(columns.length).join('?,') + '?)')
|
|
@@ -1618,7 +1670,7 @@ function import_server (bus, options)
|
|
|
1618
1670
|
}
|
|
1619
1671
|
|
|
1620
1672
|
// Deleting a row
|
|
1621
|
-
bus(table_name + '/*').
|
|
1673
|
+
bus(table_name + '/*').deleter = function (key, rest) {
|
|
1622
1674
|
if (remapped_keys.keys[key]) {
|
|
1623
1675
|
var old_key = key
|
|
1624
1676
|
var new_key = remapped_keys.keys[key]
|
|
@@ -1640,72 +1692,84 @@ function import_server (bus, options)
|
|
|
1640
1692
|
|
|
1641
1693
|
serves_auth: function serves_auth (conn, master) {
|
|
1642
1694
|
var client = this // to keep me straight while programming
|
|
1643
|
-
|
|
1644
|
-
function logout (key) {
|
|
1695
|
+
function logout (user_key) {
|
|
1645
1696
|
var clients = master.get('logged_in_clients')
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1697
|
+
clients.val = clients.val || {}
|
|
1698
|
+
|
|
1699
|
+
for (var k in clients.val) {
|
|
1700
|
+
// We used to encounter stray deleted accounts that hadn't
|
|
1701
|
+
// gotten properly garbage collected, and I added some code
|
|
1702
|
+
// here to remove them.
|
|
1703
|
+
//
|
|
1704
|
+
// However, it was a bit kludgy, and I'm rewriting the code
|
|
1705
|
+
// now, and not sure if this problem still occurs. Will watch
|
|
1706
|
+
// and see. Meanwhile, I'm commenting the code out.
|
|
1707
|
+
//
|
|
1708
|
+
// if (Object.keys(clients.val[k]).length === 0) {
|
|
1709
|
+
// client.log('Found a deleted user. Removing.', k, clients.val[k])
|
|
1710
|
+
// delete clients.val[k]
|
|
1711
|
+
// master.set(clients)
|
|
1712
|
+
// }
|
|
1713
|
+
if (clients.val[k].link === user_key) {
|
|
1714
|
+
client.log('Logging out!', k, clients.val[k])
|
|
1715
|
+
delete clients.val[k]
|
|
1716
|
+
master.set(clients)
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1657
1719
|
}
|
|
1658
1720
|
|
|
1659
1721
|
// Initialize master
|
|
1660
1722
|
if (!master.auth_initialized) {
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1723
|
+
|
|
1724
|
+
// A hash for fast lookup of a user's password
|
|
1725
|
+
master('users/passwords').getter = function (k) {
|
|
1726
|
+
// We compute it from the 'users' state
|
|
1727
|
+
master.log('users/passwords.getter: Computing!')
|
|
1728
|
+
var result = {key: 'users/passwords', val: {}}
|
|
1664
1729
|
var users = master.get('users')
|
|
1665
|
-
users.
|
|
1666
|
-
for (var i=0; i<users.
|
|
1667
|
-
var u = master.get(users.
|
|
1668
|
-
if (!(u.login || u.name)) {
|
|
1669
|
-
console.error('upass: this user has bogus name/login', u.key, u.name, u.login)
|
|
1730
|
+
users.val = users.val || []
|
|
1731
|
+
for (var i=0; i<users.val.length; i++) {
|
|
1732
|
+
var u = master.get(users.val[i].link)
|
|
1733
|
+
if (!(u.val.login || u.val.name)) {
|
|
1734
|
+
console.error('upass: this user has bogus name/login', u.key, u.val.name, u.val.login)
|
|
1670
1735
|
continue
|
|
1671
1736
|
}
|
|
1672
|
-
var login = (u.login || u.name).toLowerCase()
|
|
1737
|
+
var login = (u.val.login || u.val.name).toLowerCase()
|
|
1673
1738
|
console.assert(login, 'Missing login for user', u)
|
|
1674
|
-
if (result.hasOwnProperty(login)) {
|
|
1739
|
+
if (result.val.hasOwnProperty(login)) {
|
|
1675
1740
|
console.error("upass: this user's name is bogus, dude.", u.key)
|
|
1676
1741
|
continue
|
|
1677
1742
|
}
|
|
1678
|
-
result[login] = {user: u.key, pass: u.pass}
|
|
1743
|
+
result.val[login] = {user: u.key, pass: u.val.pass}
|
|
1679
1744
|
}
|
|
1680
1745
|
return result
|
|
1681
1746
|
}
|
|
1682
1747
|
master.auth_initialized = true
|
|
1683
1748
|
master.get('users/passwords')
|
|
1684
1749
|
|
|
1685
|
-
master('user/*').
|
|
1750
|
+
master('user/*').deleter = (key, t) => {
|
|
1686
1751
|
master.log('Deleteinggg!!!', key)
|
|
1687
1752
|
// Remove from users.all
|
|
1688
1753
|
var users = master.get('users')
|
|
1689
|
-
users.
|
|
1754
|
+
users.val = users.val.filter(u => u.link && u.link !== key)
|
|
1690
1755
|
master.set(users)
|
|
1691
1756
|
|
|
1692
1757
|
// Log out
|
|
1693
1758
|
master.log('Logging out', key)
|
|
1694
1759
|
logout(key)
|
|
1695
1760
|
|
|
1696
|
-
// Remove connection
|
|
1697
|
-
master.log('Removing connections for', key)
|
|
1698
|
-
var conns = master.get('connections')
|
|
1699
|
-
for (var k in conns) {
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
}
|
|
1761
|
+
// // Remove connection
|
|
1762
|
+
// master.log('Removing connections for', key)
|
|
1763
|
+
// var conns = master.get('connections')
|
|
1764
|
+
// for (var k in conns.val) {
|
|
1765
|
+
// console.log('Trying key', k)
|
|
1766
|
+
// if (conns.val[k].user && !conns.val[k].user.key) {
|
|
1767
|
+
// console.log('Cleaning keyless user', conss.val[k].user)
|
|
1768
|
+
// delete conns.val[k].user
|
|
1769
|
+
// master.set(conns)
|
|
1770
|
+
// continue
|
|
1771
|
+
// }
|
|
1772
|
+
// }
|
|
1709
1773
|
|
|
1710
1774
|
master.log('Dirtying users/passwords for', key)
|
|
1711
1775
|
master.dirty('users/passwords')
|
|
@@ -1717,9 +1781,9 @@ function import_server (bus, options)
|
|
|
1717
1781
|
|
|
1718
1782
|
// Authentication functions
|
|
1719
1783
|
function authenticate (login, pass) {
|
|
1720
|
-
var userpass = master.get('users/passwords')[login.toLowerCase()]
|
|
1784
|
+
var userpass = master.get('users/passwords').val[login.toLowerCase()]
|
|
1721
1785
|
master.log('authenticate: we see', {
|
|
1722
|
-
passwords: master.get('users/passwords'),
|
|
1786
|
+
passwords: master.get('users/passwords').val,
|
|
1723
1787
|
hash_to_match: userpass && userpass.pass,
|
|
1724
1788
|
password_guess: pass
|
|
1725
1789
|
})
|
|
@@ -1730,7 +1794,7 @@ function import_server (bus, options)
|
|
|
1730
1794
|
|
|
1731
1795
|
// console.log('comparing passwords', pass, userpass.pass)
|
|
1732
1796
|
if (require('bcrypt-nodejs').compareSync(pass, userpass.pass))
|
|
1733
|
-
return
|
|
1797
|
+
return userpass.user
|
|
1734
1798
|
}
|
|
1735
1799
|
function create_account (params) {
|
|
1736
1800
|
if (typeof (params.login || params.name) !== 'string')
|
|
@@ -1743,7 +1807,7 @@ function import_server (bus, options)
|
|
|
1743
1807
|
throw 'invalid name, login, pass, or email'
|
|
1744
1808
|
|
|
1745
1809
|
var passes = master.get('users/passwords')
|
|
1746
|
-
if (passes.hasOwnProperty(login))
|
|
1810
|
+
if (passes.val.hasOwnProperty(login))
|
|
1747
1811
|
throw 'there is already a user with that login or name'
|
|
1748
1812
|
|
|
1749
1813
|
// Hash password
|
|
@@ -1763,60 +1827,81 @@ function import_server (bus, options)
|
|
|
1763
1827
|
key = 'user/' + Math.random().toString(36).substring(7,13)
|
|
1764
1828
|
|
|
1765
1829
|
// Make account object
|
|
1766
|
-
var new_account = {
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1830
|
+
var new_account = {
|
|
1831
|
+
key: key, val: {
|
|
1832
|
+
name: params.name,
|
|
1833
|
+
login: params.login,
|
|
1834
|
+
pass: params.pass,
|
|
1835
|
+
email: params.email
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
for (var k in new_account.val) // Clean out falsy fields
|
|
1839
|
+
if (!new_account.val[k])
|
|
1840
|
+
delete new_account.val[k]
|
|
1772
1841
|
master.set(new_account)
|
|
1773
1842
|
|
|
1774
1843
|
var users = master.get('users')
|
|
1775
|
-
users.
|
|
1776
|
-
users.
|
|
1777
|
-
passes[login] = {user: new_account.key, pass: new_account.pass}
|
|
1844
|
+
users.val = users.val || []
|
|
1845
|
+
users.val.push({link: new_account.key})
|
|
1846
|
+
passes.val[login] = {user: new_account.key, pass: new_account.val.pass}
|
|
1778
1847
|
master.set(users)
|
|
1779
1848
|
master.set(passes)
|
|
1780
1849
|
}
|
|
1781
1850
|
|
|
1782
1851
|
// Current User
|
|
1783
|
-
client('current_user').
|
|
1784
|
-
client
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
u =
|
|
1788
|
-
|
|
1852
|
+
client('current_user').getter = function (k) {
|
|
1853
|
+
if (!conn.client)
|
|
1854
|
+
return {val: {error: 'no client'}}
|
|
1855
|
+
|
|
1856
|
+
var u = (master.get('logged_in_clients').val || {})[conn.client]
|
|
1857
|
+
var result = {val: {user: u || null, logged_in: !!u}}
|
|
1858
|
+
return result
|
|
1789
1859
|
}
|
|
1790
1860
|
|
|
1791
|
-
client('current_user').
|
|
1861
|
+
client('current_user').setter = function (o, t) {
|
|
1862
|
+
var val = o.val
|
|
1863
|
+
if (typeof val !== 'object') {
|
|
1864
|
+
console.error('current_user: set to something not an object:', val)
|
|
1865
|
+
t.abort()
|
|
1866
|
+
return
|
|
1867
|
+
}
|
|
1792
1868
|
function error (msg) {
|
|
1869
|
+
console.error(msg)
|
|
1793
1870
|
client.set.abort(o)
|
|
1794
1871
|
var c = client.get('current_user')
|
|
1795
|
-
c.error = msg
|
|
1872
|
+
c.val.error = msg
|
|
1796
1873
|
client.set.fire(c)
|
|
1797
1874
|
}
|
|
1798
1875
|
|
|
1799
1876
|
client.log('* saving: current_user!')
|
|
1800
|
-
if (
|
|
1877
|
+
if (val.client && !conn.client) {
|
|
1801
1878
|
// Set the client
|
|
1802
|
-
|
|
1803
|
-
|
|
1879
|
+
//
|
|
1880
|
+
// Note: Should this code be moved upstream, when creating a
|
|
1881
|
+
// client bus?
|
|
1882
|
+
conn.client = val.client
|
|
1883
|
+
client.client_id = val.client
|
|
1804
1884
|
client.client_ip = conn.remoteAddress
|
|
1805
1885
|
|
|
1806
|
-
if (conn.id) {
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1886
|
+
// if (conn.id) {
|
|
1887
|
+
// var connections = master.get('connections')
|
|
1888
|
+
// var logged_in_user =
|
|
1889
|
+
// (master.get('logged_in_clients').val || {})[conn.client]
|
|
1890
|
+
|
|
1891
|
+
// if (logged_in_user) {
|
|
1892
|
+
// connections.val[conn.id].user = {link: user.link}
|
|
1893
|
+
// master.set(connections)
|
|
1894
|
+
// }
|
|
1895
|
+
// }
|
|
1811
1896
|
}
|
|
1812
1897
|
else {
|
|
1813
|
-
if (
|
|
1898
|
+
if (val.create_account) {
|
|
1814
1899
|
client.log('current_user: creating account')
|
|
1815
1900
|
try {
|
|
1816
|
-
create_account(
|
|
1901
|
+
create_account(val.create_account)
|
|
1817
1902
|
client.log('Success creating account!')
|
|
1818
1903
|
var cu = client.get('current_user')
|
|
1819
|
-
cu.create_account = null
|
|
1904
|
+
cu.val.create_account = null
|
|
1820
1905
|
client.set.fire(cu)
|
|
1821
1906
|
} catch (e) {
|
|
1822
1907
|
error('Cannot create that account because ' + e)
|
|
@@ -1824,29 +1909,29 @@ function import_server (bus, options)
|
|
|
1824
1909
|
}
|
|
1825
1910
|
}
|
|
1826
1911
|
|
|
1827
|
-
if (
|
|
1912
|
+
if (val.login_as) {
|
|
1828
1913
|
// Then client is trying to log in
|
|
1829
|
-
|
|
1830
|
-
var creds = o.login_as
|
|
1914
|
+
var creds = val.login_as
|
|
1831
1915
|
var login = creds.login || creds.name
|
|
1832
1916
|
if (login && creds.pass) {
|
|
1833
1917
|
// With a username and password
|
|
1834
|
-
var
|
|
1918
|
+
var user_key = authenticate(login, creds.pass)
|
|
1835
1919
|
|
|
1836
|
-
client.log('auth said:',
|
|
1837
|
-
if (
|
|
1920
|
+
client.log('auth said:', user_key)
|
|
1921
|
+
if (user_key) {
|
|
1838
1922
|
// Success!
|
|
1839
1923
|
// Associate this user with this session
|
|
1840
1924
|
// user.log('Logging the user in!', u)
|
|
1841
1925
|
|
|
1842
1926
|
var clients = master.get('logged_in_clients')
|
|
1843
|
-
var connections = master.get('connections')
|
|
1927
|
+
// var connections = master.get('connections')
|
|
1844
1928
|
|
|
1845
|
-
clients
|
|
1846
|
-
|
|
1929
|
+
clients.val = clients.val || {}
|
|
1930
|
+
clients.val[conn.client] = {link: user_key}
|
|
1931
|
+
// connections.val[conn.id].user = {link: user_key}
|
|
1847
1932
|
|
|
1848
1933
|
master.set(clients)
|
|
1849
|
-
master.set(connections)
|
|
1934
|
+
// master.set(connections)
|
|
1850
1935
|
|
|
1851
1936
|
client.log('current_user: success logging in!')
|
|
1852
1937
|
}
|
|
@@ -1861,38 +1946,41 @@ function import_server (bus, options)
|
|
|
1861
1946
|
}
|
|
1862
1947
|
}
|
|
1863
1948
|
|
|
1864
|
-
else if (
|
|
1949
|
+
else if (val.logout) {
|
|
1865
1950
|
client.log('current_user: logging out')
|
|
1866
1951
|
var clients = master.get('logged_in_clients')
|
|
1867
|
-
var connections = master.get('connections')
|
|
1952
|
+
// var connections = master.get('connections')
|
|
1868
1953
|
|
|
1869
|
-
|
|
1870
|
-
|
|
1954
|
+
clients.val = clients.val || {}
|
|
1955
|
+
delete clients.val[conn.client]
|
|
1956
|
+
// connections.val[conn.id].user = null
|
|
1871
1957
|
|
|
1872
1958
|
master.set(clients)
|
|
1873
|
-
master.set(connections)
|
|
1959
|
+
// master.set(connections)
|
|
1874
1960
|
}
|
|
1875
1961
|
}
|
|
1876
1962
|
|
|
1877
1963
|
t.reget()
|
|
1878
1964
|
}
|
|
1879
|
-
client('current_user').
|
|
1965
|
+
client('current_user').deleter = function () {}
|
|
1880
1966
|
|
|
1881
1967
|
// Users have closet space at /user/<name>/*
|
|
1882
1968
|
var closet_space_key = /^(user\/[^\/]+)\/.*/
|
|
1883
1969
|
var private_closet_space_key = /^user\/[^\/]+\/private.*/
|
|
1884
1970
|
|
|
1885
1971
|
// User
|
|
1886
|
-
client('user/*').
|
|
1972
|
+
client('user/*').setter = function (o, t) {
|
|
1887
1973
|
var c = client.get('current_user')
|
|
1888
1974
|
var user_key = o.key.match(/^user\/([^\/]+)/)
|
|
1889
1975
|
user_key = user_key && ('user/' + user_key[1])
|
|
1890
1976
|
|
|
1891
|
-
// Only the current user can touch
|
|
1892
|
-
if (!c.logged_in || c.user.
|
|
1893
|
-
client.log('Only the current user can touch
|
|
1894
|
-
|
|
1895
|
-
|
|
1977
|
+
// Only the current user can touch himself.
|
|
1978
|
+
if (!c.val.logged_in || c.val.user.link !== user_key) {
|
|
1979
|
+
client.log('Only the current user can touch himself.', {
|
|
1980
|
+
logged_in: c.val.logged_in,
|
|
1981
|
+
as: c.val.user && c.val.user.link,
|
|
1982
|
+
touching: user_key
|
|
1983
|
+
})
|
|
1896
1984
|
client.set.abort(o)
|
|
1897
1985
|
return
|
|
1898
1986
|
}
|
|
@@ -1908,9 +1996,14 @@ function import_server (bus, options)
|
|
|
1908
1996
|
console.assert(o.key.match(/^user\/[^\/]+$/))
|
|
1909
1997
|
|
|
1910
1998
|
// Validate types
|
|
1911
|
-
if (!client.validate(o, {key: 'string',
|
|
1912
|
-
|
|
1913
|
-
|
|
1999
|
+
if (!client.validate(o, {key: 'string',
|
|
2000
|
+
val: {
|
|
2001
|
+
'?login': 'string',
|
|
2002
|
+
'?name': 'string',
|
|
2003
|
+
'?pass': 'string',
|
|
2004
|
+
'?email': 'string',
|
|
2005
|
+
/*'?pic': 'string',*/
|
|
2006
|
+
'*':'*'}})) {
|
|
1914
2007
|
client.log('This user change fails validation.')
|
|
1915
2008
|
client.set.abort(o)
|
|
1916
2009
|
return
|
|
@@ -1921,7 +2014,7 @@ function import_server (bus, options)
|
|
|
1921
2014
|
// • That resulting login must be unique across all users
|
|
1922
2015
|
|
|
1923
2016
|
// There must be at least a login or a name
|
|
1924
|
-
var login = o.login || o.name
|
|
2017
|
+
var login = o.val.login || o.val.name
|
|
1925
2018
|
if (!login) {
|
|
1926
2019
|
client.log('User must have a login or a name')
|
|
1927
2020
|
client.set.abort(o)
|
|
@@ -1932,26 +2025,26 @@ function import_server (bus, options)
|
|
|
1932
2025
|
var userpass = master.get('users/passwords')
|
|
1933
2026
|
|
|
1934
2027
|
// Validate that the login/name is not changed to something clobberish
|
|
1935
|
-
var old_login = u.login || u.name
|
|
2028
|
+
var old_login = u.val.login || u.val.name
|
|
1936
2029
|
if (old_login.toLowerCase() !== login.toLowerCase()
|
|
1937
2030
|
&& userpass.hasOwnProperty(login)) {
|
|
1938
2031
|
client.log('The login', login, 'is already taken. Aborting.')
|
|
1939
2032
|
client.set.abort(o) // Abort
|
|
1940
2033
|
|
|
1941
2034
|
o = client.get(o.key) // Add error message
|
|
1942
|
-
o.error = 'The login "' + login + '" is already taken'
|
|
2035
|
+
o.val.error = 'The login "' + login + '" is already taken'
|
|
1943
2036
|
client.set.fire(o)
|
|
1944
2037
|
|
|
1945
2038
|
return // And exit
|
|
1946
2039
|
}
|
|
1947
2040
|
|
|
1948
2041
|
// Now we can update login and name
|
|
1949
|
-
u.login = o.login
|
|
1950
|
-
u.name = o.name
|
|
2042
|
+
u.val.login = o.val.login
|
|
2043
|
+
u.val.name = o.val.name
|
|
1951
2044
|
|
|
1952
2045
|
// Hash password
|
|
1953
|
-
o.pass = o.pass && require('bcrypt-nodejs').hashSync(o.pass)
|
|
1954
|
-
u.pass = o.pass || u.pass
|
|
2046
|
+
o.val.pass = o.val.pass && require('bcrypt-nodejs').hashSync(o.val.pass)
|
|
2047
|
+
u.val.pass = o.val.pass || u.val.pass
|
|
1955
2048
|
|
|
1956
2049
|
// // Users can have pictures (remove this soon)
|
|
1957
2050
|
// // Bug: if user changes name, this picture's url doesn't change.
|
|
@@ -1970,24 +2063,24 @@ function import_server (bus, options)
|
|
|
1970
2063
|
|
|
1971
2064
|
// For anything else, go ahead and add it to the user object
|
|
1972
2065
|
var protected = {key:1, name:1, /*pic:1,*/ pass:1}
|
|
1973
|
-
for (var k in o)
|
|
2066
|
+
for (var k in o.val)
|
|
1974
2067
|
if (!protected.hasOwnProperty(k))
|
|
1975
|
-
u[k] = o[k]
|
|
1976
|
-
for (var k in u)
|
|
1977
|
-
if (!protected.hasOwnProperty(k) && !o.hasOwnProperty(k))
|
|
1978
|
-
delete u[k]
|
|
2068
|
+
u.val[k] = o.val[k]
|
|
2069
|
+
for (var k in u.val)
|
|
2070
|
+
if (!protected.hasOwnProperty(k) && !o.val.hasOwnProperty(k))
|
|
2071
|
+
delete u.val[k]
|
|
1979
2072
|
|
|
1980
2073
|
master.set(u)
|
|
1981
2074
|
}
|
|
1982
|
-
client('user/*').
|
|
2075
|
+
client('user/*').getter = function user_getter (k) {
|
|
1983
2076
|
var c = client.get('current_user')
|
|
1984
|
-
client.log('* getting:', k, 'as', c.user)
|
|
2077
|
+
client.log('* getting:', k, 'as', c.val.user)
|
|
1985
2078
|
|
|
1986
2079
|
// Users have closet space at /user/<name>/*
|
|
1987
2080
|
if (k.match(closet_space_key)) {
|
|
1988
2081
|
var obj_user = k.match(closet_space_key)[1]
|
|
1989
2082
|
if (k.match(private_closet_space_key)
|
|
1990
|
-
&& (!c.user || obj_user !== c.user.
|
|
2083
|
+
&& (!c.val.user || obj_user !== c.val.user.link)) {
|
|
1991
2084
|
client.log('hiding private closet data')
|
|
1992
2085
|
return {}
|
|
1993
2086
|
}
|
|
@@ -1996,26 +2089,27 @@ function import_server (bus, options)
|
|
|
1996
2089
|
}
|
|
1997
2090
|
|
|
1998
2091
|
// Otherwise return the actual user
|
|
1999
|
-
return user_obj(k, c.logged_in && c.user.
|
|
2092
|
+
return user_obj(k, c.val.logged_in && c.val.user.link === k)
|
|
2000
2093
|
}
|
|
2001
|
-
client('user/*').
|
|
2094
|
+
client('user/*').deleter = function () {}
|
|
2002
2095
|
function user_obj (k, logged_in) {
|
|
2003
2096
|
var o = master.clone(master.get(k))
|
|
2004
2097
|
if (k.match(/^user\/([^\/]+)\/private\/(.*)$/))
|
|
2005
2098
|
return logged_in ? o : {key: k}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2099
|
+
|
|
2100
|
+
o.val = o.val || {}
|
|
2101
|
+
delete o.val.pass
|
|
2102
|
+
if (!logged_in) {delete o.val.email; delete o.val.login}
|
|
2009
2103
|
return o
|
|
2010
2104
|
}
|
|
2011
2105
|
|
|
2012
2106
|
// Blacklist sensitive stuff on master, in case we have a shadow set up
|
|
2013
2107
|
var blacklist = 'users users/passwords logged_in_clients'.split(' ')
|
|
2014
2108
|
for (var i=0; i<blacklist.length; i++) {
|
|
2015
|
-
client(blacklist[i]).
|
|
2016
|
-
client(blacklist[i]).
|
|
2017
|
-
client(blacklist[i]).
|
|
2018
|
-
client(blacklist[i]).
|
|
2109
|
+
client(blacklist[i]).getter = function () {}
|
|
2110
|
+
client(blacklist[i]).setter = function () {}
|
|
2111
|
+
client(blacklist[i]).deleter = function () {}
|
|
2112
|
+
client(blacklist[i]).forgetter = function () {}
|
|
2019
2113
|
}
|
|
2020
2114
|
},
|
|
2021
2115
|
|
|
@@ -2024,14 +2118,14 @@ function import_server (bus, options)
|
|
|
2024
2118
|
var was_logged_in = undefined
|
|
2025
2119
|
|
|
2026
2120
|
function client_prefix (current_user) {
|
|
2027
|
-
return 'client/' + (current_user.logged_in
|
|
2028
|
-
? current_user.user.
|
|
2121
|
+
return 'client/' + (current_user.val.logged_in
|
|
2122
|
+
? current_user.val.user.link.substr('user/'.length)
|
|
2029
2123
|
: client.client_id) + '/'
|
|
2030
2124
|
}
|
|
2031
2125
|
|
|
2032
|
-
function copy_client_to_user(client,
|
|
2126
|
+
function copy_client_to_user(client, user_key) {
|
|
2033
2127
|
var old_prefix = 'client/' + client.client_id
|
|
2034
|
-
var new_prefix = 'client/' +
|
|
2128
|
+
var new_prefix = 'client/' + user_key.substr('user/'.length)
|
|
2035
2129
|
|
|
2036
2130
|
var keys = client.master.get('persisted_keys/' + client.client_id)
|
|
2037
2131
|
if (!keys.val) return
|
|
@@ -2077,13 +2171,13 @@ function import_server (bus, options)
|
|
|
2077
2171
|
client(_=>{
|
|
2078
2172
|
var c = client.get('current_user')
|
|
2079
2173
|
if (client.loading()) return
|
|
2080
|
-
if (was_logged_in == false && c.logged_in)
|
|
2174
|
+
if (was_logged_in == false && c.val.logged_in)
|
|
2081
2175
|
// User just logged in! Let's copy his stuff over
|
|
2082
|
-
copy_client_to_user(client, c.user)
|
|
2083
|
-
was_logged_in = c.logged_in
|
|
2176
|
+
copy_client_to_user(client, c.val.user.link)
|
|
2177
|
+
was_logged_in = c.val.logged_in
|
|
2084
2178
|
})
|
|
2085
2179
|
|
|
2086
|
-
client(prefix_to_sync).
|
|
2180
|
+
client(prefix_to_sync).getter = function (key) {
|
|
2087
2181
|
var c = client.get('current_user')
|
|
2088
2182
|
if (client.loading()) return
|
|
2089
2183
|
var prefix = client_prefix(c)
|
|
@@ -2100,7 +2194,7 @@ function import_server (bus, options)
|
|
|
2100
2194
|
return obj
|
|
2101
2195
|
}
|
|
2102
2196
|
|
|
2103
|
-
client(prefix_to_sync).
|
|
2197
|
+
client(prefix_to_sync).setter = function (obj) {
|
|
2104
2198
|
if (validate && !validate(obj)) {
|
|
2105
2199
|
console.warn('Validation failed on', obj)
|
|
2106
2200
|
client.set.abort(obj)
|
|
@@ -2128,7 +2222,7 @@ function import_server (bus, options)
|
|
|
2128
2222
|
p_keys && client.master.set(p_keys)
|
|
2129
2223
|
}
|
|
2130
2224
|
|
|
2131
|
-
client(prefix_to_sync).
|
|
2225
|
+
client(prefix_to_sync).deleter = function (k) {
|
|
2132
2226
|
k = client_prefix(client.get('current_user')) + k
|
|
2133
2227
|
client.master.delete(k)
|
|
2134
2228
|
|
|
@@ -2138,7 +2232,7 @@ function import_server (bus, options)
|
|
|
2138
2232
|
}
|
|
2139
2233
|
|
|
2140
2234
|
function client_persisted_keys () {
|
|
2141
|
-
if (client.get('current_user').logged_in) return
|
|
2235
|
+
if (client.get('current_user').val.logged_in) return
|
|
2142
2236
|
var result = client.master.get('persisted_keys/' + client.client_id)
|
|
2143
2237
|
if (result && !result.val) result.val = {}
|
|
2144
2238
|
return result
|
|
@@ -2154,19 +2248,19 @@ function import_server (bus, options)
|
|
|
2154
2248
|
// to the global cache
|
|
2155
2249
|
if (count === 0) {
|
|
2156
2250
|
count++
|
|
2157
|
-
if (method === '
|
|
2251
|
+
if (method === 'getter')
|
|
2158
2252
|
bus.run_handler(function get_from_master (k) {
|
|
2159
2253
|
// console.log('DEFAULT GETting', k)
|
|
2160
2254
|
var r = master_bus.get(k)
|
|
2161
2255
|
// console.log('DEFAULT GETted', r)
|
|
2162
2256
|
bus.set.fire(r, {version: master_bus.versions[r.key]})
|
|
2163
2257
|
}, method, arg)
|
|
2164
|
-
else if (method === '
|
|
2258
|
+
else if (method === 'setter')
|
|
2165
2259
|
bus.run_handler(function set_to_master (o, t) {
|
|
2166
2260
|
// console.log('DEFAULT ROUTE', t)
|
|
2167
2261
|
master_bus.set(bus.clone(o), t)
|
|
2168
2262
|
}, method, arg, {t: t})
|
|
2169
|
-
else if (method == '
|
|
2263
|
+
else if (method == 'deleter')
|
|
2170
2264
|
bus.run_handler(function delete_from_master (k, t) {
|
|
2171
2265
|
master_bus.delete(k)
|
|
2172
2266
|
return 'done'
|
|
@@ -2220,7 +2314,7 @@ function import_server (bus, options)
|
|
|
2220
2314
|
// file or recursive directory structure at fs_path
|
|
2221
2315
|
sync_files: function sync_files (state_path, file_path) {
|
|
2222
2316
|
// To do:
|
|
2223
|
-
// - Hook up a
|
|
2317
|
+
// - Hook up a deleter handler
|
|
2224
2318
|
// - recursively remove directories if all files gone
|
|
2225
2319
|
|
|
2226
2320
|
console.assert(state_path.substr(-1) !== '*'
|
|
@@ -2231,7 +2325,7 @@ function import_server (bus, options)
|
|
|
2231
2325
|
var buffer = {}
|
|
2232
2326
|
var full_file_path = require('path').join(__dirname, file_path)
|
|
2233
2327
|
|
|
2234
|
-
bus(state_path + '*').
|
|
2328
|
+
bus(state_path + '*').getter = (rest) => {
|
|
2235
2329
|
// We DO want to handle:
|
|
2236
2330
|
// - "foo"
|
|
2237
2331
|
// - "foo/*"
|
|
@@ -2257,7 +2351,7 @@ function import_server (bus, options)
|
|
|
2257
2351
|
return {_:f}
|
|
2258
2352
|
}
|
|
2259
2353
|
|
|
2260
|
-
bus(state_path + '/*').
|
|
2354
|
+
bus(state_path + '/*').setter = (o, rest, t) => {
|
|
2261
2355
|
if (rest.length>0 && rest[0] !== '/') return
|
|
2262
2356
|
var f = Buffer.from(o._, 'base64')
|
|
2263
2357
|
require('fs').writeFile(file_path + rest, f,
|
|
@@ -2271,10 +2365,10 @@ function import_server (bus, options)
|
|
|
2271
2365
|
// Installs a GET handler at route that gets state from a getter function
|
|
2272
2366
|
// Note: Makes too many textbusses. Should re-use one.
|
|
2273
2367
|
http_serve: function http_serve (route, getter) {
|
|
2274
|
-
var textbus =
|
|
2368
|
+
var textbus = make_statebus()
|
|
2275
2369
|
textbus.label = 'textbus'
|
|
2276
2370
|
var watched = new Set()
|
|
2277
|
-
textbus('*').
|
|
2371
|
+
textbus('*').getter = (filename, old) => {
|
|
2278
2372
|
return {etag: Math.random() + '',
|
|
2279
2373
|
_: getter(filename)}
|
|
2280
2374
|
}
|
|
@@ -2350,7 +2444,7 @@ function import_server (bus, options)
|
|
|
2350
2444
|
},
|
|
2351
2445
|
|
|
2352
2446
|
serve_wiki: () => {
|
|
2353
|
-
bus('edit/*').
|
|
2447
|
+
bus('edit/*').getter = () => ({_: require('./extras/wiki.coffee').code})
|
|
2354
2448
|
},
|
|
2355
2449
|
|
|
2356
2450
|
unix_socket_repl: function (filename) {
|
|
@@ -2497,7 +2591,7 @@ module.exports.import_module = function (statebus) {
|
|
|
2497
2591
|
|
|
2498
2592
|
statebus.serve = function serve (options) {
|
|
2499
2593
|
var bus = statebus()
|
|
2500
|
-
require('./
|
|
2594
|
+
require('./server-library').run_server(bus, options)
|
|
2501
2595
|
return bus
|
|
2502
2596
|
}
|
|
2503
2597
|
|