fluffy – Blame information for rev 6

Subversion Repositories:
Rev:
Rev Author Line No. Line
6 office 1 #!/usr/bin/env node
2  
3 /*************************************************************************/
4 /* Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3 */
5 /*************************************************************************/
6  
7 const url = require('url');
8 const path = require('path');
9 const fs = require('fs');
10 const mime = require('mime');
11 const auth = require("http-auth");
12 const stream = require('stream');
13 const util = require('util');
14 var formidable = require('formidable');
15 const EventEmitter = require('events').EventEmitter;
16  
17 // Local imports.
18 const Cache = require(
19 path
20 .resolve(
21 path.dirname(require.main.filename),
22 'src/server',
23 'cache'
24 )
25 );
26 const was = require(
27 path
28 .resolve(
29 path.dirname(require.main.filename),
30 'src/server',
31 'was'
32 )
33 );
34  
35 // Constructor.
36 function POST() {
37 // Create events emitters for logging and data.
38 EventEmitter.call(this);
39 };
40  
41 // Process a request.
42 POST.prototype.process = function(config, request, response, root) {
43 EventEmitter.call(this);
44 var self = this;
45  
46 // Get client details.
47 const address = request.socket.address();
48 // Get requested URL.
49 const requestURL = url.parse(
50 request.url, true
51 );
52  
53 // Perform URL re-writes.
54 Object.keys(config.site.rewrite).forEach((key, index) => {
55 if (config.site.rewrite[key].test(requestURL.path)) {
56 const originalPath = requestURL.path;
57 requestURL.path = requestURL
58 .path
59 .replace(
60 config.site.rewrite[key], key
61 );
62 requestURL.pathname = url.parse(
63 requestURL
64 .pathname
65 .replace(
66 config.site.rewrite[key], key
67 ),
68 true
69 )
70 .pathname;
71 self.emit('log', {
72 message: 'Rewrite path: ' +
73 originalPath +
74 ' to: ' +
75 requestURL.path,
76 severity: 'info'
77 });
78 }
79 });
80  
81 const trimmedPath = requestURL
82 .pathname
83 .split('/')
84 .filter(Boolean)
85 .join('/');
86 const requestPath = trimmedPath === '/' ?
87 path.join(root, trimmedPath) :
88 path.resolve(root, trimmedPath);
89  
90 var form = new formidable.IncomingForm(),
91 properfields = {};
92  
93 // Workaround for formidable not parsing multiple filed options.
94 form.on('field', function(name, value) {
95 if (!properfields[name]) {
96 properfields[name] = value;
97 } else {
98 if (properfields[name].constructor.toString().indexOf("Array") > -1) { // is array
99 properfields[name].push(value);
100 } else { // not array
101 var tmp = properfields[name];
102 properfields[name] = [];
103 properfields[name].push(tmp);
104 properfields[name].push(value);
105 }
106 }
107 });
108  
109 form.parse(request, function(error, fields, files) {
110 // If the form data could not be parsed.
111 if (error) {
112 self.emit('log', {
113 message: 'Could not parse form data from: ' +
114 address.address + ':' +
115 address.port +
116 ' requesting: ' +
117 requestURL.pathname,
118 severity: 'warning'
119 });
120 self.emit('data', {
121 status: 404,
122 data: new stream.Readable({
123 read(size) {
124 this.push(null);
125 }
126 }),
127 type: 'text/plain'
128 });
129 return;
130 }
131  
132 switch(trimmedPath) {
133 case 'add':
134 // Write the icon file.
135 var iconPath = path.join(root, 'data/' + fields['service-name'] + '.png');
136 var formFile = fs.createReadStream(files['service-icon'].path)
137 .on('open', () => {
138 formFile.pipe(
139 fs.createWriteStream(iconPath)
140 .on('error', (error) => {
141 if (error) {
142 self.emit('log', {
143 message: 'Unable to create file at: ' + iconPath,
144 severity: 'error'
145 });
146 self.emit('data', {
147 status: 404,
148 data: new stream.Readable({
149 read(size) {
150 this.push(null);
151 }
152 }),
153 type: 'text/plain'
154 });
155 return;
156 }
157 })
158 );
159 })
160 .on('error', (error) => {
161 if (error) {
162 self.emit('log', {
163 message: 'Unable to create file at: ' + iconPath,
164 severity: 'error'
165 });
166 self.emit('data', {
167 status: 404,
168 data: new stream.Readable({
169 read(size) {
170 this.push(null);
171 }
172 }),
173 type: 'text/plain'
174 });
175 return;
176 }
177 });
178  
179 // Update the data file.
180 fs.realpath(path.join(root, 'data/data.json'), (error, dataPath) => {
181 // If the path does not exist, then return early.
182 if (error) {
183 self.emit('log', {
184 message: 'Unable to access data path: ' + dataPath,
185 severity: 'error'
186 });
187 self.emit('data', {
188 status: 404,
189 data: new stream.Readable({
190 read(size) {
191 this.push(null);
192 }
193 }),
194 type: 'text/plain'
195 });
196 return;
197 }
198  
199 fs.readFile(dataPath, 'utf8', function(error, data) {
200 // Could not read data file.
201 if (error) {
202 self.emit('log', {
203 message: 'Unable to read data file.',
204 severity: 'error'
205 });
206 self.emit('data', {
207 status: 404,
208 data: new stream.Readable({
209 read(size) {
210 this.push(null);
211 }
212 }),
213 type: 'text/plain'
214 });
215 return;
216 }
217  
218 // Remove previous service if it exists.
219 var services = JSON.parse(data).filter((service) => {
220 return service.tooltip != fields['service-name'];
221 });
222  
223 services.push({
224 image: '/data/' + fields['service-name'] + '.png',
225 width: '50',
226 height: '50',
227 url: fields['service-url'],
228 target: '_top',
229 tooltip: fields['service-name']
230 });
231  
232 fs.writeFile(dataPath, JSON.stringify(services, null, 4), (error) => {
233 // Could not write data file.
234 if (error) {
235 self.emit('log', {
236 message: 'Unable to write data file.',
237 severity: 'error'
238 });
239 self.emit('data', {
240 status: 404,
241 data: new stream.Readable({
242 read(size) {
243 this.push(null);
244 }
245 }),
246 type: 'text/plain'
247 });
248 return;
249 }
250  
251 // Send the main site index.
252 self.emit('log', {
253 message: 'Added new service.',
254 severity: 'info'
255 });
256 self.emit('data', {
257 status: 200,
258 data: fs.createReadStream(path.join(root, config.site.index)),
259 type: 'text/html'
260 });
261  
262 });
263  
264 });
265  
266 });
267 break;
268 case 'remove':
269 fs.realpath(path.join(root, 'data/data.json'), (error, dataPath) => {
270 // If the path does not exist, then return early.
271 if (error) {
272 self.emit('log', {
273 message: 'Unable to access data path: ' + dataPath,
274 severity: 'error'
275 });
276 self.emit('data', {
277 status: 404,
278 data: new stream.Readable({
279 read(size) {
280 this.push(null);
281 }
282 }),
283 type: 'text/plain'
284 });
285 return;
286 }
287  
288 fs.readFile(dataPath, 'utf8', function(error, data) {
289 // Could not read data file.
290 if (error) {
291 self.emit('log', {
292 message: 'Unable to read data file.',
293 severity: 'error'
294 });
295 self.emit('data', {
296 status: 404,
297 data: new stream.Readable({
298 read(size) {
299 this.push(null);
300 }
301 }),
302 type: 'text/plain'
303 });
304 return;
305 }
306  
307 // Remove all specified services and their corresponding icon files.
308 var currentServices = JSON.parse(data);
309 var services = [];
310 currentServices.forEach(currentService => {
311 if([ properfields['remove-services'] ].some((service) => service === currentService.tooltip)) {
312 var imagePath = path.join(root, currentService.image);
313 fs.stat(imagePath, (error, stats) => {
314 // Image file does not exist.
315 if (error) {
316 self.emit('log', {
317 message: 'Image file does not exist.',
318 severity: 'warning'
319 });
320 self.emit('data', {
321 status: 404,
322 data: new stream.Readable({
323 read(size) {
324 this.push(null);
325 }
326 }),
327 type: 'text/plain'
328 });
329 return;
330 }
331 fs.unlink(imagePath, (error) => {
332 if(error) {
333 self.emit('log', {
334 message: 'Could not remove image file.',
335 severity: 'warning'
336 });
337 self.emit('data', {
338 status: 404,
339 data: new stream.Readable({
340 read(size) {
341 this.push(null);
342 }
343 }),
344 type: 'text/plain'
345 });
346 }
347 });
348 });
349 return;
350 }
351 services.push(currentService);
352 });
353  
354 // Write the data file back.
355 fs.writeFile(dataPath, JSON.stringify(services, null, 4), (error) => {
356 // Could not write data file.
357 if (error) {
358 self.emit('log', {
359 message: 'Unable to write data file.',
360 severity: 'error'
361 });
362 self.emit('data', {
363 status: 404,
364 data: new stream.Readable({
365 read(size) {
366 this.push(null);
367 }
368 }),
369 type: 'text/plain'
370 });
371 return;
372 }
373  
374 // Send the main site index.
375 self.emit('log', {
376 message: 'Removed services.',
377 severity: 'info'
378 });
379 self.emit('data', {
380 status: 200,
381 data: fs.createReadStream(path.join(root, config.site.index)),
382 type: 'text/html'
383 });
384  
385 });
386  
387 });
388 });
389 break;
390 default:
391 self.emit('log', {
392 message: 'No such path.',
393 severity: 'error'
394 });
395 self.emit('data', {
396 status: 404,
397 data: new stream.Readable({
398 read(size) {
399 this.push(null);
400 }
401 }),
402 type: 'text/plain'
403 });
404 return;
405 }
406 });
407  
408 return this;
409 };
410  
411 util.inherits(POST, EventEmitter);
412 util.inherits(Cache, EventEmitter);
413 module.exports = POST;