Files: b4c65fe0668bf7f8c17503206a51cbb417ee9727 / index.js
6444 bytesRaw
1 | |
2 | var fs = require('fs') |
3 | var path = require('path') |
4 | var ref = require('ssb-ref') |
5 | var Stack = require('stack') |
6 | |
7 | //refactor to ditch these things |
8 | var nested = require('libnested') |
9 | var pull = require('pull-stream') |
10 | var URL = require('url') |
11 | var QS = require('qs') |
12 | var u = require('./util') |
13 | var toHTML = u.toHTML |
14 | var uniq = require('lodash.uniq') |
15 | |
16 | // middleware |
17 | var Logger = require('morgan') |
18 | var Emoji = require('emoji-server') |
19 | var Static = require('ecstatic') |
20 | var BodyParser = require('urlencoded-request-parser') |
21 | var FavIcon = require('serve-favicon') |
22 | var Coherence = require('coherence-framework') |
23 | |
24 | //actions may make writes to sbot, or can set things |
25 | |
26 | require('ssb-client')(function (err, sbot) { |
27 | if(err) throw err |
28 | sbot.identities.main(function (err, id) { |
29 | sbot.id = id |
30 | |
31 | if(!sbot.id) throw new Error('sbot id missing') |
32 | var coherence = Coherence(require('./layout')) |
33 | |
34 | //core: render an avatar, select |
35 | .use('avatar', require('./apis/avatar')(sbot)) |
36 | .use('identitySelect', require('./apis/identity-select')(sbot)) |
37 | //called by preview (to clarify who you are mentioning) |
38 | .use('mentions', require('./apis/mentions')(sbot)) |
39 | |
40 | .use('messageLink', require('./apis/message-link')(sbot)) |
41 | .use('channelLink', require('./apis/channel-link')(sbot)) |
42 | |
43 | //render a single message |
44 | .use('message', require('./apis/message')(sbot)) |
45 | |
46 | //show how much things there are to do... |
47 | .use('progress', require('./apis/progress')(sbot)) |
48 | |
49 | //core message writing... |
50 | .use('preview', require('./apis/preview')(sbot)) |
51 | .use('compose', require('./apis/compose')(sbot)) |
52 | .use('publish', require('./apis/publish')(sbot)) |
53 | |
54 | //view (and filtered views) on the raw log |
55 | .use('public', require('./apis/public')(sbot)) |
56 | .use('more', require('./apis/more')(sbot)) |
57 | .use('private', require('./apis/private')(sbot)) |
58 | .use('friends', require('./apis/friends')(sbot)) |
59 | .use('search', require('./apis/search')(sbot)) |
60 | |
61 | //patchthreads |
62 | .use('thread', require('./apis/thread')(sbot)) |
63 | .use('messages/post', require('./apis/messages/post')(sbot)) |
64 | .use('messages/vote', require('./apis/messages/vote')(sbot)) |
65 | |
66 | var actions = { |
67 | //note: opts is post body |
68 | |
69 | //sets id in cookie |
70 | identitySelect: function (opts, req, cb) { |
71 | var context = req.cookies |
72 | context.id = opts.id |
73 | cb(null, null, context) |
74 | }, |
75 | |
76 | //sets id in cookie |
77 | languageSelect: function (opts, req, cb) { |
78 | throw new Error('not implemented yet') |
79 | }, |
80 | |
81 | //theme, in cookie |
82 | |
83 | publish: function (opts, req, cb) { |
84 | if(opts.content.recps === '') |
85 | delete opts.content.recps |
86 | else if('string' === typeof opts.content.recps) { |
87 | opts.content.recps = opts.content.recps.split(',') |
88 | } |
89 | |
90 | if(Array.isArray(opts.content.recps)) |
91 | opts.private = true |
92 | else if(opts.private && !opts.content.recps) { |
93 | opts.content.recps = uniq( |
94 | [opts.id] |
95 | .concat(opts.content.mentions || []) |
96 | .map(function (e) { return ref.isFeed(e) ? e : ref.isFeed(e.link) ? e.link : null }) |
97 | .filter(Boolean) |
98 | ) |
99 | } |
100 | |
101 | sbot.identities.publishAs(opts, function (err, msg) { |
102 | if(err) cb(err) |
103 | else cb() |
104 | }) |
105 | } |
106 | } |
107 | |
108 | require('http').createServer(Stack( |
109 | Logger(), |
110 | //everything breaks if blobs isn't first, but not sure why? |
111 | require('ssb-ws/blobs')(sbot, {prefix: '/blobs'}), |
112 | FavIcon(path.join(__dirname, 'static', 'favicon.ico')), |
113 | BodyParser(), |
114 | /* |
115 | some settings we want to store in a cookie: |
116 | * current identity |
117 | * current language |
118 | |
119 | stuff that shouldn't be in links. |
120 | this makes it possible to share links without including |
121 | state people might not want for them. |
122 | also: light/dark theme etc |
123 | */ |
124 | function (req, res, next) { |
125 | //just do it this simple way because it works |
126 | //I tried the cookie parser middleware but things got weird. |
127 | req.cookies = QS.parse(req.headers.cookie||'') || {id: sbot.id} |
128 | next() |
129 | }, |
130 | //handle posts. |
131 | function (req, res, next) { |
132 | if(req.method == 'GET') return next() |
133 | var id = req.cookies.id || sbot.id |
134 | var opts = req.body |
135 | |
136 | // handle preview specially, (to confirm a message) |
137 | |
138 | if(opts.type === 'preview') { |
139 | // TODO: pass opts.id in, and wether this message |
140 | // preview should allow recipient selection, or changing id. |
141 | // api.preview can set the shape of the message if it likes. |
142 | |
143 | req.url = '/preview?'+QS.stringify(opts) |
144 | return coherence(req, res, next) |
145 | } |
146 | actions[opts.type](opts, req, function (err, _opts, context) { |
147 | if(err) return next(err) |
148 | if(context) { |
149 | req.cookies = context |
150 | res.setHeader('set-Cookie', QS.stringify(context)) |
151 | } |
152 | /* |
153 | After handling the post, redirect to a normal page. |
154 | This is a work around for if you hit refresh |
155 | and the browser wants to resubmit the POST. |
156 | |
157 | I think we want to do this for most types, |
158 | exception is for preview - in which we return |
159 | the same data rendered differently and don't write |
160 | to DB at all. |
161 | |
162 | Should preview be implemented like this too? |
163 | */ |
164 | res.setHeader('location', req.url) |
165 | res.writeHead(303) |
166 | res.end() |
167 | }) |
168 | }, |
169 | |
170 | Emoji('/img/emoji'), |
171 | Static({ |
172 | root: path.join(__dirname, 'static'), baseDir: '/static' |
173 | }), |
174 | coherence |
175 | )).listen(8005, 'localhost') |
176 | |
177 | /* |
178 | generic ssb invalidation |
179 | if a message links to another, invalidate the other key. |
180 | (this will get threads, likes, etc) |
181 | if a message links to a feed, invalidate the feed. |
182 | |
183 | that doesn't cover follows though... but maybe that can be invalidated |
184 | as one thing? |
185 | */ |
186 | |
187 | pull( |
188 | sbot.createLogStream({live: true, old: false, sync: false, private: true}), |
189 | pull.drain(function (data) { |
190 | nested.each(data.value.content, function (v) { |
191 | if(ref.isMsg(v)) |
192 | coherence.invalidate(v, data.ts) |
193 | else if(ref.isFeed(v)) { |
194 | coherence.invalidate('in:'+v, data.ts) |
195 | coherence.invalidate('out:'+data.value.author, data.ts) |
196 | } |
197 | }) |
198 | }) |
199 | ) |
200 | })}) |
201 | |
202 | |
203 | |
204 | |
205 | |
206 |
Built with git-ssb-web