summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatt Arnold2025-04-09 18:18:37 -0400
committerMatt Arnold2025-04-09 18:18:37 -0400
commit4ac2861d28676bc886cf80b43a087f1f0044696a (patch)
tree48dd9e5e62860b137c4363f29d921646ff6f9889
parentd1745a9c1e46d43af005ac966cf4170192b76f97 (diff)
Threaded blogging works
-rw-r--r--.gitignore3
-rw-r--r--app.py92
-rw-r--r--forms.py16
-rw-r--r--models.py39
-rw-r--r--requirements.txt2
-rw-r--r--templates/login.html24
-rw-r--r--templates/post.html24
7 files changed, 175 insertions, 25 deletions
diff --git a/.gitignore b/.gitignore
index c2014f5..1d2d98a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -115,4 +115,5 @@ cython_debug/
 
 # Local configuration files
 config.py
-*.db
\ No newline at end of file
+*.db
+AI_HOLDING/*
\ No newline at end of file
diff --git a/app.py b/app.py
index 219b160..137a6ca 100644
--- a/app.py
+++ b/app.py
@@ -1,46 +1,108 @@
-from flask import Flask, render_template, request, redirect, url_for
+from urllib.parse import urlparse as url_parse
+
+
+from flask import Flask, render_template, request, redirect, url_for, flash
 from markdown import markdown, Markdown
 from config import config
-from models import Post, db
-from forms import PostForm
+from models import Post, db, get_replies, Faccet
+from models import User as NewUser
+from forms import PostForm, LoginForm
+from flask_login import (
+    login_user,
+    logout_user,
+    login_required,
+    current_user,
+    LoginManager,
+)
 import os
 
+
 app = Flask(__name__)
-app.config.from_object('config')
+app.config.from_object("config")
 SECRET_KEY = os.urandom(32)
-app.config['SECRET_KEY'] = SECRET_KEY
+app.config["SECRET_KEY"] = SECRET_KEY
 
 app.jinja_options = app.jinja_options.copy()
 app.jinja_env.add_extension(Markdown)
 app.jinja_env.filters["markdown"] = markdown
 
+login = LoginManager(app)
+login.login_view = "login"
+
+
+@login.user_loader
+def load_user(uid):
+    return NewUser.get(NewUser.id == uid)
+
+
+@app.route("/login", methods=["GET", "POST"])
+def login():
+    form = LoginForm()
+    if form.validate_on_submit():  # noqa
+        user = NewUser.get(NewUser.username == form.username.data)
+        if user is None or not user.check_password(form.password.data):
+            flash("Invalid username or password")
+            return redirect(url_for("login"))
+        login_user(user, remember=form.remember_me.data)
+        next_page = request.args.get("next")
+        if not next_page or url_parse(next_page).netloc != "":
+            next_page = url_for("index")
+        return redirect(next_page)
+
+    return render_template("login.html", form=form)
+
 
 @app.before_request
 def before_request():
     db.connect()
 
+
 @app.after_request
 def after_request(response):
     db.close()
     return response
 
-@app.route('/')
+
+@app.route("/")
+@login_required
 def index():
-    posts = Post.select().order_by(Post.created_at.desc())
-    return render_template('index.html', posts=posts)
+    posts = Post.select().where(Post.parent == 0).order_by(Post.created_at.desc())
+    return render_template("index.html", posts=posts)
+
 
-@app.route('/post/<int:post_id>')
+@app.route("/logout")
+@login_required
+def logout():
+    flash("Goodbye!")
+    logout_user()
+    return redirect(url_for("login"))
+
+
+@app.route("/post/<int:post_id>")
+@login_required
 def post(post_id):
     post = Post.get(Post.id == post_id)
-    return render_template('post.html', post=post)
+    replies = get_replies(post_id)
+    return render_template("post.html", post=post, replies=replies)
 
-@app.route('/create', methods=['GET', 'POST'])
+
+@app.route("/create", methods=["GET", "POST"])
+@login_required
 def create():
     form = PostForm()
+    replyto = request.args.get("reply", 0)
+    userctx = NewUser.get(NewUser.username == current_user.username)
+    asfaccet = Faccet.get(Faccet.name == userctx.default_faccet)
     if form.validate_on_submit():
-        Post.create(title=form.title.data, content=form.content.data)
-        return redirect(url_for('index'))
-    return render_template('create.html', form=form)
+        Post.create(
+            title=form.title.data,
+            content=form.content.data,
+            authour=asfaccet,
+            parent=replyto,
+        )
+        return redirect(url_for("index"))
+    return render_template("create.html", form=form)
+
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     app.run(debug=True, port=5052)
diff --git a/forms.py b/forms.py
index d8aa8c0..be0171a 100644
--- a/forms.py
+++ b/forms.py
@@ -1,8 +1,18 @@
 from flask_wtf import FlaskForm
-from wtforms import StringField, TextAreaField, SubmitField
+from wtforms import StringField, TextAreaField, SubmitField, PasswordField, BooleanField
 from wtforms.validators import DataRequired
 
+
 class PostForm(FlaskForm):
-    title = StringField('Title', validators=[DataRequired()])
-    content = TextAreaField('Content', validators=[DataRequired()])
+    title = StringField("Title", validators=[DataRequired()])
+    content = TextAreaField("Content", validators=[DataRequired()])
     submit = SubmitField("Toot!")
+
+
+class LoginForm(FlaskForm):
+    """Login Form"""
+
+    username = StringField("Username", validators=[DataRequired()])
+    password = PasswordField("Password", validators=[DataRequired()])
+    remember_me = BooleanField("Remember Me")
+    submit = SubmitField("Sign In")
diff --git a/models.py b/models.py
index 75af58b..26934d9 100644
--- a/models.py
+++ b/models.py
@@ -1,15 +1,50 @@
-from peewee import Model, CharField, TextField, DateTimeField, SqliteDatabase
+from peewee import Model, CharField, TextField, DateTimeField, SqliteDatabase, BlobField
+from peewee import IntegerField, ForeignKeyField
+from flask_login import UserMixin
+from werkzeug.security import check_password_hash, generate_password_hash
 from datetime import datetime
 from config import dbloc
+
 # from app import db
 db = SqliteDatabase(dbloc)
+
+
 class BaseModel(Model):
     class Meta:
         database = db
 
+
+class User(UserMixin, BaseModel):
+    id = IntegerField(primary_key=True)
+    username = CharField(max_length=64, index=True, unique=True)
+    default_faccet = TextField(null=False)
+    password_hash = CharField(max_length=128)
+
+    def set_password(self, password):
+        self.password_hash = generate_password_hash(password)
+
+    def check_password(self, password):
+        return check_password_hash(self.password_hash, password)
+
+
+class Faccet(BaseModel):
+    user_belongs = ForeignKeyField(User, backref="parts")
+    name = TextField(unique=True)
+    picture = BlobField()
+    bio = TextField()
+
+
 class Post(BaseModel):
+    id = IntegerField(primary_key=True)
+    authour = ForeignKeyField(Faccet, backref="creator")
+    parent = IntegerField(default=0)
     title = CharField()
     content = TextField()
     created_at = DateTimeField(default=datetime.now)
 
-db.create_tables([Post])
+
+def get_replies(post_id):
+    return Post.select().where(Post.parent == post_id).order_by(Post.created_at.desc())
+
+
+db.create_tables([User, Post, Faccet])
diff --git a/requirements.txt b/requirements.txt
index 125950d..70cfdae 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,10 +1,12 @@
 blinker==1.9.0
 click==8.1.8
 Flask==3.1.0
+Flask-Login==0.6.3
 Flask-WTF==1.2.2
 itsdangerous==2.2.0
 Jinja2==3.1.6
 MarkupSafe==3.0.2
+mistletoe==1.4.0
 peewee==3.17.9
 Werkzeug==3.1.3
 WTForms==3.2.1
diff --git a/templates/login.html b/templates/login.html
new file mode 100644
index 0000000..3a46511
--- /dev/null
+++ b/templates/login.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block content %}
+<h1>Welcome to VibeEngine</h1>
+<form action="" method="post" novalidate>
+    {{ form.hidden_tag() }}
+    <p>
+        {{ form.username.label }}<br>
+        {{ form.username(size=32) }}<br>
+        {% for error in form.username.errors %}
+        <span style="color: peru;">[{{ error }}]</span>
+        {% endfor %}
+    </p>
+    <p>
+        {{ form.password.label }}<br>
+        {{ form.password(size=32) }}<br>
+        {% for error in form.password.errors %}
+        <span style="color: peru;">[{{ error }}]</span>
+        {% endfor %}
+    </p>
+    <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
+    <p>{{ form.submit() }}</p>
+</form>
+{% endblock %}
\ No newline at end of file
diff --git a/templates/post.html b/templates/post.html
index 8272d37..d759da4 100644
--- a/templates/post.html
+++ b/templates/post.html
@@ -1,8 +1,24 @@
 {% extends 'base.html' %}
 
 {% block content %}
-    <h2>{{ post.title }}</h2>
-    <p>{{ post.created_at.strftime('%Y-%m-%d %H:%M') }}</p>
-    <div>{{ post.content|markdown|safe }}</div>
+{% if post.parent != 0 %}
+<a href="{{url_for('post', post_id=post.parent)}}"> Previous</a>
+<hr>
+{% endif %}
+<h2>{{ post.title }}</h2>
+<p>{{ post.created_at.strftime('%Y-%m-%d %H:%M') }}</p>
+<p> Authour: {{post.authour.name}}</p>
+<div>{{ post.content|markdown|safe }}</div>
+<div class="post-actions">
+    <a href="{{ url_for('create', reply=post.id)}}"> Reply</a> ~
     <a href="{{ url_for('index') }}">Back to posts</a>
-{% endblock %}
+</div>
+<hr>
+<h3> Replies to this</h3>
+<div class="post-replies">
+    <ul>
+        <li> {% for reply in replies %} <a href="{{ url_for('post', post_id=reply.id) }}">{{ reply.title }}</a> - {{
+            reply.created_at.strftime('%Y-%m-%d') }}</li> {% endfor %}
+    </ul>
+</div>
+{% endblock %}
\ No newline at end of file