Source

WebServer.js

const http = require('http');
const path = require('path');
const fs = require("fs");
const GetFileType = require("./GetFileType/index.js");
const serveIndex = require("./serve-index-1.9.1-modded/index.js")

/**
 * WebServer's options.
 * @typedef WebServerOptions
 * @type {object}
 * @property {object} [options]
 * @property {Number} [options.port=3000] - Port to host the page.
 * @property {string} [options.directory=./public/] - The directory to load the file. 
 * @property {boolean} [options.serveindex=false] - If it's true, return the file list of the directory when the directory was requested. (If index.html exists in the directory, it will be sent preferentially)
 * @property {boolean} [options.acceptonlyget=true] - Only accepts get request.
 * @property {boolean} [options.useindexhtml=true] - If it's true, returns ./index.html file when requested directory.
 * @property {string} [options.rootfile=/index.html] - You can specify a file to load as a top page. (Please specify files in the published directory.)
 * @property {string} [options.errordocument._404=`${__dirname}/assets/def_pages/404.html`] - Html file path to respond on the request methods other than GET.
 * @property {string} [options.errordocument._405=`${__dirname}/assets/def_pages/405.html`] - Html file path to respond on not found.
 */

/**
 * Create a WebServer.
 * @constructor
 * @param {WebServerOptions} [options] - WebServer Options.
 * @returns {WebServer} 
 * @example <caption>Simple example</caption>
 * const {WebServer} = require("lite-web-server");
 * var server = new WebServer();
 * 
 * server.start();
 */

class WebServer {
  constructor(opts) {
    this.opts = run(opts);
    function run(options) {
      if (options) {
        if (!options.errordocument) {
          options.errordocument = {};
        }
        var error_doc404 = options.errordocument._404 || `${__dirname}/../assets/def_pages/404.html`;
        var error_doc405 = options.errordocument._405 || `${__dirname}/../assets/def_pages/405.html`;
        try {
          var _404 = fs.readFileSync(error_doc404);
        } catch (error) {
          console.error(new Error(`Invailed 404 error file location "${error_doc404}". Default location will used.`));
          options.errordocument._404 = `${__dirname}/../assets/def_pages/404.html`;
        }
        try {
          var _405 = fs.readFileSync(error_doc405);
        } catch (error) {
          console.error(new Error(`Invailed 405 error file location "${error_doc405}". Default location will used.`));
          options.errordocument._405 = `${__dirname}/../assets/def_pages/405.html`;
        }
      }
      return options;
    }

  }

  /**
   * @returns {Promise<Object | null>} 
   */

