#!/usr/bin/env node
'use strict';
/*
	2022-09-14 Hyperling
	Coding my own website rather than using WordPress or anything bloaty.
*/

//// Libraries ////

let express = require('express');
let app = express();

const execSync = require('child_process').execSync;

const fs = require('fs');

//// Global Variables ////

const DEBUG = false;

const app_name = "hyperling.com";

let pages = [];
const pages_dir = "./pages/";
const file_types = ["php", "sh"];

let ports = [];
// Check parameters for numeric port numbers.
process.argv.forEach(function (val, index, array) {
	console.log("Parameter", index + ':', val, !isNaN(val));
	if (!isNaN(val)) {
		console.log("Adding Port", val)
		ports.push(val);
	}
});
// Default port if none were passed.
if (ports.length === 0) {
	ports.push(8080);
}

const stringsToRemove = [
	RegExp("#!/usr/bin/php\n", "g")
]

//// Functions ////

/* Code exists inside a main function so that we may use async/await.
*/
async function main() {
	console.log("...Starting Main...");

	// Getting dates in Node.js is awful, just use Linux.
	const start_datetime = "" + await execSync('date "+%Y-%m-%dT%H:%M:%S%:z"');
	const start_datetime_trimmed = start_datetime.trim();

	console.log("Set app to return HTML documents.");
	app.use(function (req, res, next) {
		res.contentType('text/html');
		next();
	});

	/* Loop through all file in the pages subdirectory and add the allowed
	// file types into the pages array for automatic router creation.
	*/
	console.log("...Starting Main...");
	let ls = await fs.promises.readdir(pages_dir);
	if (DEBUG) console.log("DEBUG:  Results of ls, ", ls);
	for (let file of ls) {
		let file_test = file.split(".");
		let file_name = file_test[0];
		let file_type = file_test[file_test.length - 1];
		if (file_types.includes(file_type)) {
			if (DEBUG) console.log("DEBUG:  Hooray!", file, "is being added.");
			pages[file_name] = pages_dir + file;
			console.log(" * Added page", file);
		} else {
			if (DEBUG) console.log("DEBUG: ", file, "is not an approved type, skipping.");
		}
	}
	console.log(" * Pages loaded: ", pages);
	//return; // Stop execution FORTESTING

	/* Create both an XML and HTML sitemap based on these entries. XML is used for
	// bots like SEO scrapers. HTML will be for human users looking for a list of
	// all pages. Some are not in the menu. Generated an example XML sitemap at
	// www.xml-sitemaps.com then stripped it apart and made it dynamic.
	*/
	let sitemap_xml = `<?xml version="1.0" encoding="UTF-8"?>
		<urlset
			xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
					http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
		>
			<url>
				<loc>https://hyperling.com/</loc>
				<lastmod>`+start_datetime_trimmed+`</lastmod>
				<priority>1.00</priority>
			</url>
			<url>
				<loc>https://hyperling.com/sitemap/</loc>
				<lastmod>`+start_datetime_trimmed+`</lastmod>
				<priority>0.80</priority>
			</url>
	`;
	let sitemap_html = `
		<html><body>
			<strong>Special Pages</strong>
			<ul>
				<li>
					<b>Main Site</b>
					(<a href="/">Local</a>)
					(<a href="https://`+app_name+`/">Hardlink</a>)
				</li>
				<li>
					<b>XML Site Map</b>
					(<a href="/sitemap.xml">Local</a>)
					(<a href="https://`+app_name+`/sitemap.xml">Hardlink</a>)
				</li>
				<li>
					<b>HTML Site Map</b>
					(<a href="/sitemap/">Local</a>)
					(<a href="https://`+app_name+`/sitemap/">Hardlink</a>)
					<i>[You are here!]</i>
				</li>
			</ul>
			<strong>Web Pages (Alphabetical)</strong>
			<ul>
	`;

	console.log("...Adding Routes...");
	let router = express.Router();

	/* AUTOMATIC METHOD BASED ON OBJECT/ARRAY OF WEB SCRIPTS
	// Creates routes with the URL of the key and location of the value.
	*/
	for (let key in pages) {
		console.log(" * Creating router for", key);
		router.get("/" + key, function (req,res) {
			console.log(key, "fulfilling request to", req.socket.remoteAddress, "asking for", req.url);
			let html = "" + execSync(pages[key]);
			stringsToRemove.forEach(string => {
				html = html.replace(string, "");
			});
			res.send(html);
		});

		/* Append the page to the sitemap variables.
		*/
		console.log(" * * Adding to sitemap.xml");
		sitemap_xml = sitemap_xml + `
				<url>
					<loc>https://hyperling.com/`+key+`/</loc>
					<lastmod>`+start_datetime_trimmed+`</lastmod>
					<priority>0.80</priority>
				</url>
		`;
		console.log(" * * Adding to sitemap.html");
		sitemap_html = sitemap_html + `
				<li>
					<b>`+key+`</b>
					(<a href="/`+key+`/">Local</a>)
					(<a href="https://`+app_name+`/`+key+`/">Hardlink</a>)
				</li>
		`;
	}

	/* Close the sitemap variables
	*/
	sitemap_xml = sitemap_xml + `
		</urlset>
	`;
	sitemap_html = sitemap_html + `
		</ul></body></html>
	`;

	// Provide sitemap.xml file for "SEO".
	console.log(" * Creating router for sitemap.xml");
	router.get('/sitemap.xml', function (req, res) {
		console.log("sitemap.xml being provided to", req.socket.remoteAddress)
		res.contentType('text/xml');
		res.send(sitemap_xml);
	});

	// Provide human-usable sitemap links.
	console.log(" * Creating router for sitemap*");
	router.get('/sitemap*', function (req, res) {
		console.log("sitemap.html being provided to", req.socket.remoteAddress)
		res.send(sitemap_html);
	});

	// Return a resource from the files folder.
	console.log(" * Creating router for files");
	router.get('/files*', function (req, res) {
		console.log("file response to", req.socket.remoteAddress, "asking for", req.url)

		// Build variables.
		const file = "." + req.path;
		const extensions = req.path.split(".");
		const extension = extensions[extensions.length-1];

		// Check extension and guess a MIME type.
		let mime;
		switch (extension) {
			case "apk":
				mime = "application/vnd.android.package-archive";
				break;
			case "jpg" || "jpeg":
				mime = "image/jpeg";
				break;
			case "png":
				mime = "image/png";
				break;
			case "html":
				mime = "text/html";
				break;
			case "zip":
				mime = "application/zip";
				break;
			case "txt" || "csv" || "md":
				mime = "text/*";
				break;
			default:
				mime = "application/" + extension;
				break;
		}

		// Return the file
		res.contentType(mime);
		let f = fs.createReadStream(file)
			.on("error", function(e) {
				res.contentType("text/plain");
				res.send(404, "File Not Found");
			})
			.pipe(res)
		;
	});

	// Originally a test, now a catch-all redirection to Home!
	console.log(" * Creating router for redirection");
	router.get('/*', function (req, res) {
		// WARNING: These are huge so only look when you need to.
		//console.log(req);
		//console.log(res);
		console.log("*wildcard* replying to", req.socket.remoteAddress, "asking for", req.url)
		let html = "" + execSync("./pages/home.php");
		stringsToRemove.forEach(string => {
			html = html.replace(string, "");
		});
		res.send(html);
	});

	app.use('', router);

	console.log("...Adding Ports...");
	ports.forEach(port => {
		app.listen(port);
		console.log(" * Now listening on port " + port + ".");
	});
	console.log("Done! Now we wait...");
}

//// Program Execution ////

main();