From cff599d83768aa284045c7c318d9a39a39c6758d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem?= Date: Sun, 29 Mar 2026 12:17:57 -0700 Subject: [PATCH] fix .beattime calculation error --- .gitignore | 3 +- README.md | 13 ++--- app.py | 18 ++++-- example.html | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ guestbook.js | 100 ++++++++++++++++++++++++++++++++ 5 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 example.html create mode 100644 guestbook.js diff --git a/.gitignore b/.gitignore index 1328b26..5725b1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ guestbook/* -import/* \ No newline at end of file +import/* +**/.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 1fe14b0..9ea27a9 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,18 @@ ![Human coded](https://img.shields.io/badge/human-coded-green?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1wZXJzb24tc3RhbmRpbmctaWNvbiBsdWNpZGUtcGVyc29uLXN0YW5kaW5nIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjUiIHI9IjEiLz48cGF0aCBkPSJtOSAyMCAzLTYgMyA2Ii8+PHBhdGggZD0ibTYgOCA2IDIgNi0yIi8+PHBhdGggZD0iTTEyIDEwdjQiLz48L3N2Zz4=) [![License: GNU General Public License v3.0](https://img.shields.io/badge/License-GNU%20GPL--v3.0-yellow)](https://www.gnu.org/licenses/gpl-3.0.en.html#license-text) -OpenGuestbook is a self-hosted lightweight Guestbook for small static websites made using python and flask. OpenGuestbook does not use databases or an admin panel for managing entries. Instead it saves each guestbook entry as a file with a human readable format in a /guestbook folder in your server. The comments can be moderated by deleting or editing files manually. +OpenGuestbook is a self-hosted lightweight open-source Guestbook for small static websites made using python & flask. OpenGuestbook does not use a database or an admin panel for managing entries. Instead it saves each guestbook entry as a file with a human readable format in a `/guestbook` folder in your server. The comments can be moderated by deleting or editing files manually. ## Features - No login required - No database required - Limited total comments per day. -- Easy setup, and low CPU and RAM use. - Privacy Friendly: No tracking, no cookies, AD blocker friendly. -- Notification support via Notfy.sh! +- Notification support via ntfy.sh! ## Deployment -OpenGuestbook comes in two parts, a backend that needs to be hosted on a server, and a JS file that cn be embedded directly on your website. -### Backend deployment with Docker +OpenGuestbook comes in two parts, a backend that needs to be hosted on a server, and a JS file that can be embedded directly on your website. +### Deployment with Docker For production, running via Docker Compose is recommended. ssh in to your server and clone the repository: ``` @@ -26,7 +25,7 @@ cd OpenGuestbook nano docker-compose.yml ``` Replace` ` with your static website url and `` with your [ntfy.sh topic](https://docs.ntfy.sh) to enable notificatons. -#### 1) Create docker-compose.yml +#### Create docker-compose.yml ```yaml version: '3.8' services: @@ -49,4 +48,4 @@ docker compose up -d --build and your guesbook server should be up and running on port 5000 on your machine! ### Front-end Script --To-do + diff --git a/app.py b/app.py index 79e7228..642f770 100644 --- a/app.py +++ b/app.py @@ -13,9 +13,7 @@ app = Flask(__name__) DAILY_LIMIT = 50 DATA_DIR = 'guestbook' -#get current UTC time and convert it to BMT -now_utc = datetime.now(timezone.utc) -bmt = now_utc + timedelta(hours=1) + currentDate = bmt.strftime('%Y-%m-%d') submissionCountDay = 0 @@ -63,9 +61,9 @@ def addComment(): return jsonify({"error": "Guestbook full for the day, try tomorrow!"}), 403 data = request.json - name = data.get('name', '').strip() - message = data.get('message', '').strip() - website = data.get('website', '').strip() + name = cleanText(data.get('name', '').strip()) + message = cleanText(data.get('message', '').strip()) + website = cleanText(data.get('website', '').strip()) if not name or not message: return jsonify({"error": "Missing fields"}), 400 @@ -123,6 +121,10 @@ def itime(): :returns: No. of beats (Swatch Internet Time) :rtype: float """ + #get current UTC time and convert it to BMT + now_utc = datetime.now(timezone.utc) + bmt = now_utc + timedelta(hours=1) + midnight = bmt.replace(hour=0, minute=0, second=0, microsecond=0) seconds_passed = (bmt - midnight).total_seconds() beats = int(math.floor(seconds_passed / 86.4)) @@ -135,6 +137,10 @@ def itime(): return beats +def cleanText(text): + cleanText = (text.replace("<", "<").replace(">", ">")) + return cleanText + def send_ntfy_notification(name, message): if not topic: return diff --git a/example.html b/example.html new file mode 100644 index 0000000..ced6933 --- /dev/null +++ b/example.html @@ -0,0 +1,160 @@ + + + + + +
+
+
+
New message
+
+ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+
+
+
+ Powered by OpenGuestbook +
+ + +
+
+

Loading entries...

+
+
+
+ + + + \ No newline at end of file diff --git a/guestbook.js b/guestbook.js new file mode 100644 index 0000000..aa15b0c --- /dev/null +++ b/guestbook.js @@ -0,0 +1,100 @@ +(function() { + const API_URL = ''; + const form = document.getElementById('gb-form'); + const list = document.getElementById('gb-entries'); + const status = document.getElementById('gb-status'); + + function escapeHtml(text) { + if (!text) return ""; + return text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + async function loadComments() { + try { + const res = await fetch(API_URL); + if (!res.ok) throw new Error("Failed to load"); + + const data = await res.json(); + + if (data.length === 0) { + list.innerHTML = "

No entries yet. Be the first!

"; + return; + } + + list.innerHTML = data.map(c => { + let nameDisplay = escapeHtml(c.name); + if (c.website) { + const safeUrl = c.website.replace(/["<>;]/g, ""); + nameDisplay = `${escapeHtml(c.name)} (${safeUrl})`; + } + + return ` +
+
+
+ ${nameDisplay} +
+
+ ${c.date} +
+
+
+ ${escapeHtml(c.message)} +
+
+ `; + }).join(''); + + } catch (err) { + console.error(err); + list.innerHTML = "

Error loading guestbook.

"; + } + } + + form.onsubmit = async (e) => { + e.preventDefault(); + const btn = form.querySelector('button'); + const nameInput = document.getElementById('gb-name'); + const msgInput = document.getElementById('gb-msg'); + const siteInput = document.getElementById('gb-site'); + + if (btn.disabled) return; + + btn.disabled = true; + btn.innerText = "Sending..."; + status.innerText = ""; + + try { + const res = await fetch(API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name: nameInput.value, + message: msgInput.value, + website: siteInput.value + }) + }); + + const data = await res.json(); + + if (!res.ok) { + alert(data.error || "Error posting comment"); + } else { + form.reset(); + loadComments(); + } + } catch (err) { + alert("Network error."); + } finally { + btn.disabled = false; + btn.innerText = "Post Comment"; + } + }; + + loadComments(); + })(); \ No newline at end of file