Version 1.0\nusable, but some stuff still to do

This commit is contained in:
deadvey 2025-07-09 17:41:24 +01:00
parent d55f104aa5
commit dcd9faa1f9
5 changed files with 207 additions and 56 deletions

5
.gitignore vendored Executable file
View File

@ -0,0 +1,5 @@
package.json
node_modules
package-lock.json
posts.js
users.js

BIN
.posts.js.swp Normal file

Binary file not shown.

157
app.js Normal file → Executable file
View File

@ -1,48 +1,111 @@
const express = require('express'); const express = require('express');
const crypto = require('crypto'); const crypto = require('crypto'); // For encrypting passwords
const { fromUnixTime, format, getUnixTime } = require("date-fns")
const fs = require('fs'); const fs = require('fs');
const users = require('./users.js'); const users = require('./users.js');
const posts = require('./posts.js'); const posts = require('./posts.js');
const config = require('./config.js'); const config = require('./config.js');
const app = express(); const app = express();
const port = 8005; const port = 8080;
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));
function unix_time_to_date_format(unix_time) { function get_userID(username) {
let date_object = new Date(unix_time) for (let i = 0; i < users.users.length; i++) {
let formatter = new Intl.DateTimeFormat(config.date_format,config.date_options); if (users.users[i]['username'] == username) {
let formatted_time = formatter.format(date_object); return i
}
return formatted_time }
return -1
} }
function replace_format_indicators(input_string, post_index) { function unix_time_to_date_format(unix_time) {
date = fromUnixTime(unix_time)
formatted_date = format(date, config.date_format)
return formatted_date
}
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}`
}
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 += ", ";
}
}
return string
}
function replace_format_indicators(input_string, post_index=0, tag_name="tag") {
post_object = posts.posts[post_index] post_object = posts.posts[post_index]
output_string = input_string output_string = input_string
.replace("%A", (post_object["tags"])) .replaceAll("%A", (post_object["tags"]))
.replace("%C", post_object["content"]) .replaceAll("%B", (hyperlink_tags(post_object["tags"])))
.replace("%D", unix_time_to_date_format(post_object["pubdate"])) .replaceAll("%C", post_object["content"].replaceAll("\n","<br/>"))
.replace("%E", unix_time_to_date_format(post_object["editdate"])) .replaceAll("%D", unix_time_to_date_format(post_object["pubdate"]))
.replace("%L", `/post/${post_index}`) .replaceAll("%E", unix_time_to_date_format(post_object["editdate"]))
.replace("%N", post_object["poster"]) .replaceAll("%F", users.users[post_object["userID"]]['prettyname'])
.replace("%P", "/post") .replaceAll("%G", tag_name)
.replace("%O", "/edit") .replaceAll("%I", users.users[post_object['userID']]['description'])
.replace("%R", "/rss") .replaceAll("%L", `/post/${post_index}`)
.replace("%S", config.seperator) .replaceAll("%N", users.users[post_object["userID"]]['username'])
.replace("%T", post_object["title"]) .replaceAll("%P", "/post")
.replace("%U", `/user/${post_object["poster"]}`) .replaceAll("%O", "/edit")
.replace("%Y", config.site_name) .replaceAll("%R", "/rss")
.replace("%W", config.site_description) .replaceAll("%S", config.seperator)
.replaceAll("%T", post_object["title"])
.replaceAll("%U", `/user/${users.users[post_object["userID"]]['username']}`)
.replaceAll("%Y", config.site_name)
.replaceAll("%W", config.site_description)
return output_string return output_string
} }
app.get(config.rss_path, (req,res) => {
if (config.rss == false) {
res.send("Sorry, RSS is disabled!")
}
else {
let rss_content = `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>${config.site_name}</title>
<link>${config.site_url}</link>
<description>${config.site_description}</description>
`
for (let i = posts.posts.length-1; i >= 0; i--) {
rss_content += `
<item>
<title>${posts.posts[i]["title"]}</title>
<link>${config.site_url}/post/${i}.${config.file_extension}</link>
<description><![CDATA[${posts.posts[i]["content"].replaceAll("\n","<br/>")}]]></description>
<guid isPermaLink="true">${config.site_url}/post/${i}</guid>
<pubDate>${unix_time_to_rss_date(posts.posts[i]['pubdate'])}</pubDate>`
for (let j = 0; j < posts.posts[i]['tags'].length; j++) {
rss_content += `<category><![CDATA[${posts.posts[i]['tags'][j]}]]></category>`
};
rss_content += "</item>"
}
rss_content += `
</channel>
</rss>`
res.setHeader('content-type', 'application/rss+xml');
res.send(rss_content)
};
});
app.get("/", (req,res) => { app.get("/", (req,res) => {
header_div = config.timeline_header header_div = config.timeline_header
header_div = replace_format_indicators(header_div, 0); header_div = replace_format_indicators(header_div);
posts_div = ""; posts_div = "";
counter = posts.posts.length - 1; counter = posts.posts.length - 1;
while ((counter >= 0) && (counter > (posts.posts.length - (config.timeline_length + 1)))) while ((counter >= 0) && (counter > (posts.posts.length - (config.timeline_length + 1))))
@ -51,13 +114,13 @@ app.get("/", (req,res) => {
posts_div += replace_format_indicators(post, counter); posts_div += replace_format_indicators(post, counter);
counter -= 1; counter -= 1;
} }
res.send(`<html><body><div id="header">${header_div}</div><div id="posts">${posts_div}</div></body></html>`); res.send(`<html><style>${config.css}</style><body><div id="header">${header_div}</div><div id="posts">${posts_div}</div></body></html>`);
}); });
app.get("/post", (req,res) => { app.get("/post", (req,res) => {
res.send(`</html><form action="/submit_post" method="POST" onsubmit="sha512password()"> res.send(`</html><style>${config.css}</style><form action="/submit_post" method="POST" onsubmit="sha512password()">
<label>Username: </label><input required name="username"><br/> <label>Username: </label><input required name="username"><br/>
<label>Password: </label><input required id="password" name="password"><br/> <label>Password: </label><input type="password" required id="password" name="password"><br/>
<label>Title: </label><input required name="title"><br/> <label>Title: </label><input required name="title"><br/>
<label>Content: </label><textarea required name="content"></textarea><br/> <label>Content: </label><textarea required name="content"></textarea><br/>
<label>Tags (comma seperated): </label><input name="tags"><br/> <label>Tags (comma seperated): </label><input name="tags"><br/>
@ -69,21 +132,35 @@ app.get("/edit", (req,res) => {
}); });
app.get("/user/:username", (req, res) => { app.get("/user/:username", (req, res) => {
header_div = `<h1>${req.params.username}</h1>` header_div = config.user_page_header
header_div = replace_format_indicators(header_div)
posts_div = ""; posts_div = "";
for (let post_index = posts.posts.length-1; post_index >= 0; post_index--) { for (let post_index = posts.posts.length-1; post_index >= 0; post_index--) {
if (posts.posts[post_index]["poster"] == req.params.username) { if (users.users[posts.posts[post_index]["userID"]]["username"] == req.params.username) {
let post = config.user_post_format; let post = config.user_post_format;
posts_div += replace_format_indicators(post, post_index); posts_div += replace_format_indicators(post, post_index);
} }
} }
res.send(`<html><body><div id="header">${header_div}</div><div id="posts">${posts_div}</div></body></html>`); res.send(`<html><style>${config.css}</style><body><div id="header">${header_div}</div><div id="posts">${posts_div}</div></body></html>`);
}); });
app.get("/post/:post_index", (req, res) => { app.get("/post/:post_index", (req, res) => {
post_div = ""; post_div = "";
let post = config.post_page_format; let post = config.post_page_format;
post_div += replace_format_indicators(post, req.params.post_index); post_div += replace_format_indicators(post, req.params.post_index);
res.send(`<html><body><div id="posts">${post_div}</div></body></html>`); res.send(`<html><style>${config.css}</style><body><div id="posts">${post_div}</div></body></html>`);
});
app.get("/tag/:tag", (req,res) => {
const tag = req.params.tag
let header_div = config.tag_page_header
header_div = replace_format_indicators(header_div,0,tag)
let page_content = ""
for (let i = posts.posts.length-1; i >= 0; i--) {
if (posts.posts[i]['tags'].includes(tag)) {
let post = config.tag_post_format;
page_content += replace_format_indicators(post, i);
};
};
res.send(`<html><style>${config.css}</style><body><div id="header">${header_div}</div><div id="posts">${page_content}</div></body></html>`);
}); });
app.post("/submit_post", (req,res) => { app.post("/submit_post", (req,res) => {
@ -92,19 +169,23 @@ app.post("/submit_post", (req,res) => {
const title = req.body.title const title = req.body.title
const content = req.body.content const content = req.body.content
const tags = req.body.tags.split(','); const tags = req.body.tags.split(',');
const datetime = new Date().getTime() const unix_timestamp = getUnixTime(new Date())
console.log(username, "is submitting a post titled:", title); console.log(username, "is submitting a post titled:", title);
if (users.users[username] == password) { // Password matches if (get_userID(username) == -1) {
res.send("User does not exist")
}
else if (users.users[get_userID(username)]['hash'] == password) { // Password matches
posts.posts.push({ posts.posts.push({
"poster": username, "userID": get_userID(username),
"title": title, "title": title,
"content": content, "content": content,
"pubdate": datetime, "pubdate": unix_timestamp,
"editdate": datetime, "editdate": unix_timestamp,
"tags": tags, "tags": tags,
}) })
fs.writeFileSync(`${config.root_path}/posts.js`, `export const posts = ${JSON.stringify(posts.posts)}`, 'utf-8'); fs.writeFileSync(`${__dirname}/posts.js`, `export const posts = ${JSON.stringify(posts.posts)}`, 'utf-8');
res.redirect(302, "/"); res.redirect(302, "/");
} }
else { else {
@ -113,5 +194,5 @@ app.post("/submit_post", (req,res) => {
}); });
app.listen(port, () => { app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`); console.log(`Server is running at http://localhost:${port} in ${config.root_path}`);
}); });

