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 insecure as hell
|
||||
|
||||
# planned features
|
||||
# planned features/todo list
|
||||
* atom
|
||||
* federation
|
||||
* federation (looks tricky)
|
||||
* sign up
|
||||
* 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');
|
||||
const express = require('express');
|
||||
const showdown = require('showdown')
|
||||
const crypto = require('crypto'); // For encrypting passwords
|
||||
// Get the libraries
|
||||
const fs = require('fs'); // For modifying and reading files
|
||||
const express = require('express'); // For running a webserver in nodejs
|
||||
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")
|
||||
|
||||
// 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") {
|
||||
initialise()
|
||||
initialise() // Creates any files such users.js, posts.js, comments.js or config.js if they are not present
|
||||
}
|
||||
|
||||
let users
|
||||
let posts
|
||||
// Define the modules now so they are global
|
||||
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 config
|
||||
|
||||
try {
|
||||
users = require('./users.js');
|
||||
// We're going to try and import the modules, users = require('./users.js');
|
||||
posts = require('./posts.js');
|
||||
comments = require('./comments.js');
|
||||
config = require('./config.js');
|
||||
}
|
||||
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("Run with --first-time to initialise the program")
|
||||
console.log(error)
|
||||
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
|
||||
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.json());
|
||||
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() {
|
||||
try {
|
||||
const users = require("./users.js");
|
||||
@ -73,18 +91,28 @@ function initialise() {
|
||||
console.log("Error copying file")
|
||||
})
|
||||
}
|
||||
console.log("Successfully initialised")
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
date = fromUnixTime(unix_time)
|
||||
formatted_date = format(date, config.date_format)
|
||||
@ -92,26 +120,41 @@ function unix_time_to_date_format(unix_time) {
|
||||
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) {
|
||||
date = fromUnixTime(unix_time)
|
||||
formatted_date = format(date, "EEE, dd MMM yyyy HH:mm:ss")
|
||||
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) {
|
||||
string = ""
|
||||
for (let tag_index = 0; tag_index < tags.length; tag_index++) {
|
||||
string += `<a href="/tag/${tags[tag_index]}">${tags[tag_index]}</a>`
|
||||
if (tag_index < tags.length - 1) {
|
||||
string = "" // Initialises the string
|
||||
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>` // Adds the tag to the string as a HTML href
|
||||
if (tag_index < tags.length - 1) { // If there are more tags, then insert a comma
|
||||
string += ", ";
|
||||
}
|
||||
}
|
||||
return string
|
||||
}
|
||||
|
||||
function replace_format_indicators(input_string, post_index=0, tag_name="tag") {
|
||||
post_object = posts.posts[post_index]
|
||||
output_string = input_string
|
||||
// See the readme format indicators section for a full list of format indicators
|
||||
// This function replaces the format indicators in a template to the content they represent
|
||||
// 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("%P", "/post")
|
||||
.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("%W", config.site_description)
|
||||
.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
|
||||
.replaceAll("%A", (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>
|
||||
</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
|
||||
.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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
let output = input
|
||||
.replaceAll("<", "<")
|
||||
@ -158,6 +205,7 @@ function escape_input(input) {
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'")
|
||||
.replaceAll("/", "/")
|
||||
.replaceAll("%", "%")
|
||||
return output
|
||||
}
|
||||
|
||||
|
@ -1 +1 @@
|
||||
248
|
||||
253
|
Loading…
x
Reference in New Issue
Block a user