summaryrefslogtreecommitdiff
path: root/app/lib/static.js
blob: 08c877fe61497647601f8c769cf37f628ce493f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import {normalize, extname} from "node:path";
import {URL} from "node:url";
import fs from "node:fs";

const mimeTypes = {
  ".html": "text/html",
  ".css":  "text/css",
  ".js":   "text/javascript",
  ".cjs":  "text/javascript",
  ".mjs":  "text/javascript",
  ".json": "application/json",
  ".wasm": "application/wasm",
  ".pdf":  "application/pdf",
  ".txt":  "text/plain",
  ".md":   "text/markdown",

  ".png":  "image/png",
  ".jpg":  "image/jpg",
  ".svg":  "image/svg+xml",
  ".gif":  "image/gif",
  ".ico":  "image/x-icon",

  ".wav":  "audio/wav",
  ".mp3":  "audio/mpeg",
  ".mp4":  "video/mp4",

  ".eot":  "application/vnd.ms-fontobject",
  ".ttf":  "application/font-ttf",
  ".woff": "application/font-woff",

  ".gltf": "model/gltf+json",
  ".glb":  "model/gltf-binary",
};

export default class Static {
  constructor(root) {
    this.root = `${root||"."}`;
    this.e404 = fs.readFileSync(`${this.root}/404.html`);
    this.e500 = fs.readFileSync(`${this.root}/500.html`);
  }

  route(req, res) {
    if (req.method !== "GET") {
      res.writeHead(405, {"Content-Type": "text/plain"});
      res.end("405 Method Not Allowed");
      return;
    }

    const pathname = new URL(req.url, "file:").pathname; // TODO double-check
    const sane = normalize(pathname).replace(/^(\.\.\/)+/, "");
    let path = `${this.root}${sane}`; //Path.join(__dirname, sane);

    fs.stat(path, (err, stats) => {
      if (err) { if (["EACCES", "ENOENT", "EPERM"].includes(err.code)) {
        res.writeHead(404, {"Content-Type": "text/html"});
        return res.end(this.e404);
      } else { console.log(err);
        res.writeHead(500, {"Content-Type": "text/html"});
        return res.end(this.e500);
      }}

      if (stats.isDirectory()) path += "/index.html";
      const ext = `${extname(path)}`.toLowerCase();
      const stream = fs.createReadStream(path);

      stream.on("error", e => { console.log(e);
        res.writeHead(500, {"Content-Type": "text/html"});
        //res.end(`<pre>Error getting the file: ${e}</pre>`);
        res.end(this.e500);
      });

      res.setHeader("Content-Type", mimeTypes[ext]||"application/octet-stream");
      // TODO Caching?

      stream.pipe(res);
    });
  }
};