site

Website's source files.
Log | Files | Refs | Submodules | LICENSE

commit cf04ebda096c62229e07701d4c949770254d39e9
parent 759c043f6cf1870a6c8d123fb8b1453d699b4331
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date:   Thu, 20 Jan 2022 15:34:54 -0800

Typescript.

Diffstat:
Mpackage-lock.json | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackage.json | 5+++++
Aserver.ts | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atsconfig.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" + ] +}