  start() {
    var _this = this;
    return new Promise(async function (resolve, reject) {
      var opt = _this.opts;
      if (!opt) {
        var options = {
          directory: "./public",
          serveindex: false,
          //notfound: `<!DOCTYPE html>\n<html>\n<head>\n<title>404 Not Found</title>\n</head>\n<body>\n<center>\n<h1>404 Not Found</h1>\n</center>\n<hr>\n<center>Node.js lite-web-server</center>\n</body>\n</html>`,
          port: 3000,
          acceptonlyget: true,
          useindexhtml: true,
          rootfile: "/index.html",
          //req405error: `<!DOCTYPE html>\n<html>\n<head>\n<title>405 Method Not Allowed</title>\n</head>\n<body>\n<center>\n<h1>405 Method Not Allowed</h1>\n</center>\n<hr>\n<center>Node.js lite-web-server</center>\n</body>\n</html>`,
          errordocument: {
            _404: `${__dirname}/../assets/def_pages/404.html`,
            _405: `${__dirname}/../assets/def_pages/405.html`
          }
        }
      } else {
        var _dir = opt.dir || opt.directory || "./public";
        if (_dir == "/") {
          var __dir = ".";
        } else if (_dir.endsWith("/")) {
          var __dir = _dir.slice(0, -1);
        } else {
          var __dir = _dir;
        }
        if (__dir.startsWith("/")) {
          var dir = "." + __dir;
        } else {
          var dir = __dir;
        }
        if (opt.acceptonlyget === false) {
          var acceptonlyget = false;
        } else {
          var acceptonlyget = true;
        }
        if (opt.useindexhtml === false) {
          var useindexhtml = false;
        } else {
          var useindexhtml = true;
        }
        if (opt.serveindex === true) {
          var serveindex = true;
        } else {
          var serveindex = false;
        }
        if (!opt.errordocument) {
          opt.errordocument = {};
        }
        var options = {
          directory: dir || "./public",
          serveindex: serveindex,
          //notfound: opt.notfound || `<!DOCTYPE html>\n<html>\n<head>\n<title>404 Not Found</title>\n</head>\n<body>\n<center>\n<h1>404 Not Found</h1>\n</center>\n<hr>\n<center>Node.js lite-web-server</center>\n</body>\n</html>`,
          port: opt.port || 3000,
          acceptonlyget: acceptonlyget,
          useindexhtml: useindexhtml,
          rootfile: opt.rootfile || "/index.html",
          //req405error: opt.req405error || `<!DOCTYPE html>\n<html>\n<head>\n<title>405 Method Not Allowed</title>\n</head>\n<body>\n<center>\n<h1>405 Method Not Allowed</h1>\n</center>\n<hr>\n<center>Node.js lite-web-server</center>\n</body>\n</html>`,
          errordocument: {
            _404: opt.errordocument._404 || `${__dirname}/../assets/def_pages/404.html`,
            _405: opt.errordocument._405 || `${__dirname}/../assets/def_pages/405.html`
          }
        }
      }

      try {
        var dir = await fs.readdirSync(options.directory);
      } catch (error) {
        reject(new Error(`Please create directory "${options.directory}" first.`));
        return;
      }
      try {
        var httpserver = http.createServer(async function (req, res) {
          if (!(req.method.toUpperCase() == "GET") && options.acceptonlyget == true) {
            try {
              var read_file = await fs.readFileSync(options.errordocument._405).toString();
              res.writeHead(405, { "Content-Type": "text/html" });
              return res.end(read_file);
            } catch (error) {
              res.writeHead(500, { "Content-Type": "text/html" });
              return res.end("<center><h1>Internal Server Error</h1></center>");
            }
          }
          var url = req.url;
          //console.log(url);
          if (url == "/") {
            var filedir = `${options.directory}${options.rootfile}`;
          } else if (url.endsWith("/") && options.useindexhtml) {
            var filedir = `${options.directory}${url}index.html`;
          } else {
            var filedir = `${options.directory}${url}`;
          }
          try {
            var httpcontent = (await GetFileType(filedir.toString())) || "text/plain";
            var file = await fs.readFileSync(filedir);
            res.writeHead(200, { "Content-Type": httpcontent });
            res.end(file);
          } catch (error) {
            try {
              try {
                var _loaddirurl = options.directory + "/";
                if (url.endsWith("/")) {
                  var __loaddirurl = _loaddirurl
                  try {
                    //console.log(_loaddirurl+url.slice(1))
                    fs.readdirSync(_loaddirurl + url.slice(1))
                  } catch (error) {
                    throw new Error(error)
                  }
                } else {
                  //console.log(req)
                  try {
                    //console.log(_loaddirurl+url.slice(1))
                    fs.readdirSync(_loaddirurl + url.slice(1))
                    res.writeHead(302, { location: `${req.url}/`, });
                    res.end();
                    return;
                  } catch (error) {
                    throw new Error(error)
                  }

                  //var loaddirurl = _loaddirurl + "/"
                }
                var loaddirurl = `${__loaddirurl}`
                //console.log(loaddirurl)
                try {
                  if (options.serveindex === false) {
                    var read_file = await fs.readFileSync(options.errordocument._404).toString();
                    var file = read_file.replace(/<!--\${404URL}-->/g, `${url}`);
                    res.writeHead(404, { "Content-Type": "text/html" });
                    res.end(file);
                    return;
                  }
                  var serveindex = serveIndex(loaddirurl, { icons: true, view: "details" });
                  serveindex(req, res);
                } catch (error) {
                  throw new Error(error)
                }

              } catch (error) {
                var read_file = await fs.readFileSync(options.errordocument._404).toString();
                var file = read_file.replace(/<!--\${404URL}-->/g, `${url}`);
                res.writeHead(404, { "Content-Type": "text/html" });
                res.end(file);
              }
            } catch (error) {
              res.writeHead(500, { "Content-Type": "text/html" });
              res.end("<center><h1>Internal Server Error</h1></center>");
            }
            //console.log(error)
          }
          //fs.Dir.readSync(options.directory+url)
          //console.log(options.directory)
        }).listen(options.port);
        _this.running = httpserver;
        _this.started = true;
        resolve(httpserver);
      } catch (error) {
        reject(new Error(error));
      }
    });
  }
}

module.exports = WebServer