did some commenting (I barely commented anything)
This commit is contained in:
parent
3a821b5eba
commit
4fb12c54f8
33
README.md
33
README.md
@ -17,8 +17,37 @@ In action on my website: [deadvey.com](https://deadvey.com)<br/>
|
|||||||
* probably scales like shit
|
* probably scales like shit
|
||||||
* probably insecure as hell
|
* probably insecure as hell
|
||||||
|
|
||||||
# planned features
|
# planned features/todo list
|
||||||
* atom
|
* atom
|
||||||
* federation
|
* federation (looks tricky)
|
||||||
* sign up
|
* sign up
|
||||||
* All strings (including in edit and post page) customisable
|
* All strings (including in edit and post page) customisable
|
||||||
|
* split code into files to tidy it up a bit
|
||||||
|
* inline comments and docs
|
||||||
|
* give each post a hard postID to prevent potential issues
|
||||||
|
* clean up code a bit
|
||||||
|
|
||||||
|
# format indicators
|
||||||
|
* %% - A literal %
|
||||||
|
* %A - List of tags
|
||||||
|
* %B - List of tags, each one with a hyperlink to that tag page
|
||||||
|
* %C - Post content
|
||||||
|
* %D - Published date in the format specified by date_format
|
||||||
|
* %E - Edited date in the format specified by date_format
|
||||||
|
* %F - Pretty name
|
||||||
|
* %G - Tag name (used for the tag page only)
|
||||||
|
* %H - Frontpage hit count
|
||||||
|
* %I - User description
|
||||||
|
* %L - URL Permanent link to the post
|
||||||
|
* %M - comments
|
||||||
|
* %N - the username of the user (poster)
|
||||||
|
* %P - URL to create a new post
|
||||||
|
* %O - URL to edit this post
|
||||||
|
* %R - Site wide RSS feed
|
||||||
|
* %S - post seperator as defined by post_seperator
|
||||||
|
* %T - Title
|
||||||
|
* %U - URL the the user (poster)
|
||||||
|
* %W - Site Description as defined by site_description
|
||||||
|
* %X - Comment submission box
|
||||||
|
* %Y - Site Name as defined by site_name
|
||||||
|
* %Z - Attribution (to me) and source code link and license
|
||||||
|
104
app.js
104
app.js
@ -1,46 +1,64 @@
|
|||||||
const fs = require('fs');
|
// Get the libraries
|
||||||
const express = require('express');
|
const fs = require('fs'); // For modifying and reading files
|
||||||
const showdown = require('showdown')
|
const express = require('express'); // For running a webserver in nodejs
|
||||||
const crypto = require('crypto'); // For encrypting passwords
|
const showdown = require('showdown') // For converting markdown to html on demand, https://showdownjs.com/
|
||||||
|
const crypto = require('crypto'); // For encrypting passwords, I use sha512
|
||||||
|
// fromUnixTime creates a date object out of an integer
|
||||||
|
// format is used to format a date object into a defined format eg yyyy-MM-dd
|
||||||
|
// getUnixTime converts a date object into an integer (unix time)
|
||||||
|
// find out more at https://date-fns.org/ \or docs: https://date-fns.org/docs/Getting-Started
|
||||||
const { fromUnixTime, format, getUnixTime } = require("date-fns")
|
const { fromUnixTime, format, getUnixTime } = require("date-fns")
|
||||||
|
|
||||||
|
// There's only one possible argument, so we can just check if the user passed that one
|
||||||
|
// TODO I plan on adding more such as --help and --post so I should make this more robust at some point
|
||||||
if (process.argv[2] == "--first-time") {
|
if (process.argv[2] == "--first-time") {
|
||||||
initialise()
|
initialise() // Creates any files such users.js, posts.js, comments.js or config.js if they are not present
|
||||||
}
|
}
|
||||||
|
|
||||||
let users
|
// Define the modules now so they are global
|
||||||
let posts
|
let users // contains a list of users, each user is an object containing username,prettyname,hash and description
|
||||||
|
let posts // contains a list of posts,
|
||||||
let comments
|
let comments
|
||||||
let config
|
let config
|
||||||
|
|
||||||
try {
|
try {
|
||||||
users = require('./users.js');
|
// We're going to try and import the modules, users = require('./users.js');
|
||||||
posts = require('./posts.js');
|
posts = require('./posts.js');
|
||||||
comments = require('./comments.js');
|
comments = require('./comments.js');
|
||||||
config = require('./config.js');
|
config = require('./config.js');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
// if they don't all import then
|
||||||
|
// inform the user to pass --first-time and exit with an error code
|
||||||
console.log("A file is missing!")
|
console.log("A file is missing!")
|
||||||
console.log("Run with --first-time to initialise the program")
|
console.log("Run with --first-time to initialise the program")
|
||||||
console.log(error)
|
console.log(error)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
let converter = new showdown.Converter({
|
|
||||||
simpleLineBreaks: true,
|
|
||||||
tables: true,
|
|
||||||
strikethrough: true,
|
|
||||||
tasklists: true,
|
|
||||||
encodeEmails: true
|
|
||||||
})
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
|
// https://showdownjs.com/docs/available-options
|
||||||
|
let converter = new showdown.Converter({
|
||||||
|
simpleLineBreaks: true, // Parse line breaks as <br/> in paragraphs (GitHub-style behavior).
|
||||||
|
tables: true, // Enable support for tables syntax.
|
||||||
|
strikethrough: true, // Enable support for strikethrough: ~~text~~
|
||||||
|
tasklists: true, // Enable support for GitHub style tasklists. - [x] and - [ ]
|
||||||
|
encodeEmails: true //Enable automatic obfuscation of email addresses. emails are encoded via character entities
|
||||||
|
})
|
||||||
|
|
||||||
|
// The footer div is globale because it's a site wide, so define it here
|
||||||
let footer_div = config.site_wide_footer
|
let footer_div = config.site_wide_footer
|
||||||
footer_div = replace_format_indicators(footer_div)
|
footer_div = replace_format_indicators(footer_div)
|
||||||
|
|
||||||
|
// Define stuff to do with express (nodejs webserver)
|
||||||
|
const app = express();
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.static(config.root_path));
|
app.use(express.static(config.root_path));
|
||||||
|
|
||||||
|
// Initialise the program by creating users.js, comments.js, posts.js and config.js
|
||||||
|
// All require default content in them to start off with
|
||||||
|
// Then exit successfully
|
||||||
|
// returns nothing
|
||||||
function initialise() {
|
function initialise() {
|
||||||
try {
|
try {
|
||||||
const users = require("./users.js");
|
const users = require("./users.js");
|
||||||
@ -73,18 +91,28 @@ function initialise() {
|
|||||||
console.log("Error copying file")
|
console.log("Error copying file")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
console.log("Successfully initialised")
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The users are stored as a list of objects [ user_object, user_object, user_object ]
|
||||||
|
// So you cannot easily find the userID (position in list) from the username
|
||||||
|
// This function returns the username for a given userID by looping over every user
|
||||||
|
// if the user is present, it returns the index of the user (integer)
|
||||||
|
// if the user is not present it returns -1
|
||||||
function get_userID(username) {
|
function get_userID(username) {
|
||||||
for (let i = 0; i < users.users.length; i++) {
|
for (let i = 0; i < users.users.length; i++) { // Loop over every user
|
||||||
if (users.users[i]['username'] == username) {
|
if (users.users[i]['username'] == username) {
|
||||||
return i
|
return i // If the username matches then return the index of that user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return -1 // If user is not present, return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The configuration defines a date format using the date-fns syntax
|
||||||
|
// this converts unix time (an integer) into a string that is formatted according to config.js
|
||||||
|
// uses date-fns's fromUnixTime() and format()
|
||||||
|
// returns the formatted date (string)
|
||||||
function unix_time_to_date_format(unix_time) {
|
function unix_time_to_date_format(unix_time) {
|
||||||
date = fromUnixTime(unix_time)
|
date = fromUnixTime(unix_time)
|
||||||
formatted_date = format(date, config.date_format)
|
formatted_date = format(date, config.date_format)
|
||||||
@ -92,26 +120,41 @@ function unix_time_to_date_format(unix_time) {
|
|||||||
return formatted_date
|
return formatted_date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is similar to the above function, however, instead of formatting to the users
|
||||||
|
// configuration, it formats to RFC-822 which is the date format used by RSS feeds
|
||||||
|
// eg "Mon, 23 May 2025 18:59:59 +0100"
|
||||||
|
// returns the formatted date (string)
|
||||||
function unix_time_to_rss_date(unix_time) {
|
function unix_time_to_rss_date(unix_time) {
|
||||||
date = fromUnixTime(unix_time)
|
date = fromUnixTime(unix_time)
|
||||||
formatted_date = format(date, "EEE, dd MMM yyyy HH:mm:ss")
|
formatted_date = format(date, "EEE, dd MMM yyyy HH:mm:ss")
|
||||||
return `${formatted_date} ${config.time_zone}`
|
return `${formatted_date} ${config.time_zone}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function accepts a list of strings eg ["string1","string2,"string3"] (any length)
|
||||||
|
// then returns a string of them each pointing to a seperate url
|
||||||
|
// eg "<a href="/tag/string1">string1</a>, <a href="/tag/string2">string2</a>, <a href="/tag/string3">string3</a>
|
||||||
|
// this is so you can have a list of tags that each point to their individual tag page
|
||||||
|
// returns: string
|
||||||
function hyperlink_tags(tags) {
|
function hyperlink_tags(tags) {
|
||||||
string = ""
|
string = "" // Initialises the string
|
||||||
for (let tag_index = 0; tag_index < tags.length; tag_index++) {
|
for (let tag_index = 0; tag_index < tags.length; tag_index++) { // Loop over each tag
|
||||||
string += `<a href="/tag/${tags[tag_index]}">${tags[tag_index]}</a>`
|
string += `<a href="/tag/${tags[tag_index]}">${tags[tag_index]}</a>` // Adds the tag to the string as a HTML href
|
||||||
if (tag_index < tags.length - 1) {
|
if (tag_index < tags.length - 1) { // If there are more tags, then insert a comma
|
||||||
string += ", ";
|
string += ", ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
function replace_format_indicators(input_string, post_index=0, tag_name="tag") {
|
// See the readme format indicators section for a full list of format indicators
|
||||||
post_object = posts.posts[post_index]
|
// This function replaces the format indicators in a template to the content they represent
|
||||||
output_string = input_string
|
// accepts the template (string),
|
||||||
|
// the post index (int) as an optional paramter to indicate what post is to be used (for replacing things like content and titles)
|
||||||
|
// the tag (strig) as an optional parameter to indicate what tag is being used (for /tag/:tag pages)
|
||||||
|
// returns the template with it's format indiactors replaced (string)
|
||||||
|
function replace_format_indicators(template, post_index=0, tag_name="tag") {
|
||||||
|
post_object = posts.posts[post_index] // Defines the post object for easy reference
|
||||||
|
output_string = template // These should always be replaceable
|
||||||
.replaceAll("%%", "%")
|
.replaceAll("%%", "%")
|
||||||
.replaceAll("%P", "/post")
|
.replaceAll("%P", "/post")
|
||||||
.replaceAll("%O", `/edit/${post_index}`)
|
.replaceAll("%O", `/edit/${post_index}`)
|
||||||
@ -119,7 +162,7 @@ function replace_format_indicators(input_string, post_index=0, tag_name="tag") {
|
|||||||
.replaceAll("%Y", config.site_name)
|
.replaceAll("%Y", config.site_name)
|
||||||
.replaceAll("%W", config.site_description)
|
.replaceAll("%W", config.site_description)
|
||||||
.replaceAll("%Z", config.attribution)
|
.replaceAll("%Z", config.attribution)
|
||||||
if (posts.posts.length > 0) {
|
if (posts.posts.length > 0) { // These can only be replaced if there are more than 0 posts in the posts list
|
||||||
output_string = output_string
|
output_string = output_string
|
||||||
.replaceAll("%A", (post_object["tags"]))
|
.replaceAll("%A", (post_object["tags"]))
|
||||||
.replaceAll("%B", (hyperlink_tags(post_object["tags"])))
|
.replaceAll("%B", (hyperlink_tags(post_object["tags"])))
|
||||||
@ -142,7 +185,7 @@ function replace_format_indicators(input_string, post_index=0, tag_name="tag") {
|
|||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</form>`)
|
</form>`)
|
||||||
}
|
}
|
||||||
if (config.enable_hitcount == true) {
|
if (config.enable_hitcount == true) { // Finally, the hitcounter should only be replaced if config.enable_hitcount is true
|
||||||
output_string = output_string
|
output_string = output_string
|
||||||
.replaceAll("%H", fs.readFileSync('hitcount.txt'))
|
.replaceAll("%H", fs.readFileSync('hitcount.txt'))
|
||||||
}
|
}
|
||||||
@ -150,6 +193,10 @@ function replace_format_indicators(input_string, post_index=0, tag_name="tag") {
|
|||||||
return output_string
|
return output_string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This escapes some potentially dangerous HTML characters with their HTML entities
|
||||||
|
// https://www.freeformatter.com/html-entities.html
|
||||||
|
// accepts a string
|
||||||
|
// returns a string with some character replaced by their entities
|
||||||
function escape_input(input) {
|
function escape_input(input) {
|
||||||
let output = input
|
let output = input
|
||||||
.replaceAll("<", "<")
|
.replaceAll("<", "<")
|
||||||
@ -158,6 +205,7 @@ function escape_input(input) {
|
|||||||
.replaceAll('"', """)
|
.replaceAll('"', """)
|
||||||
.replaceAll("'", "'")
|
.replaceAll("'", "'")
|
||||||
.replaceAll("/", "/")
|
.replaceAll("/", "/")
|
||||||
|
.replaceAll("%", "%")
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
248
|
253
|
Loading…
x
Reference in New Issue
Block a user