confirm.jsView |
---|
38 | 38 … | cb(null, data) |
39 | 39 … | }) |
40 | 40 … | } |
41 | 41 … | |
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 … | + |
43 | 50 … | var content = opts.content |
44 | 51 … | var browser = opts.browser |
45 | 52 … | var recps = opts.recps |
46 | 53 … | var publish = opts.publish || noPublish |
47 | 54 … | var getUrl = opts.getUrl |
48 | 55 … | var privatePublish = opts.privatePublish || noPrivatePublish |
49 | 56 … | var redirectBase = opts.redirectBase |
| 57 … | + var ctxs = opts.ctxs |
| 58 … | + var url = opts.url |
| 59 … | + var cb = opts.cb |
50 | 60 … | |
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) |
54 | 63 … | |
| 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 … | + |
55 | 140 … | if (browser && getUrl) { |
56 | 141 … | return cb(new TypeError('browser option and getUrl option conflict')) |
57 | 142 … | } |
58 | 143 … | if (!browser && !getUrl) { |
61 | 146 … | if (recps && !Array.isArray(recps)) { |
62 | 147 … | return cb(new TypeError('recps should be array')) |
63 | 148 … | } |
64 | 149 … | |
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) |
144 | 157 … | } |
145 | 158 … | |
146 | 159 … | module.exports = function (opts) { |
147 | 160 … | var publish = opts.publish |
149 | 162 … | var config = opts.config || {} |
150 | 163 … | var browser = config.browser || |
151 | 164 … | (os.platform() === 'darwin' ? 'open' : 'xdg-open') |
152 | 165 … | var name = opts.name || 'confirm' |
| 166 … | + var port = config.port || 0 |
153 | 167 … | |
| 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 … | + |
154 | 194 … | return { |
155 | 195 … | publish: function (content, cb) { |
156 | 196 … | confirm({ |
157 | 197 … | publish: publish, |
158 | 198 … | content: content, |
159 | 199 … | browser: browser, |
| 200 … | + ctxs: ctxs, |
| 201 … | + url: url, |
160 | 202 … | }, cb) |
161 | 203 … | }, |
162 | 204 … | privatePublish: function (content, recps, cb) { |
163 | 205 … | confirm({ |
164 | 206 … | privatePublish: privatePublish, |
165 | 207 … | recps: recps, |
166 | 208 … | content: content, |
167 | 209 … | browser: browser, |
| 210 … | + ctxs: ctxs, |
| 211 … | + url: url, |
168 | 212 … | }, cb) |
169 | 213 … | }, |
170 | 214 … | publishGetUrl: function (opts, cb) { |
171 | 215 … | confirm({ |
172 | 216 … | publish: publish, |
173 | 217 … | content: opts.content, |
174 | 218 … | redirectBase: opts.redirectBase, |
175 | 219 … | getUrl: true, |
| 220 … | + ctxs: ctxs, |
| 221 … | + url: url, |
176 | 222 … | }, cb) |
177 | 223 … | }, |
178 | 224 … | privatePublishGetUrl: function (opts, cb) { |
179 | 225 … | confirm({ |
181 | 227 … | recps: opts.recps, |
182 | 228 … | content: opts.content, |
183 | 229 … | redirectBase: opts.redirectBase, |
184 | 230 … | getUrl: true, |
| 231 … | + ctxs: ctxs, |
| 232 … | + url: url, |
185 | 233 … | }, cb) |
186 | 234 … | } |
187 | 235 … | } |
188 | 236 … | } |