Compare commits
30 Commits
93c5f13750
...
master
Author | SHA1 | Date | |
---|---|---|---|
cb7dcde7c5 | |||
553f126f2a | |||
6f9e7aee13 | |||
23add8897b | |||
84a34d94f3 | |||
783f386dfe | |||
e63fb4a27a | |||
2ada1d970f | |||
17919e3078 | |||
179a4f83bc | |||
d9d45ff6ea | |||
87fd97730e | |||
acca51da20 | |||
788672cbea | |||
521dbccc7e | |||
8ad8f01043 | |||
d27330a3db | |||
996bf0018b | |||
45af80f747 | |||
23744f4000 | |||
9305559660 | |||
35e6b94ba1 | |||
3f173fc2e3 | |||
b44762ba7c | |||
1ecc223433 | |||
22a7983737 | |||
e597fd78f7 | |||
ef7178cc3f | |||
|
bfaf957ae2 | ||
|
e6476dcd4e |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,6 +4,8 @@ posts.json
|
|||||||
comments.json
|
comments.json
|
||||||
users.json
|
users.json
|
||||||
config.json
|
config.json
|
||||||
|
data.json
|
||||||
hitcount.txt
|
hitcount.txt
|
||||||
*.swp
|
*.swp
|
||||||
webroot/
|
data/
|
||||||
|
images/*
|
||||||
|
13
README.md
13
README.md
@@ -2,6 +2,7 @@ This software aims to provide a lot of power to the web admin who is running the
|
|||||||
Customisation is unlimited with a bit of knowledge of EJS and CSS, you can edit the entire formatting of the pages, making the site truly yours!<br/>
|
Customisation is unlimited with a bit of knowledge of EJS and CSS, you can edit the entire formatting of the pages, making the site truly yours!<br/>
|
||||||
This software also aims to be compatible with text based browsers and as a result contains no client side Javascript, if you're looking for a more<br/>
|
This software also aims to be compatible with text based browsers and as a result contains no client side Javascript, if you're looking for a more<br/>
|
||||||
beautiful and featureful blogging frontend, this isn't for you.<br/>
|
beautiful and featureful blogging frontend, this isn't for you.<br/>
|
||||||
|
The AI robots.txt file is from [ai.robots.txt (MIT)](https://github.com/ai-robots-txt/ai.robots.txt)<br/>
|
||||||
|
|
||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
> This software is not finished yet, so it's very buggy and probably really insecure<br/>
|
> This software is not finished yet, so it's very buggy and probably really insecure<br/>
|
||||||
@@ -24,7 +25,8 @@ Read the [configuation guide](docs/CONFIG.md) for configuration help (in config.
|
|||||||
* hitcount
|
* 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
|
* site wide custom CSS and strings
|
||||||
|
* Search functionality
|
||||||
* Page indexes
|
* Page indexes
|
||||||
|
|
||||||
# Bugs:
|
# Bugs:
|
||||||
@@ -33,14 +35,15 @@ Read the [configuation guide](docs/CONFIG.md) for configuration help (in config.
|
|||||||
|
|
||||||
# Planned features/todo list:
|
# Planned features/todo list:
|
||||||
* federation (looks tricky)
|
* federation (looks tricky)
|
||||||
* All strings (including in edit and post page) customisable
|
|
||||||
* formatable custom strings
|
|
||||||
* inline comments and docs
|
* inline comments and docs
|
||||||
* clean up code a bit
|
* clean up code a bit
|
||||||
* /postID and /userID pages
|
* /postID and /userID pages
|
||||||
* site index
|
* Make EJS modification more user friendly (half done)
|
||||||
* Make EJS modification more user friendly
|
|
||||||
* API for returning posts, users, comments, tags other?...
|
* API for returning posts, users, comments, tags other?...
|
||||||
|
* Moderation tools including a keyword blacklist
|
||||||
|
* Request account function? Not sure how this should be implemented.
|
||||||
|
* optional SQL
|
||||||
|
* initialisation has prompts for setup process.
|
||||||
|
|
||||||
# Docs:
|
# Docs:
|
||||||
See [docs/DOCUMENTATION.md](docs/DOCUMENTATION.md)
|
See [docs/DOCUMENTATION.md](docs/DOCUMENTATION.md)
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
{"hitcount":8,"comment_counter":0}
|
|
@@ -14,13 +14,14 @@ All options show an example configuartion value and the variable type + an expla
|
|||||||
| enable_hitcount | true | Boolean | Enabling the hitcount (a number that represents the amount of front page loads (stored in hitcount.txt)) can slightly slow down loading of the front page. |
|
| enable_hitcount | true | Boolean | Enabling the hitcount (a number that represents the amount of front page loads (stored in hitcount.txt)) can slightly slow down loading of the front page. |
|
||||||
| charset | "UTF-8" | String | This is the value in the <meta charset=""> tag in the html of all pages, you should not change this unless you know why. |
|
| charset | "UTF-8" | String | This is the value in the <meta charset=""> tag in the html of all pages, you should not change this unless you know why. |
|
||||||
| root_path | "/path/to/root/of/website" | String | Anything in this directory will be in the webroot, so put favicon.ico and anything else here. |
|
| root_path | "/path/to/root/of/website" | String | Anything in this directory will be in the webroot, so put favicon.ico and anything else here. |
|
||||||
|
| data_storage | "json" | String | JSON is currently the only supported format, but SQL is going to be added/is a work in progress |
|
||||||
|
| cache_data | true | Boolean | Not caching data means you can edit the posts, users, comments etc, maunally and not have to restart the server, however, for large instances this is not reccomended as it takes longer to load the required data. Note: config.json always needs a restart |
|
||||||
## Basic Customisation
|
## Basic Customisation
|
||||||
| name | example value | variable type | explanation |
|
| name | example value | variable type | explanation |
|
||||||
|-----------------|----------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------|----------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|locale|"en"|String|Your locale, see [/locales](/locales) for a list of all locales (you can open a PR for a new translation too)|
|
|locale|"en-GB"|String|Your locale, see [/locales](/locales) for a list of all locales (you can open a PR for a new translation too)|
|
||||||
|seperator|"\<hr/\>"|String|This is what %S represents in the formatting, this will go inbetween posts and generally to seperate out content on pages.|
|
|seperator|"\<hr/\>"|String|By default, this will go inbetween posts and generally to seperate out content on pages.|
|
||||||
|site_name|"My Blog"|String|This is what %Y represents; it's the name of your instance, a human readable string.|
|
|site_name|"Pete's Blogging Site!"|String|It's the name of your blog site, a human readable string.|
|
||||||
|site_description|"Read my blogs!"|String|This is what %W represents; it's the description of your instance, a human readable string.|
|
|site_description|"Read my blogs!"|String|This is what %W represents; it's the description of your instance, a human readable string.|
|
||||||
|default_commenter_username|"Anon"|String|Default commenter username if no username is inputted in comment submission.|
|
|default_commenter_username|"Anon"|String|Default commenter username if no username is inputted in comment submission.|
|
||||||
|
|
||||||
@@ -35,13 +36,14 @@ Read more at [date-fns](https://date-fns.org/v4.1.0/docs/format)<br/>
|
|||||||
| name | example value | variable type | explanation |
|
| name | example value | variable type | explanation |
|
||||||
|------|---------------|---------------|-------------------------------|
|
|------|---------------|---------------|-------------------------------|
|
||||||
|date_format|"yyyy-MM-dd"|String|The format of date's on the website.|
|
|date_format|"yyyy-MM-dd"|String|The format of date's on the website.|
|
||||||
|time_zone|"+0000"|String|\Your offset from UTC|
|
|time_zone|"+0000"|String|Your offset from UTC|
|
||||||
|
|
||||||
## Advanced Customisation
|
## Advanced Customisation
|
||||||
* /views/* files are EJS files (used for formatting HTML) and can be editted to your liking, you might want to read [the EJS docs](https://ejs.co/#docs) for help.
|
* /views/* files are EJS files (used for formatting HTML) and can be editted to your liking, you might want to read [the EJS docs](https://ejs.co/#docs) for help.
|
||||||
* "css": "body { background: red; }"<br/>
|
* "css": "body { background: red; }"<br/>
|
||||||
String. Custom CSS to be applied to all pages, if you want more complex css, you can edit custom.css.<br/>
|
String. Custom CSS to be applied to all pages, if you want more complex css, you can edit custom.css.<br/>
|
||||||
You can also edit the custom.css file in the webroot, as by default this is linked in the global header.
|
You can also edit the custom.css file in the webroot, as by default this is linked in the global header.
|
||||||
|
* You can create a file called custom.css in the webroot and that will be loaded as a style onto every page.
|
||||||
|
|
||||||
## Custom Strings
|
## Custom Strings
|
||||||
* You can edit all the strings on the site in /locales/<your-locale>.json
|
* You can edit all the strings on the site in /locales/\<your-locale>.json
|
||||||
|
@@ -5,12 +5,13 @@
|
|||||||
"site_url": "https://example.com",
|
"site_url": "https://example.com",
|
||||||
"language": "en-US",
|
"language": "en-US",
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
|
"cache_data": false,
|
||||||
"allow_signup": true,
|
"allow_signup": true,
|
||||||
"site_description": "Read my blogs!",
|
"site_description": "Read my blogs!",
|
||||||
"timeline_length": 20,
|
"timeline_length": 20,
|
||||||
"enable_hitcount": true,
|
"enable_hitcount": true,
|
||||||
"charset": "UTF-8",
|
"charset": "UTF-8",
|
||||||
"root_path": "/path/to/webroot",
|
"root_path": "../webroot/",
|
||||||
"edit_account_base_url": "/edit_account",
|
"edit_account_base_url": "/edit_account",
|
||||||
"new_post_url": "/post",
|
"new_post_url": "/post",
|
||||||
"signup_url": "/signup",
|
"signup_url": "/signup",
|
||||||
@@ -20,17 +21,5 @@
|
|||||||
"atom": true,
|
"atom": true,
|
||||||
"date_format": "yyyy-MM-dd",
|
"date_format": "yyyy-MM-dd",
|
||||||
"time_zone": "+0000",
|
"time_zone": "+0000",
|
||||||
"string": {
|
|
||||||
"signup_agreement": "I agree to not post illegal or hateful content",
|
|
||||||
"signups_unavailable": "Sorry, this server does not allow signups",
|
|
||||||
"user_exists": "Sorry, this user already exists, try a different username",
|
|
||||||
"user_doesnt_exist": "Sorry, this user does not exist",
|
|
||||||
"comment_doesnt_exist": "This comment doesn't exist, this could be because the post it was attached to was deleted",
|
|
||||||
"post_doesnt_exist": "This post doesn't exist or was deleted",
|
|
||||||
"delete_account_confirmation": "Delete my account - (I agree that my account and all of my posts will be permanently deleted instantly)",
|
|
||||||
"incorrect_password": "Incorrect Password",
|
|
||||||
"rss_disabled": "Sorry, RSS is disabled",
|
|
||||||
"attribution": "Powered by blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Source Code</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>license (WTFPL)</a>"
|
|
||||||
},
|
|
||||||
"css": ""
|
"css": ""
|
||||||
}
|
}
|
||||||
|
@@ -38,5 +38,6 @@
|
|||||||
"posts_tagged": "Posts Tagged",
|
"posts_tagged": "Posts Tagged",
|
||||||
"home_page": "Home Page",
|
"home_page": "Home Page",
|
||||||
"site_index": "Site Index",
|
"site_index": "Site Index",
|
||||||
|
"reply": "reply",
|
||||||
"attribution": "Powered by blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Source Code</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>license (WTFPL)</a>"
|
"attribution": "Powered by blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Source Code</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>license (WTFPL)</a>"
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
"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.",
|
"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",
|
||||||
@@ -38,5 +38,6 @@
|
|||||||
"posts_tagged": "Posts Tagged",
|
"posts_tagged": "Posts Tagged",
|
||||||
"home_page": "Home Page",
|
"home_page": "Home Page",
|
||||||
"site_index": "Site Index",
|
"site_index": "Site Index",
|
||||||
|
"reply": "reply",
|
||||||
"attribution": "Powered by blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Source Code</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>license (WTFPL)</a>"
|
"attribution": "Powered by blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Source Code</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>license (WTFPL)</a>"
|
||||||
}
|
}
|
||||||
|
@@ -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",
|
||||||
@@ -35,5 +38,6 @@
|
|||||||
"posts_tagged": "Posts Tagged",
|
"posts_tagged": "Posts Tagged",
|
||||||
"home_page": "Home Page",
|
"home_page": "Home Page",
|
||||||
"site_index": "Site Index",
|
"site_index": "Site Index",
|
||||||
|
"reply": "reply",
|
||||||
"attribution": "Powered by blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Source Code</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>license (WTFPL)</a>"
|
"attribution": "Powered by blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Source Code</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>license (WTFPL)</a>"
|
||||||
}
|
}
|
||||||
|
108
src/data.js
108
src/data.js
@@ -1,26 +1,86 @@
|
|||||||
import { createRequire } from "module";
|
import { createRequire } from "module";
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const func = require('./functions.js')
|
||||||
const config = require("../config.json")
|
const config = require("../config.json")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
|
|
||||||
export function getdata(data, key='', value='') {
|
// Literally just +1 to the hitcount
|
||||||
|
export function increment_hitcount(postID = -1) { // -1 Means it will increment the timeline hitcount
|
||||||
|
if (config.data_storage == 'json') {
|
||||||
|
if (postID == -1) {
|
||||||
|
let hitcount = getdata('hitcount');
|
||||||
|
hitcount += 1
|
||||||
|
writedata('hitcount', hitcount);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let post = getdata('posts', postID);
|
||||||
|
if (typeof post.hitcount != 'undefined') {
|
||||||
|
post.hitcount += 1;
|
||||||
|
writedata('posts', post, postID)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function searchdata(term, type) { // Searches users and posts for any matches
|
||||||
|
let search_results = {"posts": [], "users": []};
|
||||||
|
// Search users
|
||||||
|
if (type.includes('post')) {
|
||||||
|
let list = getdata('posts');
|
||||||
|
list.forEach((element,index) => {
|
||||||
|
if (typeof element.deleted == 'undefined' || element.deleted == false) {
|
||||||
|
if (element.content.includes(term)) {
|
||||||
|
search_results.posts.push(element)
|
||||||
|
}
|
||||||
|
else if (element.title.includes(term)) {
|
||||||
|
search_results.posts.push(element)
|
||||||
|
}
|
||||||
|
else if (element.tags.toString().includes(term)) {
|
||||||
|
search_results.posts.push(element)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (type.includes('user')) {
|
||||||
|
let list = getdata('users');
|
||||||
|
list.forEach((element,index) => {
|
||||||
|
if (typeof element.deleted == 'undefined' || element.deleted == false) {
|
||||||
|
if (element.username.includes(term)) {
|
||||||
|
search_results.users.push(element)
|
||||||
|
}
|
||||||
|
else if (element.prettyname.includes(term)) {
|
||||||
|
search_results.users.push(element)
|
||||||
|
}
|
||||||
|
else if (element.description.includes(term)) {
|
||||||
|
search_results.users.push(element)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return search_results;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getdata(data, index=-1) {
|
||||||
|
|
||||||
if (config["data_storage"] == "json") {
|
if (config["data_storage"] == "json") {
|
||||||
if (data == "posts" || data == 'users' || data == 'comments') {
|
if (data == "posts" || data == 'users' || data == 'comments') {
|
||||||
let result = require(`../data/${data}.json`)
|
let result = func.require_module(`../data/${data}.json`)
|
||||||
if (key != '') {
|
if (index != -1) {
|
||||||
result.forEach((object, index) => {
|
if (index < result.length) {
|
||||||
if (object[key] == value) {
|
return result[index]
|
||||||
return object
|
}
|
||||||
|
else {
|
||||||
|
return 1 // This index doesn't exist
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else if (data == "hitcount") {
|
else if (data == "hitcount") {
|
||||||
let result = fs.readFileSync("../data/hitcount.txt")
|
let result = func.require_module('../data/data.json') // This file is actually called data.json
|
||||||
return result
|
return result["hitcount"]
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("Error, invalid requested")
|
console.log("Error, invalid requested")
|
||||||
@@ -28,6 +88,7 @@ export function getdata(data, key='', value='') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOT YET WORKING!
|
||||||
if (config["data_storage"] == "mysql") {
|
if (config["data_storage"] == "mysql") {
|
||||||
const mysql = require('mysql');
|
const mysql = require('mysql');
|
||||||
let con = mysql.createConnection({
|
let con = mysql.createConnection({
|
||||||
@@ -43,6 +104,7 @@ export function getdata(data, key='', value='') {
|
|||||||
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)))
|
||||||
console.log(result)
|
console.log(result)
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
@@ -50,6 +112,7 @@ export function getdata(data, key='', value='') {
|
|||||||
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)))
|
||||||
console.log(result)
|
console.log(result)
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
@@ -58,3 +121,30 @@ export function getdata(data, key='', value='') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function writedata(data, data_to_write, index=-1) {
|
||||||
|
if (config["data_storage"] == "json") {
|
||||||
|
if (data == "posts" || data == 'users' || data == 'comments') {
|
||||||
|
if (index == -1) {
|
||||||
|
fs.writeFileSync(`../data/${data}.json`, JSON.stringify(data_to_write), 'utf-8')
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
else if (index >= 0) {
|
||||||
|
let result = getdata(data);
|
||||||
|
result[index] = data_to_write;
|
||||||
|
fs.writeFileSync(`../data/${data}.json`, JSON.stringify(result), 'utf-8')
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
else if (data == "hitcount") {
|
||||||
|
let other_data = func.require_module('../data/data.json') // This file is actually called data.json
|
||||||
|
other_data.hitcount = data_to_write
|
||||||
|
fs.writeFileSync('../data/data.json', JSON.stringify(other_data), 'utf-8')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Error, invalid requested")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,19 @@ const config = require("../config.json")
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const locale = require(`../locales/${config.locale}.json`)
|
const locale = require(`../locales/${config.locale}.json`)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// https://stackoverflow.com/a/16060619
|
||||||
|
export function require_module(module) {
|
||||||
|
if (config.cache_data == false) {
|
||||||
|
delete require.cache[require.resolve(module)];
|
||||||
|
return require(module);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return require(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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"
|
||||||
// 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
|
||||||
@@ -68,23 +81,13 @@ export function get_userID(username) {
|
|||||||
return -1 // If user is not present, return -1
|
return -1 // If user is not present, return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_comment(commentID) { // TODO scales like shit
|
|
||||||
const comments = require("../data/comments.json")
|
|
||||||
for (let i = 0; i < comments.comments.length; i++) { // Loop over every post
|
|
||||||
for (let j = 0; j < comments.comments[i].length; j++) { // Then every comment in that post
|
|
||||||
if (comments.comments[i][j]["id"] == commentID) {
|
|
||||||
return comments.comments[i][j]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1 // If comment is not present, return -1
|
|
||||||
}
|
|
||||||
// This escapes some potentially dangerous HTML characters with their HTML entities
|
// This escapes some potentially dangerous HTML characters with their HTML entities
|
||||||
// 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("&", "&") // This must be first
|
||||||
.replaceAll("<", "<")
|
.replaceAll("<", "<")
|
||||||
.replaceAll(">", ">")
|
.replaceAll(">", ">")
|
||||||
.replaceAll("\\", "\")
|
.replaceAll("\\", "\")
|
||||||
@@ -92,22 +95,25 @@ export function escape_input(input) {
|
|||||||
.replaceAll("'", "'")
|
.replaceAll("'", "'")
|
||||||
.replaceAll("/", "/")
|
.replaceAll("/", "/")
|
||||||
.replaceAll("%", "%")
|
.replaceAll("%", "%")
|
||||||
.replaceAll("&", "&")
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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"
|
||||||
export function render_comment(comment_content) {
|
export function render_comment(comment_content) {
|
||||||
return comment_content
|
return comment_content
|
||||||
.replaceAll(/>> ([0-9]*)/g, "<a href='/comment/$1'>>> $1</a>")
|
.replaceAll(/>> ([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>> $1-$2</a>")
|
||||||
.replaceAll(/>>([0-9]*)/g, "<a href='/comment/$1'>>>$1</a>")
|
.replaceAll(/>>([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>>$1-$2</a>")
|
||||||
.replaceAll(/>> ([0-9]*)/g, "<a href='/comment/$1'>>> $1</a>")
|
.replaceAll(/>> ([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>> $1-$2</a>")
|
||||||
.replaceAll(/>>([0-9]*)/g, "<a href='/comment/$1'>>>$1</a>")
|
.replaceAll(/>>([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>>$1-$2</a>")
|
||||||
.replaceAll("\n", "<br/>")
|
.replaceAll("\n", "<br/>")
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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({
|
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,
|
||||||
@@ -118,12 +124,3 @@ export function render_md(content) {
|
|||||||
return md.render(content)
|
return md.render(content)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Literally just +1 to the hitcount
|
|
||||||
export function increment_hitcount() {
|
|
||||||
if (config.data_storage == 'json') {
|
|
||||||
let other_data = require('../data/data.json');
|
|
||||||
other_data.hitcount += 1
|
|
||||||
console.log(`/ Is loaded, hitcount: ${other_data.hitcount}`)
|
|
||||||
fs.writeFileSync(`../data/data.json`, `${JSON.stringify(other_data)}`, 'utf-8');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
@@ -32,7 +32,7 @@ export function initialise() {
|
|||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log("Creating generic data file")
|
console.log("Creating generic data file")
|
||||||
fs.writeFileSync(`../data/data.json`, `{"hitcount": 0, "comment_counter": 0}`)
|
fs.writeFileSync(`../data/data.json`, `{"hitcount": 0}`)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const config = require("../config.json");
|
const config = require("../config.json");
|
||||||
|
@@ -16,18 +16,23 @@ const router = express.Router();
|
|||||||
////////////////////// Form actions /////////////////////////
|
////////////////////// Form actions /////////////////////////
|
||||||
router.post("/submit_comment", (req,res) => {
|
router.post("/submit_comment", (req,res) => {
|
||||||
const unix_timestamp = getUnixTime(new Date())
|
const unix_timestamp = getUnixTime(new Date())
|
||||||
|
const postID = parseInt(req.body.post_index)
|
||||||
|
const content = func.escape_input(req.body.content)
|
||||||
let name = func.escape_input(req.body.name)
|
let name = func.escape_input(req.body.name)
|
||||||
if (name == "") {
|
if (name == "") {
|
||||||
name = config.default_commenter_username
|
name = config.default_commenter_username
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let comments = data.getdata('comments')
|
||||||
|
|
||||||
new_comment = {
|
new_comment = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"content": func.escape_input(req.body.content),
|
"content": content,
|
||||||
"id": comments.counter,
|
"id": comments[postID].length,
|
||||||
"pubdate": unix_timestamp
|
"pubdate": unix_timestamp,
|
||||||
|
"postID": postID,
|
||||||
};
|
};
|
||||||
other_data.comment_counter += 1;
|
comments[postID].push(new_comment);
|
||||||
comments[req.body.post_index].push(new_comment);
|
|
||||||
fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`, 'utf-8');
|
fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`, 'utf-8');
|
||||||
|
|
||||||
res.redirect(301,`/post/${req.body.post_index}`)
|
res.redirect(301,`/post/${req.body.post_index}`)
|
||||||
@@ -195,4 +200,26 @@ router.post("/submit_edit_post", (req,res) => {
|
|||||||
}
|
}
|
||||||
}); // /submit_edit
|
}); // /submit_edit
|
||||||
|
|
||||||
|
router.get('/search', (req, res) => {
|
||||||
|
const search_term = func.escape_input(req.query.q); // 'q' is the parameter name
|
||||||
|
let search_type = req.query.type; // eg 'post', 'user'
|
||||||
|
if (typeof search_type == 'string') { // Make the search_term an array
|
||||||
|
search_type = [ search_type ]
|
||||||
|
}
|
||||||
|
if (typeof search_type == 'undefined') { // Default to all of the types
|
||||||
|
search_type = ['user', 'post'];
|
||||||
|
}
|
||||||
|
console.log('searching for: ', search_term);
|
||||||
|
const search_results = data.searchdata(search_term, search_type); // data.searchdata returns an array of search results
|
||||||
|
|
||||||
|
res.render('partials/search', {
|
||||||
|
config,
|
||||||
|
locale,
|
||||||
|
search_results,
|
||||||
|
search_term,
|
||||||
|
search_type,
|
||||||
|
})
|
||||||
|
|
||||||
|
}); // /search
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
@@ -38,19 +38,19 @@ router.get(`${config.edit_account_base_url}/:user_id`, (req,res) => {
|
|||||||
res.render("forms/edit_account", {
|
res.render("forms/edit_account", {
|
||||||
config,
|
config,
|
||||||
locale,
|
locale,
|
||||||
user: users[userID],
|
user: data.getdata('users', userID),
|
||||||
userID
|
userID
|
||||||
});
|
});
|
||||||
}); // /delete_account
|
}); // /delete_account
|
||||||
router.get(`${config.edit_post_base_url}/:post_id`, (req,res) => {
|
router.get(`${config.edit_post_base_url}/:post_id`, (req,res) => {
|
||||||
const post_id = req.params.post_id
|
const postID = req.params.post_id
|
||||||
const post = posts[post_id]
|
const post = data.getdata('posts', postID)
|
||||||
const user = users[post['userID']]
|
const user = data.getdata('users', post.userID)
|
||||||
res.render("forms/edit_post", {
|
res.render("forms/edit_post", {
|
||||||
config,
|
config,
|
||||||
locale,
|
locale,
|
||||||
post,
|
post,
|
||||||
post_id,
|
postID,
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
}); // /edit/:post_id
|
}); // /edit/:post_id
|
||||||
|
@@ -9,9 +9,9 @@ const router = express.Router();
|
|||||||
router.get("/index/pages", (req,res) => {
|
router.get("/index/pages", (req,res) => {
|
||||||
res.render("indexes/all_pages", {
|
res.render("indexes/all_pages", {
|
||||||
config,
|
config,
|
||||||
posts,
|
posts: data.getdata('posts'),
|
||||||
users,
|
users: data.getdata('users'),
|
||||||
comments: comments.comments,
|
comments: data.getdata('comments'),
|
||||||
});
|
});
|
||||||
}); // /index/posts
|
}); // /index/posts
|
||||||
router.get("/index/posts", (req,res) => {
|
router.get("/index/posts", (req,res) => {
|
||||||
|
@@ -7,10 +7,12 @@ const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date util
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
///////////////////// Standard Pages //////////////////////
|
///////////////////// Standard Pages //////////////////////
|
||||||
|
|
||||||
|
// Timeline
|
||||||
router.get("/", (req,res) => {
|
router.get("/", (req,res) => {
|
||||||
// Increment the hitcount
|
// Increment the hitcount
|
||||||
if (config.enable_hitcount) {
|
if (config.enable_hitcount) {
|
||||||
func.increment_hitcount()
|
data.increment_hitcount()
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render("pages/timeline",
|
res.render("pages/timeline",
|
||||||
@@ -27,16 +29,18 @@ router.get("/", (req,res) => {
|
|||||||
func,
|
func,
|
||||||
})
|
})
|
||||||
}); // /
|
}); // /
|
||||||
|
|
||||||
|
// 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)
|
||||||
console.log(data.getdata('users', 'id', userID)[0])
|
let user = data.getdata('users', userID)
|
||||||
if (userID != -1) {
|
if (userID != -1) {
|
||||||
res.render("pages/user",
|
res.render("pages/user",
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
locale,
|
locale,
|
||||||
posts: data.getdata('posts'),
|
posts: data.getdata('posts'),
|
||||||
user: data.getdata('users', 'id', userID),
|
user,
|
||||||
userID: userID,
|
userID: userID,
|
||||||
comments: data.getdata('comments'),
|
comments: data.getdata('comments'),
|
||||||
fromUnixTime,
|
fromUnixTime,
|
||||||
@@ -53,23 +57,29 @@ router.get("/user/:username", (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}); // /user/:username
|
}); // /user/:username
|
||||||
|
|
||||||
|
// Posts
|
||||||
router.get("/post/:post_index", (req, res) => {
|
router.get("/post/:post_index", (req, res) => {
|
||||||
const postID = req.params.post_index
|
const postID = parseInt(req.params.post_index)
|
||||||
if (postID > posts.length-1 || posts[postID]["deleted"] == true) {
|
let post = data.getdata('posts', postID)
|
||||||
|
if (config.enable_hitcount) {
|
||||||
|
data.increment_hitcount(postID)
|
||||||
|
}
|
||||||
|
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,
|
||||||
config,
|
config,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else if (typeof posts[postID]["deleted"] == "undefined" || posts[postID]["deleted"] == false) {
|
else if (typeof post["deleted"] == "undefined" || post["deleted"] == false) {
|
||||||
res.render("pages/post",
|
res.render("pages/post",
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
locale,
|
locale,
|
||||||
post: posts[postID],
|
post,
|
||||||
postID: postID,
|
postID,
|
||||||
user: users[posts[postID].userID],
|
user: data.getdata('users', post.userID),
|
||||||
comments: comments.comments[postID],
|
comments: data.getdata('comments', postID),
|
||||||
fromUnixTime,
|
fromUnixTime,
|
||||||
format,
|
format,
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
@@ -81,6 +91,9 @@ router.get("/post/:post_index", (req, res) => {
|
|||||||
res.redirect(301,"/")
|
res.redirect(301,"/")
|
||||||
}
|
}
|
||||||
}); // /post/:post_index
|
}); // /post/:post_index
|
||||||
|
|
||||||
|
|
||||||
|
// Tags
|
||||||
router.get("/tag/:tag", (req,res) => {
|
router.get("/tag/:tag", (req,res) => {
|
||||||
const tag = req.params.tag
|
const tag = req.params.tag
|
||||||
res.render("pages/tag",
|
res.render("pages/tag",
|
||||||
@@ -88,19 +101,32 @@ router.get("/tag/:tag", (req,res) => {
|
|||||||
config,
|
config,
|
||||||
locale,
|
locale,
|
||||||
tag,
|
tag,
|
||||||
posts,
|
posts: data.getdata('posts'),
|
||||||
users,
|
users: data.getdata('users'),
|
||||||
comments: comments.comments,
|
comments: data.getdata('comments'),
|
||||||
fromUnixTime: fromUnixTime,
|
fromUnixTime,
|
||||||
format: format,
|
format,
|
||||||
getUnixTime: getUnixTime,
|
getUnixTime,
|
||||||
func,
|
func,
|
||||||
})
|
})
|
||||||
}); // /tag/:tag
|
}); // /tag/:tag
|
||||||
router.get("/comment/:commentID", (req,res) => {
|
|
||||||
const commentID = req.params.commentID;
|
|
||||||
const comment = func.get_comment(commentID)
|
// Comments
|
||||||
if (comment == -1) {
|
router.get("/comment/:postID-:commentID", (req,res) => {
|
||||||
|
const commentID = parseInt(req.params.commentID);
|
||||||
|
const postID = parseInt(req.params.postID);
|
||||||
|
|
||||||
|
let posts_comments = data.getdata('comments', postID)
|
||||||
|
let comment = 1
|
||||||
|
// For loop to find the comment with matching ID
|
||||||
|
posts_comments.forEach((current_comment, index) => {
|
||||||
|
if (current_comment.id == commentID) {
|
||||||
|
comment = posts_comments[index]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// If comment doesn't exist, show error
|
||||||
|
if (comment == 1 || posts_comments == 1) { // Comment of this ID was not found
|
||||||
res.render("partials/message", {
|
res.render("partials/message", {
|
||||||
config,
|
config,
|
||||||
message: locale.comment_doesnt_exist,
|
message: locale.comment_doesnt_exist,
|
||||||
@@ -109,14 +135,14 @@ router.get("/comment/:commentID", (req,res) => {
|
|||||||
else {
|
else {
|
||||||
res.render("pages/comment",
|
res.render("pages/comment",
|
||||||
{
|
{
|
||||||
config: config,
|
config,
|
||||||
locale,
|
locale,
|
||||||
post: posts[comment["id"]],
|
|
||||||
users,
|
|
||||||
comment,
|
comment,
|
||||||
fromUnixTime: fromUnixTime,
|
postID,
|
||||||
format: format,
|
commentID,
|
||||||
getUnixTime: getUnixTime,
|
fromUnixTime,
|
||||||
|
format,
|
||||||
|
getUnixTime,
|
||||||
func,
|
func,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -3,9 +3,7 @@ const config = require('../../config')
|
|||||||
const data = require('../data')
|
const data = require('../data')
|
||||||
const func = require('../functions')
|
const func = require('../functions')
|
||||||
|
|
||||||
let users = require('../../data/users.json');
|
const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library
|
||||||
let posts = require('../../data/posts.json');
|
|
||||||
let comments = require('../../data/comments.json');
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<form action="/submit_edit_post" method="POST" onsubmit="sha512password()">
|
<form action="/submit_edit_post" method="POST" onsubmit="sha512password()">
|
||||||
<input name="userID" type="hidden" value="<%= post['userID'] %>">
|
<input name="userID" type="hidden" value="<%= post['userID'] %>">
|
||||||
<input name="postID" type="hidden" value="<%= post_id %>">
|
<input name="postID" type="hidden" value="<%= postID %>">
|
||||||
|
|
||||||
<label><%= locale.password %>:</label><br/>
|
<label><%= locale.password %>:</label><br/>
|
||||||
<input type="password" required id="password" name="password"><br/><br/>
|
<input type="password" required id="password" name="password"><br/><br/>
|
||||||
|
@@ -1,5 +1,27 @@
|
|||||||
<a href="/"><%= locale.home_page %></a>
|
<a id='home-page-link' href="/"><%= locale.home_page %></a>
|
||||||
<a href="/index/pages"><%= locale.site_index %></a>
|
/
|
||||||
<a href="<%= config.new_post_url %>"><%= locale.new_post %></a>
|
<% if (config.rss == true) { %>
|
||||||
|
<a id='global-rss-link' href="/rss"><%= locale.rss_feed %></a>
|
||||||
|
<% } %>
|
||||||
|
/
|
||||||
|
<% if (config.atom == true) { %>
|
||||||
|
<a id='global-atom-link' href="/atom"><%= locale.atom_feed %></a>
|
||||||
|
<% } %>
|
||||||
|
/
|
||||||
|
<a id='new-post-link' href="<%= config.new_post_url %>"><%= locale.new_post %></a>
|
||||||
|
/
|
||||||
|
<% if (config.allow_signup == true) { %>
|
||||||
|
<a id='signup-link' href="<%= config.signup_url %>"><%= locale.sign_up %></a>
|
||||||
|
<% } %>
|
||||||
|
/
|
||||||
|
<form id='search-form' method="GET" action="/search" style="display: inline">
|
||||||
|
<input type="text" placeholder="🔍" name="q"><input type="submit" value="Search">
|
||||||
|
</form>
|
||||||
<br/>
|
<br/>
|
||||||
|
<div id='site-name'>
|
||||||
|
<h1><%- config.site_name %></h2>
|
||||||
|
</div>
|
||||||
|
<div id='site-description'>
|
||||||
|
<h2><%- config.site_description %></h2>
|
||||||
|
</div>
|
||||||
<%- config.seperator %>
|
<%- config.seperator %>
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
|
<div id='tag-page-title'>
|
||||||
<h1>
|
<h1>
|
||||||
<%= locale.posts_tagged %>: "<%- tag %>"
|
<%= locale.posts_tagged %>: "<%- tag %>"
|
||||||
</h1>
|
</h1>
|
||||||
|
</div>
|
||||||
<%- config.seperator %>
|
<%- config.seperator %>
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
<h1>
|
|
||||||
<%- config.site_name %>
|
|
||||||
</h1>
|
|
||||||
<h2>
|
|
||||||
<%- config.site_description %>
|
|
||||||
</h2>
|
|
||||||
<% if (config.rss == true) { %>
|
|
||||||
<a href="/rss"><%= locale.rss_feed %></a><br/>
|
|
||||||
<% } %>
|
|
||||||
<% if (config.atom == true) { %>
|
|
||||||
<a href="/atom"><%= locale.atom_feed %></a><br/>
|
|
||||||
<% } %>
|
|
||||||
<a href="<%= config.new_post_url %>"><%= locale.new_post %></a><br/>
|
|
||||||
<% if (config.allow_signup == true) { %>
|
|
||||||
<a href="<%= config.signup_url %>"><%= locale.sign_up %></a><br/>
|
|
||||||
<% } %>
|
|
||||||
<% if (config.enable_hitcount == true) { %>
|
|
||||||
<%= locale.hitcount %>: <%= hitcount %>
|
|
||||||
<% } %>
|
|
||||||
<%- config.seperator %>
|
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
|
<div id='user-page-title'>
|
||||||
<h1>
|
<h1>
|
||||||
<%= user.prettyname %>
|
<%= user.prettyname %>
|
||||||
</h1>
|
</h1>
|
||||||
<p><%# func.render_md(user.description) %></p>
|
</div>
|
||||||
<a href="<%= config.edit_account_base_url %>/<%= userID %>"><%= locale.edit_account %></a><br/>
|
<p><%- func.render_md(user.description) %></p>
|
||||||
<a href="/user/<%= user.username %>/rss"><%= locale.rss_feed %></a><br/>
|
<a id='edit-account-link' href="<%= config.edit_account_base_url %>/<%= userID %>"><%= locale.edit_account %></a><br/>
|
||||||
<a href="/user/<%= user.username %>/atom"><%= locale.atom_feed %></a>
|
<a id='rss-link' href="/user/<%= user.username %>/rss"><%= locale.rss_feed %></a><br/>
|
||||||
|
<a id='atom-link' href="/user/<%= user.username %>/atom"><%= locale.atom_feed %></a>
|
||||||
<%- config.seperator %>
|
<%- config.seperator %>
|
||||||
|
@@ -21,7 +21,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].length; comment_index++) { %>
|
||||||
<a href="/comment/<%= comments[postID][comment_index]["id"] %>"><%= comments[postID][comment_index]["id"] %></a><br/>
|
<a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
Users:<br/>
|
Users:<br/>
|
||||||
@@ -33,13 +33,13 @@
|
|||||||
Edit Posts:<br/>
|
Edit Posts:<br/>
|
||||||
<% for (let postID = 0; postID < posts.length; postID++) { %>
|
<% for (let postID = 0; postID < posts.length; postID++) { %>
|
||||||
<% if (posts[postID]["deleted"] != true) { %>
|
<% if (posts[postID]["deleted"] != true) { %>
|
||||||
<a href="/<%= config.edit_post_base_url %>/<%= postID %>">Edit <%= posts[postID]["title"] %></a><br/>
|
<a href="<%= config.edit_post_base_url %>/<%= postID %>">Edit <%= posts[postID]["title"] %></a><br/>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
Edit Users:<br/>
|
Edit Users:<br/>
|
||||||
<% for (let userID = 0; userID < users.length; userID++) { %>
|
<% for (let userID = 0; userID < users.length; userID++) { %>
|
||||||
<% if (users[userID]["deleted"] != true) { %>
|
<% if (users[userID]["deleted"] != true) { %>
|
||||||
<a href="/<%= config.edit_account_base_url %>/<%= users[userID]["username"] %>">Edit <%= users[userID]["username"] %></a><br/>
|
<a href="<%= config.edit_account_base_url %>/<%= users[userID]["username"] %>">Edit <%= users[userID]["username"] %></a><br/>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
<!-- TODO add tags -->
|
<!-- TODO add tags -->
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<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].length; comment_index++) { %>
|
||||||
<a href="/comment/<%= comments[postID][comment_index]["id"] %>"><%= comments[postID][comment_index]["id"] %></a><br/>
|
<a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -10,5 +10,17 @@
|
|||||||
<div id="comment">
|
<div id="comment">
|
||||||
<%- include("../partials/comment"); %>
|
<%- include("../partials/comment"); %>
|
||||||
</div>
|
</div>
|
||||||
|
<%- config.seperator %>
|
||||||
|
<b><%= locale.reply %>:</b>
|
||||||
|
<div id="post-commentform">
|
||||||
|
<!-- Comment form -->
|
||||||
|
<form method="POST" action="/submit_comment">
|
||||||
|
<input type="hidden" name="post_index" value="<%= postID %>">
|
||||||
|
<label><%= locale.username %>:</label><br/><input name="name"><br/><br/>
|
||||||
|
<label><%= locale.comment %>:</label><br/><textarea name="content">>> <%- postID %>-<%- commentID %>
|
||||||
|
</textarea><br/>
|
||||||
|
<button type="submit"><%= locale.submit %></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -12,8 +12,8 @@
|
|||||||
<% 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) { %>
|
||||||
<% posts[index].tags.forEach((current_tag, tag_index) => { %>
|
<% posts[index].tags.forEach((current_tag, tag_index) => { %>
|
||||||
<% if (current_tag == tag) { %>
|
<% if (current_tag.toLowerCase() == tag.toLowerCase()) { %>
|
||||||
<%- include('../posts/tag', {post: posts[index], user: users[posts[index].userID], comments: comments[index]}); %>
|
<%- include('../posts/tag', {post: posts[index], postID: index, user: users[posts[index].userID], comments: comments[index]}); %>
|
||||||
<% } %>
|
<% } %>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
@@ -22,6 +22,13 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
|
<form method="POST" action="/submit_nothing" style="display:none">
|
||||||
|
<!-- Form is used to help mitigate spam as it is the last form on the front page -->
|
||||||
|
<input type="hidden" name="post_index" value="0">
|
||||||
|
<input placeholder="username" name="name"><br/>
|
||||||
|
<textarea placeholder="comment" name="content"></textarea><br/>
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
<footer>
|
<footer>
|
||||||
<%- include('../partials/footer'); %>
|
<%- include('../partials/footer'); %>
|
||||||
</footer>
|
</footer>
|
||||||
|
@@ -1,3 +1,13 @@
|
|||||||
<b><%= comment.name %></b> <%= func.unix_time_to_date_format(comment.pubdate) %> <i>No. <%= comment.id %></i>:<br/>
|
<span id='comment-name'>
|
||||||
|
<b><%= comment.name %></b>
|
||||||
|
</span>
|
||||||
|
<span id='comment-date' class='date'>
|
||||||
|
<%= func.unix_time_to_date_format(comment.pubdate) %>
|
||||||
|
</span>
|
||||||
|
<span id='comment-id' style='display: inline'>
|
||||||
|
<a href='/comment/<%= postID %>-<%= comment.id %>'>No. <%= postID %>-<%= comment.id %></a>:<br/>
|
||||||
|
</span>
|
||||||
|
<span id='comment-content'>
|
||||||
<%- func.render_comment(comment.content) %>
|
<%- func.render_comment(comment.content) %>
|
||||||
|
</span>
|
||||||
<br/>
|
<br/>
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
<a id='site-index-link' href="/index/pages"><%= locale.site_index %></a><br/>
|
||||||
<%= locale.site_ran_by %> <%= config.site_admin %><br/>
|
<%= locale.site_ran_by %> <%= config.site_admin %><br/>
|
||||||
<%- locale.attribution %><br/>
|
<%- locale.attribution %><br/>
|
||||||
<%= locale.AI_consent %> <!-- remove consent for AI scrapers -->
|
<%= locale.AI_consent %> <!-- remove consent for AI scrapers -->
|
||||||
|
37
views/partials/search.ejs
Normal file
37
views/partials/search.ejs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<!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>
|
@@ -24,6 +24,11 @@
|
|||||||
<div id="post-editdate">
|
<div id="post-editdate">
|
||||||
<i><%= locale.last_modified %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
|
<i><%= locale.last_modified %>: <%= func.unix_time_to_date_format(post.pubdate) %></i><br/>
|
||||||
</div>
|
</div>
|
||||||
|
<% if (config.enable_hitcount == true) { %>
|
||||||
|
<div id='post-hitcount'>
|
||||||
|
Hitcount: <%- post.hitcount %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="post-commentform">
|
<div id="post-commentform">
|
||||||
@@ -38,7 +43,7 @@
|
|||||||
|
|
||||||
<div id="post-comments">
|
<div id="post-comments">
|
||||||
<% comments.forEach((comment, index) => { %>
|
<% comments.forEach((comment, index) => { %>
|
||||||
<div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
|
<div id="comment-no-<%= post.id %>-<%= comment.id %>" class="comment post-comment-<%= index %>">
|
||||||
<%- include('../partials/comment', {comment: comment}) %>
|
<%- include('../partials/comment', {comment: comment}) %>
|
||||||
</div>
|
</div>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
|
@@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
<div id="post-comments">
|
<div id="post-comments">
|
||||||
<% comments.forEach((comment, index) => { %>
|
<% comments.forEach((comment, index) => { %>
|
||||||
<div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
|
<div id="comment-no-<%= post.id %>-<%= comment.id %>" class="comment post-comment-<%= index %>">
|
||||||
<%- include('../partials/comment', {comment: comment}) %>
|
<%- include('../partials/comment', {comment: comment}) %>
|
||||||
</div>
|
</div>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
<div id="post-header">
|
<div id="post-header">
|
||||||
<h1>
|
<h1>
|
||||||
<%= post.title %>
|
<a href='/post/<%= post['id'] %>'><%= post.title %></a>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div id="post-content">
|
<div id="post-content">
|
||||||
<p>
|
<p>
|
||||||
<%- func.render_md(post.content) %><br/>
|
<%- func.render_md(post.content) %>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="post-details">
|
<div id="post-details">
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
<div id="post-comments">
|
<div id="post-comments">
|
||||||
<% comments.forEach((comment, index) => { %>
|
<% comments.forEach((comment, index) => { %>
|
||||||
<div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
|
<div id="comment-no-<%= post.id %>-<%= comment.id %>" class="comment post-comment-<%= index %>">
|
||||||
<%- include('../partials/comment', {comment: comment}) %>
|
<%- include('../partials/comment', {comment: comment}) %>
|
||||||
</div>
|
</div>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
<div id="post-comments">
|
<div id="post-comments">
|
||||||
<% comments.forEach((comment, index) => { %>
|
<% comments.forEach((comment, index) => { %>
|
||||||
<div id="comment-no<%= comment.id %>" class="post-comment-<%= index %>">
|
<div id="comment-no-<%= post.id %>-<%= comment.id %>" class="comment post-comment-<%= index %>">
|
||||||
<%- include('../partials/comment', {comment: comment}) %>
|
<%- include('../partials/comment', {comment: comment}) %>
|
||||||
</div>
|
</div>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
|
4
webroot/custom.css
Normal file
4
webroot/custom.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/* Put any CSS you want in here, it will be applied to the whole website */
|
||||||
|
* {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
99
webroot/robots.txt
Normal file
99
webroot/robots.txt
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
User-agent: AddSearchBot
|
||||||
|
User-agent: AI2Bot
|
||||||
|
User-agent: Ai2Bot-Dolma
|
||||||
|
User-agent: aiHitBot
|
||||||
|
User-agent: Amazonbot
|
||||||
|
User-agent: Andibot
|
||||||
|
User-agent: anthropic-ai
|
||||||
|
User-agent: Applebot
|
||||||
|
User-agent: Applebot-Extended
|
||||||
|
User-agent: Awario
|
||||||
|
User-agent: bedrockbot
|
||||||
|
User-agent: bigsur.ai
|
||||||
|
User-agent: Brightbot 1.0
|
||||||
|
User-agent: Bytespider
|
||||||
|
User-agent: CCBot
|
||||||
|
User-agent: ChatGPT Agent
|
||||||
|
User-agent: ChatGPT-User
|
||||||
|
User-agent: Claude-SearchBot
|
||||||
|
User-agent: Claude-User
|
||||||
|
User-agent: Claude-Web
|
||||||
|
User-agent: ClaudeBot
|
||||||
|
User-agent: CloudVertexBot
|
||||||
|
User-agent: cohere-ai
|
||||||
|
User-agent: cohere-training-data-crawler
|
||||||
|
User-agent: Cotoyogi
|
||||||
|
User-agent: Crawlspace
|
||||||
|
User-agent: Datenbank Crawler
|
||||||
|
User-agent: DeepSeekBot
|
||||||
|
User-agent: Devin
|
||||||
|
User-agent: Diffbot
|
||||||
|
User-agent: DuckAssistBot
|
||||||
|
User-agent: Echobot Bot
|
||||||
|
User-agent: EchoboxBot
|
||||||
|
User-agent: FacebookBot
|
||||||
|
User-agent: facebookexternalhit
|
||||||
|
User-agent: Factset_spyderbot
|
||||||
|
User-agent: FirecrawlAgent
|
||||||
|
User-agent: FriendlyCrawler
|
||||||
|
User-agent: Gemini-Deep-Research
|
||||||
|
User-agent: Google-CloudVertexBot
|
||||||
|
User-agent: Google-Extended
|
||||||
|
User-agent: Google-Firebase
|
||||||
|
User-agent: GoogleAgent-Mariner
|
||||||
|
User-agent: GoogleOther
|
||||||
|
User-agent: GoogleOther-Image
|
||||||
|
User-agent: GoogleOther-Video
|
||||||
|
User-agent: GPTBot
|
||||||
|
User-agent: iaskspider/2.0
|
||||||
|
User-agent: ICC-Crawler
|
||||||
|
User-agent: ImagesiftBot
|
||||||
|
User-agent: img2dataset
|
||||||
|
User-agent: ISSCyberRiskCrawler
|
||||||
|
User-agent: Kangaroo Bot
|
||||||
|
User-agent: LinerBot
|
||||||
|
User-agent: meta-externalagent
|
||||||
|
User-agent: Meta-ExternalAgent
|
||||||
|
User-agent: meta-externalfetcher
|
||||||
|
User-agent: Meta-ExternalFetcher
|
||||||
|
User-agent: meta-webindexer
|
||||||
|
User-agent: MistralAI-User
|
||||||
|
User-agent: MistralAI-User/1.0
|
||||||
|
User-agent: MyCentralAIScraperBot
|
||||||
|
User-agent: netEstate Imprint Crawler
|
||||||
|
User-agent: NovaAct
|
||||||
|
User-agent: OAI-SearchBot
|
||||||
|
User-agent: omgili
|
||||||
|
User-agent: omgilibot
|
||||||
|
User-agent: OpenAI
|
||||||
|
User-agent: Operator
|
||||||
|
User-agent: PanguBot
|
||||||
|
User-agent: Panscient
|
||||||
|
User-agent: panscient.com
|
||||||
|
User-agent: Perplexity-User
|
||||||
|
User-agent: PerplexityBot
|
||||||
|
User-agent: PetalBot
|
||||||
|
User-agent: PhindBot
|
||||||
|
User-agent: Poseidon Research Crawler
|
||||||
|
User-agent: QualifiedBot
|
||||||
|
User-agent: QuillBot
|
||||||
|
User-agent: quillbot.com
|
||||||
|
User-agent: SBIntuitionsBot
|
||||||
|
User-agent: Scrapy
|
||||||
|
User-agent: SemrushBot-OCOB
|
||||||
|
User-agent: SemrushBot-SWA
|
||||||
|
User-agent: ShapBot
|
||||||
|
User-agent: Sidetrade indexer bot
|
||||||
|
User-agent: TerraCotta
|
||||||
|
User-agent: Thinkbot
|
||||||
|
User-agent: TikTokSpider
|
||||||
|
User-agent: Timpibot
|
||||||
|
User-agent: VelenPublicWebCrawler
|
||||||
|
User-agent: WARDBot
|
||||||
|
User-agent: Webzio-Extended
|
||||||
|
User-agent: wpbot
|
||||||
|
User-agent: YaK
|
||||||
|
User-agent: YandexAdditional
|
||||||
|
User-agent: YandexAdditionalBot
|
||||||
|
User-agent: YouBot
|
||||||
|
Disallow: /
|
Reference in New Issue
Block a user