Compare commits

...

6 Commits

Author SHA1 Message Date
e3e5469e1a Made it so if an invalid user or post is loaded, a proper error message is
shown instead of just a nodeJS error message
2025-09-06 23:54:49 +01:00
c8af978259 docs and also I improved the readability/user friendliness of the EJS 2025-09-03 22:28:50 +01:00
99e07389d0 * more proffesional language in rejecting AI
* footer uses locales
2025-08-27 19:00:49 +01:00
532010f873 bug fix for site_admin config field 2025-08-27 18:53:29 +01:00
5917789c5b add webadmin as a field in the config 2025-08-27 18:51:59 +01:00
4f0941262e Withdraw consent for AI scraping 2025-08-27 18:49:33 +01:00
18 changed files with 242 additions and 112 deletions

View File

@@ -48,3 +48,4 @@ See [docs/DOCUMENTATION.md](docs/DOCUMENTATION.md)
# Customisation: # Customisation:
Customisation of settings can be done via the config.json file (use example-config.json as an example) and see [the configuration guide](docs/CONFIG.md)<br/> Customisation of settings can be done via the config.json file (use example-config.json as an example) and see [the configuration guide](docs/CONFIG.md)<br/>
Additionaly, more complex configuration of the precise template of the whole site, can be done via [EJS](https://ejs.co/) (in /views) (see [the list of things variables and functions available in EJS](docs/EJS.md) (you will need to understand EJS syntax and JavaScript, to customise this (why did I use EJS? well I originally had this weird system of format indicators with percent (%) signs and stuff (like in unix's date (`date`)) but then I was told EJS is better and it sure is, though it is a bit harder to understand but MUCH more powerful!)) Additionaly, more complex configuration of the precise template of the whole site, can be done via [EJS](https://ejs.co/) (in /views) (see [the list of things variables and functions available in EJS](docs/EJS.md) (you will need to understand EJS syntax and JavaScript, to customise this (why did I use EJS? well I originally had this weird system of format indicators with percent (%) signs and stuff (like in unix's date (`date`)) but then I was told EJS is better and it sure is, though it is a bit harder to understand but MUCH more powerful!))
Also, if you want to change any of the strings on the website, please modify or create a new, customised locale in /locales

4
docs/CLASSES_AND_IDS.md Normal file
View File

@@ -0,0 +1,4 @@
Post:
![images/post-css.png](An image showing the css id's assosciated with each part of a post)

View File

@@ -1,4 +1,5 @@
# Installation # Installation
This program is currently just ran manually.<br/>
All you need to do is clone the git repository:<br/> All you need to do is clone the git repository:<br/>
```git clone https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs.git```<br/> ```git clone https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs.git```<br/>
Then navigate to /src:<br/> Then navigate to /src:<br/>
@@ -8,4 +9,4 @@ Then run the initialisation function:<br/>
Then you should modify config.json in / to suit your needs.<br/> Then you should modify config.json in / to suit your needs.<br/>
# Running # Running
I would reccomend running the program in tmux so it does not stop running when you close the terminal window.<br/> I would reccomend running the program in tmux so it does not stop running when you close the terminal window.<br/>
I might add support for running in the background later. • There is currently no init system support. I might add this later (or you could open a PR).

BIN
docs/images/post-css.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

View File

@@ -1,4 +1,5 @@
{ {
"site_admin": "your name",
"seperator": "<hr/>", "seperator": "<hr/>",
"site_name": "My Blog", "site_name": "My Blog",
"site_url": "https://example.com", "site_url": "https://example.com",

View File

@@ -12,6 +12,7 @@
"comment": "Comment", "comment": "Comment",
"submit": "Sumbit", "submit": "Sumbit",
"site_ran_by": "Site is ran by",
"signups_unavailable": "Sorry, this server does not allow signups", "signups_unavailable": "Sorry, this server does not allow signups",
"user_exists": "Sorry, this user already exists, try a different username", "user_exists": "Sorry, this user already exists, try a different username",
"user_doesnt_exist": "Sorry, this user does not exist", "user_doesnt_exist": "Sorry, this user does not exist",
@@ -20,9 +21,11 @@
"incorrect_password": "Incorrect Password", "incorrect_password": "Incorrect Password",
"rss_disabled": "Sorry, RSS is disabled", "rss_disabled": "Sorry, RSS is disabled",
"atom_disabled": "Sorry, ATOM is disabled", "atom_disabled": "Sorry, ATOM is disabled",
"AI_consent": "The content on this website may not be copied, scraped, or used to train AI models or large language models (LLMs) without prior written consent.",
"rss_feed": "RSS Feed", "rss_feed": "RSS Feed",
"atom_feed": "ATOM Feed", "atom_feed": "ATOM Feed",
"no_tags": "No Tags",
"new_post": "New Post", "new_post": "New Post",
"edit_post": "Edit Post", "edit_post": "Edit Post",
"sign_up": "Sign Up", "sign_up": "Sign Up",

View File

@@ -12,6 +12,7 @@
"comment": "Comment", "comment": "Comment",
"submit": "Sumbit", "submit": "Sumbit",
"site_ran_by": "Site is ran by",
"signups_unavailable": "Sorry, this server does not allow signups", "signups_unavailable": "Sorry, this server does not allow signups",
"user_exists": "Sorry, this user already exists, try a different username", "user_exists": "Sorry, this user already exists, try a different username",
"user_doesnt_exist": "Sorry, this user does not exist", "user_doesnt_exist": "Sorry, this user does not exist",
@@ -20,9 +21,11 @@
"incorrect_password": "Incorrect Password", "incorrect_password": "Incorrect Password",
"rss_disabled": "Sorry, RSS is disabled", "rss_disabled": "Sorry, RSS is disabled",
"atom_disabled": "Sorry, ATOM is disabled", "atom_disabled": "Sorry, ATOM is disabled",
"AI_consent": "The·content·on·this·website·may·not·be·copied,·scraped,·or·used·to·train·AI·models·or·large·language·models·(LLMs)·without·prior·written·consent.",
"rss_feed": "RSS Feed", "rss_feed": "RSS Feed",
"atom_feed": "ATOM Feed", "atom_feed": "ATOM Feed",
"no_tags": "No Tags",
"new_post": "New Post", "new_post": "New Post",
"edit_post": "Edit Post", "edit_post": "Edit Post",
"sign_up": "Sign Up", "sign_up": "Sign Up",

View File

@@ -1,4 +1,9 @@
{ {
"name": "blogger-nodejs",
"version": "0.0.5",
"description": "Simple web logging backend",
"author": "DeaDvey",
"license": "WTFPL",
"dependencies": { "dependencies": {
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"ejs": "^3.1.10", "ejs": "^3.1.10",

View File

@@ -1,5 +1,7 @@
import { createRequire } from 'module'; import { createRequire } from 'module';
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url)
const config = require("../config.json")
const locale = require(`../locales/${config.locale}.json`)
// The configuration defines a date format using the date-fns (a datetime library) syntax // The configuration defines a date format using the date-fns (a datetime library) syntax
// eg "yyyy-MM-dd" // eg "yyyy-MM-dd"
@@ -8,7 +10,6 @@ const require = createRequire(import.meta.url)
// 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
const config = require("../config.json")
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)
return formatted_date return formatted_date
@@ -20,7 +21,6 @@ export function unix_time_to_date_format(unix_time) {
// 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
const config = require("../config.json")
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}`
@@ -37,14 +37,19 @@ 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 hyperlink_tags(tags) { export function render_tags(tags) {
let string = "" // Initialises the string let string = "" // Initialises the string
for (let tag_index = 0; tag_index < tags.length; tag_index++) { // Loop over each tag if (tags.length == 1 && tags[0] == "") {
string += `<a href="/tag/${tags[tag_index]}">${tags[tag_index]}</a>` // Adds the tag to the string as a HTML href string = locale.no_tags; // If there are no tags, output something
if (tag_index < tags.length - 1) { // If there are more tags, then insert a comma }
string += ", "; else {
} 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 return string
} }
// The users are stored as a list of objects [ user_object, user_object, user_object ] // The users are stored as a list of objects [ user_object, user_object, user_object ]
@@ -101,8 +106,6 @@ export function render_comment(comment_content) {
}; };
export function render_md(content) { export function render_md(content) {
const markdownit = require("markdown-it") const markdownit = require("markdown-it")
const config = require("../config.json")
const locale = require(`../locales/${config.locale}.json`)
const md = markdownit({ const md = markdownit({
html: false, html: false,
xhtmlOut: false, xhtmlOut: false,

View File

@@ -191,24 +191,32 @@ app.get("/", (req,res) => {
app.get("/user/:username", (req, res) => { app.get("/user/:username", (req, res) => {
const userID = func.get_userID(req.params.username) const userID = func.get_userID(req.params.username)
console.log(userID) console.log(userID)
console.log(users[userID].prettyname) if (userID != -1) {
res.render("pages/user", res.render("pages/user",
{ {
config, config,
locale, locale,
posts, posts,
user: users[userID], user: users[userID],
userID: userID, userID: userID,
comments: comments.comments, comments: comments.comments,
fromUnixTime: fromUnixTime, fromUnixTime: fromUnixTime,
format: format, format: format,
getUnixTime: getUnixTime, getUnixTime: getUnixTime,
func, func,
}) })
}
else if (userID == -1) {
res.render("partials/message",
{
message: locale.user_doesnt_exist,
config,
})
}
}); // /user/:username }); // /user/:username
app.get("/post/:post_index", (req, res) => { app.get("/post/:post_index", (req, res) => {
const postID = req.params.post_index const postID = req.params.post_index
if (posts[postID]["deleted"] == true) { if (postID > posts.length-1 || posts[postID]["deleted"] == true) {
res.render("partials/message", { res.render("partials/message", {
message: locale.post_doesnt_exist, message: locale.post_doesnt_exist,
config, config,

View File

@@ -13,7 +13,7 @@
<% if ( posts[index].deleted != true) { %> <% if ( posts[index].deleted != true) { %>
<% posts[index].tags.forEach((current_tag, tag_index) => { %> <% posts[index].tags.forEach((current_tag, tag_index) => { %>
<% if (current_tag == tag) { %> <% if (current_tag == tag) { %>
<%- include('../posts/tag', {post: posts[index], postID: index}); %> <%- include('../posts/tag', {post: posts[index], user: users[posts[index].userID], comments: comments[index]}); %>
<% } %> <% } %>
<% }) %> <% }) %>
<% } %> <% } %>

View File

@@ -18,7 +18,7 @@
<div id="posts"> <div id="posts">
<% for (let index = posts.length - 1; index >= 0; index--) { %> <% for (let index = posts.length - 1; index >= 0; index--) { %>
<% if (posts[index]["deleted"] != true) { %> <% if (posts[index]["deleted"] != true) { %>
<%- include('../posts/timeline', {post: posts[index], postID: index, user: users[posts[index].userID]}); %> <%- include('../posts/timeline', {post: posts[index], postID: index, user: users[posts[index].userID], comments: comments[index]}); %>
<% } %> <% } %>
<% } %> <% } %>
</div> </div>

View File

@@ -11,7 +11,7 @@
<div id="posts"> <div id="posts">
<% for (let index = posts.length - 1; index >= 0; index--) { %> <% for (let index = posts.length - 1; index >= 0; index--) { %>
<% if (posts[index].userID == userID) { %> <% if (posts[index].userID == userID) { %>
<%- include('../posts/user', {post: posts[index], postID: index, user: user}); %> <%- include('../posts/user', {post: posts[index], postID: index, user: user, comments: comments[index]}); %>
<% } %> <% } %>
<% } %> <% } %>
</div> </div>

View File

@@ -1,2 +1,3 @@
Site is ran by deadvey<br/> <%= locale.site_ran_by %> <%= config.site_admin %><br/>
<%- locale.attribution %> <%- locale.attribution %><br/>
<%= locale.AI_consent %> <!-- remove consent for AI scrapers -->

View File

@@ -1,28 +1,48 @@
<h1> <div id="post-header">
<%= post.title %> <h1>
</h1> <%= post.title %>
<%- func.render_md(post.content) %><br/> </h1>
<i> </div>
<%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.username %></a><br/> <div id="post-content">
</i> <p>
<%- func.hyperlink_tags(post.tags) %><br/> <%- func.render_md(post.content) %><br/>
<a href="<%= config.edit_post_base_url %>/<%= postID %>"><%= locale.edit_post %></a><br/> </p>
<i><%= locale.published %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/> </div>
<i><%= locale.last_modified %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/> <div id="post-details">
<div id="post-author">
<%- config.seperator %> <i><%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.username %></a><br/></i>
</div>
<!-- Comment form --> <div id="post-tags">
<form method="POST" action="/submit_comment"> <%- func.render_tags(post.tags) %><br/>
<input type="hidden" name="post_index" value="<%= postID %>"> </div>
<label><%= locale.username %>:</label><br/><input name="name"><br/><br/> <div id="post-edit">
<label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/> <a href="<%= config.edit_post_base_url %>/<%= post["id"] %>"><%= locale.edit_post %></a><br/>
<button type="submit"><%= locale.submit %></button> </div>
</form> <div id="post-pubdate">
<i><%= locale.published %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
<% comments.forEach((comment, postID) => { %> </div>
<%- include('../partials/comment', {comment: comment}) %> <div id="post-editdate">
<% }) %> <i><%= locale.last_modified %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
</div>
</div>
<div id="post-commentform">
<!-- Comment form -->
<form method="POST" action="/submit_comment">
<input type="hidden" name="post_index" value="<%= post["id"] %>">
<label><%= locale.username %>:</label><br/><input name="name"><br/><br/>
<label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/>
<button type="submit"><%= locale.submit %></button>
</form>
</div>
<div id="post-comments">
<% comments.forEach((comment, index) => { %>
<div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
<%- include('../partials/comment', {comment: comment}) %>
</div>
<% }) %>
</div>
<%- config.seperator %> <%- config.seperator %>

View File

@@ -1,23 +1,51 @@
<h3> <div id="post-header">
<%= post.title %> <h1>
</h3> <%= post.title %>
<%- func.render_md(post.content) %><br/> </h1>
<a href="<%= config.edit_post_base_url %>/<%= postID %>"><%= locale.edit_post %></a><br/> </div>
<a href="/post/<%- postID %>"><%= locale.permalink %></a><br/> <div id="post-content">
<%- func.hyperlink_tags(post.tags) %> <p>
<br/> <%- func.render_md(post.content) %><br/>
</p>
</div>
<div id="post-details">
<div id="post-author">
<i><%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.username %></a><br/></i>
</div>
<div id="post-permalink">
<a href="/post/<%= post["id"] %>"><%= locale.permalink %><a/>
</div>
<div id="post-tags">
<%- func.render_tags(post.tags) %><br/>
</div>
<div id="post-edit">
<a href="<%= config.edit_post_base_url %>/<%= post["id"] %>"><%= locale.edit_post %></a><br/>
</div>
<div id="post-pubdate">
<i><%= locale.published %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
</div>
<div id="post-editdate">
<i><%= locale.last_modified %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
</div>
</div>
<!-- Comment form --> <div id="post-commentform">
<form method="POST" action="/submit_comment"> <!-- Comment form -->
<input type="hidden" name="post_index" value="<%= postID %>"> <form method="POST" action="/submit_comment">
<label><%= locale.username %>:</label><br/><input name="name"><br/><br/> <input type="hidden" name="post_index" value="<%= post["id"] %>">
<label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/> <label><%= locale.username %>:</label><br/><input name="name"><br/><br/>
<button type="submit"><%= locale.submit %></button> <label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/>
</form> <button type="submit"><%= locale.submit %></button>
</form>
</div>
<% comments[postID].forEach((comment) => { %> <div id="post-comments">
<%- include('../partials/comment', {comment: comment}) %> <% comments.forEach((comment, index) => { %>
<% }) %> <div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
<%- include('../partials/comment', {comment: comment}) %>
</div>
<% }) %>
</div>
<%- config.seperator %> <%- config.seperator %>

View File

@@ -1,25 +1,51 @@
<h3> <div id="post-header">
<%= post.title %> <h1>
</h3> <%= post.title %>
<%- func.render_md(post.content) %><br/> </h1>
<a href="<%= config.edit_post_base_url %>/<%= postID %>"><%= locale.edit_post %></a><br/> </div>
<a href="/post/<%- postID %>"><%= locale.permalink %></a><br/> <div id="post-content">
<i> <p>
<%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.username %></a><br/> <%- func.render_md(post.content) %><br/>
</i> </p>
<br/> </div>
<div id="post-details">
<div id="post-author">
<i><%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.username %></a><br/></i>
</div>
<div id="post-permalink">
<a href="/post/<%= post["id"] %>"><%= locale.permalink %><a/>
</div>
<div id="post-tags">
<%- func.render_tags(post.tags) %><br/>
</div>
<div id="post-edit">
<a href="<%= config.edit_post_base_url %>/<%= post["id"] %>"><%= locale.edit_post %></a><br/>
</div>
<div id="post-pubdate">
<i><%= locale.published %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
</div>
<div id="post-editdate">
<i><%= locale.last_modified %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
</div>
</div>
<!-- Comment form --> <div id="post-commentform">
<form method="POST" action="/submit_comment"> <!-- Comment form -->
<input type="hidden" name="post_index" value="<%= postID %>"> <form method="POST" action="/submit_comment">
<label><%= locale.username %>:</label><br/><input name="name"><br/><br/> <input type="hidden" name="post_index" value="<%= post["id"] %>">
<label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/> <label><%= locale.username %>:</label><br/><input name="name"><br/><br/>
<button type="submit"><%= locale.submit %></button> <label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/>
</form> <button type="submit"><%= locale.submit %></button>
</form>
</div>
<% comments[postID].forEach((comment, postID) => { %> <div id="post-comments">
<%- include('../partials/comment', {comment: comment}) %> <% comments.forEach((comment, index) => { %>
<% }) %> <div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
<%- include('../partials/comment', {comment: comment}) %>
</div>
<% }) %>
</div>
<%- config.seperator %> <%- config.seperator %>

View File

@@ -1,22 +1,48 @@
<h3> <div id="post-header">
<%= post.title %> <h1>
</h3> <%= post.title %>
<%- func.render_md(post.content) %><br/> </h1>
<a href="<%= config.edit_post_base_url %>/<%= postID %>"><%= locale.edit_post %></a><br/> </div>
<a href="/post/<%- postID %>"><%= locale.permalink %></a><br/> <div id="post-content">
<br/> <p>
<%- func.render_md(post.content) %><br/>
</p>
</div>
<div id="post-details">
<div id="post-permalink">
<a href="/post/<%= post["id"] %>"><%= locale.permalink %><a/>
</div>
<div id="post-tags">
<%- func.render_tags(post.tags) %><br/>
</div>
<div id="post-edit">
<a href="<%= config.edit_post_base_url %>/<%= post["id"] %>"><%= locale.edit_post %></a><br/>
</div>
<div id="post-pubdate">
<i><%= locale.published %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
</div>
<div id="post-editdate">
<i><%= locale.last_modified %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
</div>
</div>
<!-- Comment form --> <div id="post-commentform">
<form method="POST" action="/submit_comment"> <!-- Comment form -->
<input type="hidden" name="post_index" value="<%= postID %>"> <form method="POST" action="/submit_comment">
<label><%= locale.username %>:</label><br/><input name="name"><br/><br/> <input type="hidden" name="post_index" value="<%= post["id"] %>">
<label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/> <label><%= locale.username %>:</label><br/><input name="name"><br/><br/>
<button type="submit"><%= locale.submit %></button> <label><%= locale.comment %>:</label><br/><textarea name="content"></textarea><br/>
</form> <button type="submit"><%= locale.submit %></button>
</form>
</div>
<% comments[postID].forEach((comment, postID) => { %> <div id="post-comments">
<%- include('../partials/comment', {comment: comment}) %> <% comments.forEach((comment, index) => { %>
<% }) %> <div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
<%- include('../partials/comment', {comment: comment}) %>
</div>
<% }) %>
</div>
<%- config.seperator %> <%- config.seperator %>