UNPKG

3.6 kBJavaScriptView Raw
1/**
2 * @license MIT
3 * Copyright (c) 2016 Craig Monro (cmroanirgo)
4 **/
5
6/*
7* This is the public api for 'view'
8* Returns a Promise
9*
10*/
11"use strict";
12
13var l = require('ergo-utils').log.module('ergo-api-view');
14var _ = require('ergo-utils')._;
15var fs = require('ergo-utils').fs.extend(require('fs-extra'));
16var watch_api = require('./watch');
17var Promise = require('bluebird')
18var http = require("http"),
19 url = require("url"),
20 path = require("path");
21var posix = path.posix; // for ALWAYS slosh-y stuff (for. uri's)
22
23
24var _server = null;
25function _serveWeb(options, context) { // book, port, web_root) {
26
27 options.web_root = context.getOutPath();
28 options.uri_root = posix.join('/', options.uri_root || '', '/');
29 options.port = options.port || 8181;
30
31 if (_server && _server.listening)
32 {
33 l.logw("Re-initialising...")
34 _server.options = options;
35 return _server;
36 }
37
38 l.log('Serving web from: ' + options.web_root)
39
40 _server = http.createServer(function(request, response) {
41 var uri = url.parse(request.url).pathname;
42
43 // strip out the uri root, making it ALWAYS relative to the root
44 if (options.uri_root != '/') {
45 var reluri = posix.relative(options.uri_root, uri);
46 var newuri = posix.join('/', reluri);
47 l.logd("uri '"+uri+"' => '"+reluri+"' => '"+newuri+"'");
48
49 if (reluri.length==0 && newuri=='/' && uri.substr(-1)!='/') {
50 // a request for '/blah', rather than '/blah/'
51 l.logw("302. '"+uri+"' => '"+reluri+"' => '"+newuri+"' redirecting");
52 response.writeHead(302, {"Location": "http://localhost:"+options.port+options.uri_root});
53 response.end();
54 return;
55 }
56
57 if (reluri.length>1 && reluri.substr(0,2)=='..' && uri.indexOf('favicon.ico')<0) { // bad food. reject it
58 l.loge("403. '"+uri+"' => '"+reluri+"' => '"+newuri+"' becomes a path outside the document root");
59 response.writeHead(403, {"Content-Type": "text/plain"});
60 response.write("403 Forbidden\nWhen trying to open: '" + newuri + "'");
61 response.end();
62 return;
63 }
64
65 uri = newuri;
66 }
67
68 var filename = path.join(options.web_root, uri);
69
70 fs.stat(filename, function(err, stats) {
71 if(err) {
72 l.loge("404. '"+filename+"'. Reason: " + err);
73 response.writeHead(404, {"Content-Type": "text/plain"});
74 response.write("404 Not Found\nWhen trying to open: '" + filename + "'");
75 response.end();
76 return;
77 }
78
79 if (stats.isDirectory())
80 filename = path.join(filename,'index.html');
81
82 fs.readFile(filename, "binary", function(err, file) {
83 if(err) {
84 l.loge("Failed to open '"+filename+"'. Reason: " + err);
85 response.writeHead(500, {"Content-Type": "text/plain"});
86 response.write(err + "\n");
87 response.end();
88 return;
89 }
90
91 response.writeHead(200);
92 response.write(file, "binary");
93 response.end();
94 });
95 });
96 });
97
98 _server.options = options;
99 l.logd("Ready to start")
100 _server.listen(parseInt(options.port, 10));
101 l.log("Web server started at\n => http://localhost:" + options.port + options.uri_root + "\nPress Ctrl + C to shutdown...");
102
103 return _server;
104}
105
106
107
108
109module.exports = function(options) {
110 return Promise.try(function() {
111 options = options || {};
112 var context = require('./config').getContextSync(options.working_dir);
113 context.mergeRuntimeOptions(options);
114
115 if (_.isDefined(options['watch']))
116 {
117 watch_api.watch(options, context);
118 l.logd("Watcher ready")
119 }
120 else if (_.isDefined(options['build'])) // else because the watch_api takes care of it otherwise
121 {
122 l.log("Building...")
123 watch_api.build(options, context);
124 }
125 return _serveWeb(options, context);
126 });
127}
\No newline at end of file