From 10b7960dd98fac663d9938a3ed7654815328eacb Mon Sep 17 00:00:00 2001 From: Yuki Joou Date: Thu, 1 Jun 2023 11:48:52 +0200 Subject: [PATCH] Initial commit: Added current work! --- .gitignore | 3 ++ .prettierrc | 4 +++ README.md | 28 +++++++++++++++ app.py | 75 +++++++++++++++++++++++++++++++++++++++++ database/quotes.sql | 6 ++++ database/setup_db.py | 9 +++++ static/proof-of-work.js | 33 ++++++++++++++++++ static/style.css | 29 ++++++++++++++++ templates/index.html | 63 ++++++++++++++++++++++++++++++++++ 9 files changed, 250 insertions(+) create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 README.md create mode 100644 app.py create mode 100644 database/quotes.sql create mode 100644 database/setup_db.py create mode 100644 static/proof-of-work.js create mode 100644 static/style.css create mode 100644 templates/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cbaba04 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +__pycache__/ +database/data.sqlite \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8dc2e72 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 4, + "useTabs": true +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..63751ce --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# GayBook + +This is a simple GuestBook software written in Python, using Flask! + +It uses a sqlite database for storage, and avoids spam with a simple proof-of-work system + +## Running + +You'll need python and flask installed on your machine. + +First, set up the database + +```console + $ cd database/ + $ python setup_db.py +``` + +Then, run the Flask app + +```console + $ python app.py +``` + +To run in debug mode, use + +```console + $ FLASK_DEBUG=1 python app.py +``` \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..41ecfda --- /dev/null +++ b/app.py @@ -0,0 +1,75 @@ +import flask +import dataclasses +import sqlite3 +import datetime +import hashlib + +app = flask.Flask(__name__) + + +def get_database_connection(): + database_connection = sqlite3.connect("database/data.sqlite") + database_connection.row_factory = sqlite3.Row + return database_connection + + +@dataclasses.dataclass +class Quote: + author: str + content: str + timestamp: datetime.datetime = datetime.datetime.now() + + def proof_of_work_is_valid(self, work: str): + to_hash = f"{self.author}\0{self.content}\0{work}" + hashed = hashlib.sha1(to_hash.encode("utf-8")).hexdigest() + return hashed[:5] == "00000" + + +def get_all_quotes() -> list[Quote]: + db_connection = get_database_connection() + db_quotes = db_connection.execute( + "SELECT author, content, timestamp FROM quotes ORDER BY timestamp DESC" + ).fetchall() + db_connection.close() + + quotes: list[Quote] = [] + for db_quote in db_quotes: + quotes.append(Quote(db_quote["author"], db_quote["content"], db_quote["timestamp"])) + + return quotes + + +def add_quote(new_quote: Quote): + db_connexion = get_database_connection() + db_connexion.execute( + "INSERT INTO quotes (author, content) VALUES (?, ?)", + (new_quote.author, new_quote.content) + ) + db_connexion.commit() + db_connexion.close() + + +@app.route('/', methods=["POST", "GET"]) +def index(): + message: str = "" + response_code = 200 + + if flask.request.method == "POST": + form = flask.request.form + if "username" not in form or "message" not in form or "proof-of-work" not in form: + message = "Did not provide username, quote or proof of work." + response_code = 400 + else: + quote = Quote(form["username"], form["message"]) + if quote.proof_of_work_is_valid(form["proof-of-work"]): + add_quote(Quote(form["username"], form["message"])) + message = "Your quote was added successfully" + else: + message = "You didn't do the work, silly!" + response_code = 400 + + return flask.render_template("index.html", quotes=get_all_quotes(), message=message), response_code + + +if __name__ == "__main__": + app.run() diff --git a/database/quotes.sql b/database/quotes.sql new file mode 100644 index 0000000..2dd6712 --- /dev/null +++ b/database/quotes.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS quotes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + author TEXT NOT NULL, + content TEXT NOT NULL +); \ No newline at end of file diff --git a/database/setup_db.py b/database/setup_db.py new file mode 100644 index 0000000..7e3bce8 --- /dev/null +++ b/database/setup_db.py @@ -0,0 +1,9 @@ +import sqlite3 + +connection = sqlite3.connect("data.sqlite") + +with open("quotes.sql") as fp: + connection.execute(fp.read()) + +connection.commit() +connection.close() \ No newline at end of file diff --git a/static/proof-of-work.js b/static/proof-of-work.js new file mode 100644 index 0000000..1136a09 --- /dev/null +++ b/static/proof-of-work.js @@ -0,0 +1,33 @@ +const generateButton = document.getElementById("generate-pow"); +generateButton.style.display = "block"; + +const usernameField = document.getElementById("username"); +const messageField = document.getElementById("message"); +const powField = document.getElementById("proof-of-work"); + +const hash = (message) => + crypto.subtle.digest("SHA-1", + (new TextEncoder()).encode(message) + ); +const isValidHash = (hashBuffer) => + Array.from(new Uint8Array(hashBuffer)) + .map(byte => byte.toString(16).padStart(2, "0")) + .join("").startsWith("00000"); + +const messageBase = () => usernameField.value + "\0" + messageField.value + "\0"; + +generateButton.onclick = async (event) => { + event.preventDefault(); + const base = messageBase(); + let currentNonce = 0; + + powField.disabled = true; + powField.value = "Generating... Please wait"; + + console.log("Starting PoW generation, this may take a while...."); + while (!isValidHash(await hash(base + currentNonce))) + ++currentNonce; + console.log("Done!"); + powField.value = currentNonce; + powField.disabled = false; +}; \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..dde44c9 --- /dev/null +++ b/static/style.css @@ -0,0 +1,29 @@ +body { + font-family: monospace; + background: #330080; + color: #e5e5e5; + + display: flex; + flex-direction: column; + align-items: center; +} + +header { + display: flex; + flex-direction: column; + align-items: center; +} + +main { + width: 40rem; +} + +textarea { + width: 100%; + resize: vertical; +} + +#website-message { + background: purple; + padding: 0.5rem; +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..a18485a --- /dev/null +++ b/templates/index.html @@ -0,0 +1,63 @@ + + + + + + gaybook + + + + +
+

The GayBook™

+

If you pass by, feel free to leave a message here!

+
+ +
+ {% if message != "" %} +
+

Message from the website

+

{{ message }}

+
+ {% endif %} + +
+

Leave a quote!

+
+ + +
+ + +
+ + + + +
+ +
+
+ +
+

Past quotes

+ {% for quote in quotes %} +

From: {{ quote.author }}, at {{ quote.timestamp }}

+

{{ quote.content }}

+
+ {% endfor %} +
+
+ + \ No newline at end of file