100
config.js Normal file → Executable file
View File

@ -1,28 +1,30 @@
export const seperator = "<hr/>" export const seperator = "<hr/>"
export const site_name = "DeaDvey's Blog" export const site_name = "Deadvey's Blog"
export const site_url = "https://deadvey.com"
export const site_description = "Films, tech, random shit" export const site_description = "Films, tech, random shit"
export const timeline_length = 20 export const timeline_length = 20
export const root_path = "/home/gaming/code/web/blogger" // Anything in this directory will be in the webroot, so put favicon.ico and anything else here.
export const root_path = "/var/www/deadvey.com/blog"
// Date format using https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString //export const federation = true
export const date_format = 'en-GB'; //export const fediverse_url = "deadvey.com"
export const date_options = { export const rss = true
day: '2-digit', export const rss_path = "/rss"
month: '2-digit',
year: 'numeric', // https://date-fns.org/v4.1.0/docs/format
hour: '2-digit', export const date_format = "yyyy-MM-dd"
minute: '2-digit', export const time_zone = "+0000"
second: '2-digit',
hour12: true,
timeZone: 'UTC'
}
//// Format ///// //// Format /////
// The syntax for this is pretty simple // The syntax for this is pretty simple
// %A - List of tags // %A - List of tags
// %B - List of tags, each one with a hyperlink to that tag page
// %C - Post content // %C - Post content
// %D - Published date in the format specified by date_format // %D - Published date in the format specified by date_format
// %E - Edited 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)
// %I - User description
// %L - URL Permanent link to the post // %L - URL Permanent link to the post
// %N - the username of the user (poster) // %N - the username of the user (poster)
// %P - URL to create a new post // %P - URL to create a new post
@ -38,17 +40,21 @@ export const timeline_header = `<h1>%Y</h1>
<h2>%W</h2> <h2>%W</h2>
<a href="%P">Create Post</a><br/> <a href="%P">Create Post</a><br/>
<a href="%O">Edit Post</a><br/> <a href="%O">Edit Post</a><br/>
<a href="%R">RSS Feed</a> <a href="%R">RSS Feed</a><br/>
%S` %S`
export const user_page_header = `<h1>%F's posts:</h1>
%I
%S`
export const tag_page_header = `<h1>Posts tagged: %G</h1>%S`
// ---------------------------------------------
export const user_post_format = `<h2>%T</h2> export const user_post_format = `<h2>%T</h2>
<p>%C</p> <p>%C</p>
<i>%A</i><br/> <i>%B</i><br/>
<a href="%L">Permalink</a><br/> <a href="%L">Permalink</a><br/>
%S` %S`
export const post_page_format = `<h1>%T</h1> export const post_page_format = `<h1>%T</h1>
<p>%C</p> <p>%C</p>
<i>%A</i><br/> <i>%B</i><br/>
<i>By <a href="%U">%N</a></i><br/< <i>By <a href="%U">%N</a></i><br/<
<i>Posted: %D</i><br/> <i>Posted: %D</i><br/>
<i>Edited: %E</i>` <i>Edited: %E</i>`
@ -57,3 +63,61 @@ export const timeline_post_format = `<h3>%T</h3>
<a href="%L">Permalink</a><br/> <a href="%L">Permalink</a><br/>
<i>By <a href="%U">%N</a></i> <i>By <a href="%U">%N</a></i>
%S` %S`
export const tag_post_format = `<h3>%T</h3>
<p>%C</p>
<i>%B</i><br/>
<a href="%L">Permalink</a><br/>
<i>By <a href="%U">%N</a></i>
%S`
/// Custom CSS to be applied to every page
export const css = `
@media (prefers-color-scheme: light) {
body {
background: #ebdbb2;
color: #282828;
}
code {
background: #bdae93;
}
a {
color: #076678;
text-decoration: none;
}
a:visited {
color: #8f3f71;
}
input,textarea,button {
background: #ebdbb2;
color: #282828;
border: 1px solid #282828;
}
}
@media (prefers-color-scheme: dark) {
body {
background: #282828;
color: #ebdbb2;
}
code {
background: #665c54;
box-decoration-break: clone;
display: block;
white-space: pre;
max-width: 50%;
min-width: 100px;
}
a {
color: #83a598;
text-decoration: none;
}
a:visited {
color: #d3869b;
}
input,textarea,button {
background: #282828;
color: #ebdbb2;
border: 1px solid #ebdbb2;
}
}
`

1
testposts.js Executable file
View File

@ -0,0 +1 @@
export const posts = [{"poster":"deadvey","title":"title","content":"content","pubdate":1749292431190,"editdate":1749292431190,"tags":["test","ignore"]},{"poster":"deadvey","title":"post2","content":"awhfjiawfh","pubdate":1749303915416,"editdate":1749303915416,"tags":["test","ignore","hello","goodbye"]},{"poster":"deadvey","title":"test3","content":"afd","pubdate":1750859453223,"editdate":1750859453223,"tags":["test","ignore","hello"]},{"poster":"deadvey","title":"test3","content":"awd","pubdate":1750859714618,"editdate":1750859714618,"tags":["awdwf"]}]