UNPKG

undo3d

Version:

Undo3D helps you build free-roaming 3D web apps where thousands of users can collaborate creatively in real time. Expect the first public beta mid-2019, and the first production release mid-2020.

124 lines (96 loc) 4.38 kB
//// Mainly needed to serve new extensions like .mjs and .wasm with the proper //// MIME types. Later on we might add some handy developer helpers... //// INIT //// Ensure we’re running in Node, and that this script is in ‘undo3d/support/’. if ('object' !== typeof process || 'function' !== typeof require) throw Error('Run in Node.js') const containerDirs = __dirname.split('/').slice(-2).join('/') if ('undo3d/support' !== containerDirs ) throw Error(`In ‘${containerDirs}/’ not ‘undo3d/support/’`) //// Load Node’s 'http' and 'exec' modules, and set some constants. const app = require('http').createServer(server) , { exec } = require('child_process') , { readFileSync, existsSync } = require('fs') , { join } = require('path') , port = process.env.PORT || 3000 // Heroku sets the environment $PORT value , dir = join(__dirname, '..') //// Shut down if the server hits an error. This wouldn’t be good in production! app.on('error', e => { console.error('Server error: ' + e.message) app.close() }) //// Start the server and open a browser window. app.listen( port, () => { console.log(`Server is listening on port ${port}`) exec(`open http://localhost:${port}/`) //@TODO test in Windows and Linux }) //// SERVE //// Serve the proper response. function server (req, res) { //// Any GET request is a static file. if ('GET' === req.method) return serveFile(req, res) //// Anything else is an error. return error(res, 9300, 405, 'Use GET') } //// Serve a file. function serveFile (req, res) { //// Serve a request for the homepage. if ('/' === req.url || '/test' === req.url || '/test/' === req.url) { res.writeHead(200, { 'Content-Type':'text/html' }) res.end( readFileSync( join(dir, req.url, 'index.html') ) ) return } //// Get the extension, MIME type and absolute path. const ext = (req.url.match(/\.([a-z]{2,4}2?)$/) || [])[1] , mime = extToMimeType(ext) , path = join(dir, req.url) if (! ext) return error(res, 9101, 404, 'Invalid extension') if (! mime) return error(res, 9102, 404, `Extension .${ext} not recognised`) //// Check that the resource exists. if (! existsSync(path) ) return error(res, 9100, 404, 'No such resource, ' + req.url) //// Serve the request. res.writeHead(200, { 'Content-Type':mime }) res.end( readFileSync(path) ) } //// UTILITY //// Sends a response after a request which failed. function error (res, code, status, remarks, contentType='text/plain') { const headers = { 'Content-Type': contentType } remarks = remarks.replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\n/g, '\\n') res.writeHead(status, headers) res.end(`{ "code":${code}, "error":"${remarks}", "status":${status} }\n`) return false } //// Returns a given extension’s MIME type, or undefined if not recognised. function extToMimeType (ext) { return ({ js: 'application/javascript' // a standard JavaScript script , mjs: 'application/javascript' // the new JavaScript module extension , json: 'application/json' // JSON data, eg package.json , mem: 'application/wasm' // asm.js memory, used by threecap.js.mem @TODO proper MIME type? , wasm: 'application/wasm' // WebAssembly memory , mp3: 'audio/mpeg' // MP3 audio , ttf: 'font/ttf' // TrueType Font , otf: 'font/otf' // OpenType Font , woff: 'font/woff' // Web Open Font Format , woff2:'font/woff2' // Web Open Font Format 2.0 , gif: 'image/gif' // GIF image , jpg: 'image/jpeg' // JPEG image , jpeg: 'image/jpeg' // JPEG image , png: 'image/png' // PNG image , svg: 'image/svg+xml' // SVG image , ico: 'image/x-icon' // usually favicon.ico , css: 'text/css' // CSS stylesheet , htm: 'text/html' // HTML page , html: 'text/html' // HTML page , txt: 'text/plain' // plain text document , mp4: 'video/mp4' // MP4 video , webm: 'video/webm' // WEBM video })[ext] }