Compare commits
15 Commits
ef8711b0e1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9398919711 | ||
| 545a848479 | |||
| 54ffac931d | |||
| 18b842e48c | |||
|
|
c7bc64e59e | ||
| 9383bd8058 | |||
| d554fce402 | |||
| d17fcf2dd2 | |||
| a1fa0e3dbc | |||
|
f44a15ed41
|
|||
|
ab1fa5e69a
|
|||
| be75382ead | |||
| 3d58c5b244 | |||
| 54b6f018cf | |||
| 7d38752f34 |
0
.gitignore
vendored
Executable file → Normal file
0
.gitignore
vendored
Executable file → Normal file
@@ -21,8 +21,8 @@ Read the [configuation guide](docs/CONFIG.md) for configuration help (in config.
|
||||
* user creation, modification and deletion via frontend
|
||||
* multi user
|
||||
* powerful customisation via EJS
|
||||
* Configuration via config.json
|
||||
* site wide and user specific rss, atom
|
||||
* hitcount
|
||||
* Markdown syntax in posts
|
||||
* Commenting on posts and replying to other comments
|
||||
* site wide custom CSS and strings
|
||||
|
||||
5
docs/CONFIG.md
Executable file → Normal file
5
docs/CONFIG.md
Executable file → Normal file
@@ -16,10 +16,13 @@ All options show an example configuartion value and the variable type + an expla
|
||||
| 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 |
|
||||
| request_data_limit | 20 | Integer | The maximum number of objects to return (latest), so if set to 20, then only the 20 most recent posts will ever show |
|
||||
| root_path | '/var/www/blog_root' | String | Relative or Absolute path to the root directory of your static content, holds files such as favicon.ico, custom.css and robots.txt |
|
||||
| locale | 'en_GB' | String | The locale to use which determines the language used and minor cultural differences |
|
||||
## Basic Customisation
|
||||
| name | example value | variable type | explanation |
|
||||
|-----------------|----------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|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)|
|
||||
|locale|"en-US"|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|By default, this will go inbetween posts and generally to seperate out content on pages.|
|
||||
|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.|
|
||||
|
||||
2
example-config.json
Executable file → Normal file
2
example-config.json
Executable file → Normal file
@@ -9,7 +9,7 @@
|
||||
"cache_data": false,
|
||||
"allow_signup": true,
|
||||
"site_description": "Read my blogs!",
|
||||
"timeline_length": 20,
|
||||
"request_data_limit": 20,
|
||||
"enable_hitcount": true,
|
||||
"charset": "UTF-8",
|
||||
"root_path": "../webroot/",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"delete_account_confirmation": "Delete my account - (I agree that my account and all of my posts will be permanently deleted instantly)",
|
||||
"signup_agreement": "I agree to not post illegal or hateful content",
|
||||
"comment": "Comment",
|
||||
"submit": "Sumbit",
|
||||
"submit": "Submit",
|
||||
|
||||
"site_ran_by": "Site is ran by",
|
||||
"signups_unavailable": "Sorry, this server does not allow signups",
|
||||
@@ -39,5 +39,6 @@
|
||||
"home_page": "Home Page",
|
||||
"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>",
|
||||
"translated_by": "DeaDvey"
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"delete_account_confirmation": "Delete my account - (I agree that my account and all of my posts will be permanently deleted instantly)",
|
||||
"signup_agreement": "I agree to not post illegal or hateful content",
|
||||
"comment": "Comment",
|
||||
"submit": "Sumbit",
|
||||
"submit": "Submit",
|
||||
|
||||
"site_ran_by": "Site is ran by",
|
||||
"signups_unavailable": "Sorry, this server does not allow signups",
|
||||
@@ -39,5 +39,6 @@
|
||||
"home_page": "Home Page",
|
||||
"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>",
|
||||
"translated_by": "DeaDvey"
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"delete_account_confirmation": "Delete my account - (I agree that my account and all of my posts will be permanently deleted instantly)",
|
||||
"signup_agreement": "I agree to not post illegal or hateful content",
|
||||
"comment": "Comment",
|
||||
"submit": "Sumbit",
|
||||
"submit": "Submit",
|
||||
|
||||
"site_ran_by": "Site is ran by",
|
||||
"signups_unavailable": "Sorry, this server does not allow signups",
|
||||
@@ -39,5 +39,6 @@
|
||||
"home_page": "Home Page",
|
||||
"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>",
|
||||
"translated_by": "DeaDvey"
|
||||
}
|
||||
|
||||
43
locales/es-ES.json
Normal file
43
locales/es-ES.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"quotes": "«»‹›",
|
||||
"password": "Contraseña",
|
||||
"username": "Nombre de Usuario",
|
||||
"prettyname": "Nombre Bonito",
|
||||
"description": "Descripción (enlaces de redes, sobre que escribes, etc), soporta markdown",
|
||||
"title": "Título",
|
||||
"post_content": "Contenido de la Publicación, soporta markdown",
|
||||
"tags": "Etiquetas (separados por coma)",
|
||||
"delete_account_confirmation": "Eliminar mi cuenta - (Estoy de acuerdo con que todas mis publicaciones serán permanentemente eliminadas al instante)",
|
||||
"signup_agreement": "Acepto no publicar contenido ilegal o de odio",
|
||||
"comment": "Comentar",
|
||||
"submit": "Enviar",
|
||||
|
||||
"site_ran_by": "El sitio es llevado por",
|
||||
"signups_unavailable": "Lo siento, este servidor no permite registrarse",
|
||||
"user_exists": "Lo siento, este usuario ya existe, prueba otro diferente",
|
||||
"user_doesnt_exist": "Lo siento, este usuario no existe",
|
||||
"comment_doesnt_exist": "Este comentario no existe, esto puede ser porque la publicación en la que estaba adjunto se ha eliminado",
|
||||
"post_doesnt_exist": "Esta publicación no existe o se ha aliminado",
|
||||
"incorrect_password": "Contraseña Incorrecta",
|
||||
"rss_disabled": "Lo siento, RSS está desactivado",
|
||||
"atom_disabled": "Lo siento, ATOM está desactivado",
|
||||
"AI_consent": "El contenido de este sitio no debe ser copiado, raspado, o usado para entrenar modelos de IA o de lenguaje (LLMs) sin consentimiento previo.",
|
||||
"rss_feed": "Feed RSS",
|
||||
"atom_feed": "Feed ATOM",
|
||||
"no_tags": "Sin Etiquetas",
|
||||
"new_post": "Nueva Publicación",
|
||||
"edit_post": "Editar Publicación",
|
||||
"sign_up": "Registrarse",
|
||||
"edit_account": "Editar Cuenta",
|
||||
"permalink": "Enlace Permanente",
|
||||
"written_by": "Escrito por",
|
||||
"published": "Publicado",
|
||||
"last_modified": "Última Modificación",
|
||||
"hitcount": "Visitas",
|
||||
"post_tagged": "Publicaciones Etiquetadas",
|
||||
"home_page": "Página Principal",
|
||||
"site_index": "Índice del Sitio",
|
||||
"reply": "Responder",
|
||||
"attribution": "Empujado por blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Código Fuente</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>licencia (WTFPL)</a>",
|
||||
"translated_by": "Javalsai"
|
||||
}
|
||||
44
locales/ja_JP.json
Normal file
44
locales/ja_JP.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"quotes": "“”‘’",
|
||||
"password": "パスワード",
|
||||
"username": "ユーザー名",
|
||||
"prettyname": "きれいな名前",
|
||||
"description": "説明 (例えばSNSのリンクや何を書くなど)、 マークダウンをスポートする",
|
||||
"title": "題名",
|
||||
"post_content": "投稿の内容、マークダウンをスポートする",
|
||||
"tags": "タグ (カンマで区切られています)",
|
||||
"delete_account_confirmation": "アカウントを削除する - (アカウントと投稿の全部をいつまでも削除するに賛成します。)",
|
||||
"signup_agreement": "違法なコネテントと憎らしいコンテントをポストしないに賛成します。",
|
||||
"comment": "コメント",
|
||||
"submit": "提出する",
|
||||
|
||||
"site_ran_by": "アドミン:",
|
||||
"signups_unavailable": "申し訳ございませんでもこのサーバーはサインアップ",
|
||||
"user_exists": "申し訳ございませんでもこのユーザー名をつかえります。別のユーザー名を入ります。",
|
||||
"user_doesnt_exist": "申し訳ございませんでもこのアカウントがいません。",
|
||||
"comment_doesnt_exist": "このコメントがない、これから投稿を削除したかもしれない",
|
||||
"post_doesnt_exist": "この投稿がないか又は削除しました。",
|
||||
"incorrect_password": "パスワードが違う",
|
||||
"rss_disabled": "申し訳ございませんでもRSSが使用不可能なります。",
|
||||
"atom_disabled": "申し訳ございませんでもATOMが使用不可能なります。",
|
||||
"AI_consent": "書面による同意がないとこのホームページの内容はコピーするか又はスクレイピングするか又はAIモデルか大規模言語モデル(LLM)を仕込むことが禁断します。",
|
||||
|
||||
"rss_feed": "RSSのフィード",
|
||||
"atom_feed": "ATOMのフィード",
|
||||
"no_tags": "タグがない",
|
||||
"new_post": "新しい投稿",
|
||||
"edit_post": "投稿をエディットする",
|
||||
"sign_up": "サインアップ",
|
||||
"edit_account": "アカウントをエディットする",
|
||||
"permalink": "恒久リンク",
|
||||
"written_by": "作家は",
|
||||
"published": "発行の日付",
|
||||
"last_modified": "全変更",
|
||||
"hitcount": "ヒット数",
|
||||
"posts_tagged": "投稿をタグするの数",
|
||||
"home_page": "ホーム",
|
||||
"site_index": "ホームページの索引",
|
||||
"reply": "返事",
|
||||
"attribution": "blogger-nodejsで作成されています: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>ソースコード</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>ライセンス (WTFPL)</a>",
|
||||
"translated_by": "Nullifier"
|
||||
}
|
||||
44
locales/sv-SE.json
Normal file
44
locales/sv-SE.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"quotes": "“”‘’",
|
||||
"password": "Lösenord",
|
||||
"username": "Användarnamn",
|
||||
"prettyname": "Vackert namn",
|
||||
"description": "Beskrivning (sociala länkar, vad du skriver om m.m.), stöder markdown",
|
||||
"title": "Titel",
|
||||
"post_content": "Inläggsinnehåll, stöder markdown",
|
||||
"tags": "Taggar (komma separerade)",
|
||||
"delete_account_confirmation": "Radera mitt konto - (Jag förstår och accepterar att mitt konto och alla mina inlägg kommer att raderas permanent omedelbart)",
|
||||
"signup_agreement": "Jag samtycker till att inte publicera olagligt eller hatiskt innehåll",
|
||||
"comment": "Kommentera",
|
||||
"submit": "Skicka",
|
||||
|
||||
"site_ran_by": "Webbplatsen drivs av",
|
||||
"signups_unavailable": "Tyvärr, denna server tillåter inte registreringar",
|
||||
"user_exists": "Tyvärr, den här användaren finns redan. Försök med ett annat användarnamn",
|
||||
"user_doesnt_exist": "Tyvärr, den här användaren finns inte",
|
||||
"comment_doesnt_exist": "Denna kommentar finns inte, vilket kan bero på att inlägget den var kopplad till har raderats",
|
||||
"post_doesnt_exist": "Det här inlägget finns inte eller har raderats",
|
||||
"incorrect_password": "Felaktigt Lösenord",
|
||||
"rss_disabled": "Tyvärr, RSS är inaktiverat",
|
||||
"atom_disabled": "Tyvärr, ATOM är inaktiverat",
|
||||
"AI_consent": "Innehållet på denna webbplats får inte kopieras, skrapas eller användas för att träna AI-modeller eller stora språkmodeller (LLM) utan skriftligt samtycke.",
|
||||
|
||||
"rss_feed": "RSS-flöde",
|
||||
"atom_feed": "ATOM-flöde",
|
||||
"no_tags": "Inga taggar",
|
||||
"new_post": "Nytt inlägg",
|
||||
"edit_post": "Redigera inlägg",
|
||||
"sign_up": "Registrera",
|
||||
"edit_account": "Redigera konto",
|
||||
"permalink": "Permalänk",
|
||||
"written_by": "Skriven av",
|
||||
"published": "Publicerad",
|
||||
"last_modified": "Senast ändrad",
|
||||
"hitcount": "Besökare",
|
||||
"posts_tagged": "Taggade inlägg",
|
||||
"home_page": "Startsida",
|
||||
"site_index": "Webbplatsindex",
|
||||
"reply": "Svara",
|
||||
"attribution": "Drivs av blogger-nodejs: <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs'>Källkod</a>, <a href='https://git.javalsai.tuxcord.net/deadvey/blogger-nodejs/raw/branch/master/LICENSE'>licens (WTFPL)</a>",
|
||||
"translated_by": "pickzelle"
|
||||
}
|
||||
55
locales/template.jsonc
Normal file
55
locales/template.jsonc
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"quotes": "“”‘’", // Single and Double quotes, according to https://github.com/markdown-it/markdown-it format
|
||||
|
||||
// Placeholders in form inputs!
|
||||
"password": "Password",
|
||||
"username": "Username",
|
||||
"prettyname": "Prettyname",
|
||||
"description": "Description (social links, what you write about etc), supports markdown", // Should explain what can be entered into the user description/bio
|
||||
"title": "Title", // Post title
|
||||
"post_content": "Post Content, supports markdown",
|
||||
"tags": "Tags (comma seperated)", // An input field that allows you to enter a comma seperated list of tags like: 'sus,test,haha'
|
||||
"delete_account_confirmation": "Delete my account - (I agree that my account and all of my posts will be permanently deleted instantly)", // Should make it clear that all user data and posts will be deleted
|
||||
"signup_agreement": "I agree to not post illegal or hateful content", // Should make it clear that you cannot post illegal or hateful content
|
||||
"comment": "Comment",
|
||||
"submit": "Sumbit",
|
||||
|
||||
"site_ran_by": "Site is ran by", // eg 'Site is ran by Bob', it shows up in the footer of each page
|
||||
|
||||
// Error messages, should just apologise and make it clear the error
|
||||
"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",
|
||||
"incorrect_password": "Incorrect Password",
|
||||
"rss_disabled": "Sorry, RSS is disabled",
|
||||
"atom_disabled": "Sorry, ATOM is disabled",
|
||||
|
||||
// Disclaimer, not legally binding
|
||||
"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.",
|
||||
|
||||
// Hyperlinks to pages and plain text that shows up on the website
|
||||
"rss_feed": "RSS Feed",
|
||||
"atom_feed": "ATOM Feed",
|
||||
"no_tags": "No Tags",
|
||||
"new_post": "New Post",
|
||||
"edit_post": "Edit Post",
|
||||
"sign_up": "Sign Up",
|
||||
"edit_account": "Edit Account",
|
||||
"permalink": "Permalink",
|
||||
"written_by": "Written by", // A post is written/authored by x person
|
||||
"published": "Published", // Published on this date
|
||||
"last_modified": "Last Modified", // Last modified on this date
|
||||
"hitcount": "Hitcount", // The number of views/hits/visits to a page, eg: 'hitcount: 53'
|
||||
"posts_tagged": "Posts Tagged",
|
||||
"home_page": "Home Page", // The main or default page, ie index.html
|
||||
"site_index": "Site Index", // Or 'site map'
|
||||
"reply": "reply", // Reply to a comment
|
||||
|
||||
// Attribution for the source code, don't change the URLs obviously, just the text within them.
|
||||
"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>",
|
||||
|
||||
// Comma seperated list of people who contributed to this translation
|
||||
"translated_by": "DeaDvey"
|
||||
}
|
||||
2
package.json
Executable file → Normal file
2
package.json
Executable file → Normal file
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"date-fns": "^4.1.0",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^5.1.0",
|
||||
"express": "^5.2.1",
|
||||
"express-router": "^0.0.1",
|
||||
"markdown-it": "^14.1.0",
|
||||
"mysql": "^2.18.1",
|
||||
|
||||
120
src/data.js
120
src/data.js
@@ -14,8 +14,12 @@ export function increment_hitcount(postID = -1) { // -1 Means it will increment
|
||||
writedata('hitcount', hitcount);
|
||||
}
|
||||
else {
|
||||
let post = getdata('posts', postID);
|
||||
if (typeof post.hitcount != 'undefined') {
|
||||
let post = getdata('posts','id', postID);
|
||||
if (post == 1) // Does not exist
|
||||
{
|
||||
return 1
|
||||
}
|
||||
else if (typeof post.hitcount != 'undefined') {
|
||||
post.hitcount += 1;
|
||||
writedata('posts', post, postID)
|
||||
return 0
|
||||
@@ -68,60 +72,74 @@ export function searchdata(term, type) { // Searches users and posts for any mat
|
||||
return search_results;
|
||||
};
|
||||
|
||||
export function getdata(data, index=-1) {
|
||||
|
||||
if (config["data_storage"] == "json") {
|
||||
if (data == "posts" || data == 'users' || data == 'comments') {
|
||||
let result = func.require_module(`../data/${data}.json`)
|
||||
if (index != -1) {
|
||||
if (index < result.length) {
|
||||
return result[index]
|
||||
}
|
||||
return 1 // This index doesn't exist
|
||||
export function getdata(table_name, key=-1, value=-1) {
|
||||
let result = undefined
|
||||
switch (config["data_storage"]) {
|
||||
case 'json':
|
||||
switch (table_name) {
|
||||
case 'users':
|
||||
case 'posts':
|
||||
case 'comments':
|
||||
result = func.require_module(`../data/${table_name}.json`)
|
||||
if (key != -1) {
|
||||
if (key == 'id')
|
||||
{ // id is the index
|
||||
if (value < result.length && value >= 0)
|
||||
{
|
||||
return result[value]
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("No object of this ID exists for the selected table")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return result[func.find_key_value_pair(result, key, value)]
|
||||
return -1 // This index doesn't exist
|
||||
}
|
||||
return result.slice(- config['data_request_limit'])
|
||||
break;
|
||||
case 'hitcount':
|
||||
result = func.require_module('../data/data.json') // This file is actually called data.json
|
||||
return result["hitcount"]
|
||||
break;
|
||||
default:
|
||||
console.log("Error, invalid requested")
|
||||
return -1
|
||||
break;
|
||||
}
|
||||
return result
|
||||
}
|
||||
else if (data == "hitcount") {
|
||||
let result = func.require_module('../data/data.json') // This file is actually called data.json
|
||||
return result["hitcount"]
|
||||
}
|
||||
else {
|
||||
console.log("Error, invalid requested")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
// NOT YET WORKING!
|
||||
if (config["data_storage"] == "mysql") {
|
||||
const mysql = require('mysql');
|
||||
let con = mysql.createConnection({
|
||||
host: config.database.host,
|
||||
user: config.database.user,
|
||||
password: config.database.password,
|
||||
database: config.database.database,
|
||||
});
|
||||
case 'mysql':
|
||||
const mysql = require('mysql');
|
||||
let con = mysql.createConnection({
|
||||
host: config.database.host,
|
||||
user: config.database.user,
|
||||
password: config.database.password,
|
||||
database: config.database.database,
|
||||
});
|
||||
|
||||
con.connect(function(err) {
|
||||
if (err) throw err;
|
||||
con.connect(function(err) {
|
||||
if (err) throw err;
|
||||
|
||||
if (data == "posts" || data == 'users' || data == 'comments') {
|
||||
con.query(`SELECT * FROM ${data}`, function (err, result, fields) {
|
||||
if (err) throw err;
|
||||
result = Object.values(JSON.parse(JSON.stringify(result)))
|
||||
console.log(result)
|
||||
return result;
|
||||
});
|
||||
}
|
||||
else if (data == 'hitcount') {
|
||||
con.query(`SELECT paramValue FROM params WHERE paramName = '${data}'`, function (err, result, fields) {
|
||||
if (err) throw err;
|
||||
result = Object.values(JSON.parse(JSON.stringify(result)))
|
||||
console.log(result)
|
||||
return result;
|
||||
});
|
||||
if (data == "posts" || data == 'users' || data == 'comments') {
|
||||
con.query(`SELECT * FROM ${data}`, function (err, result, fields) {
|
||||
if (err) throw err;
|
||||
result = Object.values(JSON.parse(JSON.stringify(result)))
|
||||
console.log(result)
|
||||
return result;
|
||||
});
|
||||
}
|
||||
else if (data == 'hitcount') {
|
||||
con.query(`SELECT paramValue FROM params WHERE paramName = '${data}'`, function (err, result, fields) {
|
||||
if (err) throw err;
|
||||
result = Object.values(JSON.parse(JSON.stringify(result)))
|
||||
console.log(result)
|
||||
return result;
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,15 @@ 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) {
|
||||
export function require_module(module)
|
||||
{
|
||||
if (config.cache_data == false)
|
||||
{
|
||||
delete require.cache[require.resolve(module)];
|
||||
return require(module);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
return require(module);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +25,8 @@ export function require_module(module) {
|
||||
// this converts unix time (an integer) into a string that is formatted according to config.js
|
||||
// uses date-fns's fromUnixTime() and format() functions
|
||||
// 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
|
||||
let date = fromUnixTime(unix_time)
|
||||
let formatted_date = format(date, config.date_format)
|
||||
@@ -33,14 +37,16 @@ export function unix_time_to_date_format(unix_time) {
|
||||
// eg "Mon, 23 May 2025 18:59:59 +0100"
|
||||
// accepts unix time (int)
|
||||
// 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
|
||||
let date = fromUnixTime(unix_time)
|
||||
let formatted_date = format(date, "EEE, dd MMM yyyy HH:mm:ss")
|
||||
return `${formatted_date} ${config.time_zone}`
|
||||
}
|
||||
// And again with atom's date format
|
||||
export function unix_time_to_atom_date(unix_time) {
|
||||
export function unix_time_to_atom_date(unix_time)
|
||||
{
|
||||
const { fromUnixTime, format, getUnixTime } = require("date-fns") // A date utility library
|
||||
let date = fromUnixTime(unix_time)
|
||||
let formatted_date = format(date, "yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
@@ -51,13 +57,17 @@ export function unix_time_to_atom_date(unix_time) {
|
||||
// eg "<a href="/tag/string1">string1</a>, <a href="/tag/string2">string2</a>, <a href="/tag/string3">string3</a>"
|
||||
// this is so you can have a list of tags that each point to their individual tag page
|
||||
// returns: string
|
||||
export function render_tags(tags) {
|
||||
export function render_tags(tags)
|
||||
{
|
||||
let string = "" // Initialises the string
|
||||
if (tags.length == 1 && tags[0] == "") {
|
||||
if (tags.length == 1 && tags[0] == "")
|
||||
{
|
||||
string = ''; // If there are no tags, output nothing
|
||||
}
|
||||
else {
|
||||
for (let tag_index = 0; tag_index < tags.length; tag_index++) { // Loop over each tag
|
||||
else
|
||||
{
|
||||
for (let tag_index = 0; tag_index < tags.length; tag_index++)
|
||||
{ // Loop over each tag
|
||||
string += `<a href="/tag/${tags[tag_index].trim()}">#${tags[tag_index].trim()}</a> ` // Adds the tag to the string as a HTML href
|
||||
}
|
||||
}
|
||||
@@ -68,10 +78,13 @@ export function render_tags(tags) {
|
||||
// This function returns the username for a given userID by looping over every user
|
||||
// if the user is present, it returns the index of the user (integer)
|
||||
// if the user is not present it returns -1
|
||||
export function get_userID(username) {
|
||||
const users = require("../data/users.json")
|
||||
for (let i = 0; i < users.length; i++) { // Loop over every user
|
||||
if (users[i]['username'] == username) {
|
||||
export function get_userID(username)
|
||||
{
|
||||
const users = require_module("../data/users.json")
|
||||
for (let i = 0; i < users.length; i++)
|
||||
{ // Loop over every user
|
||||
if (users[i]['username'] == username)
|
||||
{
|
||||
return i // If the username matches then return the index of that user
|
||||
}
|
||||
}
|
||||
@@ -82,7 +95,8 @@ export function get_userID(username) {
|
||||
// https://www.freeformatter.com/html-entities.html
|
||||
// accepts a string
|
||||
// returns a string with some character replaced by their entities
|
||||
export function escape_input(input) {
|
||||
export function escape_input(input)
|
||||
{
|
||||
let output = input
|
||||
.replaceAll("&", "&") // This must be first
|
||||
.replaceAll("<", "<")
|
||||
@@ -97,7 +111,8 @@ export function escape_input(input) {
|
||||
|
||||
// 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
|
||||
.replaceAll(/>> ([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>> $1-$2</a>")
|
||||
.replaceAll(/>>([0-9]*)-([0-9]*)/g, "<a href='/comment/$1-$2'>>>$1-$2</a>")
|
||||
@@ -108,9 +123,11 @@ export function render_comment(comment_content) {
|
||||
};
|
||||
|
||||
// Renders a string into markdown using markdown-it library
|
||||
export function render_md(content) {
|
||||
export function render_md(content)
|
||||
{
|
||||
const markdownit = require("markdown-it")
|
||||
const md = markdownit({ // this is just defining some options for markdown-it, should I add this to config.json?
|
||||
const md = markdownit
|
||||
({ // this is just defining some options for markdown-it, should I add this to config.json?
|
||||
html: false,
|
||||
xhtmlOut: false,
|
||||
breaks: true,
|
||||
@@ -121,3 +138,11 @@ export function render_md(content) {
|
||||
return md.render(content)
|
||||
};
|
||||
|
||||
export function find_key_value_pair(data_array, key, value) {
|
||||
for (let i = 0; i < data_array.length; i++) {
|
||||
if (data_array[i][key] == value) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
};
|
||||
|
||||
@@ -31,11 +31,10 @@ router.post("/submit_comment", (req,res) => {
|
||||
new_comment = {
|
||||
"name": name,
|
||||
"content": content,
|
||||
"id": comments[postID].length,
|
||||
"id": comments[postID]['comments'].length,
|
||||
"pubdate": unix_timestamp,
|
||||
"postID": postID,
|
||||
};
|
||||
comments[postID].push(new_comment);
|
||||
comments[postID]['comments'].push(new_comment);
|
||||
fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`, 'utf-8');
|
||||
}
|
||||
|
||||
@@ -59,8 +58,9 @@ router.post("/submit_post", (req,res) => {
|
||||
|
||||
else if (users[func.get_userID(username)]['hash'] == password) { // Password matches
|
||||
console.log(username, "is submitting a post titled:", title);
|
||||
id = posts.length
|
||||
posts.push({
|
||||
"id": posts.length,
|
||||
"id": id,
|
||||
"userID": func.get_userID(username),
|
||||
"title": title,
|
||||
"content": content,
|
||||
@@ -69,7 +69,7 @@ router.post("/submit_post", (req,res) => {
|
||||
"tags": tags,
|
||||
})
|
||||
fs.writeFileSync(`../data/posts.json`, `${JSON.stringify(posts)}`, 'utf-8');
|
||||
comments.push([])
|
||||
comments.push({'id': id, 'comments': []})
|
||||
fs.writeFileSync(`../data/comments.json`, `${JSON.stringify(comments)}`)
|
||||
res.redirect(302, "/");
|
||||
}
|
||||
@@ -216,7 +216,7 @@ router.get('/search', (req, res) => {
|
||||
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', {
|
||||
res.render('pages/search', {
|
||||
config,
|
||||
locale,
|
||||
search_results,
|
||||
|
||||
@@ -38,14 +38,14 @@ router.get(`${config.edit_account_base_url}/:user_id`, (req,res) => {
|
||||
res.render("forms/edit_account", {
|
||||
config,
|
||||
locale,
|
||||
user: data.getdata('users', userID),
|
||||
user: data.getdata('users', 'id', userID),
|
||||
userID
|
||||
});
|
||||
}); // /delete_account
|
||||
router.get(`${config.edit_post_base_url}/:post_id`, (req,res) => {
|
||||
const postID = req.params.post_id
|
||||
const post = data.getdata('posts', postID)
|
||||
const user = data.getdata('users', post.userID)
|
||||
const post = data.getdata('posts','id', postID)
|
||||
const user = data.getdata('users', 'id', post.userID)
|
||||
res.render("forms/edit_post", {
|
||||
config,
|
||||
locale,
|
||||
|
||||
@@ -13,7 +13,7 @@ router.get("/index/pages", (req,res) => {
|
||||
users: data.getdata('users'),
|
||||
comments: data.getdata('comments'),
|
||||
});
|
||||
}); // /index/posts
|
||||
}); // /index/pages
|
||||
router.get("/index/posts", (req,res) => {
|
||||
res.render("indexes/posts", {
|
||||
config,
|
||||
@@ -25,13 +25,13 @@ router.get("/index/users", (req,res) => {
|
||||
config,
|
||||
users: data.getdata('users'),
|
||||
});
|
||||
}); // /index/posts
|
||||
}); // /index/users
|
||||
router.get("/index/comments", (req,res) => {
|
||||
res.render("indexes/comments", {
|
||||
config,
|
||||
comments: data.getdata('comments'),
|
||||
});
|
||||
}); // /index/posts
|
||||
}); // /index/comments
|
||||
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -33,7 +33,7 @@ router.get("/", (req,res) => {
|
||||
// Users
|
||||
router.get("/user/:username", (req, res) => {
|
||||
const userID = func.get_userID(req.params.username)
|
||||
let user = data.getdata('users', userID)
|
||||
let user = data.getdata('users', 'id', userID)
|
||||
if (userID != -1) {
|
||||
res.render("pages/user",
|
||||
{
|
||||
@@ -61,25 +61,25 @@ router.get("/user/:username", (req, res) => {
|
||||
// Posts
|
||||
router.get("/post/:post_index", (req, res) => {
|
||||
const postID = parseInt(req.params.post_index)
|
||||
let post = data.getdata('posts', postID)
|
||||
let post = data.getdata('posts','id', postID)
|
||||
if (post == 1) { // data.getdata returns error code 1 if nothing is available
|
||||
res.render("partials/message", {
|
||||
message: locale.post_doesnt_exist,
|
||||
config,
|
||||
})
|
||||
}
|
||||
if (config.enable_hitcount) {
|
||||
data.increment_hitcount(postID)
|
||||
}
|
||||
if (typeof post["deleted"] == "undefined" || post["deleted"] == false) {
|
||||
else if (typeof post["deleted"] == "undefined" || post["deleted"] == false) {
|
||||
if (config.enable_hitcount) {
|
||||
data.increment_hitcount(postID)
|
||||
}
|
||||
res.render("pages/post",
|
||||
{
|
||||
config,
|
||||
locale,
|
||||
post,
|
||||
postID,
|
||||
user: data.getdata('users', post.userID),
|
||||
comments: data.getdata('comments', postID),
|
||||
user: data.getdata('users','id', post.userID),
|
||||
comments: data.getdata('comments','id', postID)["comments"],
|
||||
fromUnixTime,
|
||||
format,
|
||||
getUnixTime,
|
||||
@@ -117,7 +117,7 @@ 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 posts_comments = data.getdata('comments', 'id', postID)["comments"]
|
||||
let comment = 1
|
||||
// For loop to find the comment with matching ID
|
||||
posts_comments.forEach((current_comment, index) => {
|
||||
|
||||
@@ -11,30 +11,7 @@ const ejs = require("ejs")
|
||||
const func = require("./functions.js")
|
||||
const data = require("./data.js")
|
||||
|
||||
|
||||
// Define the modules now so they are global
|
||||
let users // contains a list of users, each user is an object containing username,prettyname,hash and description
|
||||
let posts // contains a list of posts,
|
||||
let comments // contains a list of comments
|
||||
let config // contains a set of configuration for the site, see example-config.js for an example
|
||||
|
||||
config = require('../config.json');
|
||||
if (config["data_storage"] == "json") {
|
||||
try {
|
||||
// We're going to try and import the modules,
|
||||
users = require('../data/users.json');
|
||||
posts = require('../data/posts.json');
|
||||
comments = require('../data/comments.json');
|
||||
}
|
||||
catch (error) {
|
||||
// if they don't all import then
|
||||
// inform the user to pass --first-time and exit with an error code
|
||||
console.log("A file is missing!")
|
||||
console.log("Run 'make' to initialise the data files")
|
||||
console.log(error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Import the locale
|
||||
try {
|
||||
@@ -67,6 +44,45 @@ app.use('/', standard_pages_routes);
|
||||
app.use('/', forms_routes);
|
||||
app.use('/', form_actions_routes);
|
||||
|
||||
function perform_checks()
|
||||
{
|
||||
console.log("Performing startup checks...")
|
||||
exit_flag = false
|
||||
required_values = ['site_admin','seperator','site_name','site_url','locale','port','cache_data','allow_signup','site_description','request_data_limit','enable_hitcount','charset','root_path','edit_account_base_url','new_post_url','signup_url','default_commenter_username','rss','atom','date_format','time_zone','css']
|
||||
// Perform some standard checks:
|
||||
|
||||
// data_storage
|
||||
switch (config.data_storage)
|
||||
{
|
||||
case 'mysql':
|
||||
case 'json':
|
||||
break
|
||||
default:
|
||||
console.log("[ ERROR ] invalid value in `data_storage`\nPlease modify config.json. Value should be 'mysql' or 'json'.")
|
||||
exit_flag = true
|
||||
}
|
||||
// auto_generated
|
||||
if (config.auto_generated)
|
||||
{
|
||||
console.log("[ ERROR ] `autogenerated` option set to true\nplease edit the config.json file to include your relevant information, then set to false or remove the autogenerated option.")
|
||||
exit_flag = true
|
||||
}
|
||||
// Check required values are present
|
||||
required_values.forEach((value) => { // Use a loop to check each required value is present
|
||||
if (typeof config[value] == 'undefined') {
|
||||
exit_flag = true
|
||||
console.log(`[ ERROR ] \`${value}\` is undefined\nPlease set it to something, read the documentation for help.`)
|
||||
}
|
||||
});
|
||||
if (exit_flag)
|
||||
{
|
||||
console.log("Exiting due to errors.")
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
perform_checks()
|
||||
|
||||
|
||||
app.listen(config.port, () => {
|
||||
console.log(`Server is running at http://localhost:${config.port} webroot: ${config.root_path}`);
|
||||
console.log("Running in: ", __dirname)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<% }; %>
|
||||
Comments:<br/>
|
||||
<% for (let postID = 0; postID < comments.length; postID++) { %>
|
||||
<% for (let comment_index = 0; comment_index < comments[postID].length; comment_index++) { %>
|
||||
<% for (let comment_index = 0; comment_index < comments[postID]['comments'].length; comment_index++) { %>
|
||||
<a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/>
|
||||
<% }; %>
|
||||
<% }; %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<% for (let postID = 0; postID < comments.length; postID++) { %>
|
||||
<% for (let comment_index = 0; comment_index < comments[postID].length; comment_index++) { %>
|
||||
<% for (let comment_index = 0; comment_index < comments[postID]['comments'].length; comment_index++) { %>
|
||||
<a href="/comment/<%= postID %>-<%= comment_index %>"><%= postID %>-<%= comment_index %></a><br/>
|
||||
<% }; %>
|
||||
<% }; %>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<%- include('../partials/head'); %>
|
||||
</head>
|
||||
<body>
|
||||
<div id='header'>
|
||||
<div id='site-header'>
|
||||
<%- include('../headers/site_wide'); %>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div id="post-details">
|
||||
<span id="post-author">
|
||||
<i><%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.username %></a></i>
|
||||
<i><%= locale.written_by %> <a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
|
||||
</span>
|
||||
-
|
||||
<span id="post-pubdate">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div id="post-details">
|
||||
<span id="post-author">
|
||||
<i><a href="/user/<%= user.username %>"><%= user.username %></a></i>
|
||||
<i><a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
|
||||
</span>
|
||||
-
|
||||
<span id="post-pubdate">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div id="post-details">
|
||||
<span id="post-author">
|
||||
<i><a href="/user/<%= user.username %>"><%= user.username %></a></i>
|
||||
<i><a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
|
||||
</span>
|
||||
-
|
||||
<span id="post-pubdate">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div id="post-details">
|
||||
<span id="post-author">
|
||||
<i><a href="/user/<%= user.username %>"><%= user.username %></a></i>
|
||||
<i><a href="/user/<%= user.username %>"><%= user.prettyname %></a></i>
|
||||
</span>
|
||||
-
|
||||
<span id="post-pubdate">
|
||||
|
||||
@@ -15,11 +15,23 @@ body {
|
||||
input, textarea, button {
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
field-sizing: content; /* Only supported by Chromium */
|
||||
font-size: 20px;
|
||||
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
field-sizing: content; /* Only supported by Chromium */
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
#search-form input {
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user