]> littlesong.place Git - littlesongplace.git/commitdiff
Initial commit
authorChris Fulljames <christianfulljames@gmail.com>
Sat, 4 Jan 2025 12:21:19 +0000 (07:21 -0500)
committerChris Fulljames <christianfulljames@gmail.com>
Sat, 4 Jan 2025 12:21:19 +0000 (07:21 -0500)
main.py [new file with mode: 0644]
requirements.txt [new file with mode: 0644]
schema.sql [new file with mode: 0644]
templates/base.html [new file with mode: 0644]
templates/index.html [new file with mode: 0644]
templates/login.html [new file with mode: 0644]
templates/profile.html [new file with mode: 0644]
templates/signup.html [new file with mode: 0644]
templates/users.html [new file with mode: 0644]
todo.txt [new file with mode: 0644]

diff --git a/main.py b/main.py
new file mode 100644 (file)
index 0000000..8a8ed81
--- /dev/null
+++ b/main.py
@@ -0,0 +1,147 @@
+import os
+import sqlite3
+import uuid
+from pathlib import Path, PosixPath
+
+import bcrypt
+import click
+from flask import Flask, render_template, request, redirect, g, session, abort
+from werkzeug.utils import secure_filename
+
+app = Flask(__name__)
+app.secret_key = "TODO"
+
+@app.route("/")
+def index():
+    username = session.get("username", None)
+    return render_template("index.html", username=username)
+
+@app.get("/signup")
+def signup_get():
+    return render_template("signup.html")
+
+@app.post("/signup")
+def signup_post():
+    print(request.form)
+    username = request.form["username"]
+    password = request.form["password"]
+    password_confirm = request.form["password_confirm"]
+
+    error = None
+    if not username.isalnum():
+        error = "Username cannot contain special characters"
+    elif len(username) < 3:
+        error ="Username must be at least 3 characters"
+
+    elif password != password_confirm:
+        error = "Passwords do not match"
+    elif len(password) < 8:
+        error = "Password must be at least 8 characters"
+
+    if query_db("select * from users where username = ?", [username], one=True):
+        error = f"Username '{username}' is already taken"
+
+    if error:
+        return render_template("signup.html", error=error)
+
+    password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
+    query_db("insert into users (username, password) values (?, ?)", [username, password])
+    get_db().commit()
+
+    return render_template("login.html", note="User created.  Sign in to continue")
+
+@app.get("/login")
+def login_get():
+    return render_template("login.html")
+
+@app.post("/login")
+def login_post():
+    username = request.form["username"]
+    password = request.form["password"]
+
+    user_data = query_db("select * from users where username = ?", [username], one=True)
+
+    if user_data and bcrypt.checkpw(password.encode(), user_data["password"]):
+        # Successful login
+        session["username"] = username
+        session.permanent = True
+        return redirect("/")
+
+    return render_template("login.html", error="Invalid username/password")
+
+@app.get("/logout")
+def logout():
+    if "username" in session:
+        session.pop("username")
+
+    return redirect("/")
+
+@app.get("/users")
+def users():
+    users = [row["username"] for row in query_db("select username from users")]
+    return render_template("users.html", users=users)
+
+@app.get("/users/<name>")
+def users_profile(name):
+    username = session.get("username", None)
+    songsdir = f"static/users/{username}/songs/"
+    songspath = Path(f"static/users/{username}/songs")
+    songs = []
+    if songspath.exists():
+        songs = [child.name for child in songspath.iterdir() if child.suffix.lower() == ".mp3"]
+        print(songs)
+    return render_template("profile.html", name=name, username=username, songs=songs)
+
+@app.post("/uploadsong")
+def upload_song():
+    if not "username" in session:
+        abort(401)
+
+    username = session["username"]
+    userpath = Path(f"static/users/{username}/songs")
+    if not userpath.exists():
+        os.makedirs(userpath)
+
+    file = request.files["song"]
+    filename = secure_filename(file.filename)
+    filepath = userpath / filename
+    file.save(filepath)
+
+    return redirect(f"/users/{username}")
+
+
+################################################################################
+# Database
+################################################################################
+
+def get_db():
+    db = getattr(g, '_database', None)
+    if db is None:
+        db = g._database = sqlite3.connect("database.db")
+        db.row_factory = sqlite3.Row
+    return db
+
+@app.teardown_appcontext
+def close_db(exception):
+    db = getattr(g, '_database', None)
+    if db is not None:
+        db.close()
+
+def query_db(query, args=(), one=False):
+    cur = get_db().execute(query, args)
+    rv = cur.fetchall()
+    cur.close()
+    return (rv[0] if rv else None) if one else rv
+
+@click.command("init-db")
+def init_db():
+    """Clear the existing data and create new tables"""
+    with app.app_context():
+        db = get_db()
+        with app.open_resource('schema.sql', mode='r') as f:
+            db.cursor().executescript(f.read())
+        db.commit()
+
+app.teardown_appcontext(close_db)
+app.cli.add_command(init_db)
+
diff --git a/requirements.txt b/requirements.txt
new file mode 100644 (file)
index 0000000..bb2793b
--- /dev/null
@@ -0,0 +1,2 @@
+flask
+bcrypt
diff --git a/schema.sql b/schema.sql
new file mode 100644 (file)
index 0000000..2be8601
--- /dev/null
@@ -0,0 +1,7 @@
+DROP TABLE IF EXISTS users;
+
+CREATE TABLE users (
+  id INTEGER PRIMARY KEY AUTOINCREMENT,
+  username TEXT UNIQUE NOT NULL,
+  password TEXT NOT NULL
+);
diff --git a/templates/base.html b/templates/base.html
new file mode 100644 (file)
index 0000000..1ab2582
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+    <head>
+        <title>{% block title %} Base {% endblock %}</title>
+    </head>
+    <body>
+        <div class="navbar">
+            {% if username %}
+            <p>Signed in as {{ username }}</p>
+            <a href="/logout">Sign Out</a><br>
+            <a href="/users/{{ username }}">My Profile</a><br>
+            {% else %}
+            <a href="/login">Sign In</a><br>
+            <a href="/signup">Create Account</a><br>
+            {% endif %}
+            <a href="/users">All Users</a><br>
+            <a href="/">Home</a>
+        </div>
+
+        {% block body %}
+        {% endblock %}
+    </body>
+</html>
+
diff --git a/templates/index.html b/templates/index.html
new file mode 100644 (file)
index 0000000..5cb6467
--- /dev/null
@@ -0,0 +1,2 @@
+{% extends "base.html" %}
+
diff --git a/templates/login.html b/templates/login.html
new file mode 100644 (file)
index 0000000..6b490c1
--- /dev/null
@@ -0,0 +1,32 @@
+{% extends "base.html" %}
+
+{% block title %}Login{% endblock %}
+
+{% block body %}
+
+{% if note %}
+<div class="note">{{ note }}</div>
+{% endif %}
+
+<form method="post" action="/login">
+    <div class="login-form">
+        <label for="username">Username</label>
+        <input type="text" name="username" required></input>
+    </div>
+
+    <div class="login-form">
+        <label for="password">Password</label>
+        <input type="password" name="password" required></input>
+    </div>
+
+    <div class="login-form">
+        <input type="submit" value="Sign Up"></input>
+    </div>
+</form>
+
+{% if error %}
+<div class="login-error">{{ error }}</div>
+{% endif %}
+
+{% endblock %}
+
diff --git a/templates/profile.html b/templates/profile.html
new file mode 100644 (file)
index 0000000..7112ab5
--- /dev/null
@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+
+{% block title %}{{ name }}'s Profile{% endblock %}
+
+{% block body %}
+
+<h1 class="name">{{ name }}</h1>
+
+{% if name == username %}
+<form action="/uploadsong" method="post" enctype="multipart/form-data">
+    <div class="upload-form">
+        <input type="file" name="song" accept=".mp3"></input>
+    </div>
+    <div class="upload-form">
+        <input type="submit" value="Upload"></input>
+    </div>
+</form>
+{% endif %}
+
+{% for song in songs %}
+<div class="song">
+    {{song}}
+    <audio src="/static/users/{{ name }}/songs/{{ song }}" controls></audio>
+</div>
+{% endfor %}
+
+{% endblock %}
diff --git a/templates/signup.html b/templates/signup.html
new file mode 100644 (file)
index 0000000..5f7a07f
--- /dev/null
@@ -0,0 +1,41 @@
+{% extends "base.html" %}
+
+{% block title %}Create Account{% endblock %}
+
+{% block body %}
+<h1>Create a new account</h1>
+<p>Welcome to the site!</p>
+<h2>Rules:</h2>
+<ol>
+    <li>Be nice</li>
+    <li>Be cool</li>
+    <li>Have fun</li>
+</ol>
+<form method="post">
+    <div class="signup-form">
+        <label for="username">Username</label>
+        <input type="text" name="username" required></input>
+    </div>
+
+    <div class="signup-form">
+        <label for="password">Password</label>
+        <input type="password" name="password" required></input>
+    </div>
+
+    <div class="signup-form">
+        <label for="password_confirm">Confirm Password</label>
+        <input type="password" name="password_confirm" required></input>
+    </div>
+
+    <div class="signup-form">
+        <input type="submit" value="Sign Up"></input>
+    </div>
+</form>
+
+{% if error %}
+<div class="signup-error">Error: {{ error }}</div>
+{% endif %}
+
+{% endblock %}
+
+
diff --git a/templates/users.html b/templates/users.html
new file mode 100644 (file)
index 0000000..0e9b644
--- /dev/null
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% block title %}All Users{% endblock %}
+
+{% block body %}
+
+<h1>All Users</h1>
+{% for user in users %}
+<a href="/users/{{ user }}">{{ user }}</a><br>
+{% endfor %}
+
+{% endblock %}
diff --git a/todo.txt b/todo.txt
new file mode 100644 (file)
index 0000000..b419060
--- /dev/null
+++ b/todo.txt
@@ -0,0 +1,12 @@
+- file upload
+- file download
+- user file list
+- user profile
+
+URL
+secrethideout.net
+sneaky.place
+sneaky.website
+hotnew.singles
+hotlocal.singles
+