Files: 2761a4d2dadc72d5445a4987705dd0822fde18cf / index.js
2329 bytesRaw
1 | var FlumeQueryLinks = require('./lib/flumeview-links-raw') |
2 | var ref = require('ssb-ref') |
3 | var deepEqual = require('deep-equal') |
4 | var extend = require('xtend') |
5 | var matchChannel = /^#[^\s#]+$/ |
6 | var ssbKeys = require('ssb-keys') |
7 | var toUrlFriendly = require('base64-url').escape |
8 | |
9 | var indexes = [ |
10 | { key: 'DTS', value: [['dest'], ['timestamp']] }, |
11 | { key: 'DTA', value: [['dest'], ['value', 'timestamp']] }, // asserted timestamp |
12 | { key: 'TDT', value: [['value', 'content', 'type'], ['dest'], ['value', 'timestamp']] } |
13 | ] |
14 | |
15 | var indexVersion = 3 |
16 | |
17 | exports.name = 'backlinks' |
18 | exports.version = require('./package.json').version |
19 | exports.manifest = { |
20 | read: 'source' |
21 | } |
22 | |
23 | exports.init = function (ssb, config) { |
24 | return ssb._flumeUse( |
25 | `backlinks-${toUrlFriendly(ssb.id.slice(1, 10))}`, |
26 | FlumeQueryLinks(indexes, extractLinks, indexVersion, unbox) |
27 | ) |
28 | |
29 | function unbox (msg) { |
30 | if (typeof msg.value.content === 'string') { |
31 | var value = unboxValue(msg.value) |
32 | if (value) { |
33 | return { |
34 | key: msg.key, value: value, timestamp: msg.timestamp |
35 | } |
36 | } |
37 | } |
38 | return msg |
39 | } |
40 | |
41 | function unboxValue (value) { |
42 | var plaintext = null |
43 | try { |
44 | plaintext = ssbKeys.unbox(value.content, ssb.keys.private) |
45 | } catch (ex) {} |
46 | if (!plaintext) return null |
47 | return { |
48 | previous: value.previous, |
49 | author: value.author, |
50 | sequence: value.sequence, |
51 | timestamp: value.timestamp, |
52 | hash: value.hash, |
53 | content: plaintext, |
54 | private: true |
55 | } |
56 | } |
57 | } |
58 | |
59 | function extractLinks (msg, emit) { |
60 | var links = new Set() |
61 | walk(msg.value.content, function (path, value) { |
62 | // HACK: handle legacy channel mentions |
63 | if (deepEqual(path, ['channel']) && typeof value === 'string' && value.length < 30) { |
64 | value = `#${value.replace(/\s/g, '')}` |
65 | } |
66 | |
67 | // TODO: should add channel matching to ref.type |
68 | if (ref.type(value) || isChannel(value)) { |
69 | links.add(value) |
70 | } |
71 | }) |
72 | links.forEach(link => { |
73 | emit(extend(msg, { |
74 | dest: link |
75 | })) |
76 | }) |
77 | } |
78 | |
79 | function isChannel (value) { |
80 | return typeof value === 'string' && value.length < 30 && matchChannel.test(value) |
81 | } |
82 | |
83 | function walk (obj, fn, prefix) { |
84 | if (obj && typeof obj === 'object') { |
85 | for (var k in obj) { |
86 | walk(obj[k], fn, (prefix || []).concat(k)) |
87 | } |
88 | } else { |
89 | fn(prefix, obj) |
90 | } |
91 | } |
92 |
Built with git-ssb-web