diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..0ca753a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +package.json +node_modules +package-lock.json +posts.js +users.js diff --git a/.posts.js.swp b/.posts.js.swp new file mode 100644 index 0000000..1e9bcef Binary files /dev/null and b/.posts.js.swp differ diff --git a/app.js b/app.js old mode 100644 new mode 100755 index 8c07af8..10d13e2 --- a/app.js +++ b/app.js @@ -1,48 +1,111 @@ 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 users = require('./users.js'); const posts = require('./posts.js'); const config = require('./config.js'); const app = express(); -const port = 8005; +const port = 8080; app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(express.static(config.root_path)); -function unix_time_to_date_format(unix_time) { - let date_object = new Date(unix_time) - let formatter = new Intl.DateTimeFormat(config.date_format,config.date_options); - let formatted_time = formatter.format(date_object); - - return formatted_time +function get_userID(username) { + for (let i = 0; i < users.users.length; i++) { + if (users.users[i]['username'] == username) { + return i + } + } + 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 += `${tags[tag_index]}` + 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] output_string = input_string - .replace("%A", (post_object["tags"])) - .replace("%C", post_object["content"]) - .replace("%D", unix_time_to_date_format(post_object["pubdate"])) - .replace("%E", unix_time_to_date_format(post_object["editdate"])) - .replace("%L", `/post/${post_index}`) - .replace("%N", post_object["poster"]) - .replace("%P", "/post") - .replace("%O", "/edit") - .replace("%R", "/rss") - .replace("%S", config.seperator) - .replace("%T", post_object["title"]) - .replace("%U", `/user/${post_object["poster"]}`) - .replace("%Y", config.site_name) - .replace("%W", config.site_description) + .replaceAll("%A", (post_object["tags"])) + .replaceAll("%B", (hyperlink_tags(post_object["tags"]))) + .replaceAll("%C", post_object["content"].replaceAll("\n","
")) + .replaceAll("%D", unix_time_to_date_format(post_object["pubdate"])) + .replaceAll("%E", unix_time_to_date_format(post_object["editdate"])) + .replaceAll("%F", users.users[post_object["userID"]]['prettyname']) + .replaceAll("%G", tag_name) + .replaceAll("%I", users.users[post_object['userID']]['description']) + .replaceAll("%L", `/post/${post_index}`) + .replaceAll("%N", users.users[post_object["userID"]]['username']) + .replaceAll("%P", "/post") + .replaceAll("%O", "/edit") + .replaceAll("%R", "/rss") + .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 } +app.get(config.rss_path, (req,res) => { + if (config.rss == false) { + res.send("Sorry, RSS is disabled!") + } + else { + let rss_content = ` + + + ${config.site_name} + ${config.site_url} + ${config.site_description} + ` + for (let i = posts.posts.length-1; i >= 0; i--) { + rss_content += ` + + ${posts.posts[i]["title"]} + ${config.site_url}/post/${i}.${config.file_extension} + ")}]]> + ${config.site_url}/post/${i} + ${unix_time_to_rss_date(posts.posts[i]['pubdate'])}` + for (let j = 0; j < posts.posts[i]['tags'].length; j++) { + rss_content += `` + }; + rss_content += "" + } + rss_content += ` + + ` + res.setHeader('content-type', 'application/rss+xml'); + res.send(rss_content) + }; +}); + app.get("/", (req,res) => { header_div = config.timeline_header - header_div = replace_format_indicators(header_div, 0); + header_div = replace_format_indicators(header_div); posts_div = ""; counter = posts.posts.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); counter -= 1; } - res.send(`
${posts_div}
`); + res.send(`
${posts_div}
`); }); app.get("/post", (req,res) => { - res.send(`
+ res.send(`
-
+



@@ -69,21 +132,35 @@ app.get("/edit", (req,res) => { }); app.get("/user/:username", (req, res) => { - header_div = `

${req.params.username}

` + header_div = config.user_page_header + header_div = replace_format_indicators(header_div) posts_div = ""; 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; posts_div += replace_format_indicators(post, post_index); } } - res.send(`
${posts_div}
`); + res.send(`
${posts_div}
`); }); app.get("/post/:post_index", (req, res) => { post_div = ""; let post = config.post_page_format; post_div += replace_format_indicators(post, req.params.post_index); - res.send(`
${post_div}
`); + res.send(`
${post_div}
`); +}); +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(`
${page_content}
`); }); app.post("/submit_post", (req,res) => { @@ -92,19 +169,23 @@ app.post("/submit_post", (req,res) => { const title = req.body.title const content = req.body.content 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); - 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({ - "poster": username, + "userID": get_userID(username), "title": title, "content": content, - "pubdate": datetime, - "editdate": datetime, + "pubdate": unix_timestamp, + "editdate": unix_timestamp, "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, "/"); } else { @@ -113,5 +194,5 @@ app.post("/submit_post", (req,res) => { }); 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}`); }); diff --git a/config.js b/config.js old mode 100644 new mode 100755 index 7cc5b05..30fe7b2 --- a/config.js +++ b/config.js @@ -1,28 +1,30 @@ export const seperator = "
" -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 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 date_format = 'en-GB'; -export const date_options = { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: true, - timeZone: 'UTC' -} +//export const federation = true +//export const fediverse_url = "deadvey.com" +export const rss = true +export const rss_path = "/rss" + +// https://date-fns.org/v4.1.0/docs/format +export const date_format = "yyyy-MM-dd" +export const time_zone = "+0000" //// Format ///// // The syntax for this is pretty simple // %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) +// %I - User description // %L - URL Permanent link to the post // %N - the username of the user (poster) // %P - URL to create a new post @@ -38,17 +40,21 @@ export const timeline_header = `

%Y

%W

Create Post
Edit Post
-RSS Feed +RSS Feed
%S` - +export const user_page_header = `

%F's posts:

+%I +%S` +export const tag_page_header = `

Posts tagged: %G

%S` +// --------------------------------------------- export const user_post_format = `

%T

%C

-%A
+%B
Permalink
%S` export const post_page_format = `

%T

%C

-%A
+%B
By %N
Posted: %D
Edited: %E` @@ -57,3 +63,61 @@ export const timeline_post_format = `

%T

Permalink
By %N %S` +export const tag_post_format = `

%T

+

%C

+%B
+Permalink
+By %N +%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; + } +} +` diff --git a/testposts.js b/testposts.js new file mode 100755 index 0000000..93e170b --- /dev/null +++ b/testposts.js @@ -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"]}]