Files: 85f4193eae88cdee0f08d19cc1b5d4a5475bd1b1 / index.js
7197 bytesRaw
1 | var fs = require('fs') |
2 | var path = require('path') |
3 | var apis = require('./apis') |
4 | var nested = require('libnested') |
5 | var Stack = require('stack') |
6 | var URL = require('url') |
7 | var QS = require('qs') |
8 | var u = require('./util') |
9 | var toHTML = u.toHTML |
10 | var h = u.h |
11 | var pull = require('pull-stream') |
12 | var toPull = require('stream-to-pull-stream') |
13 | var Logger = require('morgan') |
14 | var BodyParser = require('urlencoded-request-parser') |
15 | var Coherence = require('coherence-framework') |
16 | |
17 | var doctype = '<!DOCTYPE html \n PUBLIC "-//W3C//DTD HTML 4.01//EN"\n "http://www.w3.org/TR/html4/strict.dtd">' |
18 | |
19 | //actions may make writes to sbot, or can set things |
20 | var actions = { |
21 | //note: opts is post body |
22 | identitySelect: function (opts, apply, req, cb) { |
23 | var context = this.context |
24 | context.id = opts.id |
25 | cb(null, opts, context) |
26 | }, |
27 | preview: function (opts, apply, req, cb) { |
28 | cb(null, opts) |
29 | }, |
30 | publish: function (opts, apply, req, cb) { |
31 | if(opts.content.recps === '') |
32 | delete opts.content.recps |
33 | else if('string' === typeof opts.content.recps) { |
34 | opts.content.recps = opts.content.recps.split(',') |
35 | } |
36 | |
37 | if(Array.isArray(opts.content.recps)) |
38 | opts.private = true |
39 | |
40 | sbot.identities.publishAs(opts, function (err, msg) { |
41 | if(err) cb(err) |
42 | else cb() |
43 | }) |
44 | }, |
45 | bounce: function (opts, cb) { |
46 | delete opts.type |
47 | cb(null, opts) |
48 | } |
49 | } |
50 | |
51 | var coherence = Coherence(function (opts, content, apply) { |
52 | return ['html', |
53 | ['head', {profile: "http://www.w3.org/2005/10/profile"}, |
54 | ['meta', {charset: 'UTF-8'}], |
55 | ['link', {href: '/static/style.css', rel: 'stylesheet'}], |
56 | ['script', {src: coherence.scriptUrl}], |
57 | ['link', {rel: 'icon', type: 'image/png', href: '/favicon.ico'}], |
58 | ], |
59 | ['body', |
60 | ['div#AppHeader', |
61 | ['nav', |
62 | ['div', {style: 'display:flex;flex-direction:row'}, |
63 | ['h2', 'yap'], |
64 | ['img', {src: '/favicon.ico'}] |
65 | ], |
66 | ['a', {href: '/public'}, 'Public'], |
67 | ['a', {href: '/private'}, 'Private'], |
68 | // ['a', {href: '/gatherings'}, 'Gatherings'], |
69 | ['form', {method: 'GET', action: '/search'}, |
70 | ['input', {type: 'text', name: 'query', placeholder: 'Search'}], |
71 | ['input', {type: 'hidden', name: 'limit', value: 20}], |
72 | ['button', {}, 'go'] |
73 | ], |
74 | apply('identitySelect', {main: true}) |
75 | ], |
76 | apply('progress', {}) |
77 | ], |
78 | ['div.main', content] |
79 | ] |
80 | ] |
81 | }) |
82 | |
83 | var favicon = fs.readFileSync(path.join(__dirname, 'static', 'favicon.ico')) |
84 | |
85 | //stack, but check if you called next twice! |
86 | function _Stack() { |
87 | var args = [].slice.call(arguments) |
88 | return Stack.apply(this, args.map(function (fn) { |
89 | return function (req, res, next) { |
90 | var err = new Error('already called') |
91 | var called = false |
92 | fn(req, res, function _next (err) { |
93 | if(err) console.error(err) |
94 | if(called) throw called |
95 | called = new Error('called already') |
96 | return next(err) |
97 | }) |
98 | |
99 | } |
100 | })) |
101 | } |
102 | |
103 | require('ssb-client')(function (err, sbot) { |
104 | if(err) throw err |
105 | |
106 | coherence |
107 | .use('avatar', require('./apis/avatar')(sbot)) |
108 | .use('identitySelect', require('./apis/identity-select')(sbot)) |
109 | .use('public', require('./apis/public')(sbot)) |
110 | .use('private', require('./apis/private')(sbot)) |
111 | .use('message', require('./apis/message')(sbot)) |
112 | .use('messages/post', require('./apis/messages/post')(sbot)) |
113 | .use('messages/vote', require('./apis/messages/vote')(sbot)) |
114 | .use('progress', require('./apis/progress')(sbot)) |
115 | .use('thread', require('./apis/thread')(sbot)) |
116 | .use('compose', require('./apis/compose')(sbot)) |
117 | .use('publish', require('./apis/publish')(sbot)) |
118 | .use('preview', require('./apis/preview')(sbot)) |
119 | .use('friends', require('./apis/friends')(sbot)) |
120 | .use('search', require('./apis/search')(sbot)) |
121 | .use('mentions', require('./apis/mentions')(sbot)) |
122 | |
123 | require('http').createServer(_Stack( |
124 | Logger(), |
125 | //everything breaks if blobs isn't first, but not sure why? |
126 | require('ssb-ws/blobs')(sbot, {prefix: '/blobs'}), |
127 | /* |
128 | some settings we want to store in a cookie: |
129 | * current identity |
130 | * current language |
131 | |
132 | stuff that shouldn't be in links. |
133 | this makes it possible to share links without including |
134 | state people might not want for them. |
135 | also: light/dark theme etc |
136 | */ |
137 | function (req, res, next) { |
138 | if(req.url == '/favicon.ico') |
139 | return res.end(favicon) |
140 | if(req.url == '/') |
141 | return res.end('<h1>yap<img src="/favicon.ico"></h1>') |
142 | |
143 | next() |
144 | }, |
145 | BodyParser(), |
146 | function context (req, res, next) { |
147 | req.context = QS.parse(req.headers.cookie||'') || {id: sbot.id} |
148 | req.context.id = req.context.id || sbot.id |
149 | next() |
150 | }, |
151 | //handle posts. |
152 | function (req, res, next) { |
153 | if(req.method == 'GET') return next() |
154 | var id = req.context.id || sbot.id |
155 | // var opts = QS.parse(req.body) |
156 | var opts = req.body |
157 | console.log('B', opts) |
158 | function callApi (path, opts) { |
159 | try { |
160 | var fn = nested.get(apis, path) |
161 | if(!fn) return next() |
162 | return fn(opts, apply, req) |
163 | } catch(err) { |
164 | next(err) |
165 | } |
166 | } |
167 | if(opts.type === 'preview') { |
168 | // TODO: pass opts.id in, and wether this message |
169 | // preview should allow recipient selection, or changing id. |
170 | // api.preview can set the shape of the message if it likes. |
171 | |
172 | |
173 | req.url = '/preview?'+QS.stringify(opts) |
174 | coherence(req, res, next) |
175 | |
176 | //XXX this isn't working |
177 | |
178 | // toHTML(layout.call(self, callApi(['preview'], opts))) (function (err, result) { |
179 | // if(err) next(err) |
180 | // else res.end('<!DOCTYPE html>'+result.outerHTML) |
181 | // }) |
182 | return |
183 | } |
184 | actions[opts.type](opts, apply, req, function (err, _opts, context) { |
185 | if(err) return next(err) |
186 | if(context) { |
187 | req.context = context |
188 | res.setHeader('set-cookie', QS.stringify(context)) |
189 | } |
190 | /* |
191 | after handling the post, |
192 | redirect to a normal page. |
193 | this is a work around for if you hit refresh |
194 | and the browser wants to resubmit the POST. |
195 | |
196 | I think we want to do this for most types, |
197 | exception is for preview - in which we return |
198 | the same data rendered differently and don't write |
199 | to DB at all. |
200 | */ |
201 | res.setHeader('location', req.url) |
202 | res.writeHead(303) |
203 | res.end() |
204 | }) |
205 | }, |
206 | |
207 | //HANDLE BLOBS |
208 | require('emoji-server')('/img/emoji'), |
209 | function (req, res, next) { |
210 | if(!/\/partial\//.test(req.url)) return next() |
211 | req.url = req.url.substring('/partial'.length) |
212 | render(true, req, res, next) |
213 | }, |
214 | //static files |
215 | function (req, res, next) { |
216 | if(/\/static\/[\w\d-_]+\.\w+/.test(req.url)) { |
217 | console.log("STATIC", req.url) |
218 | fs.createReadStream(path.join(__dirname, req.url)).pipe(res) |
219 | } else |
220 | next() |
221 | }, |
222 | //mount coherence! |
223 | coherence |
224 | )).listen(8005) |
225 | }) |
226 | |
227 |
Built with git-ssb-web