git ssb

3+

cel / ssb-publishguard



Commit 4681c34eca9aa16ef5e452b7a7d54b67c65fafc1

Use ssb-ws if available

cel committed on 3/19/2019, 7:25:15 AM
Parent: 0dc8433933a96a2338ce1948f308f6a305675003

Files changed

README.mdchanged
confirm.jschanged
index.jschanged
README.mdView
@@ -21,11 +21,15 @@
2121 ```
2222 {
2323 "publishguard": {
2424 "browser": "dillo", // command to spawn confirm page
25 + "port": 0, // port number to listen on if not using ssb-ws
2526 }
2627 }
2728 ```
29 +If publishguard detects ssb-ws, it will use ssb-ws as the web server.
30 +Otherwise, it will spawn its own web server, on the localhost port specified in
31 +the config, or a random port if none (0) is specified.
2832
2933 ## Usage
3034
3135 It wraps the `publish` and `private.publish` sbot calls, so you can continue
confirm.jsView
@@ -38,21 +38,106 @@
3838 cb(null, data)
3939 })
4040 }
4141
42-function confirm(opts, cb) {
42 +function serve(req, res, ctxs) {
43 + var q = req.uri.query
44 + var opts = ctxs[q.id]
45 + if (!opts) {
46 + res.writeHead(404)
47 + return res.end('Not Found')
48 + }
49 +
4350 var content = opts.content
4451 var browser = opts.browser
4552 var recps = opts.recps
4653 var publish = opts.publish || noPublish
4754 var getUrl = opts.getUrl
4855 var privatePublish = opts.privatePublish || noPrivatePublish
4956 var redirectBase = opts.redirectBase
57 + var ctxs = opts.ctxs
58 + var url = opts.url
59 + var cb = opts.cb
5060
51- if (typeof cb !== 'function') {
52- throw new TypeError('bad callback')
53- }
61 + if (req.method === 'POST') return getForm(req, function (err, data) {
62 + if (err) return res.end(err.stack || err)
5463
64 + if (!data.token || data.token !== opts.token) {
65 + res.writeHead(403)
66 + return res.end('Invalid token')
67 + }
68 +
69 + function publish1(content, recps, cb) {
70 + if (recps) privatePublish(content, recps, cb)
71 + else publish(content, cb)
72 + }
73 +
74 + if (data.publish) {
75 + return publish1(content, recps, function (err, msg) {
76 + if (err) {
77 + res.writeHead(500, {'Content-Type': 'text/html'})
78 + res.end('<!doctype html><html><head><meta charset=utf-8>'
79 + + '<title>publish error</title></head><body>'
80 + + '<h1>' + escapeHTML(err.name || err) + '</h1>'
81 + + '<pre>' + escapeHTML(err.stack || '') + '</pre>'
82 + + '</body></html>')
83 + delete ctxs[q.id]
84 + return !getUrl && cb(err)
85 + }
86 + var json = JSON.stringify(msg, 0, 2)
87 + if (redirectBase) {
88 + var url = redirectBase + encodeURIComponent(msg.key)
89 + res.writeHead(302, {'Location': url})
90 + res.end()
91 + } else {
92 + res.writeHead(200, {'Content-Type': 'text/html'})
93 + res.end('<!doctype html><html><head><meta charset=utf-8>'
94 + + '<title>published</title></head><body>'
95 + + '<pre>' + escapeHTML(json) + '</pre>'
96 + + '</body></html>')
97 + }
98 + delete ctxs[q.id]
99 + return !getUrl && cb(null, msg)
100 + })
101 + }
102 + if (data.cancel) {
103 + res.writeHead(200, {'Content-Type': 'text/html'})
104 + res.end('<!doctype html><html><head><meta charset=utf-8>'
105 + + '<title>canceled</title></head><body>'
106 + + '<p>Cancelled</p>'
107 + + '</body></html>')
108 + delete ctxs[q.id]
109 + return !getUrl && cb(new Error('User cancelled'))
110 + }
111 + })
112 +
113 + if (!opts.token) opts.token = crypto.randomBytes(32).toString('base64')
114 + var contentJson = JSON.stringify(content, null, 2)
115 + var recpsJson = JSON.stringify(recps, null, 2)
116 + res.writeHead(200, {'Content-Type': 'text/html'})
117 + return res.end('<!doctype html><html><head><meta charset=utf-8>'
118 + + '<title>publish</title></head><body>'
119 + + '<form action="" method="post">'
120 + + (recps ? '<pre>' + recpsJson + '</pre>' : '')
121 + + '<pre>' + escapeHTML(contentJson) + '</pre>'
122 + + '<input type="hidden" name="id" value="' + escapeHTML(q.id) + '">'
123 + + '<input type="hidden" name="token" value="' + escapeHTML(opts.token) + '">'
124 + + '<input type="submit" name="publish" value="Publish"> '
125 + + '<input type="submit" name="cancel" value="Cancel">'
126 + + '</form></body></html>')
127 +}
128 +
129 +function confirm(opts, cb) {
130 + var browser = opts.browser
131 + var recps = opts.recps
132 + var getUrl = opts.getUrl
133 + var ctxs = opts.ctxs
134 + var url = opts.url
135 +
136 + if (typeof cb !== 'function') throw new TypeError('bad callback')
137 + if (!opts.ctxs) throw new TypeError('missing ctxs')
138 + if (!opts.url) throw new TypeError('missing url')
139 +
55140 if (browser && getUrl) {
56141 return cb(new TypeError('browser option and getUrl option conflict'))
57142 }
58143 if (!browser && !getUrl) {
@@ -61,87 +146,15 @@
61146 if (recps && !Array.isArray(recps)) {
62147 return cb(new TypeError('recps should be array'))
63148 }
64149
65- var token = crypto.randomBytes(32).toString('base64')
66-
67- function publish1(content, recps, cb) {
68- if (recps) privatePublish(content, recps, cb)
69- else publish(content, cb)
70- }
71-
72- var server = http.createServer(function (req, res) {
73- var url = URL.parse(req.url, true)
74- if (url.pathname !== '/') {
75- res.writeHead(404)
76- return res.end('Not Found')
77- }
78-
79- if (req.method === 'POST') return getForm(req, function (err, data) {
80- if (err) {
81- res.writeHead(500)
82- return res.end(err.stack || err)
83- }
84-
85- if (data.token !== token) {
86- res.writeHead(403)
87- return res.end('Invalid token')
88- }
89-
90- if (data.publish) {
91- return publish1(content, recps, function (err, msg) {
92- if (err) {
93- res.writeHead(500, {'Content-Type': 'text/html'})
94- res.end('<!doctype html><html><head><meta charset=utf8><title>publish error</title></head><body>'
95- + '<h1>' + escapeHTML(err.name || err) + '</h1>'
96- + '<pre>' + escapeHTML(err.stack || '') + '</pre>'
97- + '</body></html>')
98- server.close()
99- return !getUrl && cb(err)
100- }
101- var json = JSON.stringify(msg, 0, 2)
102- if (redirectBase) {
103- var url = redirectBase + encodeURIComponent(msg.key)
104- res.writeHead(302, {'Location': url})
105- res.end()
106- } else {
107- res.writeHead(200, {'Content-Type': 'text/html'})
108- res.end('<!doctype html><html><head><meta charset=utf8><title>published</title></head><body>'
109- + '<pre>' + escapeHTML(json) + '</pre>'
110- + '</body></html>')
111- }
112- server.close()
113- return !getUrl && cb(null, msg)
114- })
115- }
116- if (data.cancel) {
117- res.writeHead(200, {'Content-Type': 'text/html'})
118- res.end('<!doctype html><html><head><meta charset=utf8><title>canceled</title></head><body>'
119- + '<p>Cancelled</p>'
120- + '</body></html>')
121- if (server) server.close()
122- return !getUrl && cb(new Error('User cancelled'))
123- }
124- })
125-
126- var contentJson = JSON.stringify(content, null, 2)
127- var recpsJson = JSON.stringify(recps, null, 2)
128- res.writeHead(200, {'Content-Type': 'text/html'})
129- res.end('<!doctype html><html><head><meta charset=utf8><title>publish</title></head><body>'
130- + '<form action="/" method="post">'
131- + (recps ? '<pre>' + recpsJson + '</pre>' : '')
132- + '<pre>' + escapeHTML(contentJson) + '</pre>'
133- + '<input type="hidden" name="token" value="' + token + '">'
134- + '<input type="submit" name="publish" value="Publish"> '
135- + '<input type="submit" name="cancel" value="Cancel">'
136- + '</form>'
137- + '</body></html>')
138- }).listen(0, '127.0.0.1', function () {
139- var port = this.address().port
140- var url = 'http://127.0.0.1:' + port + '/'
141- if (browser) proc.spawn(browser, [url], {stdio: 'inherit'})
142- else cb(null, url)
143- }).on('error', cb)
150 + var id = crypto.randomBytes(16).toString('base64')
151 + ctxs[id] = opts
152 + var reqUrl = opts.url + '?id=' + encodeURIComponent(id)
153 + if (browser) {
154 + proc.spawn(browser, [reqUrl], {stdio: 'inherit'})
155 + opts.cb = cb
156 + } else cb(null, reqUrl)
144157 }
145158
146159 module.exports = function (opts) {
147160 var publish = opts.publish
@@ -149,31 +162,64 @@
149162 var config = opts.config || {}
150163 var browser = config.browser ||
151164 (os.platform() === 'darwin' ? 'open' : 'xdg-open')
152165 var name = opts.name || 'confirm'
166 + var port = config.port || 0
153167
168 + var ctxs = {}
169 + var url
170 + if (opts.ws && opts.ws.use) {
171 + var wsPort = opts.wsConfig && opts.wsConfig.port || 8989
172 + var prefix = '/publishguard'
173 + url = 'http://localhost' + ':' + wsPort + prefix
174 + opts.ws.use(function (req, res, next) {
175 + req.uri = URL.parse(req.url, true)
176 + if (req.uri.pathname !== prefix) return next()
177 + serve(req, res, ctxs)
178 + })
179 + } else {
180 + var server = http.createServer(function (req, res) {
181 + req.uri = URL.parse(req.url, true)
182 + if (req.uri.pathname !== '/') {
183 + res.writeHead(404)
184 + return res.end('Not Found')
185 + }
186 + serve(req, res, ctxs)
187 + }).listen(port, '127.0.0.1', function () {
188 + if (port === 0) port = this.address().port
189 + url = 'http://127.0.0.1:' + port + '/'
190 + console.log('[publishguard] listening on ' + url)
191 + })
192 + }
193 +
154194 return {
155195 publish: function (content, cb) {
156196 confirm({
157197 publish: publish,
158198 content: content,
159199 browser: browser,
200 + ctxs: ctxs,
201 + url: url,
160202 }, cb)
161203 },
162204 privatePublish: function (content, recps, cb) {
163205 confirm({
164206 privatePublish: privatePublish,
165207 recps: recps,
166208 content: content,
167209 browser: browser,
210 + ctxs: ctxs,
211 + url: url,
168212 }, cb)
169213 },
170214 publishGetUrl: function (opts, cb) {
171215 confirm({
172216 publish: publish,
173217 content: opts.content,
174218 redirectBase: opts.redirectBase,
175219 getUrl: true,
220 + ctxs: ctxs,
221 + url: url,
176222 }, cb)
177223 },
178224 privatePublishGetUrl: function (opts, cb) {
179225 confirm({
@@ -181,8 +227,10 @@
181227 recps: opts.recps,
182228 content: opts.content,
183229 redirectBase: opts.redirectBase,
184230 getUrl: true,
231 + ctxs: ctxs,
232 + url: url,
185233 }, cb)
186234 }
187235 }
188236 }
index.jsView
@@ -20,11 +20,14 @@
2020 }
2121 sbotPublish(ciphertext, cb)
2222 }
2323 confirm = confirmer({
24 + ws: sbot.ws,
2425 publish: sbotPublish,
2526 privatePublish: sbotPrivatePublish,
26- config: config.publishguard
27 + config: config.publishguard,
28 + ws: sbot.ws,
29 + wsConfig: config.ws
2730 })
2831 sbot.publish = confirm.publish
2932 if (!sbot.private) sbot.private = {}
3033 sbot.private.publish = confirm.privatePublish

Built with git-ssb-web