280 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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 = `
 | |
| 		<!DOCTYPE html>
 | |
| 		<html lang="en">
 | |
| 		<head>
 | |
| 			<title>
 | |
| 				Sitemap for Hyperling
 | |
| 			</title>
 | |
| 		</head>
 | |
| 		<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.
 | |
| 	*/
 | |
| 	// TBD/TODO would be great to log this as "DATE: ADDRESS hit ROUTER requesting RESOURCE"
 | |
| 	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".
 | |
| 	// TBD/TODO would be great to log this as "DATE: ADDRESS hit ROUTER requesting RESOURCE"
 | |
| 	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.
 | |
| 	// TBD/TODO would be great to log this as "DATE: ADDRESS hit ROUTER requesting RESOURCE"
 | |
| 	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.
 | |
| 	// TBD/TODO would be great to log this as "DATE: ADDRESS hit ROUTER requesting RESOURCE"
 | |
| 	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":
 | |
| 			case "jpeg":
 | |
| 				mime = "image/jpeg";
 | |
| 				break;
 | |
| 			case "png":
 | |
| 				mime = "image/png";
 | |
| 				break;
 | |
| 			case "html":
 | |
| 				mime = "text/html";
 | |
| 				break;
 | |
| 			case "zip":
 | |
| 				mime = "application/zip";
 | |
| 				break;
 | |
| 			case "md":
 | |
| 			case "txt":
 | |
| 			case "csv":
 | |
| 				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();
 |