Changed how comments are stored and how data is retrieved

This commit is contained in:
2025-11-27 11:34:12 +00:00
parent ef8711b0e1
commit 7d38752f34
14 changed files with 130 additions and 128 deletions

View File

@@ -21,8 +21,8 @@ Read the [configuation guide](docs/CONFIG.md) for configuration help (in config.
* user creation, modification and deletion via frontend * user creation, modification and deletion via frontend
* multi user * multi user
* powerful customisation via EJS * powerful customisation via EJS
* Configuration via config.json
* site wide and user specific rss, atom * site wide and user specific rss, atom
* hitcount
* Markdown syntax in posts * Markdown syntax in posts
* Commenting on posts and replying to other comments * Commenting on posts and replying to other comments
* site wide custom CSS and strings * site wide custom CSS and strings

View File

@@ -14,7 +14,7 @@ export function increment_hitcount(postID = -1) { // -1 Means it will increment
writedata('hitcount', hitcount); writedata('hitcount', hitcount);
} }
else { else {
let post = getdata('posts', postID); let post = getdata('posts','id', postID);
if (typeof post.hitcount != 'undefined') { if (typeof post.hitcount != 'undefined') {
post.hitcount += 1; post.hitcount += 1;
writedata('posts', post, postID) writedata('posts', post, postID)
@@ -68,60 +68,62 @@ export function searchdata(term, type) { // Searches users and posts for any mat
return search_results; return search_results;
}; };
export function getdata(data, index=-1) { export function getdata(data_type, key=-1, value=-1) {
let result = undefined
if (config["data_storage"] == "json") { switch (config["data_storage"]) {
if (data == "posts" || data == 'users' || data == 'comments') { case 'json':
let result = func.require_module(`../data/${data}.json`) switch (data_type) {
if (index != -1) { case 'users':
if (index < result.length) { case 'posts':
return result[index] case 'comments':
} result = func.require_module(`../data/${data_type}.json`)
return 1 // This index doesn't exist if (key != -1) {
return result[func.find_key_value_pair(result, key, value)]
return -1 // This index doesn't exist
}
return result
break;
case 'hitcount':
result = func.require_module('../data/data.json') // This file is actually called data.json
return result["hitcount"]
break;
default:
console.log("Error, invalid requested")
return -1
break;
} }
return result break;
}
else if (data == "hitcount") {
let result = func.require_module('../data/data.json') // This file is actually called data.json
return result["hitcount"]
}
else {
console.log("Error, invalid requested")
return 1
}
}
// NOT YET WORKING! // NOT YET WORKING!
if (config["data_storage"] == "mysql") { case 'mysql':
const mysql = require('mysql'); const mysql = require('mysql');
let con = mysql.createConnection({ let con = mysql.createConnection({
host: config.database.host, host: config.database.host,
user: config.database.user, user: config.database.user,
password: config.database.password, password: config.database.password,
database: config.database.database, database: config.database.database,
}); });
con.connect(function(err) { con.connect(function(err) {
if (err) throw err; if (err) throw err;
if (data == "posts" || data == 'users' || data == 'comments') { if (data == "posts" || data == 'users' || data == 'comments') {
con.query(`SELECT * FROM ${data}`, function (err, result, fields) { con.query(`SELECT * FROM ${data}`, function (err, result, fields) {
if (err) throw err; if (err) throw err;
result = Object.values(JSON.parse(JSON.stringify(result))) result = Object.values(JSON.parse(JSON.stringify(result)))
console.log(result) console.log(result)
return result; return result;
}); });
} }
else if (data == 'hitcount') { else if (data == 'hitcount') {
con.query(`SELECT paramValue FROM params WHERE paramName = '${data}'`, function (err, result, fields) { con.query(`SELECT paramValue FROM params WHERE paramName = '${data}'`, function (err, result, fields) {
if (err) throw err; if (err) throw err;
result = Object.values(JSON.parse(JSON.stringify(result))) result = Object.values(JSON.parse(JSON.stringify(result)))
console.log(result) console.log(result)
return result; return result;
}); });
} }
}); });
} }
} }

View File

@@ -7,12 +7,15 @@ const locale = require(`../locales/${config.locale}.json`)
// This function requires a module without caching it // This function requires a module without caching it
// So the server doesn't need to be restarted, though this can slow it down a bit. // So the server doesn't need to be restarted, though this can slow it down a bit.
// https://stackoverflow.com/a/16060619 // https://stackoverflow.com/a/16060619
export function require_module(module) { export function require_module(module)
if (config.cache_data == false) { {
if (config.cache_data == false)
{
delete require.cache[require.resolve(module)]; delete require.cache[require.resolve(module)];
return require(module); return require(module);
} }
else { else
{
return require(module); return require(module);
} }
} }
@@ -22,7 +25,8 @@ export function require_module(module) {
// this converts unix time (an integer) into a string that is formatted according to config.js // this converts unix time (an integer) into a string that is formatted according to config.js
// uses date-fns's fromUnixTime() and format() functions // uses date-fns's fromUnixTime() and format() functions
// returns the formatted date (string) // returns the formatted date (string)
export function unix_time_to_date_format(unix_time) { export function unix_time_to_date_format(unix_time)
{
const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library
let date = fromUnixTime(unix_time) let date = fromUnixTime(unix_time)
let formatted_date = format(date, config.date_format) let formatted_date = format(date, config.date_format)
@@ -33,14 +37,16 @@ export function unix_time_to_date_format(unix_time) {
// eg "Mon, 23 May 2025 18:59:59 +0100" // eg "Mon, 23 May 2025 18:59:59 +0100"
// accepts unix time (int) // accepts unix time (int)
// returns the formatted date (string) // returns the formatted date (string)
export function unix_time_to_rss_date(unix_time) { export function unix_time_to_rss_date(unix_time)
{
const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library
let date = fromUnixTime(unix_time) let date = fromUnixTime(unix_time)
let formatted_date = format(date, "EEE, dd MMM yyyy HH:mm:ss") let formatted_date = format(date, "EEE, dd MMM yyyy HH:mm:ss")
return `${formatted_date} ${config.time_zone}` return `${formatted_date} ${config.time_zone}`
} }
// And again with atom's date format // And again with atom's date format
export function unix_time_to_atom_date(unix_time) { export function unix_time_to_atom_date(unix_time)
{
const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library
let date = fromUnixTime(unix_time) let date = fromUnixTime(unix_time)
let formatted_date = format(date, "yyyy-MM-dd'T'HH:mm:ss'Z'") let formatted_date = format(date, "yyyy-MM-dd'T'HH:mm:ss'Z'")
@@ -51,13 +57,17 @@ export function unix_time_to_atom_date(unix_time) {
// eg "<a href="/tag/string1">string1</a>, <a href="/tag/string2">string2</a>, <a href="/tag/string3">string3</a>" // 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 // this is so you can have a list of tags that each point to their individual tag page
// returns: string // returns: string
export function render_tags(tags) { export function render_tags(tags)
{
let string = "" // Initialises the string let string = "" // Initialises the string
if (tags.length == 1 && tags[0] == "") { if (tags.length == 1 && tags[0] == "")
{
string = ''; // If there are no tags, output nothing string = ''; // If there are no tags, output nothing
} }
else { else
for (let tag_index = 0; tag_index < tags.length; tag_index++) { // Loop over each tag {
for (let tag_index = 0; tag_index < tags.length; tag_index++)
{ // Loop over each tag
string += `<a href="/tag/${tags[tag_index].trim()}">#${tags[tag_index].trim()}</a> ` // Adds the tag to the string as a HTML href string += `<a href="/tag/${tags[tag_index].trim()}">#${tags[tag_index].trim()}</a> ` // Adds the tag to the string as a HTML href
} }
} }
@@ -68,10 +78,13 @@ export function render_tags(tags) {
// This function returns the username for a given userID by looping over every user // 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 present, it returns the index of the user (integer)
// if the user is not present it returns -1 // if the user is not present it returns -1
export function get_userID(username) { export function get_userID(username)
const users = require("../data/users.json") {
for (let i = 0; i < users.length; i++) { // Loop over every user const users = require_module("../data/users.json")
if (users[i]['username'] == username) { for (let i = 0; i < users.length; i++)
{ // Loop over every user
if (users[i]['username'] == username)
{
return i // If the username matches then return the index of that user return i // If the username matches then return the index of that user
} }
} }
@@ -82,7 +95,8 @@ export function get_userID(username) {
// https://www.freeformatter.com/html-entities.html // https://www.freeformatter.com/html-entities.html
// accepts a string // accepts a string
// returns a string with some character replaced by their entities // returns a string with some character replaced by their entities
export function escape_input(input) { export function escape_input(input)
{
let output = input let output = input
.replaceAll("&", "&amp;") // This must be first .replaceAll("&", "&amp;") // This must be first
.replaceAll("<", "&lt;") .replaceAll("<", "&lt;")
@@ -97,7 +111,8 @@ export function escape_input(input) {
// Render comment content by replacing the >> int with a url link to that comment // Render comment content by replacing the >> int with a url link to that comment
// Syntax: ">> postID-commentID" // Syntax: ">> postID-commentID"
export function render_comment(comment_content) { export function render_comment(comment_content)
{
return comment_content return comment_content
.replaceAll(/>> ([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>> $1-$2</a>") .replaceAll(/>> ([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>> $1-$2</a>")
.replaceAll(/>>([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>>$1-$2</a>") .replaceAll(/>>([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>>$1-$2</a>")
@@ -108,9 +123,11 @@ export function render_comment(comment_content) {
}; };
// Renders a string into markdown using markdown-it library // Renders a string into markdown using markdown-it library
export function render_md(content) { export function render_md(content)
{
const markdownit = require("markdown-it") const markdownit = require("markdown-it")
const md = markdownit({ // this is just defining some options for markdown-it, should I add this to config.json? const md = markdownit
({ // this is just defining some options for markdown-it, should I add this to config.json?
html: false, html: false,
xhtmlOut: false, xhtmlOut: false,
breaks: true, breaks: true,
@@ -121,3 +138,11 @@ export function render_md(content) {
return md.render(content) return md.render(content)
}; };
export function find_key_value_pair(data_array, key, value) {
for (let i = 0; i < data_array.length; i++) {
if (data_array[i][key] == value) {
return i
}
}
return -1
};

View File

@@ -31,11 +31,10 @@ router.post("/submit_comment", (req,res) => {
new_comment = { new_comment = {
"name": name, "name": name,
"content": content, "content": content,
"id": comments[postID].length, "id": comments[postID]['comments'].length,
"pubdate": unix_timestamp, "pubdate": unix_timestamp,
"postID": postID,
}; };
comments[postID].push(new_comment); comments[postID]['comments'].push(new_comment);
fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`, 'utf-8'); fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`, 'utf-8');
} }
@@ -59,8 +58,9 @@ router.post("/submit_post", (req,res) => {
else if (users[func.get_userID(username)]['hash'] == password) { // Password matches else if (users[func.get_userID(username)]['hash'] == password) { // Password matches
console.log(username, "is submitting a post titled:", title); console.log(username, "is submitting a post titled:", title);
id = posts.length
posts.push({ posts.push({
"id": posts.length, "id": id,
"userID": func.get_userID(username), "userID": func.get_userID(username),
"title": title, "title": title,
"content": content, "content": content,
@@ -69,7 +69,7 @@ router.post("/submit_post", (req,res) => {
"tags": tags, "tags": tags,
}) })
fs.writeFileSync(`../data/posts.json`, `${JSON.stringify(posts)}`, 'utf-8'); fs.writeFileSync(`../data/posts.json`, `${JSON.stringify(posts)}`, 'utf-8');
comments.push([]) comments.push({'id': id, 'comments': []})
fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`) fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`)
res.redirect(302, "/"); res.redirect(302, "/");
} }
@@ -216,7 +216,7 @@ router.get('/search', (req, res) => {
console.log('searching for: ', search_term); console.log('searching for: ', search_term);
const search_results = data.searchdata(search_term, search_type); // data.searchdata returns an array of search results const search_results = data.searchdata(search_term, search_type); // data.searchdata returns an array of search results
res.render('partials/search', { res.render('pages/search', {
config, config,
locale, locale,
search_results, search_results,

View File

@@ -13,7 +13,7 @@ router.get("/index/pages", (req,res) => {
users: data.getdata('users'), users: data.getdata('users'),
comments: data.getdata('comments'), comments: data.getdata('comments'),
}); });
}); // /index/posts }); // /index/pages
router.get("/index/posts", (req,res) => { router.get("/index/posts", (req,res) => {
res.render("indexes/posts", { res.render("indexes/posts", {
config, config,
@@ -25,13 +25,13 @@ router.get("/index/users", (req,res) => {
config, config,
users: data.getdata('users'), users: data.getdata('users'),
}); });
}); // /index/posts }); // /index/users
router.get("/index/comments", (req,res) => { router.get("/index/comments", (req,res) => {
res.render("indexes/comments", { res.render("indexes/comments", {
config, config,
comments: data.getdata('comments'), comments: data.getdata('comments'),
}); });
}); // /index/posts }); // /index/comments
module.exports = router; module.exports = router;

View File

@@ -33,7 +33,7 @@ router.get("/", (req,res) => {
// Users // Users
router.get("/user/:username", (req, res) => { router.get("/user/:username", (req, res) => {
const userID = func.get_userID(req.params.username) const userID = func.get_userID(req.params.username)
let user = data.getdata('users', userID) let user = data.getdata('users', 'id', userID)
if (userID != -1) { if (userID != -1) {
res.render("pages/user", res.render("pages/user",
{ {
@@ -61,7 +61,7 @@ router.get("/user/:username", (req, res) => {
// Posts // Posts
router.get("/post/:post_index", (req, res) => { router.get("/post/:post_index", (req, res) => {
const postID = parseInt(req.params.post_index) const postID = parseInt(req.params.post_index)
let post = data.getdata('posts', postID) let post = data.getdata('posts','id', postID)
if (post == 1) { // data.getdata returns error code 1 if nothing is available if (post == 1) { // data.getdata returns error code 1 if nothing is available
res.render("partials/message", { res.render("partials/message", {
message: locale.post_doesnt_exist, message: locale.post_doesnt_exist,
@@ -69,7 +69,7 @@ router.get("/post/:post_index", (req, res) => {
}) })
} }
if (config.enable_hitcount) { if (config.enable_hitcount) {
data.increment_hitcount(postID) data.increment_hitcount(postID)
} }
if (typeof post["deleted"] == "undefined" || post["deleted"] == false) { if (typeof post["deleted"] == "undefined" || post["deleted"] == false) {
res.render("pages/post", res.render("pages/post",
@@ -78,8 +78,8 @@ router.get("/post/:post_index", (req, res) => {
locale, locale,
post, post,
postID, postID,
user: data.getdata('users', post.userID), user: data.getdata('users','id', post.userID),
comments: data.getdata('comments', postID), comments: data.getdata('comments','id', postID)["comments"],
fromUnixTime, fromUnixTime,
format, format,
getUnixTime, getUnixTime,
@@ -117,7 +117,7 @@ router.get("/comment/:postID-:commentID", (req,res) => {
const commentID = parseInt(req.params.commentID); const commentID = parseInt(req.params.commentID);
const postID = parseInt(req.params.postID); const postID = parseInt(req.params.postID);
let posts_comments = data.getdata('comments', postID) let posts_comments = data.getdata('comments', 'id', postID)["comments"]
let comment = 1 let comment = 1
// For loop to find the comment with matching ID // For loop to find the comment with matching ID
posts_comments.forEach((current_comment, index) => { posts_comments.forEach((current_comment, index) => {

View File

@@ -20,7 +20,7 @@
<% }; %> <% }; %>
Comments:<br/> Comments:<br/>
<% for (let postID = 0; postID < comments.length; postID++) { %> <% for (let postID = 0; postID < comments.length; postID++) { %>
<% for (let comment_index = 0; comment_index < comments[postID].length; comment_index++) { %> <% for (let comment_index = 0; comment_index < comments[postID]['comments'].length; comment_index++) { %>
<a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/> <a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/>
<% }; %> <% }; %>
<% }; %> <% }; %>

View File

@@ -5,7 +5,7 @@
</head> </head>
<body> <body>
<% for (let postID = 0; postID < comments.length; postID++) { %> <% for (let postID = 0; postID < comments.length; postID++) { %>
<% for (let comment_index = 0; comment_index < comments[postID].length; comment_index++) { %> <% for (let comment_index = 0; comment_index < comments[postID]['comments'].length; comment_index++) { %>
<a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/> <a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/>
<% }; %> <% }; %>
<% }; %> <% }; %>

View File

@@ -1,37 +0,0 @@
<!DOCTYPE html>
<html lang='<%- config.locale %>'>
<head>
<%- include('../partials/head'); %>
</head>
<body>
<div id='header'>
<%- include('../headers/site_wide'); %>
</div>
<div id='advanced-search'>
<form method="GET" action="/search">
<label>Search Term:</label>
<input type='text' placeholder='🔍' name='q' value='<%- search_term %>'><br/>
<label>Search for:</label><br/>
<label>Post:</label>
<input type="checkbox" name="type" value="post" <% if (search_type.includes('post')) {%>checked<% } %>><br/>
<label>User:</label>
<input type="checkbox" name="type" value="user" <% if (search_type.includes('user')) {%>checked<% } %>><br/>
<input type="submit" value="Submit">
</form>
</div>
<%- config.seperator %>
<div id='results'>
<% search_results.posts.forEach((result, index) => { %>
<a href="/post/<%- result.id %>"><%- result.title %></a><br/>
<% }); %>
<% search_results.users.forEach((result, index) => { %>
<a href="/user/<%- result.username %>"><%- result.prettyname %></a><br/>
<% }); %>
</div>
</body>
</html>

View File

@@ -15,7 +15,7 @@
</div> </div>
<div id="post-details"> <div id="post-details">
<span id="post-author"> <span id="post-author">
<i><%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.username %></a></i> <i><%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
</span> </span>
- -
<span id="post-pubdate"> <span id="post-pubdate">

View File

@@ -15,7 +15,7 @@
</div> </div>
<div id="post-details"> <div id="post-details">
<span id="post-author"> <span id="post-author">
<i><a href="/user/<%= user.username %>"><%= user.username %></a></i> <i><a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
</span> </span>
- -
<span id="post-pubdate"> <span id="post-pubdate">

View File

@@ -15,7 +15,7 @@
</div> </div>
<div id="post-details"> <div id="post-details">
<span id="post-author"> <span id="post-author">
<i><a href="/user/<%= user.username %>"><%= user.username %></a></i> <i><a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
</span> </span>
- -
<span id="post-pubdate"> <span id="post-pubdate">

View File

@@ -15,7 +15,7 @@
</div> </div>
<div id="post-details"> <div id="post-details">
<span id="post-author"> <span id="post-author">
<i><a href="/user/<%= user.username %>"><%= user.username %></a></i> <i><a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
</span> </span>
- -
<span id="post-pubdate"> <span id="post-pubdate">

View File

@@ -15,11 +15,23 @@ body {
input, textarea, button { input, textarea, button {
border: none; border: none;
border-radius: 5px; border-radius: 5px;
field-sizing: content; /* Only supported by Chromium */ font-size: 20px;
}
textarea {
width: 100%;
height: 250px;
field-sizing: content; /* Only supported by Chromium */
} }
a { a {
text-decoration: none; text-decoration: none;
} }
#search-form input {
border: none;
border-radius: 0px;
font-size: 14px;
}
}
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }