commit cf04ebda096c62229e07701d4c949770254d39e9
parent 759c043f6cf1870a6c8d123fb8b1453d699b4331
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date: Thu, 20 Jan 2022 15:34:54 -0800
Typescript.
Diffstat:
M | package-lock.json | | | 176 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | package.json | | | 5 | +++++ |
A | server.ts | | | 138 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tsconfig.json | | | 14 | ++++++++++++++ |
4 files changed, 333 insertions(+), 0 deletions(-)
diff --git a/package-lock.json b/package-lock.json
@@ -8,6 +8,97 @@
"express": "^4.17.2",
"express-handlebars": "^6.0.2",
"handlebars": "^4.7.7"
+ },
+ "devDependencies": {
+ "@types/express": "^4.17.13",
+ "@types/express-handlebars": "^6.0.0",
+ "@types/node": "^17.0.10"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dev": true,
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+ "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+ "dev": true,
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-handlebars": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@types/express-handlebars/-/express-handlebars-6.0.0.tgz",
+ "integrity": "sha512-L0G9j7xc9k6FavI9CCUueJf7YHGE5b+gXV4NGGcObTbLqvZ2TwebEsrFb1b8NEc2kDf22Mu+jCDPjvWzfCS0Gw==",
+ "deprecated": "This is a stub types definition. express-handlebars provides its own type definitions, so you do not need this installed.",
+ "dev": true,
+ "dependencies": {
+ "express-handlebars": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.28",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz",
+ "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "17.0.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz",
+ "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==",
+ "dev": true
+ },
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.13.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+ "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
}
},
"node_modules/accepts": {
@@ -661,6 +752,91 @@
}
},
"dependencies": {
+ "@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dev": true,
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/express": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+ "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+ "dev": true,
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-handlebars": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@types/express-handlebars/-/express-handlebars-6.0.0.tgz",
+ "integrity": "sha512-L0G9j7xc9k6FavI9CCUueJf7YHGE5b+gXV4NGGcObTbLqvZ2TwebEsrFb1b8NEc2kDf22Mu+jCDPjvWzfCS0Gw==",
+ "dev": true,
+ "requires": {
+ "express-handlebars": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.28",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz",
+ "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "17.0.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz",
+ "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==",
+ "dev": true
+ },
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "@types/serve-static": {
+ "version": "1.13.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+ "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+ "dev": true,
+ "requires": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
diff --git a/package.json b/package.json
@@ -3,5 +3,10 @@
"express": "^4.17.2",
"express-handlebars": "^6.0.2",
"handlebars": "^4.7.7"
+ },
+ "devDependencies": {
+ "@types/express": "^4.17.13",
+ "@types/express-handlebars": "^6.0.0",
+ "@types/node": "^17.0.10"
}
}
diff --git a/server.ts b/server.ts
@@ -0,0 +1,138 @@
+// Cropyright (C) Ryan Jeffrey 2022
+// A simple express server that uses handlebars.
+
+import express from 'express';
+import path from 'path';
+import exphbs, { engine } from 'express-handlebars';
+import fs from 'fs';
+
+const app = express();
+const port = process.env.PORT || 3000;
+
+class LSStat {
+ perms: string;
+ numLinks: number;
+ fileSize: number;
+ mtime: string;
+ basename: string;
+
+ constructor(thePath: string) {
+ let stats = fs.statSync(thePath);
+
+ // ls file permissions. Convert into an easier format to use.
+ if(!stats) {
+ console.error("Could not stat", thePath);
+ return;
+ }
+ // Convert mode to string.
+ let unixFilePermissions = (stats.mode & parseInt('777', 8)).toString(8);
+ let permsResult = permissionToString(parseInt(unixFilePermissions[0]));
+ permsResult += permissionToString(parseInt(unixFilePermissions[1]));
+ permsResult += permissionToString(parseInt(unixFilePermissions[2]));
+
+ let prefixChar = '-';
+ if(stats.isDirectory()) {
+ prefixChar = 'd';
+ }
+
+ this.perms = `${prefixChar}${permsResult}`;
+ this.numLinks = stats.nlink;
+ this.fileSize = stats.size;
+ this.mtime = lsTime(stats.mtimeMs);
+ this.basename = path.parse(path.basename(thePath)).name;
+ }
+}
+
+function getMonthByNumber(i: number) : string {
+ const months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
+ 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
+ return (i in months) ? months[i] : "";
+}
+
+// Get the mtime in the same format that LS would.
+function lsTime(timeMS: number) : string {
+ let fileDate = new Date(timeMS);
+ let addString = "";
+ // If the file was updated this year then set the last column to the
+ // hour and minute. Else, the last column should be the year.
+ if((new Date()).getFullYear() != fileDate.getFullYear())
+ addString = `${fileDate.getHours()}:${fileDate.getMinutes()}`;
+ else
+ addString = ` ${fileDate.getFullYear()}`;
+ return `${getMonthByNumber(fileDate.getMonth())} ${fileDate.getDate()} ${addString}`;
+}
+
+function permissionToString(i: number) : string {
+ // Unix file permission array. The mode is the index in the array.
+ const permStrings = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'];
+ return (i in permStrings) ? permStrings[i] : "";
+}
+
+
+function lsList(theDir: string, ext: string, ...files: string[]) : LSStat[] {
+ let fileStats: LSStat[] = [];
+ files.forEach((element: string) => {
+ fileStats.push(new LSStat(path.join(theDir, element + ext)));
+ });
+ return fileStats;
+}
+
+function lsDir(thePath: string) : LSStat[] {
+ let fileStats: LSStat[] = [];
+ fs.readdir(thePath, (err, files) => {
+ if(err) {
+ return console.error('Cannot scane directory ', thePath, ": ", err);
+ }
+
+ files.forEach((file) => {
+ fileStats.push(new LSStat(file));
+ });
+ });
+
+ return fileStats;
+}
+
+// App config
+
+app.engine('handlebars', engine({ defaultLayout: 'main' }));
+app.set('view engine', 'handlebars');
+app.set('views', "./views");
+
+app.use(express.static(path.join(__dirname, 'public')));
+app.use(express.json());
+
+// TODO maybe a system that exports org to handlebars.
+
+// Get the requested post
+app.get('/posts/:post', (req, res, next) => {
+ let post = req.params.post.toLowerCase();
+ if(fs.existsSync(post)) {
+ res.status(200).render('writing', { text : fs.readFileSync(post) });
+ }
+ else {
+ // Page not found.
+ res.status(404).render('404');
+ }
+});
+// Posts index file.
+app.get('/posts', (req, res, next) => {
+ res.status(200).render('indexWriting');
+});
+// index.html should be before 404 and after everything else
+
+
+app.get('/', (req, res, next) => {
+ let test = lsList('public', '.html', 'main', 'software', 'sneed');
+ res.status(200).render('index', {
+ entries: test
+ });
+});
+
+// 404 is last.
+app.get('*', (req, res) => {
+ res.status(404).render('404');
+});
+
+app.listen(port, () => {
+ console.log('== Server is listening on port', port);
+});
diff --git a/tsconfig.json b/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "module": "commonjs",
+ "noImplicitAny": true,
+ "removeComments": true,
+ "preserveConstEnums": true,
+ "sourceMap": true,
+ "target": "es2021"
+ },
+ "files": [
+ "server.ts"
+ ]
+}