commit a5c4ffe3e986752d1e6d5a751ed9af31c9885279 Author: i-am-called-glitchy Date: Tue Jun 3 12:00:58 2025 +0000 git init m8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddb4c63 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +users.txt +.venv +venv +env +.env +notes diff --git a/static/note.js b/static/note.js new file mode 100644 index 0000000..4d2437d --- /dev/null +++ b/static/note.js @@ -0,0 +1,37 @@ +const textarea = document.getElementById("editor"); +const status = document.getElementById("status"); + +fetch("/api/" + noteName) + .then(res => res.ok ? res.text() : "") + .then(text => { + textarea.value = text; + status.textContent = "Loaded"; + }); + +let timeout; +let last = ""; + +textarea.addEventListener("input", () => { + status.textContent = "Typing..."; + clearTimeout(timeout); + timeout = setTimeout(() => { + const text = textarea.value; + if (text !== last) { + fetch("/api/" + noteName, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ content: text }) + }).then(res => { + if (res.ok) { + last = text; + status.textContent = "Saved"; + } else { + status.textContent = "Save failed"; + } + }); + } else { + status.textContent = "No changes"; + } + }, 500); +}); + diff --git a/templates/note.html b/templates/note.html new file mode 100644 index 0000000..aaf2f90 --- /dev/null +++ b/templates/note.html @@ -0,0 +1,20 @@ + + + + + {{ note }} + + + + +
Loading...
+ + + + + diff --git a/test.html b/test.html new file mode 100644 index 0000000..c4c6816 --- /dev/null +++ b/test.html @@ -0,0 +1,69 @@ + + + + + Autosave Textbox + + + +

Autosave Textbox

+ +
Idle
+ + + + + diff --git a/testflask.py b/testflask.py new file mode 100644 index 0000000..2e3feef --- /dev/null +++ b/testflask.py @@ -0,0 +1,140 @@ +from flask import Flask, request, session, redirect, url_for, render_template, jsonify +from werkzeug.security import generate_password_hash, check_password_hash +from cryptography.fernet import Fernet +import os +import hashlib +import base64 +import dotenv + +dotenv.load_dotenv() + +app = Flask(__name__) +app.secret_key = os.getenv("SECRET") + +USERS_FILE = "users.txt" +DATA_DIR = "notes" +os.makedirs(DATA_DIR, exist_ok=True) + + +# === UTILS === + +def get_user(): + return session.get("user") + +def get_note_path(user, note): + safe_user = user.replace("/", "_") + safe_note = note.replace("/", "_") + user_dir = os.path.join(DATA_DIR, safe_user) + os.makedirs(user_dir, exist_ok=True) + return os.path.join(user_dir, safe_note + ".txt") + +def get_key_for_user(user, password): + salt = b"fixed_salt" # You can store a per-user salt in USERS_FILE if needed + key = hashlib.pbkdf2_hmac("sha256", password.encode(), salt + user.encode(), 100_000) + return base64.urlsafe_b64encode(key[:32]) + + +# === ROUTES === + +@app.route("/api/", methods=["GET", "POST"]) +def api(note): + user = get_user() + key = session.get("key") + if not user or not key: + return "Unauthorized", 401 + + fernet = Fernet(key.encode()) + path = get_note_path(user, note) + + if request.method == "GET": + if not os.path.exists(path): + return "", 200 + with open(path, "rb") as f: + try: + return fernet.decrypt(f.read()).decode(), 200 + except Exception: + return "Corrupted note or invalid key", 500 + + if request.method == "POST": + data = request.get_json() + if not data or "content" not in data: + return "Bad request", 400 + ciphertext = fernet.encrypt(data["content"].encode()) + with open(path, "wb") as f: + f.write(ciphertext) + return jsonify({"status": "saved"}) + + +@app.route("/n/") +@app.route("/notes/") +def serve_note(note): + if not get_user(): + return redirect(url_for("login")) + return render_template("note.html", note=note) + + +@app.route("/register", methods=["GET", "POST"]) +def register(): + if request.method == "POST": + username = request.form.get("username") + password = request.form.get("password") + if not username or not password: + return "Missing fields", 400 + + with open(USERS_FILE, "a+") as f: + f.seek(0) + for line in f: + if line.strip().split(":")[0] == username: + return "User already exists", 400 + hashed_pw = generate_password_hash(password) + f.write(f"{username}:{hashed_pw}\n") + + session["user"] = username + session["key"] = get_key_for_user(username, password).decode() + return redirect(url_for("serve_note", note="home")) + + return ''' +
+ Username:
+ Password:
+ +
+

Already have an account? Login here.

+ ''' + + +@app.route("/login", methods=["GET", "POST"]) +def login(): + if request.method == "POST": + username = request.form.get("username") + password = request.form.get("password") + + with open(USERS_FILE, "r") as f: + for line in f: + user, stored_hash = line.strip().split(":", 1) + if user == username and check_password_hash(stored_hash, password): + session["user"] = username + session["key"] = get_key_for_user(username, password).decode() + return redirect(url_for("serve_note", note="home")) + return "Invalid login", 401 + + return ''' +
+ Username:
+ Password:
+ +
+

Don't have an account? Register here.

+ ''' + + +@app.route("/logout") +def logout(): + session.pop("user", None) + session.pop("key", None) + return redirect(url_for("login")) + + +if __name__ == "__main__": + app.run(debug=True) +