From aacb40fd2c69db69ebdd75051bdaf3cfe1162c73 Mon Sep 17 00:00:00 2001 From: poikilos <7557867+poikilos@users.noreply.github.com> Date: Tue, 19 Mar 2019 18:43:13 -0400 Subject: [PATCH] fix responses, allow selectable (non-playername) skins --- webapp/.gitignore | 2 + webapp/CHANGELOG.md | 9 +- webapp/README.md | 13 +- webapp/masterserver.js | 32 +- webapp/minetestinfo.js | 89 +- webapp/no-javascript.html | 7 + webapp/node_modules/cookie-parser/HISTORY.md | 5 + webapp/node_modules/cookie-parser/README.md | 32 +- webapp/node_modules/cookie-parser/index.js | 113 +-- .../node_modules/cookie-parser/package.json | 41 +- webapp/package-lock.json | 222 +++-- webapp/package.json | 6 +- webapp/server.js | 890 ++++++++++-------- .../home.handlebars | 0 .../layouts/main.handlebars | 0 webapp/views/notes.txt | 1 + webapp/views/pages/index.ejs | 42 + webapp/views/pages/result.ejs | 32 + webapp/views/pages/skin-selection-form.ejs | 69 ++ webapp/views/pages/skin-upload-form.ejs | 45 + webapp/views/partials/footer.ejs | 1 + webapp/views/partials/head.ejs | 8 + webapp/views/partials/header.ejs | 18 + 23 files changed, 1064 insertions(+), 613 deletions(-) create mode 100644 webapp/no-javascript.html rename webapp/{views => views.deprecated}/home.handlebars (100%) rename webapp/{views => views.deprecated}/layouts/main.handlebars (100%) create mode 100644 webapp/views/notes.txt create mode 100644 webapp/views/pages/index.ejs create mode 100644 webapp/views/pages/result.ejs create mode 100644 webapp/views/pages/skin-selection-form.ejs create mode 100644 webapp/views/pages/skin-upload-form.ejs create mode 100644 webapp/views/partials/footer.ejs create mode 100644 webapp/views/partials/head.ejs create mode 100644 webapp/views/partials/header.ejs diff --git a/webapp/.gitignore b/webapp/.gitignore index 6dd754e..b00f350 100644 --- a/webapp/.gitignore +++ b/webapp/.gitignore @@ -1,3 +1,5 @@ +/public/ + # Logs logs *.log diff --git a/webapp/CHANGELOG.md b/webapp/CHANGELOG.md index c0d016f..6911186 100644 --- a/webapp/CHANGELOG.md +++ b/webapp/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [git] - 2019-03-19 ### Added - skin uploading via webapp (chooses Bucket_Game, or ENLIVEN if present) -- `npm install formidable mv` (switched from fs.rename to mv due to +- choosing existing (non-player `skin_*.png`) files +- `npm install multer` + - mv is no longer needed--had switched from fs.rename to mv due to rename not working across filesystems (tmp is commonly on a different - volume). + volume), but then switched from formidable to multer since multer has + built-in functionality to cancel upload if too large, but formidable + crashes (see ), so mv is + no longer needed) diff --git a/webapp/README.md b/webapp/README.md index ff79ab8..e142d16 100755 --- a/webapp/README.md +++ b/webapp/README.md @@ -12,11 +12,16 @@ EnlivenMinetest Node.js webapp for web management of minetest npm install ``` +## Features +* upload skin + ## Usage * start like: `node server.js` -* then it will listen on port 3000 -* change skin at localhost:3000/skin-form +* public/skins will be created automatically. To force updating skins + during startup, delete the public/skins directory if already exists + and is outdated. +* In browser, go to http://localhost:64638 * for security, no overwrite is allowed @@ -35,6 +40,8 @@ npm install not `RUN_IN_PLACE` * choose minetest worlds directory separately from bin in case not `RUN_IN_PLACE` +* try https://github.com/timbuchwaldt/node-async-fileupload +* try nodemon (automatically reloads changed js) ## Developer Notes @@ -80,7 +87,7 @@ fi cd "$target_dir" npm init #except changed jade to pug -npm install express static-favicon morgan cookie-parser body-parser debug pug passport passport-local mongoose formidable mv +npm install express static-favicon morgan cookie-parser body-parser debug pug passport passport-local mongoose multer mv #NOTE: multiparty has streaming like busboy, but is non-trivial to implement ``` diff --git a/webapp/masterserver.js b/webapp/masterserver.js index b69f044..07aa7df 100644 --- a/webapp/masterserver.js +++ b/webapp/masterserver.js @@ -1,32 +1,32 @@ var express = require('express'); var app = express(); - +var mt = require('./minetestinfo.js'); app.get('/get-players', function (req, res) { - res.setHeader('Content-Type', 'application/json'); - res.send(JSON.stringify(players)); + res.setHeader('Content-Type', 'application/json'); + res.send(JSON.stringify(mt.players(true))); }); -var last_announce_string = "none"; +var previousAnnounceStr = "none"; app.get('/last-announce', function (req, res) { - res.setHeader('Content-Type', 'text/plain'); - res.send(last_announce_string); + res.setHeader('Content-Type', 'text/plain'); + res.send(previousAnnounceStr); }); app.get('/announce', function (req, res) { - last_announce_string = JSON.stringify(req.body); - console.log("announce got:"+last_announce_string); - res.setHeader('Content-Type', 'text/plain'); - res.send(); + previousAnnounceStr = JSON.stringify(req.body); + console.log("announce got:" + previousAnnounceStr); + res.setHeader('Content-Type', 'text/plain'); + res.send(); }); var server = app.listen(3000, function () { - //console.log('express-handlebars example server listening on: 3000'); - var host = server.address().address; - var port = server.address().port; - console.log("listserver address:"); - console.log(JSON.stringify(server.address())); - console.log("(experimental WIP) Minetest master server is listening at http://%s:%s", host, port); + //console.log('express-handlebars example server listening on: 3000'); + var host = server.address().address; + var port = server.address().port; + console.log("listserver address:"); + console.log(JSON.stringify(server.address())); + console.log("(experimental WIP) Minetest master server is listening at http://%s:%s", host, port); }); diff --git a/webapp/minetestinfo.js b/webapp/minetestinfo.js index bc053cb..86cc3f7 100644 --- a/webapp/minetestinfo.js +++ b/webapp/minetestinfo.js @@ -9,17 +9,94 @@ exports.minetestPath = function() { } const myName = "minetestinfo.js"; -var skinDir = ""; -exports.skinDir = function () { - return skinDir; +var skinsPath = ""; +exports.skinsPath = function () { + return skinsPath; +} + +var selectableSkinFileNames = []; + +exports.selectableSkinFileNames = function() { + return selectableSkinFileNames; +} + +exports.players = function(isLoggedIn) { + return []; // TODO: implement this +} + +exports.setSkin = function(userName, skinFileName) { + var indirectName = "player_" + userName + ".skin"; + var indirectPath = exports.skinsPath() + "/" + indirectName; + //if (skinName.endsWith('.png')) { + //console.log("WARNING: skinName should not specify extension--removing .png"); + //skinName = skinName.substring(0, skinName.length-4); + //} + //var skinFileName = skinName + ".png"; + fs.writeFile(indirectPath, skinFileName, function(err, data) { + if (err) { + msg = err.message; + console.log(err); + // res.write(msg + "
") + } + else { + // res.write("Before the skin is applied, The minetestserver instance must be restarted.
") + msg = "Successfully wrote " + skinFileName; + console.log(msg + " to " + indirectPath + "."); + // res.write(msg + "
") + } + // res.end(ending); + }); } exports.regeneratePaths = function () { - skinDir = minetestPath + "/games/Bucket_Game/mods/codercore/coderskins/textures"; + skinsPath = minetestPath + "/games/Bucket_Game/mods/codercore/coderskins/textures"; if (fs.existsSync( minetestPath + "/games/ENLIVEN")) { - skinDir = minetestPath + "/games/ENLIVEN/mods/codercore/coderskins/textures"; + skinsPath = minetestPath + "/games/ENLIVEN/mods/codercore/coderskins/textures"; + } + console.log("[" + myName + "] skinsPath: \"" + skinsPath + "\""); + var publicPath = __dirname + "/public"; + var publicSkinsPath = publicPath + "/skins"; + if (!fs.existsSync(publicPath)) { + fs.mkdirSync(publicPath, 0744); + } + if (!fs.existsSync(publicSkinsPath)) { + fs.mkdirSync(publicSkinsPath, 0744); + fs.readdir(skinsPath, (err, files) => { + selectableSkinFileNames = []; + files.forEach(file => { + if (file.startsWith("skin_") && file.endsWith(".png")) { + var srcPath = skinsPath + '/' + file; + var dstPath = publicSkinsPath + '/' + file; + console.log("copying '" + srcPath + "' to '" + dstPath + "'"); + fs.copyFile(srcPath, dstPath, fs.constants.COPYFILE_EXCL, (err) => { + if (err) throw err; + selectableSkinFileNames.push(file); + // console.log('source.txt was copied to destination.txt'); + }); + } + else { + console.log("not a skin: " + file) + } + // console.log(file); + }); + }); } - console.log("[" + myName + "] skinDir: \"" + skinDir + "\""); + else { + fs.readdir(publicSkinsPath, (err, files) => { + selectableSkinFileNames = []; + files.forEach(file => { + if (file.startsWith("skin_") && file.endsWith(".png")) { + selectableSkinFileNames.push(file); + // console.log("detected existing " + file); + } + else { + console.log("bad skin: " + file) + } + // console.log(file); + }); + }); + } + } var thisMinetest = "/tank/local/owner/minetest"; if (fs.existsSync(thisMinetest)) { diff --git a/webapp/no-javascript.html b/webapp/no-javascript.html new file mode 100644 index 0000000..4116f54 --- /dev/null +++ b/webapp/no-javascript.html @@ -0,0 +1,7 @@ + + + +This feature requires javascript.
+Back to Home + + diff --git a/webapp/node_modules/cookie-parser/HISTORY.md b/webapp/node_modules/cookie-parser/HISTORY.md index 4f28f50..6ee7ece 100644 --- a/webapp/node_modules/cookie-parser/HISTORY.md +++ b/webapp/node_modules/cookie-parser/HISTORY.md @@ -1,3 +1,8 @@ +1.4.4 / 2019-02-12 +================== + + * perf: normalize `secret` argument only once + 1.4.3 / 2016-05-26 ================== diff --git a/webapp/node_modules/cookie-parser/README.md b/webapp/node_modules/cookie-parser/README.md index 05dbdc5..c17536a 100644 --- a/webapp/node_modules/cookie-parser/README.md +++ b/webapp/node_modules/cookie-parser/README.md @@ -1,8 +1,7 @@ # cookie-parser -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Node.js Version][node-version-image]][node-version-url] +[![NPM Version][npm-version-image]][npm-url] +[![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] @@ -19,7 +18,7 @@ $ npm install cookie-parser ## API ```js -var express = require('express') +var express = require('express') var cookieParser = require('cookie-parser') var app = express() @@ -38,11 +37,11 @@ Parse a cookie value as a JSON cookie. This will return the parsed JSON value if ### cookieParser.JSONCookies(cookies) -Given an object, this will iterate over the keys and call `JSONCookie` on each value. This will return the same object passed in. +Given an object, this will iterate over the keys and call `JSONCookie` on each value, replacing the original value with the parsed value. This returns the same object that was passed in. ### cookieParser.signedCookie(str, secret) -Parse a cookie value as a signed cookie. This will return the parsed unsigned value if it was a signed cookie and the signature was valid, otherwise it will return the passed value. +Parse a cookie value as a signed cookie. This will return the parsed unsigned value if it was a signed cookie and the signature was valid. If the value was not signed, the original value is returned. If the value was signed but the signature could not be validated, `false` is returned. The `secret` argument can be an array or string. If a string is provided, this is used as the secret. If an array is provided, an attempt will be made to unsign the cookie with each secret in order. @@ -55,14 +54,18 @@ The `secret` argument can be an array or string. If a string is provided, this i ## Example ```js -var express = require('express') +var express = require('express') var cookieParser = require('cookie-parser') var app = express() app.use(cookieParser()) -app.get('/', function(req, res) { +app.get('/', function (req, res) { + // Cookies that have not been signed console.log('Cookies: ', req.cookies) + + // Cookies that have been signed + console.log('Signed Cookies: ', req.signedCookies) }) app.listen(8080) @@ -73,13 +76,10 @@ app.listen(8080) ### [MIT Licensed](LICENSE) -[npm-image]: https://img.shields.io/npm/v/cookie-parser.svg +[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/cookie-parser/master +[coveralls-url]: https://coveralls.io/r/expressjs/cookie-parser?branch=master +[npm-downloads-image]: https://badgen.net/npm/dm/cookie-parser [npm-url]: https://npmjs.org/package/cookie-parser -[node-version-image]: https://img.shields.io/node/v/cookie-parser.svg -[node-version-url]: https://nodejs.org/en/download -[travis-image]: https://img.shields.io/travis/expressjs/cookie-parser/master.svg +[npm-version-image]: https://badgen.net/npm/v/cookie-parser +[travis-image]: https://badgen.net/travis/expressjs/cookie-parser/master [travis-url]: https://travis-ci.org/expressjs/cookie-parser -[coveralls-image]: https://img.shields.io/coveralls/expressjs/cookie-parser/master.svg -[coveralls-url]: https://coveralls.io/r/expressjs/cookie-parser?branch=master -[downloads-image]: https://img.shields.io/npm/dm/cookie-parser.svg -[downloads-url]: https://npmjs.org/package/cookie-parser diff --git a/webapp/node_modules/cookie-parser/index.js b/webapp/node_modules/cookie-parser/index.js index 59816a2..dd6d479 100644 --- a/webapp/node_modules/cookie-parser/index.js +++ b/webapp/node_modules/cookie-parser/index.js @@ -5,26 +5,26 @@ * MIT Licensed */ -'use strict'; +'use strict' /** * Module dependencies. * @private */ -var cookie = require('cookie'); -var signature = require('cookie-signature'); +var cookie = require('cookie') +var signature = require('cookie-signature') /** * Module exports. * @public */ -module.exports = cookieParser; -module.exports.JSONCookie = JSONCookie; -module.exports.JSONCookies = JSONCookies; -module.exports.signedCookie = signedCookie; -module.exports.signedCookies = signedCookies; +module.exports = cookieParser +module.exports.JSONCookie = JSONCookie +module.exports.JSONCookies = JSONCookies +module.exports.signedCookie = signedCookie +module.exports.signedCookies = signedCookies /** * Parse Cookie header and populate `req.cookies` @@ -36,39 +36,40 @@ module.exports.signedCookies = signedCookies; * @public */ -function cookieParser(secret, options) { - return function cookieParser(req, res, next) { +function cookieParser (secret, options) { + var secrets = !secret || Array.isArray(secret) + ? (secret || []) + : [secret] + + return function cookieParser (req, res, next) { if (req.cookies) { - return next(); + return next() } - var cookies = req.headers.cookie; - var secrets = !secret || Array.isArray(secret) - ? (secret || []) - : [secret]; + var cookies = req.headers.cookie - req.secret = secrets[0]; - req.cookies = Object.create(null); - req.signedCookies = Object.create(null); + req.secret = secrets[0] + req.cookies = Object.create(null) + req.signedCookies = Object.create(null) // no cookies if (!cookies) { - return next(); + return next() } - req.cookies = cookie.parse(cookies, options); + req.cookies = cookie.parse(cookies, options) // parse signed cookies if (secrets.length !== 0) { - req.signedCookies = signedCookies(req.cookies, secrets); - req.signedCookies = JSONCookies(req.signedCookies); + req.signedCookies = signedCookies(req.cookies, secrets) + req.signedCookies = JSONCookies(req.signedCookies) } // parse JSON cookies - req.cookies = JSONCookies(req.cookies); + req.cookies = JSONCookies(req.cookies) - next(); - }; + next() + } } /** @@ -79,15 +80,15 @@ function cookieParser(secret, options) { * @public */ -function JSONCookie(str) { +function JSONCookie (str) { if (typeof str !== 'string' || str.substr(0, 2) !== 'j:') { - return undefined; + return undefined } try { - return JSON.parse(str.slice(2)); + return JSON.parse(str.slice(2)) } catch (err) { - return undefined; + return undefined } } @@ -99,21 +100,21 @@ function JSONCookie(str) { * @public */ -function JSONCookies(obj) { - var cookies = Object.keys(obj); - var key; - var val; +function JSONCookies (obj) { + var cookies = Object.keys(obj) + var key + var val for (var i = 0; i < cookies.length; i++) { - key = cookies[i]; - val = JSONCookie(obj[key]); + key = cookies[i] + val = JSONCookie(obj[key]) if (val) { - obj[key] = val; + obj[key] = val } } - return obj; + return obj } /** @@ -125,28 +126,28 @@ function JSONCookies(obj) { * @public */ -function signedCookie(str, secret) { +function signedCookie (str, secret) { if (typeof str !== 'string') { - return undefined; + return undefined } if (str.substr(0, 2) !== 's:') { - return str; + return str } var secrets = !secret || Array.isArray(secret) ? (secret || []) - : [secret]; + : [secret] for (var i = 0; i < secrets.length; i++) { - var val = signature.unsign(str.slice(2), secrets[i]); + var val = signature.unsign(str.slice(2), secrets[i]) if (val !== false) { - return val; + return val } } - return false; + return false } /** @@ -159,23 +160,23 @@ function signedCookie(str, secret) { * @public */ -function signedCookies(obj, secret) { - var cookies = Object.keys(obj); - var dec; - var key; - var ret = Object.create(null); - var val; +function signedCookies (obj, secret) { + var cookies = Object.keys(obj) + var dec + var key + var ret = Object.create(null) + var val for (var i = 0; i < cookies.length; i++) { - key = cookies[i]; - val = obj[key]; - dec = signedCookie(val, secret); + key = cookies[i] + val = obj[key] + dec = signedCookie(val, secret) if (val !== dec) { - ret[key] = dec; - delete obj[key]; + ret[key] = dec + delete obj[key] } } - return ret; + return ret } diff --git a/webapp/node_modules/cookie-parser/package.json b/webapp/node_modules/cookie-parser/package.json index 88737ad..b282179 100644 --- a/webapp/node_modules/cookie-parser/package.json +++ b/webapp/node_modules/cookie-parser/package.json @@ -1,28 +1,28 @@ { - "_from": "cookie-parser", - "_id": "cookie-parser@1.4.3", + "_from": "cookie-parser@^1.4.3", + "_id": "cookie-parser@1.4.4", "_inBundle": false, - "_integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", + "_integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==", "_location": "/cookie-parser", "_phantomChildren": {}, "_requested": { - "type": "tag", + "type": "range", "registry": true, - "raw": "cookie-parser", + "raw": "cookie-parser@^1.4.3", "name": "cookie-parser", "escapedName": "cookie-parser", - "rawSpec": "", + "rawSpec": "^1.4.3", "saveSpec": null, - "fetchSpec": "latest" + "fetchSpec": "^1.4.3" }, "_requiredBy": [ "#USER", "/" ], - "_resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", - "_shasum": "0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5", - "_spec": "cookie-parser", - "_where": "/home/owner/GitHub/EnlivenMinetest/webapp", + "_resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz", + "_shasum": "e6363de4ea98c3def9697b93421c09f30cf5d188", + "_spec": "cookie-parser@^1.4.3", + "_where": "/home/owner/git/EnlivenMinetest/webapp", "author": { "name": "TJ Holowaychuk", "email": "tj@vision-media.ca", @@ -43,11 +43,19 @@ "cookie-signature": "1.0.6" }, "deprecated": false, - "description": "cookie parsing with signatures", + "description": "Parse HTTP request cookies", "devDependencies": { - "istanbul": "0.4.3", - "mocha": "2.5.3", - "supertest": "1.1.0" + "deep-equal": "1.0.1", + "eslint": "5.13.0", + "eslint-config-standard": "12.0.0", + "eslint-plugin-import": "2.16.0", + "eslint-plugin-markdown": "1.0.0", + "eslint-plugin-node": "7.0.1", + "eslint-plugin-promise": "4.0.1", + "eslint-plugin-standard": "4.0.0", + "istanbul": "0.4.5", + "mocha": "5.2.0", + "supertest": "3.4.2" }, "engines": { "node": ">= 0.8.0" @@ -69,9 +77,10 @@ "url": "git+https://github.com/expressjs/cookie-parser.git" }, "scripts": { + "lint": "eslint --plugin markdown --ext js,md .", "test": "mocha --reporter spec --bail --check-leaks test/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" }, - "version": "1.4.3" + "version": "1.4.4" } diff --git a/webapp/package-lock.json b/webapp/package-lock.json index b13276f..3f6dde0 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -13,16 +13,16 @@ "negotiator": "0.6.1" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, "body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -40,13 +40,18 @@ "type-is": "~1.6.15" } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "dicer": "0.2.5", + "readable-stream": "1.1.x" } }, "bytes": { @@ -54,10 +59,45 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } }, "content-disposition": { "version": "0.5.2", @@ -75,9 +115,9 @@ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz", + "integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==", "requires": { "cookie": "0.3.1", "cookie-signature": "1.0.6" @@ -88,6 +128,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -106,11 +151,25 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -177,11 +236,6 @@ "unpipe": "~1.0.0" } }, - "formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" - }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -192,18 +246,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -220,15 +262,6 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -239,6 +272,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -272,14 +310,6 @@ "mime-db": "~1.33.0" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", @@ -298,14 +328,19 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "multer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.1.tgz", + "integrity": "sha512-zzOLNRxzszwd+61JFuAo0fxdQfvku12aNJgnla0AQ+hHxFmfc/B7jBVuPr5Rmvu46Jze/iJrFpSOsD7afO8SDw==", "requires": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" } }, "n-readlines": { @@ -313,16 +348,16 @@ "resolved": "https://registry.npmjs.org/n-readlines/-/n-readlines-0.2.8.tgz", "integrity": "sha512-FRr6GU0vooiPAuHMBt/Pspm4htItJKCehs8Q2urdUuigXsdQzP8V03UaQedeCmiygqJwtuGhnidWHRHhUOAg9w==" }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" - }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -331,29 +366,21 @@ "ee-first": "1.1.1" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, "proxy-addr": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", @@ -407,12 +434,15 @@ } } }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "glob": "^6.0.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, "safe-buffer": { @@ -461,6 +491,16 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -470,11 +510,21 @@ "mime-types": "~2.1.18" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -485,10 +535,10 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" } } } diff --git a/webapp/package.json b/webapp/package.json index f3743aa..4b4f89b 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -12,10 +12,10 @@ "license": "BSD", "dependencies": { "body-parser": "^1.18.2", - "cookie-parser": "^1.4.3", + "cookie-parser": "^1.4.4", + "ejs": "^2.6.1", "express": "^4.7.2", - "formidable": "^1.2.1", - "mv": "^2.1.1", + "multer": "^1.4.1", "n-readlines": "^0.2.8" } } diff --git a/webapp/server.js b/webapp/server.js index 5701d9b..fb6864d 100644 --- a/webapp/server.js +++ b/webapp/server.js @@ -2,457 +2,529 @@ // Howto: see README.md //function getUserHome() { - //return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']; + //return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']; //} -var tz_offset = 240; //subtract this from server time to get local time; 4hrs is 240; 5hrs is 300 -// TODO: handle tz_offset not divisible by 60 -// var selected_date_s = null; -// selected_date_s = "2018-05-08"; +var tzOffsetMinutes = 240; //subtract this from server time to get local time; 4hrs is 240; 5hrs is 300 +// TODO: handle tzOffsetMinutes not divisible by 60 +// var selectedDateStr = null; +// selectedDateStr = "2018-05-08"; -var express = require('express'), +const express = require('express'), + multer = require('multer'), // var exphbs = require("express-handlebars"); // exphbs = require('../../'); // "express-handlebars" - cookieParser = require('cookie-parser'), - bodyParser = require('body-parser'), - //session = require('express-session'), - fs = require('fs'), - readlines = require('n-readlines'); + cookieParser = require('cookie-parser'), + bodyParser = require('body-parser'), + //session = require('express-session'), + fs = require('fs'), + readlines = require('n-readlines'); const os = require('os'); -var formidable = require('formidable') -var querystring = require("querystring"); // built-in -var mv = require('mv'); -// TODO: var config = require(storage_path + '/config.js') // config file contains all tokens and other private info +//var formidable = require('formidable'); +const querystring = require("querystring"); // built-in +// TODO: var config = require(storagePath + '/config.js') // config file contains all tokens and other private info // var fun = require('./functions.js'); // functions file contains our non-app-specific functions including those for our Passport and database work var mt = require('./minetestinfo.js'); // functions file contains our non-app-specific functions including those for our Passport and database work - // var util = require('util') var app = express(); +app.set('view engine', 'ejs'); +// see https://medium.com/@TheJesseLewis/how-to-make-a-basic-html-form-file-upload-using-multer-in-an-express-node-js-app-16dac2476610 +const port = process.env.PORT || 64638; +app.use(bodyParser.urlencoded({extended:false})); // handle body requests +app.use(bodyParser.json()); // make JSON work +app.use('/public', express.static(__dirname + '/public')); + //app.engine('handlebars', exphbs({defaultLayout: 'main'})); //app.set('view engine', 'handlebars'); var players = []; -var player_indices = {}; -var play_dates = []; +var playerIndices = {}; +var activityDates = []; //see https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction -var prev_line = null; +var previousLine = null; //#region derived from mtsenliven.py -var msgprefix_flags = ["WARNING[Server]: ", "ACTION[Server]: "] -var msgprefix_lists = {} // where flag is key -var mf_len = msgprefix_flags.length; -for (var mf_i=0; mf_i -1) { - msgprefix_number = mf_i; - msgprefix = msgprefix_flags[mf_i]; - break; - } - } - var skip_date_enable = false; - for (var uf_i=0; uf_i -1) { - verb_number = uf_i; - verb = unique_flags[uf_i]; - date_s = line.substring(0,10).trim(); - //if (selected_date_s==null || selected_date_s==date_s) { - //console.log("(verbose message in process_logline) using '" + date_s + "' since selected '"+selected_date_s+"'"); - time_s = line.substring(time_start_i, time_start_i+8); - //console.log("using time "+time_s); - if (msgprefix!=null) { - player_name = line.substring(msgprefix_i+msgprefix.length, verb_i).trim(); - var ip_flag = " ["; - var ip_i = player_name.indexOf(ip_flag); - if (ip_i > -1) { - player_ip = player_name.substring(ip_i+ip_flag.length, player_name.length-1); - player_name = player_name.substring(0,ip_i); - } - } - else { - player_name = "<missing msgprefix&rt;"; - } - //} - //else { - // skip_date_enable = true; - //console.log("WARNING in process_logline: skipping '" + date_s + "' since not '"+selected_date_s+"'"); - //} - break; - } - } - var index = -1; // player index - if (player_name != null) { - if (player_name.length > 0) { - if (player_indices.hasOwnProperty(player_name)) { - index = player_indices[player_name]; - index_msg = "cached "; - } - else { - index = players.length; - //players.push({}); - players[index] = {}; - players[index].display_name = player_name; - player_indices[player_name] = index; - //console.log("created new index "+index); - } - } - else { - console.log("WARNING in process_logline: zero-length player name"); - } - } - if (index<0 && (verb=="leaves game"||verb=="joins game")) { - console.log("(ERROR in process_logline) " + index_msg + - "index was '"+index+"' but date was present '" + - date_s + "' for '"+line+"' (no player found, but" + - "verb is a player verb)."); - } - var play_date_enable = false; - if (verb == "leaves game") { - if (index > -1) { - var play_i = -1; - if (!players[index].hasOwnProperty("plays")) { - players[index].plays = {}; - } - if (!players[index].plays.hasOwnProperty(date_s)) { - //leave login time blank--player must have logged in before the available part of the log began - players[index].plays[date_s] = []; - players[index].plays[date_s].push({}); - play_i = 0; - } - else { - if (players[index].plays[date_s].length==0) players[index].plays[date_s].push({}); - play_i = players[index].plays[date_s].length - 1; - if (players[index].plays[date_s][play_i].hasOwnProperty("logout_time")) { - //If last entry is incomplete, start a new one: - players[index].plays[date_s].push({}); - play_i++; - } - } - players[index].plays[date_s][play_i].logout_time = time_s; - play_date_enable = true; - } - } - else if (verb == "joins game") { - if (index > -1) { - if (player_ip!=null) { - players[index].player_ip = player_ip; - var play_i = -1; - if (!players[index].hasOwnProperty("plays")) { - players[index].plays = {}; - } - if (!players[index].plays.hasOwnProperty(date_s)) { - players[index].plays[date_s] = []; - play_i = 0; - } - else play_i = players[index].plays[date_s].length; - players[index].plays[date_s].push({}); - //console.log(verb+" on "+date_s+" (length "+players[index].plays[date_s].length+") play "+play_i+"+1 for player ["+index+"] "+player_name+"..."); - players[index].plays[date_s][play_i].login_time = time_s; - play_date_enable = true; - } - // else redundant (server writes " joins game " again - // and shows list of players instead of ip). - //TODO: else analyze list of players to confirm in case player logged in all day - } - } - if (play_date_enable) { - if (date_s.length>0) { - if (play_dates.indexOf(date_s) < 0) { - play_dates.push(date_s); - } - } - } +function processLogLine(line, lineNumber) { + //selectedDateStr + //TODO: use storeUniqueLogData instead of this function + var playerName = null; + var verb = ""; + var timeStr = ""; + var dateStr = ""; + var playerIP = null; + const timeStartInt = 11; + var ufLen = uniqueFlags.length; + mfLen = msgPrefixFlags.length; + var verbIndex = -1; + var verbNumber = -1; + var msgPrefixIndex = -1; + var msgPrefixNumber = -1; + var msgprefix = null; + var indexMsg = ""; + for (var mfIndex=0; mfIndex -1) { + msgPrefixNumber = mfIndex; + msgprefix = msgPrefixFlags[mfIndex]; + break; + } + } + var skipDateEnable = false; + for (var ufIndex=0; ufIndex -1) { + verbNumber = ufIndex; + verb = uniqueFlags[ufIndex]; + dateStr = line.substring(0,10).trim(); + //if (selectedDateStr==null || selectedDateStr==dateStr) { + //console.log("(verbose message in processLogLine) using '" + dateStr + "' since selected '"+selectedDateStr+"'"); + timeStr = line.substring(timeStartInt, timeStartInt+8); + //console.log("using time "+timeStr); + if (msgprefix!=null) { + playerName = line.substring(msgPrefixIndex+msgprefix.length, verbIndex).trim(); + var ipFlag = " ["; + var ipIndex = playerName.indexOf(ipFlag); + if (ipIndex > -1) { + playerIP = playerName.substring(ipIndex+ipFlag.length, playerName.length-1); + playerName = playerName.substring(0, ipIndex); + } + } + else { + playerName = "<missing msgprefix&rt;"; + } + //} + //else { + // skipDateEnable = true; + //console.log("WARNING in processLogLine: skipping '" + dateStr + "' since not '"+selectedDateStr+"'"); + //} + break; + } + } + var index = -1; // player index + if (playerName != null) { + if (playerName.length > 0) { + if (playerIndices.hasOwnProperty(playerName)) { + index = playerIndices[playerName]; + indexMsg = "cached "; + } + else { + index = players.length; + //players.push({}); + players[index] = {}; + players[index].displayName = playerName; + playerIndices[playerName] = index; + //console.log("created new index "+index); + } + } + else { + console.log("WARNING in processLogLine: zero-length player name"); + } + } + if (index<0 && (verb=="leaves game"||verb=="joins game")) { + console.log("(ERROR in processLogLine) " + indexMsg + + "index was '"+index+"' but date was present '" + + dateStr + "' for '"+line+"' (no player found, but" + + "verb is a player verb)."); + } + var playDateEnable = false; + if (verb == "leaves game") { + if (index > -1) { + var playIndex = -1; + if (!players[index].hasOwnProperty("plays")) { + players[index].plays = {}; + } + if (!players[index].plays.hasOwnProperty(dateStr)) { + //leave login time blank--player must have logged in before the available part of the log began + players[index].plays[dateStr] = []; + players[index].plays[dateStr].push({}); + playIndex = 0; + } + else { + if (players[index].plays[dateStr].length==0) players[index].plays[dateStr].push({}); + playIndex = players[index].plays[dateStr].length - 1; + if (players[index].plays[dateStr][playIndex].hasOwnProperty("logoutTime")) { + //If last entry is incomplete, start a new one: + players[index].plays[dateStr].push({}); + playIndex++; + } + } + players[index].plays[dateStr][playIndex].logoutTime = timeStr; + playDateEnable = true; + } + } + else if (verb == "joins game") { + if (index > -1) { + if (playerIP!=null) { + players[index].playerIP = playerIP; + var playIndex = -1; + if (!players[index].hasOwnProperty("plays")) { + players[index].plays = {}; + } + if (!players[index].plays.hasOwnProperty(dateStr)) { + players[index].plays[dateStr] = []; + playIndex = 0; + } + else playIndex = players[index].plays[dateStr].length; + players[index].plays[dateStr].push({}); + //console.log(verb+" on "+dateStr+" (length "+players[index].plays[dateStr].length+") play "+playIndex+"+1 for player ["+index+"] "+playerName+"..."); + players[index].plays[dateStr][playIndex].loginTime = timeStr; + playDateEnable = true; + } + // else redundant (server writes " joins game " again + // and shows list of players instead of ip). + //TODO: else analyze list of players to confirm in case player logged in all day + } + } + if (playDateEnable) { + if (dateStr.length>0) { + if (activityDates.indexOf(dateStr) < 0) { + activityDates.push(dateStr); + } + } + } } -function store_unique_log_data(output, line_number, err_flag=false) { - var ret = ""; - var output_strip = output.trim(); - var u_prefix = "active block modifiers took "; - var u_suffix = "ms (longer than 200ms)"; - // (out_bytes is bytes) - var show_enable = true; - var found_flag = null; - var f_i = null; - var always_show_enable = false; - var msg_msg = "previous message"; - var uf_len = unique_flags.length; - for (var uf_i=0; uf_i= 0) { - found_flag = msgprefix_flags[mf_i]; - break; - } - } - if (found_flag!=null) { - var sub_msg = output.substring(f_i+flag.length).trim(); - var nuw_len = non_unique_wraps.length; - for (var nuw_i=0; nuw_i -1) { - show_enable = false; - } - else { - msgprefix_lists[found_flag].push(sub_msg); - } - } - } - if (show_enable) { - ret = output_strip; - if (found_flag != null) { - ret += "\n [ EnlivenMinetest ] " + msg_msg + " will be suppressed"; - } - } - return ret; +function storeUniqueLogData(output, lineNumber, errFlag=false) { + var ret = ""; + var outputTrim = output.trim(); + var uPrefix = "active block modifiers took "; + var uSuffix = "ms (longer than 200ms)"; + // (outBytes is bytes) + var showEnable = true; + var foundFlag = null; + var fIndex = null; + var alwaysShowEnable = false; + var msgMsg = "previous message"; + var ufLen = uniqueFlags.length; + for (var ufIndex=0; ufIndex= 0) { + foundFlag = msgPrefixFlags[mfIndex]; + break; + } + } + if (foundFlag!=null) { + var subMsg = output.substring(fIndex+flag.length).trim(); + var nUWLen = nonUniqueWraps.length; + for (var nUWIndex=0; nUWIndex -1) { + showEnable = false; + } + else { + msgPrefixLists[foundFlag].push(subMsg); + } + } + } + if (showEnable) { + ret = outputTrim; + if (foundFlag != null) { + ret += "\n [ EnlivenMinetest ] " + msgMsg + " will be suppressed"; + } + } + return ret; } -function read_log() { - if (players==null) players = []; - if (player_indices==null) player_indices = {}; - // os.homedir() + "/.minetest/debug_archived/2018/05/08.txt", - // var log_paths = [os.homedir() + "/.minetest/debug.txt"]; - var log_paths = [os.homedir() + "/minetest/bin/debug.txt"]; - var lp_len = log_paths.length; - for (var lp_i=0; lp_i'+"\n"); + //res.write('
'+"\n"); + //res.write('User Name (case-sensitive): '+"\n"); + //res.write('Select a png image to upload:'+"\n"); + //res.write(''+"\n"); + //res.write(''+"\n"); + //res.write('
'+"\n"); + //res.end(ending); + var msg = ""; + res.render('pages/skin-upload-form', { + msg: msg + }); +}); +app.get('/skin-selection-form', function(req, res, next) { + var msg = ""; + res.render('pages/skin-selection-form', { + msg: msg, + skinFileNames: mt.selectableSkinFileNames() + }); }); -//using express & formidable: -app.post('/set-skin', function (req, res){ - var form = new formidable.IncomingForm(); - // from coderskins/readme.txt: - //To install a specific skin for a specific player, name the PNG file - //to be used as follows: - //player_NAME.png - //where NAME is the player's in-game nick. Then copy the PNG file into - //the mod's "textures" directory. - //The PNG file should be a standard Minetest 64x32 or Minecraft 64x64 - //"skin" file. - //Or, if you prefer, create a text file, in the mod's "textures" direc- - //tory with a similar filename: - //player_NAME.skin - //(OldCoder, 2019) - var directPath = ""; - var indirectPath = ""; - var destNameNoExt = ""; - var msg = "Uploading..."; - form.parse(req, function(err, fields, files) { - if (err) next(err); - destNameNoExt = destNameNoExt = "player_" + fields.userName; - directPath = mt.skinDir() + "/" + destNameNoExt + ".png"; - indirectPath = mt.skinDir() + "/" + destNameNoExt + ".skin"; - // TODO: make sure my_file and userName values are present - if (files.hasOwnProperty('userFile')) { - if (fields.hasOwnProperty('userName')) { - var originalPath = files.userFile.path; - console.log("trying to rename " + files.userFile.path - + " to " + directPath); - // NOTE: rename does not work if tmp is on different device (common) - mv(files.userFile.path, directPath, function(err) { - // fs.rename(files.userFile.path, directPath, function(err) { - if (err) { - msg = "Failed to rename " + originalPath - + " to " + directPath; - console.log(msg); - console.log(JSON.stringify(err)); - msg += "
\n"; - //next(err); -// TODO: why does next above show: -//ReferenceError: next is not defined - //at /home/owner/git/EnlivenMinetest/webapp/server.js:355:6 - //at FSReqWrap.oncomplete (fs.js:135:15) +// see "new way" of handling multer errors: https://github.com/expressjs/multer#error-handling +var singleSkinUpload = skinUpload.single('userFile'); +app.post('/upload-skin', function(req, res) { + singleSkinUpload(req, res, function(err) { + if (err instanceof multer.MulterError) { + // A Multer error occurred when uploading. + res.render('multer error', { error: err }); + //res.render('pages/result', { + //msg: "An error occurred in processing the form: " + err, + //}); + } else if (err) { + // An unknown error occurred when uploading. + res.render('unknown error', { error: err }); + //res.render('pages/result', { + //msg: "An error occurred in processing: " + err, + //}); + } + else { + // var ending = ""; + var msg = ""; + // ending += 'Back to Main Site
' + "\n"; + // ending += 'Back to Upload
' + "\n"; + // ending += ''; + // res.write(''); + // res.write(''); + if (!req.fileValidationError) { + // res.write('

Complete!

'); + msg = "Complete!"; + } + else { + msg = req.fileValidationError; + // res.write('

' + req.fileValidationError + '

'); + } + //res.end(ending); + res.render('pages/result', { + msg: msg + }); + } + }) +}); - } - else { - var thisData = destNameNoExt + ".png"; - fs.writeFile(indirectPath, thisData, function(err, data) { - if (err) console.log(err); - console.log("Successfully wrote " + thisData - + " to "+indirectPath+"."); - }); - } - res.end(); - }); - } - else { - console.log("userName is undefined."); - } - } - else { - console.log("userFile is undefined."); - } - }); - //form.on('fileBegin', function (name, file){ - ////file.path = __dirname + '/uploads/' + file.name; - //// file.path = skinDir + "/" + file.name; - //// manual_path = "player_" + - //}); - form.on('file', function (name, file){ - msg = 'Uploaded ' + file.name + "
\n"; - console.log(msg); +app.get('/select-skin', function(req, res, next) { + mt.setSkin(req.query.userName, req.query.skinFileName); + res.render('pages/result', { + msg: "Complete." }); - //res.sendFile(__dirname + '/index.html'); - res.redirect("/?msg=" + querystring.stringify(msg)); }); -app.get('/', function (req, res) { - var ret = ""; - ret += ''; - // Whenever server starts the following is logged - // (see also etc/example-input.txt): - // - //------------- - // Separator - //------------- - // - var selected_date_s = null; - if (req.query.date) selected_date_s = req.query.date - if (req.query.msg != undefined) { - ret += "
\n"; - //ret += "" + querystring.parse(req.query.msg) + "
\n"; - // line above causes: - //TypeError: Cannot convert object to primitive value - //at /home/owner/git/EnlivenMinetest/webapp/server.js:390:16 - //at Layer.handle [as handle_request] (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/layer.js:95:5) - //at next (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/route.js:137:13) - //at Route.dispatch (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/route.js:112:3) - //at Layer.handle [as handle_request] (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/layer.js:95:5) - //at /home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/index.js:281:22 - //at Function.process_params (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/index.js:335:12) - //at next (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/index.js:275:10) - //at expressInit (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/middleware/init.js:40:5) - //at Layer.handle [as handle_request] (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/layer.js:95:5) +app.get('/', function(req, res, next) { + //var ret = ""; - ret += "
\n"; - } - ret += "Server must restart for uploaded skins to take effect.
\n"; - ret += "assuming minetestserver ran as: " + os.homedir() + "
\n"; - ret += "timezone (tz_offset/60*-1): " + (Math.floor(tz_offset/60)*-1) + '
\n'; - ret += 'date: ' + selected_date_s + '
'+"\n"; - //ret += 'var play_dates = [];'; - var pdLength = 0; - if (play_dates != null) pdLength = play_dates.length; - for (var pd_i = 0; pd_i < pdLength; pd_i++) { - //ret += 'play_dates.push("' + play_dates[pd_i] + '");'; - if (selected_date_s!=play_dates[pd_i]) { - ret += ''+play_dates[pd_i]+' '; - } - else { - ret += play_dates[pd_i]+' '; - } - } - //if (selected_date_s==null) { - //ret += '2018-05-08'; - //} - //see ~/.minetest/debug.txt - //and logs archived by EnlivenMinetest: - //~/.minetest/debug_archived/2018/05/08.txt - ret += ` -
- -Your browser does not support the canvas element. - -
loading...
-`; - ret += ''; - res.send(ret); - //res.render('home'); + //res.write(''); + //res.write(''); + // Whenever server starts the following is logged + // (see also etc/example-input.txt): + // + //------------- + // Separator + //------------- + // + var selectedDateStr = null; + var msg = ""; + if (req.query.date) selectedDateStr = req.query.date + if (req.query.msg != undefined) { + //res.write("
"); + //res.write("" + querystring.parse(req.query.msg) + "
\n"); + // line above causes: + //TypeError: Cannot convert object to primitive value + //at /home/owner/git/EnlivenMinetest/webapp/server.js:390:16 + //at Layer.handle [as handle_request] (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/layer.js:95:5) + //at next (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/route.js:137:13) + //at Route.dispatch (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/route.js:112:3) + //at Layer.handle [as handle_request] (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/layer.js:95:5) + //at /home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/index.js:281:22 + //at Function.process_params (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/index.js:335:12) + //at next (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/index.js:275:10) + //at expressInit (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/middleware/init.js:40:5) + //at Layer.handle [as handle_request] (/home/owner/git/EnlivenMinetest/webapp/node_modules/express/lib/router/layer.js:95:5) + + //res.write("
"); + } + //res.write('

Upload Skin

'); + //res.write("

Server info

"); + //res.write("
    "); + //res.write("
  • assuming minetestserver ran as: " + os.homedir() + "
  • "); + //res.write("
  • timezone (tzOffsetMinutes/60*-1): " + (Math.floor(tzOffsetMinutes/60)*-1) + '' + "
  • "); + //res.write('
  • date: ' + ((selectedDateStr!=null)?selectedDateStr:"not selected") + '' + "
  • "); + //res.write('var activityDates = [];'); + var pdLength = 0; + if (activityDates != null) pdLength = activityDates.length; + var logDates = []; + for (var pdIndex = 0; pdIndex < pdLength; pdIndex++) { + //res.write('activityDates.push("' + activityDates[pdIndex] + '");'); + if (selectedDateStr!=activityDates[pdIndex]) { + //res.write(''+activityDates[pdIndex]+' '); + logDates.push( { date:activityDates[pdIndex], active:true}); + } + else { + logDates.push( { date:activityDates[pdIndex], active:false}); + //res.write(activityDates[pdIndex]+' '); + } + } + //if (selectedDateStr==null) { + //res.write('2018-05-08'); + //} + //see ~/.minetest/debug.txt + //and logs archived by EnlivenMinetest: + //~/.minetest/debug_archived/2018/05/08.txt + //res.write(''); + //res.end(''); + //res.render('home'); + + res.render('pages/index', { + msg: msg, + serverUser: os.homedir(), + tzOffset: (Math.floor(tzOffsetMinutes/60)*-1), + selectedDate: ((selectedDateStr!=null)?selectedDateStr:"not selected"), + logDates: logDates + }); }); -var server = app.listen(64638, function () { - // 8123 is default for Minecraft DynMap - // 64638 spells 'minet' on a telephone keypad, but 6463, 6472 is already Discord RPC server - //console.log('express-handlebars example server listening on: 3000'); - var host = server.address().address; - var port = server.address().port; - console.log("server address:"); - console.log(JSON.stringify(server.address())); - console.log("reading log..."); - read_log(); - console.log("EnlivenMinetest webapp is listening at http://%s:%s", host, port); +var server = app.listen(port, function () { + // 8123 is default for Minecraft DynMap + // 64638 spells 'minet' on a telephone keypad, but 6463, 6472 is already Discord RPC server + //console.log('express-handlebars example server listening on: ' + port); + var host = server.address().address; + var port = server.address().port; + console.log("server address:"); + console.log(JSON.stringify(server.address())); + console.log("reading log..."); + readLog(); + console.log("EnlivenMinetest webapp is listening at http://%s:%s", host, port); }); diff --git a/webapp/views/home.handlebars b/webapp/views.deprecated/home.handlebars similarity index 100% rename from webapp/views/home.handlebars rename to webapp/views.deprecated/home.handlebars diff --git a/webapp/views/layouts/main.handlebars b/webapp/views.deprecated/layouts/main.handlebars similarity index 100% rename from webapp/views/layouts/main.handlebars rename to webapp/views.deprecated/layouts/main.handlebars diff --git a/webapp/views/notes.txt b/webapp/views/notes.txt new file mode 100644 index 0000000..d05c4be --- /dev/null +++ b/webapp/views/notes.txt @@ -0,0 +1 @@ +https://scotch.io/tutorials/use-ejs-to-template-your-node-application diff --git a/webapp/views/pages/index.ejs b/webapp/views/pages/index.ejs new file mode 100644 index 0000000..040e400 --- /dev/null +++ b/webapp/views/pages/index.ejs @@ -0,0 +1,42 @@ + + + + <% include ../partials/head %> + + + +
    + <% include ../partials/header %> +
    + +
    +
    +

    EnlivenMinetest

    +

    Welcome.

    +

     

    +

    <%= msg %>

    +
    +

    assuming minetestserver ran as: <%= serverUser %>

    +

    timezone (tz_offset/60*-1): <%= tzOffset %>

    +

    selected date: <%= selectedDate %>

    +

    Activity Dates:

    + +
      + <% logDates.forEach(function(logDate) { %> +
    • <%= logDate.date %> - <%= logDate.active %>
    • + <% }); %> +
    +
    + +Your browser does not support the canvas element. + +
    loading...
    + +
    + +
    + <% include ../partials/footer %> +
    + + + diff --git a/webapp/views/pages/result.ejs b/webapp/views/pages/result.ejs new file mode 100644 index 0000000..4392270 --- /dev/null +++ b/webapp/views/pages/result.ejs @@ -0,0 +1,32 @@ + + + + <% include ../partials/head %> + + + +
    + <% include ../partials/header %> +
    + +
    +
    +

    EnlivenMinetest

    +

    +

     

    +

    <%= msg %>

    +
    +
    + +Your browser does not support the canvas element. + +
    loading...
    + +
    + +
    + <% include ../partials/footer %> +
    + + + diff --git a/webapp/views/pages/skin-selection-form.ejs b/webapp/views/pages/skin-selection-form.ejs new file mode 100644 index 0000000..411de01 --- /dev/null +++ b/webapp/views/pages/skin-selection-form.ejs @@ -0,0 +1,69 @@ + + + + <% include ../partials/head %> + + + +
    + <% include ../partials/header %> +
    + +
    +
    +
    +
    +

    EnlivenMinetest

    +

    Choose Skin.

    +

     

    +

    <%= msg %>

    +
    +
    +
    +
    User Name (case-sensitive):
    + + +
    +
    +
    + +
    + <% skinFileNames.forEach(function(skinFileName) { %> + <%= skinFileName %> + <% }); %> + +
    +
    + +
    +
    +

    Links:

    +

    Server Page

    +
    +
    + +
    +
    + +
    + <% include ../partials/footer %> +
    + + + diff --git a/webapp/views/pages/skin-upload-form.ejs b/webapp/views/pages/skin-upload-form.ejs new file mode 100644 index 0000000..a65cccc --- /dev/null +++ b/webapp/views/pages/skin-upload-form.ejs @@ -0,0 +1,45 @@ + + + + <% include ../partials/head %> + + + +
    + <% include ../partials/header %> +
    + +
    +
    +
    +
    +

    EnlivenMinetest

    +

    Upload Skin.

    +

     

    +

    <%= msg %>

    +
    +
    +
    +
    User Name (case-sensitive):
    +
    Select a png image to upload:
    +
    +
    +
    +
    + +
    +
    +

    Links:

    +

    Server Page

    +
    +
    + +
    +
    + +
    + <% include ../partials/footer %> +
    + + + diff --git a/webapp/views/partials/footer.ejs b/webapp/views/partials/footer.ejs new file mode 100644 index 0000000..f15dc62 --- /dev/null +++ b/webapp/views/partials/footer.ejs @@ -0,0 +1 @@ +

    Copyright © 2019 Jake Gustafson

    diff --git a/webapp/views/partials/head.ejs b/webapp/views/partials/head.ejs new file mode 100644 index 0000000..6183cd8 --- /dev/null +++ b/webapp/views/partials/head.ejs @@ -0,0 +1,8 @@ + +EnlivenMinetest + + + + diff --git a/webapp/views/partials/header.ejs b/webapp/views/partials/header.ejs new file mode 100644 index 0000000..cba1918 --- /dev/null +++ b/webapp/views/partials/header.ejs @@ -0,0 +1,18 @@ +