statebus 7.0.3 → 7.0.6
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/coffee.js +12 -0
- package/extras/sockjs.js +27 -0
- package/extras/tests.js +110 -106
- package/package.json +6 -4
- package/{servers.js → server-library.js} +338 -247
- package/statebus.js +140 -77
|
@@ -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,33 +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
177
|
// We only return JSON
|
|
186
178
|
res.setHeader('Content-Type', 'application/json')
|
|
187
179
|
|
|
188
|
-
//
|
|
189
|
-
// cbus.honk = 'statelog'
|
|
190
|
-
var key = req.path.substr(1)
|
|
191
|
-
cbus.get(key, cb = (o) => {
|
|
192
|
-
var body = to_http_body(o)
|
|
180
|
+
// console.log('http_in: doing cbus.get(', key, ')')
|
|
193
181
|
|
|
194
|
-
|
|
182
|
+
var send_to_client = (o, t) => {
|
|
183
|
+
// console.trace('http_in: Sending update of', key)
|
|
184
|
+
var body = to_http_body(o)
|
|
195
185
|
|
|
196
186
|
// Note: if body === undefined, we need to send the
|
|
197
187
|
// equivalent of a 404. This is missing in braid spec:
|
|
@@ -200,14 +190,35 @@ function import_server (bus, options)
|
|
|
200
190
|
|
|
201
191
|
// And shut down the connection if there's no subscription
|
|
202
192
|
if (!req.subscribe)
|
|
203
|
-
|
|
204
|
-
}
|
|
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)
|
|
205
219
|
}
|
|
206
220
|
|
|
207
221
|
else if (req.method === 'PUT') {
|
|
208
|
-
// Make a temporary client bus
|
|
209
|
-
var cbus = bus.bus_for_http_client(req, res)
|
|
210
|
-
|
|
211
222
|
// Maybe the new state is expressed in patches
|
|
212
223
|
if (req.headers.patches) {
|
|
213
224
|
console.error("We don't actually handle PUT patches yet")
|
|
@@ -234,9 +245,31 @@ function import_server (bus, options)
|
|
|
234
245
|
res.end()
|
|
235
246
|
return
|
|
236
247
|
}
|
|
237
|
-
|
|
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
|
+
|
|
238
270
|
res.statusCode = 200
|
|
239
271
|
res.end()
|
|
272
|
+
cbus.http_unsubscribe(req)
|
|
240
273
|
})
|
|
241
274
|
}
|
|
242
275
|
}
|
|
@@ -246,16 +279,39 @@ function import_server (bus, options)
|
|
|
246
279
|
|
|
247
280
|
bus_for_http_client: function (req, res) {
|
|
248
281
|
var bus = this
|
|
249
|
-
if (!bus.bus_for_http_client.counter)
|
|
282
|
+
if (!bus.bus_for_http_client.counter) {
|
|
250
283
|
bus.bus_for_http_client.counter = 0
|
|
251
|
-
|
|
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()
|
|
252
298
|
cbus.label = 'client_http' + bus.bus_for_http_client.counter++
|
|
253
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
|
|
254
310
|
|
|
255
|
-
//
|
|
311
|
+
// And log into it as the client
|
|
256
312
|
cbus.serves_auth({remoteAddress: req.connection.remoteAddress}, bus)
|
|
257
313
|
bus.options.client(cbus)
|
|
258
|
-
cbus.set({key: 'current_user', client: req.
|
|
314
|
+
cbus.set({key: 'current_user', val: {client: req.peer}})
|
|
259
315
|
return cbus
|
|
260
316
|
},
|
|
261
317
|
|
|
@@ -358,7 +414,7 @@ function import_server (bus, options)
|
|
|
358
414
|
// var client_busses = {} // XXX work in progress
|
|
359
415
|
var log = master.log
|
|
360
416
|
if (client_bus_func) {
|
|
361
|
-
master.set({key: 'connections'}) // Clean out old sessions
|
|
417
|
+
master.set({key: 'connections', val: {}}) // Clean out old sessions
|
|
362
418
|
var connections = master.get('connections')
|
|
363
419
|
}
|
|
364
420
|
var s = require('sockjs').createServer({
|
|
@@ -376,11 +432,11 @@ function import_server (bus, options)
|
|
|
376
432
|
// - When disconnecting, decrement the number, and if it gets
|
|
377
433
|
// to zero, delete the client bus.
|
|
378
434
|
|
|
379
|
-
connections[conn.id] = {client: conn.id, // client is deprecated
|
|
380
|
-
|
|
435
|
+
connections.val[conn.id] = {client: conn.id, // client is deprecated
|
|
436
|
+
id: conn.id}
|
|
381
437
|
master.set(connections)
|
|
382
438
|
|
|
383
|
-
var client =
|
|
439
|
+
var client = make_statebus()
|
|
384
440
|
client.label = 'client' + client_num++
|
|
385
441
|
master.label = master.label || 'master'
|
|
386
442
|
client.master = master
|
|
@@ -489,7 +545,7 @@ function import_server (bus, options)
|
|
|
489
545
|
for (var key in our_gets_in)
|
|
490
546
|
client.forget(key, sockjs_pubber)
|
|
491
547
|
if (client_bus_func) {
|
|
492
|
-
delete connections[conn.id]; master.set(connections)
|
|
548
|
+
delete connections.val[conn.id]; master.set(connections)
|
|
493
549
|
master.delete('connection/' + conn.id)
|
|
494
550
|
client.delete_bus()
|
|
495
551
|
}
|
|
@@ -499,7 +555,7 @@ function import_server (bus, options)
|
|
|
499
555
|
if (client_bus_func && !master.options.__secure) {
|
|
500
556
|
|
|
501
557
|
// A connection
|
|
502
|
-
client('connection/*').
|
|
558
|
+
client('connection/*').getter = function (key, star) {
|
|
503
559
|
var result = bus.clone(master.get(key))
|
|
504
560
|
if (master.options.connections.include_users && result.user)
|
|
505
561
|
result.user = client.get(result.user.key)
|
|
@@ -507,7 +563,7 @@ function import_server (bus, options)
|
|
|
507
563
|
result.client = star // Deprecated. Delete this line in v7.
|
|
508
564
|
return result
|
|
509
565
|
}
|
|
510
|
-
client('connection/*').
|
|
566
|
+
client('connection/*').setter = function (o, star, t) {
|
|
511
567
|
// Check permissions before editing
|
|
512
568
|
if (star !== conn.id && !master.options.connections.edit_others) {
|
|
513
569
|
t.abort()
|
|
@@ -519,7 +575,7 @@ function import_server (bus, options)
|
|
|
519
575
|
}
|
|
520
576
|
|
|
521
577
|
// Your connection
|
|
522
|
-
client('connection').
|
|
578
|
+
client('connection').getter = function () {
|
|
523
579
|
// subscribe to changes in authentication
|
|
524
580
|
client.get('current_user')
|
|
525
581
|
|
|
@@ -527,22 +583,22 @@ function import_server (bus, options)
|
|
|
527
583
|
delete result.key
|
|
528
584
|
return result
|
|
529
585
|
}
|
|
530
|
-
client('connection').
|
|
586
|
+
client('connection').setter = function (o) {
|
|
531
587
|
o = client.clone(o)
|
|
532
588
|
o.key = 'connection/' + conn.id
|
|
533
589
|
client.set(o)
|
|
534
590
|
}
|
|
535
591
|
|
|
536
592
|
// All connections
|
|
537
|
-
client('connections').
|
|
538
|
-
client('connections').
|
|
593
|
+
client('connections').setter = function noop (t) {t.abort()}
|
|
594
|
+
client('connections').getter = function () {
|
|
539
595
|
var result = []
|
|
540
|
-
var conns = master.get('connections')
|
|
596
|
+
var conns = master.get('connections').val
|
|
541
597
|
for (var connid in conns)
|
|
542
598
|
if (connid !== 'key')
|
|
543
599
|
result.push(client.get('connection/' + connid))
|
|
544
600
|
|
|
545
|
-
return {
|
|
601
|
+
return {val: result}
|
|
546
602
|
}
|
|
547
603
|
}
|
|
548
604
|
})
|
|
@@ -558,13 +614,6 @@ function import_server (bus, options)
|
|
|
558
614
|
WebSocket = require('websocket').w3cwebsocket
|
|
559
615
|
return new WebSocket(url + '/' + bus.options.websocket_path + '/websocket')
|
|
560
616
|
},
|
|
561
|
-
client_creds: function client_creds (server_url) {
|
|
562
|
-
// Right now the server just creates a different random id each time
|
|
563
|
-
// it connects.
|
|
564
|
-
return {clientid: (Math.random().toString(36).substring(2)
|
|
565
|
-
+ Math.random().toString(36).substring(2)
|
|
566
|
-
+ Math.random().toString(36).substring(2))}
|
|
567
|
-
},
|
|
568
617
|
|
|
569
618
|
file_store: (function () {
|
|
570
619
|
// Make a database
|
|
@@ -646,7 +695,7 @@ function import_server (bus, options)
|
|
|
646
695
|
}
|
|
647
696
|
on_set.priority = true
|
|
648
697
|
bus(prefix).on_set = on_set
|
|
649
|
-
bus(prefix).
|
|
698
|
+
bus(prefix).deleter = function (key) {
|
|
650
699
|
delete db[key]
|
|
651
700
|
if (active) save_later()
|
|
652
701
|
}
|
|
@@ -712,7 +761,7 @@ function import_server (bus, options)
|
|
|
712
761
|
return decodeURIComponent(k.replace('%2E', '.'))
|
|
713
762
|
}
|
|
714
763
|
|
|
715
|
-
bus(prefix).
|
|
764
|
+
bus(prefix).getter = function (key, t) {
|
|
716
765
|
firebase_ref.child(encode_firebase_key(key)).on('value', function (x) {
|
|
717
766
|
t.done(x.val() || {})
|
|
718
767
|
}, function (err) { t.abort() })
|
|
@@ -722,19 +771,19 @@ function import_server (bus, options)
|
|
|
722
771
|
firebase_ref.child(encode_firebase_key(o.key)).set(o)
|
|
723
772
|
}
|
|
724
773
|
|
|
725
|
-
// bus(prefix).
|
|
774
|
+
// bus(prefix).setter = function (o, t) {
|
|
726
775
|
// firebase_ref.child(encode_firebase_key(o.key)).set(o, (err) => {
|
|
727
776
|
// err ? t.abort() : t.done()
|
|
728
777
|
// })
|
|
729
778
|
// }
|
|
730
779
|
|
|
731
|
-
bus(prefix).
|
|
780
|
+
bus(prefix).deleter = function (key, t) {
|
|
732
781
|
firebase_ref.child(encode_firebase_key(key)).set(null, (err) => {
|
|
733
782
|
err ? t.abort() : t.done()
|
|
734
783
|
})
|
|
735
784
|
}
|
|
736
785
|
|
|
737
|
-
bus(prefix).
|
|
786
|
+
bus(prefix).forgetter = function (key, t) {
|
|
738
787
|
firebase_ref.child(encode_firebase_key(key)).off()
|
|
739
788
|
}
|
|
740
789
|
},
|
|
@@ -826,7 +875,7 @@ function import_server (bus, options)
|
|
|
826
875
|
: o))
|
|
827
876
|
|
|
828
877
|
// Publish this to the bus
|
|
829
|
-
bus.set.fire(obj, {
|
|
878
|
+
bus.set.fire(obj, {getter: true})
|
|
830
879
|
})
|
|
831
880
|
}
|
|
832
881
|
|
|
@@ -855,13 +904,13 @@ function import_server (bus, options)
|
|
|
855
904
|
if (opts.set_sync) {
|
|
856
905
|
var old_route = bus.route
|
|
857
906
|
bus.route = function (key, method, arg, t) {
|
|
858
|
-
if (method === '
|
|
907
|
+
if (method === 'setter') on_set(arg)
|
|
859
908
|
return old_route(key, method, arg, t)
|
|
860
909
|
}
|
|
861
910
|
} else
|
|
862
911
|
bus(prefix).on_set_sync = on_set
|
|
863
912
|
|
|
864
|
-
bus(prefix).
|
|
913
|
+
bus(prefix).deleter = function (key) {
|
|
865
914
|
if (opts.use_transactions && !open_transaction){
|
|
866
915
|
console.time('save db')
|
|
867
916
|
db.prepare('BEGIN TRANSACTION').run()
|
|
@@ -959,7 +1008,7 @@ function import_server (bus, options)
|
|
|
959
1008
|
}
|
|
960
1009
|
pg_set.priority = true
|
|
961
1010
|
bus(opts.prefix).on_set = pg_set
|
|
962
|
-
bus(opts.prefix).
|
|
1011
|
+
bus(opts.prefix).deleter = function (key) {
|
|
963
1012
|
db.query('delete from store where key = $1', [key])
|
|
964
1013
|
}
|
|
965
1014
|
|
|
@@ -1019,7 +1068,7 @@ function import_server (bus, options)
|
|
|
1019
1068
|
bus.usage_log_nots = nots
|
|
1020
1069
|
|
|
1021
1070
|
// Aggregate all accesses by day, to get daily active users
|
|
1022
|
-
bus('usage').
|
|
1071
|
+
bus('usage').getter = () => {
|
|
1023
1072
|
bus.get('time/' + refresh_interval)
|
|
1024
1073
|
var days = []
|
|
1025
1074
|
var last_day
|
|
@@ -1058,7 +1107,7 @@ function import_server (bus, options)
|
|
|
1058
1107
|
return {_: days}
|
|
1059
1108
|
}
|
|
1060
1109
|
|
|
1061
|
-
bus('recent_hits/*').
|
|
1110
|
+
bus('recent_hits/*').getter = (rest) => {
|
|
1062
1111
|
bus.get('time/' + refresh_interval)
|
|
1063
1112
|
var result = []
|
|
1064
1113
|
for (var row of db.prepare('select * from usage where '
|
|
@@ -1074,7 +1123,7 @@ function import_server (bus, options)
|
|
|
1074
1123
|
return {_: result}
|
|
1075
1124
|
}
|
|
1076
1125
|
|
|
1077
|
-
bus('recent_referers/*').
|
|
1126
|
+
bus('recent_referers/*').getter = (rest) => {
|
|
1078
1127
|
bus.get('time/' + refresh_interval)
|
|
1079
1128
|
var result = []
|
|
1080
1129
|
for (var row of db.prepare('select * from usage where '
|
|
@@ -1123,7 +1172,7 @@ function import_server (bus, options)
|
|
|
1123
1172
|
return times
|
|
1124
1173
|
}
|
|
1125
1174
|
|
|
1126
|
-
bus('socket_load_times').
|
|
1175
|
+
bus('socket_load_times').getter = () => {
|
|
1127
1176
|
return {_: sock_open_times()}
|
|
1128
1177
|
}
|
|
1129
1178
|
},
|
|
@@ -1135,21 +1184,21 @@ function import_server (bus, options)
|
|
|
1135
1184
|
},
|
|
1136
1185
|
|
|
1137
1186
|
time () {
|
|
1138
|
-
if (bus('time*').
|
|
1187
|
+
if (bus('time*').getter.length > 0)
|
|
1139
1188
|
// Then it's already installed
|
|
1140
1189
|
return
|
|
1141
1190
|
|
|
1142
1191
|
// Time ticker
|
|
1143
1192
|
var timeouts = {}
|
|
1144
1193
|
bus('time*', {
|
|
1145
|
-
|
|
1194
|
+
getter: (key, rest, t) => {
|
|
1146
1195
|
if (key === 'time') rest = '/1000'
|
|
1147
1196
|
timeout = parseInt(rest.substr(1))
|
|
1148
1197
|
function f () { bus.set.fire({key: key, val: Date.now()}) }
|
|
1149
1198
|
timeouts[key] = setInterval(f, timeout)
|
|
1150
1199
|
f()
|
|
1151
1200
|
},
|
|
1152
|
-
|
|
1201
|
+
forgetter: (key, rest) => {
|
|
1153
1202
|
if (key === 'time') key = 'time/1000'
|
|
1154
1203
|
clearTimeout(timeouts[key])
|
|
1155
1204
|
}
|
|
@@ -1220,8 +1269,8 @@ function import_server (bus, options)
|
|
|
1220
1269
|
// - Is there security hole if users have a ? or a / in their name?
|
|
1221
1270
|
// - Make the master.posts/ state not require /
|
|
1222
1271
|
// - Make standard url tools for optional slashes, and ? query params
|
|
1223
|
-
// - Should a e.g. client
|
|
1224
|
-
// - 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
|
|
1225
1274
|
|
|
1226
1275
|
// Helpers
|
|
1227
1276
|
function get_posts (args) {
|
|
@@ -1276,15 +1325,15 @@ function import_server (bus, options)
|
|
|
1276
1325
|
// }
|
|
1277
1326
|
|
|
1278
1327
|
// Define state on master
|
|
1279
|
-
if (master('posts_for/*').
|
|
1328
|
+
if (master('posts_for/*').getter.length === 0) {
|
|
1280
1329
|
// Get posts for each user
|
|
1281
|
-
master('posts_for/*').
|
|
1330
|
+
master('posts_for/*').getter = (json) => {
|
|
1282
1331
|
watch_for_dirt('posts-for/' + json.for)
|
|
1283
1332
|
return {_: get_posts(json)}
|
|
1284
1333
|
}
|
|
1285
1334
|
// Saving any post will dirty the list for all users mentioned in
|
|
1286
1335
|
// the post
|
|
1287
|
-
master('post/*').
|
|
1336
|
+
master('post/*').setter = (old, New, t) => {
|
|
1288
1337
|
// To do: diff the cc, to, and from lists, and only dirty
|
|
1289
1338
|
// posts_for people who have been changed
|
|
1290
1339
|
|
|
@@ -1310,40 +1359,40 @@ function import_server (bus, options)
|
|
|
1310
1359
|
}
|
|
1311
1360
|
|
|
1312
1361
|
function user_addy (u) {
|
|
1362
|
+
console.error('fix this! not ported to v7 links')
|
|
1313
1363
|
return u.name + '@' + opts.domain
|
|
1314
1364
|
}
|
|
1315
1365
|
function current_addy () {
|
|
1316
1366
|
var c = client.get('current_user')
|
|
1317
|
-
|
|
1367
|
+
console.error('fix this! not ported to v7 links')
|
|
1368
|
+
return c.val.logged_in ? user_addy(c.val.user) : 'public'
|
|
1318
1369
|
}
|
|
1319
1370
|
function is_author (post) {
|
|
1320
1371
|
var from = post._.from
|
|
1321
|
-
var c = client.get('current_user')
|
|
1322
1372
|
return from.includes(current_addy())
|
|
1323
1373
|
}
|
|
1324
1374
|
function can_see (post) {
|
|
1325
|
-
|
|
1375
|
+
console.error('fix this! not ported to v7')
|
|
1326
1376
|
var allowed = post._.to.concat(post._.cc).concat(post._.from)
|
|
1327
1377
|
return allowed.includes(current_addy()) || allowed.includes('public')
|
|
1328
1378
|
}
|
|
1329
|
-
var drtbus = master//
|
|
1379
|
+
var drtbus = master//make_statebus()
|
|
1330
1380
|
function dirty (key) { drtbus.set({key: 'dirty-'+key, n: Math.random()}) }
|
|
1331
1381
|
function watch_for_dirt (key) { drtbus.get('dirty-'+key) }
|
|
1332
1382
|
|
|
1333
|
-
client('current_email').
|
|
1383
|
+
client('current_email').getter = () => {
|
|
1334
1384
|
return {_: current_addy()}
|
|
1335
1385
|
}
|
|
1336
1386
|
|
|
1337
1387
|
// Define state on client
|
|
1338
|
-
client('posts*').
|
|
1388
|
+
client('posts*').getter = (k, rest) => {
|
|
1339
1389
|
var args = bus.parse(rest.substr(1))
|
|
1340
|
-
var c = client.get('current_user')
|
|
1341
1390
|
args.for = current_addy()
|
|
1342
1391
|
var e = master.get('posts_for/' + JSON.stringify(args))
|
|
1343
1392
|
return {_: master.clone(e._).map(x=>client.get(x.key))}
|
|
1344
1393
|
}
|
|
1345
1394
|
|
|
1346
|
-
client('post/*').
|
|
1395
|
+
client('post/*').getter = (k) => {
|
|
1347
1396
|
var e = master.clone(master.get(k))
|
|
1348
1397
|
if (!e._) return {}
|
|
1349
1398
|
if (!can_see(e))
|
|
@@ -1355,7 +1404,7 @@ function import_server (bus, options)
|
|
|
1355
1404
|
return e
|
|
1356
1405
|
}
|
|
1357
1406
|
|
|
1358
|
-
client('post/*').
|
|
1407
|
+
client('post/*').setter = (o, t) => {
|
|
1359
1408
|
if (!(client.validate(o, {key: 'string',
|
|
1360
1409
|
_: {to: 'array',
|
|
1361
1410
|
cc: 'array',
|
|
@@ -1390,7 +1439,7 @@ function import_server (bus, options)
|
|
|
1390
1439
|
t.done()
|
|
1391
1440
|
}
|
|
1392
1441
|
|
|
1393
|
-
client('post/*').
|
|
1442
|
+
client('post/*').deleter = (key, o, t) => {
|
|
1394
1443
|
// Validate
|
|
1395
1444
|
if (!is_author(o)) {
|
|
1396
1445
|
console.error('User', current_addy(),
|
|
@@ -1414,14 +1463,14 @@ function import_server (bus, options)
|
|
|
1414
1463
|
t.done()
|
|
1415
1464
|
}
|
|
1416
1465
|
|
|
1417
|
-
client('friends').
|
|
1418
|
-
return {_: (master.get('users').
|
|
1466
|
+
client('friends').getter = t => {
|
|
1467
|
+
return {_: (master.get('users').val||[])
|
|
1419
1468
|
.map(u=>client.get('email/' + user_addy(u)))
|
|
1420
1469
|
.concat([client.get('email/public')])
|
|
1421
1470
|
}
|
|
1422
1471
|
}
|
|
1423
1472
|
|
|
1424
|
-
client('email/*').
|
|
1473
|
+
client('email/*').getter = (rest) => {
|
|
1425
1474
|
var m = rest.match(email_regex)
|
|
1426
1475
|
var result = {address: rest}
|
|
1427
1476
|
if (rest === 'public') {
|
|
@@ -1444,7 +1493,7 @@ function import_server (bus, options)
|
|
|
1444
1493
|
|
|
1445
1494
|
sqlite_query_server: function sqlite_query_server (db) {
|
|
1446
1495
|
var get = bus.get
|
|
1447
|
-
bus('table_columns/*').
|
|
1496
|
+
bus('table_columns/*').getter =
|
|
1448
1497
|
function get_table_columns (key, rest) {
|
|
1449
1498
|
if (typeof key !== 'string')
|
|
1450
1499
|
console.log(handlers.hash)
|
|
@@ -1467,7 +1516,7 @@ function import_server (bus, options)
|
|
|
1467
1516
|
}
|
|
1468
1517
|
|
|
1469
1518
|
|
|
1470
|
-
bus('table_foreign_keys/*').
|
|
1519
|
+
bus('table_foreign_keys/*').getter =
|
|
1471
1520
|
function table_foreign_keys (key, rest) {
|
|
1472
1521
|
var table_name = rest
|
|
1473
1522
|
var foreign_keys = get('sql/PRAGMA foreign_key_list(' + table_name + ')').rows
|
|
@@ -1479,7 +1528,7 @@ function import_server (bus, options)
|
|
|
1479
1528
|
return result
|
|
1480
1529
|
}
|
|
1481
1530
|
|
|
1482
|
-
bus('sql/*').
|
|
1531
|
+
bus('sql/*').getter =
|
|
1483
1532
|
function sql (key, rest) {
|
|
1484
1533
|
get('time/60000')
|
|
1485
1534
|
var query = rest
|
|
@@ -1556,7 +1605,7 @@ function import_server (bus, options)
|
|
|
1556
1605
|
// ************************
|
|
1557
1606
|
|
|
1558
1607
|
// Getting the whole table, or a single row
|
|
1559
|
-
bus(table_name + '*').
|
|
1608
|
+
bus(table_name + '*').getter = function (key, rest) {
|
|
1560
1609
|
if (rest === '')
|
|
1561
1610
|
// Return the whole table
|
|
1562
1611
|
return render_table()
|
|
@@ -1576,7 +1625,7 @@ function import_server (bus, options)
|
|
|
1576
1625
|
}
|
|
1577
1626
|
|
|
1578
1627
|
// Saving a row
|
|
1579
|
-
bus(table_name + '/*').
|
|
1628
|
+
bus(table_name + '/*').setter = function (obj, rest) {
|
|
1580
1629
|
var columns = table_columns.columns
|
|
1581
1630
|
var key = remapped_keys.keys[obj.key] || obj.key
|
|
1582
1631
|
|
|
@@ -1602,7 +1651,7 @@ function import_server (bus, options)
|
|
|
1602
1651
|
}
|
|
1603
1652
|
|
|
1604
1653
|
// Inserting a new row
|
|
1605
|
-
bus('new/' + table_name + '/*').
|
|
1654
|
+
bus('new/' + table_name + '/*').setter = function (obj) {
|
|
1606
1655
|
var columns = table_columns.columns
|
|
1607
1656
|
var stmt = ('insert into ' + table_name + ' (' + columns.join(',')
|
|
1608
1657
|
+ ') values (' + new Array(columns.length).join('?,') + '?)')
|
|
@@ -1621,7 +1670,7 @@ function import_server (bus, options)
|
|
|
1621
1670
|
}
|
|
1622
1671
|
|
|
1623
1672
|
// Deleting a row
|
|
1624
|
-
bus(table_name + '/*').
|
|
1673
|
+
bus(table_name + '/*').deleter = function (key, rest) {
|
|
1625
1674
|
if (remapped_keys.keys[key]) {
|
|
1626
1675
|
var old_key = key
|
|
1627
1676
|
var new_key = remapped_keys.keys[key]
|
|
@@ -1643,72 +1692,84 @@ function import_server (bus, options)
|
|
|
1643
1692
|
|
|
1644
1693
|
serves_auth: function serves_auth (conn, master) {
|
|
1645
1694
|
var client = this // to keep me straight while programming
|
|
1646
|
-
|
|
1647
|
-
function logout (key) {
|
|
1695
|
+
function logout (user_key) {
|
|
1648
1696
|
var clients = master.get('logged_in_clients')
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
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
|
+
}
|
|
1660
1719
|
}
|
|
1661
1720
|
|
|
1662
1721
|
// Initialize master
|
|
1663
1722
|
if (!master.auth_initialized) {
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
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: {}}
|
|
1667
1729
|
var users = master.get('users')
|
|
1668
|
-
users.
|
|
1669
|
-
for (var i=0; i<users.
|
|
1670
|
-
var u = master.get(users.
|
|
1671
|
-
if (!(u.login || u.name)) {
|
|
1672
|
-
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)
|
|
1673
1735
|
continue
|
|
1674
1736
|
}
|
|
1675
|
-
var login = (u.login || u.name).toLowerCase()
|
|
1737
|
+
var login = (u.val.login || u.val.name).toLowerCase()
|
|
1676
1738
|
console.assert(login, 'Missing login for user', u)
|
|
1677
|
-
if (result.hasOwnProperty(login)) {
|
|
1739
|
+
if (result.val.hasOwnProperty(login)) {
|
|
1678
1740
|
console.error("upass: this user's name is bogus, dude.", u.key)
|
|
1679
1741
|
continue
|
|
1680
1742
|
}
|
|
1681
|
-
result[login] = {user: u.key, pass: u.pass}
|
|
1743
|
+
result.val[login] = {user: u.key, pass: u.val.pass}
|
|
1682
1744
|
}
|
|
1683
1745
|
return result
|
|
1684
1746
|
}
|
|
1685
1747
|
master.auth_initialized = true
|
|
1686
1748
|
master.get('users/passwords')
|
|
1687
1749
|
|
|
1688
|
-
master('user/*').
|
|
1750
|
+
master('user/*').deleter = (key, t) => {
|
|
1689
1751
|
master.log('Deleteinggg!!!', key)
|
|
1690
1752
|
// Remove from users.all
|
|
1691
1753
|
var users = master.get('users')
|
|
1692
|
-
users.
|
|
1754
|
+
users.val = users.val.filter(u => u.link && u.link !== key)
|
|
1693
1755
|
master.set(users)
|
|
1694
1756
|
|
|
1695
1757
|
// Log out
|
|
1696
1758
|
master.log('Logging out', key)
|
|
1697
1759
|
logout(key)
|
|
1698
1760
|
|
|
1699
|
-
// Remove connection
|
|
1700
|
-
master.log('Removing connections for', key)
|
|
1701
|
-
var conns = master.get('connections')
|
|
1702
|
-
for (var k in conns) {
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
}
|
|
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
|
+
// }
|
|
1712
1773
|
|
|
1713
1774
|
master.log('Dirtying users/passwords for', key)
|
|
1714
1775
|
master.dirty('users/passwords')
|
|
@@ -1720,9 +1781,9 @@ function import_server (bus, options)
|
|
|
1720
1781
|
|
|
1721
1782
|
// Authentication functions
|
|
1722
1783
|
function authenticate (login, pass) {
|
|
1723
|
-
var userpass = master.get('users/passwords')[login.toLowerCase()]
|
|
1784
|
+
var userpass = master.get('users/passwords').val[login.toLowerCase()]
|
|
1724
1785
|
master.log('authenticate: we see', {
|
|
1725
|
-
passwords: master.get('users/passwords'),
|
|
1786
|
+
passwords: master.get('users/passwords').val,
|
|
1726
1787
|
hash_to_match: userpass && userpass.pass,
|
|
1727
1788
|
password_guess: pass
|
|
1728
1789
|
})
|
|
@@ -1733,7 +1794,7 @@ function import_server (bus, options)
|
|
|
1733
1794
|
|
|
1734
1795
|
// console.log('comparing passwords', pass, userpass.pass)
|
|
1735
1796
|
if (require('bcrypt-nodejs').compareSync(pass, userpass.pass))
|
|
1736
|
-
return
|
|
1797
|
+
return userpass.user
|
|
1737
1798
|
}
|
|
1738
1799
|
function create_account (params) {
|
|
1739
1800
|
if (typeof (params.login || params.name) !== 'string')
|
|
@@ -1746,7 +1807,7 @@ function import_server (bus, options)
|
|
|
1746
1807
|
throw 'invalid name, login, pass, or email'
|
|
1747
1808
|
|
|
1748
1809
|
var passes = master.get('users/passwords')
|
|
1749
|
-
if (passes.hasOwnProperty(login))
|
|
1810
|
+
if (passes.val.hasOwnProperty(login))
|
|
1750
1811
|
throw 'there is already a user with that login or name'
|
|
1751
1812
|
|
|
1752
1813
|
// Hash password
|
|
@@ -1766,60 +1827,81 @@ function import_server (bus, options)
|
|
|
1766
1827
|
key = 'user/' + Math.random().toString(36).substring(7,13)
|
|
1767
1828
|
|
|
1768
1829
|
// Make account object
|
|
1769
|
-
var new_account = {
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
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]
|
|
1775
1841
|
master.set(new_account)
|
|
1776
1842
|
|
|
1777
1843
|
var users = master.get('users')
|
|
1778
|
-
users.
|
|
1779
|
-
users.
|
|
1780
|
-
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}
|
|
1781
1847
|
master.set(users)
|
|
1782
1848
|
master.set(passes)
|
|
1783
1849
|
}
|
|
1784
1850
|
|
|
1785
1851
|
// Current User
|
|
1786
|
-
client('current_user').
|
|
1787
|
-
client
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
u =
|
|
1791
|
-
|
|
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
|
|
1792
1859
|
}
|
|
1793
1860
|
|
|
1794
|
-
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
|
+
}
|
|
1795
1868
|
function error (msg) {
|
|
1869
|
+
console.error(msg)
|
|
1796
1870
|
client.set.abort(o)
|
|
1797
1871
|
var c = client.get('current_user')
|
|
1798
|
-
c.error = msg
|
|
1872
|
+
c.val.error = msg
|
|
1799
1873
|
client.set.fire(c)
|
|
1800
1874
|
}
|
|
1801
1875
|
|
|
1802
1876
|
client.log('* saving: current_user!')
|
|
1803
|
-
if (
|
|
1877
|
+
if (val.client && !conn.client) {
|
|
1804
1878
|
// Set the client
|
|
1805
|
-
|
|
1806
|
-
|
|
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
|
|
1807
1884
|
client.client_ip = conn.remoteAddress
|
|
1808
1885
|
|
|
1809
|
-
if (conn.id) {
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
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
|
+
// }
|
|
1814
1896
|
}
|
|
1815
1897
|
else {
|
|
1816
|
-
if (
|
|
1898
|
+
if (val.create_account) {
|
|
1817
1899
|
client.log('current_user: creating account')
|
|
1818
1900
|
try {
|
|
1819
|
-
create_account(
|
|
1901
|
+
create_account(val.create_account)
|
|
1820
1902
|
client.log('Success creating account!')
|
|
1821
1903
|
var cu = client.get('current_user')
|
|
1822
|
-
cu.create_account = null
|
|
1904
|
+
cu.val.create_account = null
|
|
1823
1905
|
client.set.fire(cu)
|
|
1824
1906
|
} catch (e) {
|
|
1825
1907
|
error('Cannot create that account because ' + e)
|
|
@@ -1827,29 +1909,29 @@ function import_server (bus, options)
|
|
|
1827
1909
|
}
|
|
1828
1910
|
}
|
|
1829
1911
|
|
|
1830
|
-
if (
|
|
1912
|
+
if (val.login_as) {
|
|
1831
1913
|
// Then client is trying to log in
|
|
1832
|
-
|
|
1833
|
-
var creds = o.login_as
|
|
1914
|
+
var creds = val.login_as
|
|
1834
1915
|
var login = creds.login || creds.name
|
|
1835
1916
|
if (login && creds.pass) {
|
|
1836
1917
|
// With a username and password
|
|
1837
|
-
var
|
|
1918
|
+
var user_key = authenticate(login, creds.pass)
|
|
1838
1919
|
|
|
1839
|
-
client.log('auth said:',
|
|
1840
|
-
if (
|
|
1920
|
+
client.log('auth said:', user_key)
|
|
1921
|
+
if (user_key) {
|
|
1841
1922
|
// Success!
|
|
1842
1923
|
// Associate this user with this session
|
|
1843
1924
|
// user.log('Logging the user in!', u)
|
|
1844
1925
|
|
|
1845
1926
|
var clients = master.get('logged_in_clients')
|
|
1846
|
-
var connections = master.get('connections')
|
|
1927
|
+
// var connections = master.get('connections')
|
|
1847
1928
|
|
|
1848
|
-
clients
|
|
1849
|
-
|
|
1929
|
+
clients.val = clients.val || {}
|
|
1930
|
+
clients.val[conn.client] = {link: user_key}
|
|
1931
|
+
// connections.val[conn.id].user = {link: user_key}
|
|
1850
1932
|
|
|
1851
1933
|
master.set(clients)
|
|
1852
|
-
master.set(connections)
|
|
1934
|
+
// master.set(connections)
|
|
1853
1935
|
|
|
1854
1936
|
client.log('current_user: success logging in!')
|
|
1855
1937
|
}
|
|
@@ -1864,38 +1946,41 @@ function import_server (bus, options)
|
|
|
1864
1946
|
}
|
|
1865
1947
|
}
|
|
1866
1948
|
|
|
1867
|
-
else if (
|
|
1949
|
+
else if (val.logout) {
|
|
1868
1950
|
client.log('current_user: logging out')
|
|
1869
1951
|
var clients = master.get('logged_in_clients')
|
|
1870
|
-
var connections = master.get('connections')
|
|
1952
|
+
// var connections = master.get('connections')
|
|
1871
1953
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1954
|
+
clients.val = clients.val || {}
|
|
1955
|
+
delete clients.val[conn.client]
|
|
1956
|
+
// connections.val[conn.id].user = null
|
|
1874
1957
|
|
|
1875
1958
|
master.set(clients)
|
|
1876
|
-
master.set(connections)
|
|
1959
|
+
// master.set(connections)
|
|
1877
1960
|
}
|
|
1878
1961
|
}
|
|
1879
1962
|
|
|
1880
1963
|
t.reget()
|
|
1881
1964
|
}
|
|
1882
|
-
client('current_user').
|
|
1965
|
+
client('current_user').deleter = function () {}
|
|
1883
1966
|
|
|
1884
1967
|
// Users have closet space at /user/<name>/*
|
|
1885
1968
|
var closet_space_key = /^(user\/[^\/]+)\/.*/
|
|
1886
1969
|
var private_closet_space_key = /^user\/[^\/]+\/private.*/
|
|
1887
1970
|
|
|
1888
1971
|
// User
|
|
1889
|
-
client('user/*').
|
|
1972
|
+
client('user/*').setter = function (o, t) {
|
|
1890
1973
|
var c = client.get('current_user')
|
|
1891
1974
|
var user_key = o.key.match(/^user\/([^\/]+)/)
|
|
1892
1975
|
user_key = user_key && ('user/' + user_key[1])
|
|
1893
1976
|
|
|
1894
|
-
// Only the current user can touch
|
|
1895
|
-
if (!c.logged_in || c.user.
|
|
1896
|
-
client.log('Only the current user can touch
|
|
1897
|
-
|
|
1898
|
-
|
|
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
|
+
})
|
|
1899
1984
|
client.set.abort(o)
|
|
1900
1985
|
return
|
|
1901
1986
|
}
|
|
@@ -1911,9 +1996,14 @@ function import_server (bus, options)
|
|
|
1911
1996
|
console.assert(o.key.match(/^user\/[^\/]+$/))
|
|
1912
1997
|
|
|
1913
1998
|
// Validate types
|
|
1914
|
-
if (!client.validate(o, {key: 'string',
|
|
1915
|
-
|
|
1916
|
-
|
|
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
|
+
'*':'*'}})) {
|
|
1917
2007
|
client.log('This user change fails validation.')
|
|
1918
2008
|
client.set.abort(o)
|
|
1919
2009
|
return
|
|
@@ -1924,7 +2014,7 @@ function import_server (bus, options)
|
|
|
1924
2014
|
// • That resulting login must be unique across all users
|
|
1925
2015
|
|
|
1926
2016
|
// There must be at least a login or a name
|
|
1927
|
-
var login = o.login || o.name
|
|
2017
|
+
var login = o.val.login || o.val.name
|
|
1928
2018
|
if (!login) {
|
|
1929
2019
|
client.log('User must have a login or a name')
|
|
1930
2020
|
client.set.abort(o)
|
|
@@ -1935,26 +2025,26 @@ function import_server (bus, options)
|
|
|
1935
2025
|
var userpass = master.get('users/passwords')
|
|
1936
2026
|
|
|
1937
2027
|
// Validate that the login/name is not changed to something clobberish
|
|
1938
|
-
var old_login = u.login || u.name
|
|
2028
|
+
var old_login = u.val.login || u.val.name
|
|
1939
2029
|
if (old_login.toLowerCase() !== login.toLowerCase()
|
|
1940
2030
|
&& userpass.hasOwnProperty(login)) {
|
|
1941
2031
|
client.log('The login', login, 'is already taken. Aborting.')
|
|
1942
2032
|
client.set.abort(o) // Abort
|
|
1943
2033
|
|
|
1944
2034
|
o = client.get(o.key) // Add error message
|
|
1945
|
-
o.error = 'The login "' + login + '" is already taken'
|
|
2035
|
+
o.val.error = 'The login "' + login + '" is already taken'
|
|
1946
2036
|
client.set.fire(o)
|
|
1947
2037
|
|
|
1948
2038
|
return // And exit
|
|
1949
2039
|
}
|
|
1950
2040
|
|
|
1951
2041
|
// Now we can update login and name
|
|
1952
|
-
u.login = o.login
|
|
1953
|
-
u.name = o.name
|
|
2042
|
+
u.val.login = o.val.login
|
|
2043
|
+
u.val.name = o.val.name
|
|
1954
2044
|
|
|
1955
2045
|
// Hash password
|
|
1956
|
-
o.pass = o.pass && require('bcrypt-nodejs').hashSync(o.pass)
|
|
1957
|
-
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
|
|
1958
2048
|
|
|
1959
2049
|
// // Users can have pictures (remove this soon)
|
|
1960
2050
|
// // Bug: if user changes name, this picture's url doesn't change.
|
|
@@ -1973,24 +2063,24 @@ function import_server (bus, options)
|
|
|
1973
2063
|
|
|
1974
2064
|
// For anything else, go ahead and add it to the user object
|
|
1975
2065
|
var protected = {key:1, name:1, /*pic:1,*/ pass:1}
|
|
1976
|
-
for (var k in o)
|
|
2066
|
+
for (var k in o.val)
|
|
1977
2067
|
if (!protected.hasOwnProperty(k))
|
|
1978
|
-
u[k] = o[k]
|
|
1979
|
-
for (var k in u)
|
|
1980
|
-
if (!protected.hasOwnProperty(k) && !o.hasOwnProperty(k))
|
|
1981
|
-
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]
|
|
1982
2072
|
|
|
1983
2073
|
master.set(u)
|
|
1984
2074
|
}
|
|
1985
|
-
client('user/*').
|
|
2075
|
+
client('user/*').getter = function user_getter (k) {
|
|
1986
2076
|
var c = client.get('current_user')
|
|
1987
|
-
client.log('* getting:', k, 'as', c.user)
|
|
2077
|
+
client.log('* getting:', k, 'as', c.val.user)
|
|
1988
2078
|
|
|
1989
2079
|
// Users have closet space at /user/<name>/*
|
|
1990
2080
|
if (k.match(closet_space_key)) {
|
|
1991
2081
|
var obj_user = k.match(closet_space_key)[1]
|
|
1992
2082
|
if (k.match(private_closet_space_key)
|
|
1993
|
-
&& (!c.user || obj_user !== c.user.
|
|
2083
|
+
&& (!c.val.user || obj_user !== c.val.user.link)) {
|
|
1994
2084
|
client.log('hiding private closet data')
|
|
1995
2085
|
return {}
|
|
1996
2086
|
}
|
|
@@ -1999,26 +2089,27 @@ function import_server (bus, options)
|
|
|
1999
2089
|
}
|
|
2000
2090
|
|
|
2001
2091
|
// Otherwise return the actual user
|
|
2002
|
-
return user_obj(k, c.logged_in && c.user.
|
|
2092
|
+
return user_obj(k, c.val.logged_in && c.val.user.link === k)
|
|
2003
2093
|
}
|
|
2004
|
-
client('user/*').
|
|
2094
|
+
client('user/*').deleter = function () {}
|
|
2005
2095
|
function user_obj (k, logged_in) {
|
|
2006
2096
|
var o = master.clone(master.get(k))
|
|
2007
2097
|
if (k.match(/^user\/([^\/]+)\/private\/(.*)$/))
|
|
2008
2098
|
return logged_in ? o : {key: k}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2099
|
+
|
|
2100
|
+
o.val = o.val || {}
|
|
2101
|
+
delete o.val.pass
|
|
2102
|
+
if (!logged_in) {delete o.val.email; delete o.val.login}
|
|
2012
2103
|
return o
|
|
2013
2104
|
}
|
|
2014
2105
|
|
|
2015
2106
|
// Blacklist sensitive stuff on master, in case we have a shadow set up
|
|
2016
2107
|
var blacklist = 'users users/passwords logged_in_clients'.split(' ')
|
|
2017
2108
|
for (var i=0; i<blacklist.length; i++) {
|
|
2018
|
-
client(blacklist[i]).
|
|
2019
|
-
client(blacklist[i]).
|
|
2020
|
-
client(blacklist[i]).
|
|
2021
|
-
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 () {}
|
|
2022
2113
|
}
|
|
2023
2114
|
},
|
|
2024
2115
|
|
|
@@ -2027,14 +2118,14 @@ function import_server (bus, options)
|
|
|
2027
2118
|
var was_logged_in = undefined
|
|
2028
2119
|
|
|
2029
2120
|
function client_prefix (current_user) {
|
|
2030
|
-
return 'client/' + (current_user.logged_in
|
|
2031
|
-
? current_user.user.
|
|
2121
|
+
return 'client/' + (current_user.val.logged_in
|
|
2122
|
+
? current_user.val.user.link.substr('user/'.length)
|
|
2032
2123
|
: client.client_id) + '/'
|
|
2033
2124
|
}
|
|
2034
2125
|
|
|
2035
|
-
function copy_client_to_user(client,
|
|
2126
|
+
function copy_client_to_user(client, user_key) {
|
|
2036
2127
|
var old_prefix = 'client/' + client.client_id
|
|
2037
|
-
var new_prefix = 'client/' +
|
|
2128
|
+
var new_prefix = 'client/' + user_key.substr('user/'.length)
|
|
2038
2129
|
|
|
2039
2130
|
var keys = client.master.get('persisted_keys/' + client.client_id)
|
|
2040
2131
|
if (!keys.val) return
|
|
@@ -2080,13 +2171,13 @@ function import_server (bus, options)
|
|
|
2080
2171
|
client(_=>{
|
|
2081
2172
|
var c = client.get('current_user')
|
|
2082
2173
|
if (client.loading()) return
|
|
2083
|
-
if (was_logged_in == false && c.logged_in)
|
|
2174
|
+
if (was_logged_in == false && c.val.logged_in)
|
|
2084
2175
|
// User just logged in! Let's copy his stuff over
|
|
2085
|
-
copy_client_to_user(client, c.user)
|
|
2086
|
-
was_logged_in = c.logged_in
|
|
2176
|
+
copy_client_to_user(client, c.val.user.link)
|
|
2177
|
+
was_logged_in = c.val.logged_in
|
|
2087
2178
|
})
|
|
2088
2179
|
|
|
2089
|
-
client(prefix_to_sync).
|
|
2180
|
+
client(prefix_to_sync).getter = function (key) {
|
|
2090
2181
|
var c = client.get('current_user')
|
|
2091
2182
|
if (client.loading()) return
|
|
2092
2183
|
var prefix = client_prefix(c)
|
|
@@ -2103,7 +2194,7 @@ function import_server (bus, options)
|
|
|
2103
2194
|
return obj
|
|
2104
2195
|
}
|
|
2105
2196
|
|
|
2106
|
-
client(prefix_to_sync).
|
|
2197
|
+
client(prefix_to_sync).setter = function (obj) {
|
|
2107
2198
|
if (validate && !validate(obj)) {
|
|
2108
2199
|
console.warn('Validation failed on', obj)
|
|
2109
2200
|
client.set.abort(obj)
|
|
@@ -2131,7 +2222,7 @@ function import_server (bus, options)
|
|
|
2131
2222
|
p_keys && client.master.set(p_keys)
|
|
2132
2223
|
}
|
|
2133
2224
|
|
|
2134
|
-
client(prefix_to_sync).
|
|
2225
|
+
client(prefix_to_sync).deleter = function (k) {
|
|
2135
2226
|
k = client_prefix(client.get('current_user')) + k
|
|
2136
2227
|
client.master.delete(k)
|
|
2137
2228
|
|
|
@@ -2141,7 +2232,7 @@ function import_server (bus, options)
|
|
|
2141
2232
|
}
|
|
2142
2233
|
|
|
2143
2234
|
function client_persisted_keys () {
|
|
2144
|
-
if (client.get('current_user').logged_in) return
|
|
2235
|
+
if (client.get('current_user').val.logged_in) return
|
|
2145
2236
|
var result = client.master.get('persisted_keys/' + client.client_id)
|
|
2146
2237
|
if (result && !result.val) result.val = {}
|
|
2147
2238
|
return result
|
|
@@ -2157,19 +2248,19 @@ function import_server (bus, options)
|
|
|
2157
2248
|
// to the global cache
|
|
2158
2249
|
if (count === 0) {
|
|
2159
2250
|
count++
|
|
2160
|
-
if (method === '
|
|
2251
|
+
if (method === 'getter')
|
|
2161
2252
|
bus.run_handler(function get_from_master (k) {
|
|
2162
2253
|
// console.log('DEFAULT GETting', k)
|
|
2163
2254
|
var r = master_bus.get(k)
|
|
2164
2255
|
// console.log('DEFAULT GETted', r)
|
|
2165
2256
|
bus.set.fire(r, {version: master_bus.versions[r.key]})
|
|
2166
2257
|
}, method, arg)
|
|
2167
|
-
else if (method === '
|
|
2258
|
+
else if (method === 'setter')
|
|
2168
2259
|
bus.run_handler(function set_to_master (o, t) {
|
|
2169
2260
|
// console.log('DEFAULT ROUTE', t)
|
|
2170
2261
|
master_bus.set(bus.clone(o), t)
|
|
2171
2262
|
}, method, arg, {t: t})
|
|
2172
|
-
else if (method == '
|
|
2263
|
+
else if (method == 'deleter')
|
|
2173
2264
|
bus.run_handler(function delete_from_master (k, t) {
|
|
2174
2265
|
master_bus.delete(k)
|
|
2175
2266
|
return 'done'
|
|
@@ -2223,7 +2314,7 @@ function import_server (bus, options)
|
|
|
2223
2314
|
// file or recursive directory structure at fs_path
|
|
2224
2315
|
sync_files: function sync_files (state_path, file_path) {
|
|
2225
2316
|
// To do:
|
|
2226
|
-
// - Hook up a
|
|
2317
|
+
// - Hook up a deleter handler
|
|
2227
2318
|
// - recursively remove directories if all files gone
|
|
2228
2319
|
|
|
2229
2320
|
console.assert(state_path.substr(-1) !== '*'
|
|
@@ -2234,7 +2325,7 @@ function import_server (bus, options)
|
|
|
2234
2325
|
var buffer = {}
|
|
2235
2326
|
var full_file_path = require('path').join(__dirname, file_path)
|
|
2236
2327
|
|
|
2237
|
-
bus(state_path + '*').
|
|
2328
|
+
bus(state_path + '*').getter = (rest) => {
|
|
2238
2329
|
// We DO want to handle:
|
|
2239
2330
|
// - "foo"
|
|
2240
2331
|
// - "foo/*"
|
|
@@ -2260,7 +2351,7 @@ function import_server (bus, options)
|
|
|
2260
2351
|
return {_:f}
|
|
2261
2352
|
}
|
|
2262
2353
|
|
|
2263
|
-
bus(state_path + '/*').
|
|
2354
|
+
bus(state_path + '/*').setter = (o, rest, t) => {
|
|
2264
2355
|
if (rest.length>0 && rest[0] !== '/') return
|
|
2265
2356
|
var f = Buffer.from(o._, 'base64')
|
|
2266
2357
|
require('fs').writeFile(file_path + rest, f,
|
|
@@ -2274,10 +2365,10 @@ function import_server (bus, options)
|
|
|
2274
2365
|
// Installs a GET handler at route that gets state from a getter function
|
|
2275
2366
|
// Note: Makes too many textbusses. Should re-use one.
|
|
2276
2367
|
http_serve: function http_serve (route, getter) {
|
|
2277
|
-
var textbus =
|
|
2368
|
+
var textbus = make_statebus()
|
|
2278
2369
|
textbus.label = 'textbus'
|
|
2279
2370
|
var watched = new Set()
|
|
2280
|
-
textbus('*').
|
|
2371
|
+
textbus('*').getter = (filename, old) => {
|
|
2281
2372
|
return {etag: Math.random() + '',
|
|
2282
2373
|
_: getter(filename)}
|
|
2283
2374
|
}
|
|
@@ -2353,7 +2444,7 @@ function import_server (bus, options)
|
|
|
2353
2444
|
},
|
|
2354
2445
|
|
|
2355
2446
|
serve_wiki: () => {
|
|
2356
|
-
bus('edit/*').
|
|
2447
|
+
bus('edit/*').getter = () => ({_: require('./extras/wiki.coffee').code})
|
|
2357
2448
|
},
|
|
2358
2449
|
|
|
2359
2450
|
unix_socket_repl: function (filename) {
|
|
@@ -2500,7 +2591,7 @@ module.exports.import_module = function (statebus) {
|
|
|
2500
2591
|
|
|
2501
2592
|
statebus.serve = function serve (options) {
|
|
2502
2593
|
var bus = statebus()
|
|
2503
|
-
require('./
|
|
2594
|
+
require('./server-library').run_server(bus, options)
|
|
2504
2595
|
return bus
|
|
2505
2596
|
}
|
|
2506
2597
|
|