diff options
| -rw-r--r-- | 404/index.css | 3 | ||||
| -rw-r--r-- | 404/index.html | 16 | ||||
| -rw-r--r-- | Cont/404/index.css | 3 | ||||
| -rw-r--r-- | Cont/404/index.html | 26 | ||||
| -rw-r--r-- | Cont/Images/back.png | bin | 0 -> 3528 bytes | |||
| -rw-r--r-- | Cont/Images/help.png (renamed from help.png) | bin | 2347 -> 2347 bytes | |||
| -rw-r--r-- | Cont/devs/index.css | 18 | ||||
| -rw-r--r-- | Cont/devs/index.html | 26 | ||||
| -rw-r--r-- | Cont/index.css (renamed from index.css) | 0 | ||||
| -rw-r--r-- | Cont/index.html (renamed from index.html) | 6 | ||||
| -rw-r--r-- | Cont/non-main.css | 26 | ||||
| -rw-r--r-- | README.md | 18 | ||||
| -rw-r--r-- | database.js | 52 | ||||
| -rw-r--r-- | dbparse.js | 365 | ||||
| -rw-r--r-- | package.json | 29 | ||||
| -rw-r--r-- | parse.js | 1 | ||||
| -rw-r--r-- | server.js | 82 | 
17 files changed, 619 insertions, 52 deletions
diff --git a/404/index.css b/404/index.css deleted file mode 100644 index ceb8016..0000000 --- a/404/index.css +++ /dev/null @@ -1,3 +0,0 @@ -body { -	padding-top: calc(30vh); -} diff --git a/404/index.html b/404/index.html deleted file mode 100644 index e6771ad..0000000 --- a/404/index.html +++ /dev/null @@ -1,16 +0,0 @@ -<!DOCTYPE html> -<html> -	<head> -		<meta charset="utf-8"> -		<title>Page not found</title> -		<link href="/index.css" rel="stylesheet" type="text/css"> -		<link href="/404/index.css" rel="stylesheet" type="text/css"> -		<meta name="viewport" content="width=device-width, initial-scale=1"> -	</head> -	<body> -		<div class="float-block"> -			<h1>404: sivua \(path\) ei löytynyt.</h1> -			<p>Voi myös olla, että sinulla ei ole oikeuksia sivun tarkasteluun tai että palvelimella on tapahtunut virhe. Jos epäilet jälkimmäistä, otathan yhteyttä joel.kronqvist@edu.lohja.fi</p> -		</div> -	</body> -</html> diff --git a/Cont/404/index.css b/Cont/404/index.css new file mode 100644 index 0000000..664d7fb --- /dev/null +++ b/Cont/404/index.css @@ -0,0 +1,3 @@ +body { +	padding-top: 30vh; +} diff --git a/Cont/404/index.html b/Cont/404/index.html new file mode 100644 index 0000000..5b01684 --- /dev/null +++ b/Cont/404/index.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="fi"> +	<head> +		<meta charset="utf-8"> +		<title>Page not found</title> +		<link href="/index.css" rel="stylesheet" type="text/css"> +		<link href="/non-main.css" rel="stylesheet" type="text/css"> +		<link href="/404/index.css" rel="stylesheet" type="text/css"> +		<meta name="viewport" content="width=device-width, initial-scale=1"> +	</head> +	<body> +		<div class="float-block"> +			<h1>404: sivua \(path\) ei löytynyt.</h1> +			<p>Voi myös olla, että palvelimella on tapahtunut virhe. Jos epäilet jälkimmäistä, otathan yhteyttä palvelusta vastaaviin henkilöihin.</p> +		</div> + +		<a class="back" href="/"><img src="/Images/back.png" alt="Takaisin etusivulle"></a> + +		<footer> +			<a href="https://www.github.com/JoelHMikael/FoodJS">Lähdekoodi</a> +			<span>|</span> +			<a href="/devs">Ota yhteyttä</a> +		</footer> + +	</body> +</html> diff --git a/Cont/Images/back.png b/Cont/Images/back.png Binary files differnew file mode 100644 index 0000000..01b9bc7 --- /dev/null +++ b/Cont/Images/back.png diff --git a/help.png b/Cont/Images/help.png Binary files differindex 51d84df..51d84df 100644 --- a/help.png +++ b/Cont/Images/help.png diff --git a/Cont/devs/index.css b/Cont/devs/index.css new file mode 100644 index 0000000..1cce621 --- /dev/null +++ b/Cont/devs/index.css @@ -0,0 +1,18 @@ +.column { +	display: inline-block; +	padding: 0; +	margin: .5em; +	box-sizing: border-box; +	vertical-align: middle; +	overflow-wrap: break-word; +	width: 100%; +} + + +@media screen and (min-width: 700px) +{ +	.column { +		width: calc(33% - 2em); +	} +} + diff --git a/Cont/devs/index.html b/Cont/devs/index.html new file mode 100644 index 0000000..69116c6 --- /dev/null +++ b/Cont/devs/index.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="fi"> +	<head> +		<meta charset="utf-8"> +		<title>Developers</title> +		<link href="/index.css" rel="stylesheet" type="text/css"> +		<link href="/non-main.css" rel="stylesheet" type="text/css"> +		<link href="/devs/index.css" rel="stylesheet" type="text/css"> +		<meta name="viewport" content="width=device-width, initial-scale=1"> +	</head> +	<body> +		<h1 class="shadow">Sivun kehittäjät</h1> +		<p>Jos muokkaat/lisäät jotain tähän projektiin, voit pyytää serverin ylläpidosta vastaavilta, että nimesi lisätään tietokantaan.</p> + +		\(devs\) + +		<a class="back" href="/"><img src="/Images/back.png" alt="Takaisin etusivulle"></a> + +		<footer> +			<a href="https://www.github.com/JoelHMikael/FoodJS">Lähdekoodi</a> +			<span>|</span> +			<a href="/devs">Ota yhteyttä</a> +		</footer> + +	</body> +</html> diff --git a/index.css b/Cont/index.css index 1f5a422..1f5a422 100644 --- a/index.css +++ b/Cont/index.css diff --git a/index.html b/Cont/index.html index 90e4a9b..c35b7c1 100644 --- a/index.html +++ b/Cont/index.html @@ -1,5 +1,5 @@  <!DOCTYPE html> -<html> +<html lang="fi">  	<head>  		<meta charset="utf-8">  		<title>LYLL-ruokailuvuoro</title> @@ -19,7 +19,7 @@  					<label for="index" class="shadow">Opettaja, kurssi tai luokka:</label>  					<br>  					<input type="text" name="index" placeholder="\(example-input\)"> -					<img src="/help.png" class="info" alt="Siis häh?"> +					<img src="/Images/help.png" class="info" alt="Siis häh?">  					<span><p class="infoblock highlight">Syötä tähän kenttään yhdeltätoista alkavan tuntisi opettaja, kurssikoodi tai luokka. Älä syötä useampaa edellä mainituista, yksi on tarpeeksi.</p></span>  					<br>  					<label for="day">Päivä:</label> @@ -57,7 +57,7 @@  		<footer>  			<a href="https://www.github.com/JoelHMikael/FoodJS">Lähdekoodi</a>  			<span>|</span> -			<a href="https://www.example.com">Ota yhteyttä</a> +			<a href="/devs">Ota yhteyttä</a>  		</footer>  	</body>  </html> diff --git a/Cont/non-main.css b/Cont/non-main.css new file mode 100644 index 0000000..d8003ef --- /dev/null +++ b/Cont/non-main.css @@ -0,0 +1,26 @@ +.back { +	display: block; +	margin-left: auto; +	margin-right: auto; +} + +.back > * { +} + +.back > img { +	width: 4rem; +	height: 4rem; +	margin: 1rem; +	filter: drop-shadow(.125rem .125rem 0 black); +	position: relative; +	bottom: .125rem; +	right: .125rem; +	vertical-align: middle; +	display: inline; +} + +.back > img:hover { +	filter: drop-shadow(.20rem .20rem 0 black); +	bottom: .20rem; +	right: .20rem; +} @@ -4,6 +4,24 @@ Readme coming soon!  ## Setup  If you want to set up the server, you will have to get a SSL certificate or generate one yourself. If you want to run a dedicated server that can update, you also need to add the cron jobs from crontab\_add. You must create a MySQL DB and give its login info in ../dblogin.txt. The database should have the following tables set up: +CREATE TABLE shiftnames ( +	day INT, +	id INT, +	name VARCHAR(128) NOT NULL, +	PRIMARY KEY (day, id) +); +CREATE TABLE classes ( +	course VARCHAR(6) PRIMARY KEY, +	class VARCHAR(4) +); +CREATE TABLE shifts ( +	day INT, +	shift INT, +	course VARCHAR(6), +	teacher VARCHAR(4), +	class VARCHAR(4), +	PRIMARY KEY (day, course) +);  CREATE TABLE devs (  	id INT PRIMARY KEY AUTO_INCREMENT,  	name VARCHAR(30) NOT NULL, diff --git a/database.js b/database.js index 3a2822a..30396fe 100644 --- a/database.js +++ b/database.js @@ -1,35 +1,55 @@ +const mysql = require("mysql2"); +  class Database  { -	constructor(credentials, log) +	constructor(credentials)  	{  		this.connection = mysql.createConnection(credentials); -		this.log = log;  	} -	query(q) +	query(query, values)  	{ -		return new Promise((resolve, reject), () => +		return new Promise((resolve, reject) =>  		{ -			this.connection.query(q, (err, res, fields) => +			this.connection.query(query, values, (err, res, fields) =>  			{ -				if (err) -				{ -					this.log(err); -					reject(err); -				} +				if (err) reject(err); +				resolve(res); +			}); +		}); +	} +	execute(query, values) +	{ +		return new Promise((resolve, reject) => +		{ +			this.connection.execute(query, values, (err, res, fields) => +			{ +				if (err) reject(err);  				resolve(res);  			});  		}); + +	} +	query_raw(query) +	{ +		return new Promise((resolve, reject) => +		{ +			this.connection.query(query, (err, res, fields) => +			{ +				if (err) +					reject(err) +				resolve(res); +			}); +		})  	}  	close()  	{ -		this.connection.end(err => +		return new Promise((resolve, reject) =>  		{ -			if (err) +			this.connection.end(err =>  			{ -				this.log(err); -				reject(err); -			} -			resolve(); +				if (err) reject(err); +				resolve(); +			});  		});  	}  } diff --git a/dbparse.js b/dbparse.js new file mode 100644 index 0000000..b260d50 --- /dev/null +++ b/dbparse.js @@ -0,0 +1,365 @@ + + +// String searching +function getCharAmount(s, c) +{ +	let n = 0; +	for (let c_i = 0; c_i < s.length; c_i++) +	{ +		n += +(s[c_i] === c); +	} +	return n; +} +function getNextChar(s, c, i = 0) +{ +	if (!(Number.isInteger(i) && (i >= 0))) +		return -1; +	for (; i < s.length; i++) +	{ +		if (s[i] === c) +			return i; +	} +	return -1; +} +function getNextLine(s, i) +{ +	i = getNextChar(s, "\n", i); +	i += +(i !== -1) * 1; +	return i; +} +function getToLineStartingWith(s, ss, start = 0) +{ +	if (!(Number.isInteger(start) && (start >= 0))) +		return -1 +	 +	let i = start; +	do +	{ +		if (s.substring(i, i + ss.length) === ss) +			break; +		i = getNextLine(s, i); +	} while(i !== -1) +	 +	return i; +} +function findExpression(data, expr, start = 0) +{ +	if (start == -1) +		return -1; +	if (!(Number.isInteger(start) && (start >= 0))) +		throw new TypeError("Start must be a positive integer!"); +	if (typeof expr !== "string") +		return -1; +	while ((data.substring(start, start + expr.length) !== expr) && (start + expr.length < data.length)) +		start++; +	if (data.substring(start, start + expr.length) !== expr) +		return -1; +	return start; +} + +// Normalizing +function parseCluttered(s) +{ +	if (!(typeof s === "string")) +		return ""; +	return s.replaceAll(".", "").replaceAll(" ", "").toUpperCase(); +} + +// Class parsing +async function writeClasses(classData, DB) +{ +	classData = parseCluttered(classData) + "\n"; // newline so that loop can find last value +	await DB.query_raw("DELETE FROM classes"); +	// parse data to dict +	let i = 0; +	while (i < classData.length) +	{ +		let separator = getNextChar(classData, ":", i); +		if (separator === -1) +			break; +		let lineEnd = getNextChar(classData, "\n", i); +		let key = classData.substring(i, separator); +		let val = classData.substring(separator + 1, lineEnd); +		i = lineEnd + 1; +		let res = await DB.execute("INSERT INTO classes VALUES (?, ?)", [key, val]); +	} +} +/* +function parseShift(data, weekdays = ["MAANANTAISIN", "TIISTAISIN", "KESKIVIIKKOISIN", "TORSTAISIN", "PERJANTAISIN"]) +{ +	 +	let i = 0; +	let db = []; +	while (db.length < weekdays.length) +	{ +		let day = []; +		 +		i = getNextChar(data, "\n", findExpression(data, weekdays[db.length])); +		let end; +		if (db.length === weekdays.length - 1) +			end = data.length; +		else +			end = findExpression(data, weekdays[db.length + 1]); +		let unparsedDay = data.substring(i + 1, end); +		day = parseDay(unparsedDay); + +		db.push(day); +	} +	return db; +} + +function parseDay(day) +{ +	let shifts = {}; +	let i = getToLineStartingWith(day, "RUOKAILUVUORO"); +	do +	{ +		let endOfLine = getNextChar(day, "\n", i); +		let shiftDesc = day.substring(i, endOfLine); +		i = getNextChar(day, "\n", i) + 1; +		i = getNextChar(day, "\n", i) + 1; +		if (getNextChar(day, "\n", i) === -1) +			endOfLine = day.length; +		else +			endOfLine = getNextChar(day, "\n", i); +		let unparsedIndexes = day.substring(i, endOfLine); +		shifts[shiftDesc] = parseLine(unparsedIndexes); +		i = getToLineStartingWith(day, "RUOKAILUVUORO", i); +	} while (i !== -1); +	return shifts; +} +function parseLine(line, toRemove = " ja KAHDEN TUTKINNON OPINNOT 1., 2. ja 3. VUOSITASON RYHMÄT ") +{ +	let i = 0; +	let courses = []; +	let teachers = []; +	// get to the teachers & courses +	let nextSpace = 0; + +	if (line.substring(line.length - toRemove.length, line.length) === toRemove) +		line = line.substring(0, line.length - toRemove.length); +	line = line.replaceAll(",", "").replaceAll("ja ", "").replaceAll(" + ", "+"); + +	while (i < line.length) +	{ +		if (line[i] === "+") +		{ + +			nextSpace = getNextChar(line, " ", i); +			let nextNextSpace = getNextChar(line, " ", nextSpace + 1); +			if (nextNextSpace === -1) +				nextNextSpace = line.length; +			line = `${line.substring(0, i)} ${line.substring(nextSpace + 1, nextNextSpace)} ${line.substring(i + 1, line.length)}`; +			i = nextNextSpace - 1; +		} +		i++; +	} +	nextSpace = 0; +	i = 0; + +	const getElement = list => +	{ +		nextSpace = getNextChar(line, " ", i); +		if (nextSpace === -1) +			nextSpace = line.length; +		list.push(line.substring(i, nextSpace)); +		i = nextSpace + 1; +	} + +	do +	{ +		getElement(courses); +		getElement(teachers); +	} while (i < line.length) + +	return [courses, teachers]; +} + +*/ +async function parseLine(data, day, shift, DB) +{ +	// "preprocessing" +	let i = 0; +	let courses = []; +	let teachers = []; +	const toRemove = " ja KAHDEN TUTKINNON OPINNOT 1., 2. ja 3. VUOSITASON RYHMÄT "; +	if (data.substring(data.length - toRemove.length, data.length) === toRemove) +		data = data.substring(0, data.length - toRemove.length); +	data = data.replaceAll(",", "").replaceAll("ja ", "").replaceAll(" + ", "+"); + +	while (i < data.length) +	{ +		if (data[i] === "+") +		{ + +			nextSpace = getNextChar(data, " ", i); +			let nextNextSpace = getNextChar(data, " ", nextSpace + 1); +			if (nextNextSpace === -1) +				nextNextSpace = data.length; +			data = `${data.substring(0, i)} ${data.substring(nextSpace + 1, nextNextSpace)} ${data.substring(i + 1, data.length)}`; +			i = nextNextSpace - 1; +		} +		i++; +	} +	nextSpace = 0; +	i = 0; + +	const getElement = list => +	{ +		nextSpace = getNextChar(data, " ", i); +		if (nextSpace === -1) +			nextSpace = data.length; +		list.push(data.substring(i, nextSpace)); +		i = nextSpace + 1; +	} + +	do +	{ +		getElement(courses); +		getElement(teachers); +	} while (i < data.length) + +	let values = "VALUES"; +	for(let el = 0; el < courses.length; el++) +	{ +		values += ` ROW(${day}, ${shift}, '${courses[el]}', '${teachers[el]}', NULL),`; +	} +	values = values.substring(0, values.length - 1); +	return DB.execute(`INSERT INTO shifts ${values}`, []); +} + +async function parseDay(data, day, DB) +{ +	let i = getToLineStartingWith(data, "RUOKAILUVUORO"); +	let indexOfShift = 1; +	while (i !== -1) +	{ +		let endOfLine = getNextChar(data, "\n", i); +		// Insert the food shift name +		let shiftName = DB.execute("INSERT INTO shiftnames VALUES (?, ?, ?)", [day, indexOfShift, data.substring(i, endOfLine)]); +		// get to the teachers & courses +		i = endOfLine + 1; +		i = getNextChar(data, "\n", i) + 1; +		if (getNextChar(data, "\n", i) === -1) +			endOfLine = data.length; +		else +			endOfLine = getNextChar(data, "\n", i); +		let unparsedIndexes = data.substring(i, endOfLine); + +		// do the magic +		let lineParse = parseLine(unparsedIndexes, day, indexOfShift, DB); + +		i = getToLineStartingWith(data, "RUOKAILUVUORO", i); +		indexOfShift++; +		await Promise.all([shiftName, lineParse]); +	} +	return 0; +} + +async function writeShifts(data, DB) +{ +	weekdays = ["MAANANTAISIN", "TIISTAISIN", "KESKIVIIKKOISIN", "TORSTAISIN", "PERJANTAISIN"]; +	let deletions = Promise.all([ +		DB.query_raw("DELETE FROM shifts"), +		DB.query_raw("DELETE FROM shiftnames") +	]); + +	// iterate over the weekdays +	let i = 0; +	for (let day = 0; day < weekdays.length; day++) +	{ +		// find the start of the shifts of the day +		i = getNextChar(data, "\n", findExpression(data, weekdays[day], i)); + +		// find the end of the shifts of the day +		let end = [ +			data.length, +			findExpression(data, weekdays[day + 1], i) +		][+(day !== weekdays.length - 1)]; + +		await deletions; // wait for deletion to get completed before proceeding to write new data to the table + +		// do the magic: +		let shifts = data.substring(i, end); +		await parseDay(shifts, day, DB); + +		i = end; +	} +	return 0; +} + +/* +function getIndexType(index) +{ +	if (/^[A-Za-zåäöÅÄÖ]{2,3}\d{2,3}$/.test(index)) +		return "course"; +	if (/^[A-Za-zåäöÅÄÖ]{4}$/.test(index)) +		return "teacher"; +	if (/^\w\d{3}$/.test(index)) +		return "class"; +} + +function getShift(day, index, db) // day: int, 1 = monday; index: string of course/teacher; db: parsed shifts +{ +	if ((typeof day !== "number") || isNaN(day) || (typeof index !== "string")) +		return -1; + +	let shifts = db[day - 1]; + +	let _endOfIndex = parseInt(index.substring(2, 4)); +	let type = getIndexType(index); +	if (type === undefined) +		return {}; +	let type_index = +(type === "teacher") + (+(type === "class") * 2); + +	let res = {}; +	for (const [key, val] of Object.entries(shifts)) +	{ +		let indexes = val[type_index]; +		for (let i = 0; i < indexes.length; i++) +		{ +			if (indexes[i] === index) +				res[key] = [val[0][i], val[1][i], val[2][i]]; +		} +	} +	return res; +} + +function randInt(start, stop) +{ +	return start + Math.floor(Math.random() * (stop - start)); +} + +function getIndexes(db, day, shift, type) +{ +	let d = db[day]; +	let sh = Object.values(d)[shift][type]; +	return Object.values(db[day])[shift][type]; +} + +function getRandomIndex(db, day = randInt(0, 5), shift = randInt(0, 3), type = randInt(0, 3)) +{ +	let el; +	let i = 0; +	let indexes = getIndexes(db, day, shift, type); +	while ((el === undefined) && (i < indexes.length)) +	{ +		el = indexes[i]; +		i++; +	} +	if (el == undefined) +		return getRandomIndex(db); +	return el; +} + +exports.build	= parseShift; +exports.indexType = getIndexType; +exports.classes = parseClasses; +exports.get 	= getShift; +exports.cluttered = parseCluttered; +exports.find = findExpression; +exports.getNextChar = getNextChar; +exports.randomIndex = getRandomIndex;*/ + +exports.classes = writeClasses; +exports.build = writeShifts; diff --git a/package.json b/package.json new file mode 100644 index 0000000..cad9a81 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ +  "name": "lyll-ruoka", +  "version": "1.0.0", +  "description": "Server to serve information about the food shifts and foods in LYLL", +  "main": "server.js", +  "scripts": { +    "test": "echo \"Error: no test specified\" && exit 1", +    "start": "node server.js" +  }, +  "repository": { +    "type": "git", +    "url": "git+https://github.com/JoelHMikael/FoodJS.git" +  }, +  "keywords": [ +    "food", +    "server", +    "database", +    "school" +  ], +  "author": "Joel Kronqvist", +  "license": "UNLICENSED", +  "bugs": { +    "url": "https://github.com/JoelHMikael/FoodJS/issues" +  }, +  "homepage": "https://github.com/JoelHMikael/FoodJS#readme", +  "dependencies": { +    "mysql2": "^2.3.3" +  } +} @@ -261,4 +261,5 @@ exports.classes = parseClasses;  exports.get 	= getShift;  exports.cluttered = parseCluttered;  exports.find = findExpression; +exports.getNextChar = getNextChar;  exports.randomIndex = getRandomIndex; @@ -3,7 +3,8 @@ const fs	= require("fs");  const url	= require("url");  const parse	= require("./parse.js");  const scrape	= require("./scrape.js"); -const mysql	= require("mysql2"); +const SQL_DBS	= require("./database.js"); +const DBPARSE	= require("./dbparse.js");  async function init() @@ -11,24 +12,36 @@ async function init()  	const weekdays = [undefined, "MAANANTAI", "TIISTAI", "KESKIVIIKKO", "TORSTAI", "PERJANTAI", undefined];  	const build = { -		"./index.html": buildMain, -		"./index.css": buildDefault, -		"./404/index.css": buildDefault, -		"./help.png": buildImage +		"./Cont/index.html": buildMain, +		"./Cont/index.css": buildDefault, +		"./Cont/devs/index.html": buildDevs, +		"./Cont/devs/index.css": buildDefault, +		"./Cont/404/index.css": buildDefault, +		"./Cont/non-main.css": buildDefault, +		"./Cont/Images/help.png": buildImage, +		"./Cont/Images/back.png": buildImage  	}; -	const errorPath = "./404/index.html"; +	const errorPath = "./Cont/404/index.html";  	// await for needed things in async -	let [shiftCont, classCont, foodsThisWeek, foodsNextWeek] = await Promise.all([ +	let [shiftCont, classCont, foodsThisWeek, foodsNextWeek, dbcredentials] = await Promise.all([  		openFile("./shifts.txt"),  		openFile("./classes.txt"),  		scrape.food(scrape.link(1)), -		scrape.food(scrape.link(2)) +		scrape.food(scrape.link(2)), +		openFile("../dblogin.txt")  	]); +	// get the MySQL DB connection +	const SQLDB = new SQL_DBS.Database(JSON.parse(dbcredentials)); +  	// get the food shift "database"  	shiftCont = shiftCont.toString("utf-8").replaceAll("\r", ""); // \r because of the \r\n newline on windows which creates problems  	classCont = classCont.toString("utf-8").replaceAll("\r", ""); + +	await DBPARSE.classes(classCont, SQLDB); +	await DBPARSE.build(shiftCont, SQLDB); +  	let DB = parse.build(shiftCont);  	parse.classes(classCont, DB); @@ -54,9 +67,9 @@ async function init()  			index: ind,  			day: d  		}; -		let path = "." + antiXSS(q.pathname); -		if (path == "./") -			path = "./index.html"; +		let path = "./Cont" + antiXSS(q.pathname); +		if (isDir(path)) +			path += ["/index.html", "index.html"][+(path[path.length - 1] === "/")];  		// pack the data required by the builders  		let data; @@ -65,7 +78,8 @@ async function init()  			"path404": errorPath,  			"query": q.query,  			"db": DB, -			"foods": foods +			"foods": foods, +			"sqldb": SQLDB  		};  		// build the page @@ -77,10 +91,21 @@ async function init()  	}  	// start server -	http.createServer(server).listen(8080); +	const runningServer = http.createServer(server).listen(8080); +	 +	// stop server +	function closeServer() { +		SQLDB.close(); +		runningServer.close(); +	} +	process.on("SIGINT", closeServer); +	process.on("SIGQUIT", closeServer); +	process.on("SIGTERM", closeServer);  } + +  function validateIndex(sus)  {  	return antiXSS(parse.cluttered(sus)); @@ -93,6 +118,12 @@ function antiXSS(sus)  	return replace(sus, ["<", ">", "(", ")"], ["<", ">", "(", ")"]);  } +function isDir(path) +{ +	return (parse.getNextChar(path.substring(1), ".") === -1); +} + +  function replace(s, from, to)  {  	for (let i = 0; i < from.length; i++) @@ -131,7 +162,7 @@ async function buildMain(args)  	let day = d.getDay();  	const actualDay = day;  	day = +((day === 0) || (day === 6)) + (+(!(day === 0) && !(day === 6)) * day); -	if ((typeof query.day === "string") && (parseInt(query.day).toString() === query.day) && (!isNaN(parseInt(query.day))) && (parseInt(query.day) > 0) && (parseInt(query.day) < 7)) +	if ((typeof query.day === "string") && (parseInt(query.day).toString() === query.day) && (!isNaN(parseInt(query.day))) && (parseInt(query.day) > 0) && (parseInt(query.day) < 6))  		day = parseInt(query.day);  	data_string = data_string.replace(`<option value=\"${day}\">`, `<option value=\"${day}\" selected>`); @@ -197,8 +228,31 @@ async function buildMain(args)  	return data_string;  } +async function buildDevs(args) +{ +	const path = args["path"]; +	const data = await openFile(path); +	const DB = args["sqldb"]; + +	let res = ""; +	let devs = await DB.query_raw("SELECT name, description, contact FROM devs"); +	for (let dev = 0; dev < devs.length; dev++) +	{ +		let devInfo = devs[dev]; +		res += '<div class="float-block">' + +				`<p class="column">${devInfo.name}</p>` + +				`<p class="column">${devInfo.description}</p>` + +				`<a href="mailto:${devInfo.contact}" class="column" style="white-space: nowrap; overflow: hidden; overflow-wrap: normal; text-overflow: ellipsis;">${devInfo.contact}</a>` + +			'</div>'; +	} + +	return build_replace(data.toString("utf-8"), {"devs": res}); +} + +  async function build404(args)  { +	args["path"] = args["path"].substring("./Cont".length);  	const data = await openFile(args["path404"]);  	const data_string = data.toString("utf-8");  	return data_string.replace("\\(path\\)", args["path"]);  | 
