From f569620fced52e11d74301105241196bf0dab9fe Mon Sep 17 00:00:00 2001 From: 0x01fe Date: Wed, 27 Mar 2024 14:50:43 -0500 Subject: [PATCH 01/11] started adding the comment thing --- .gitignore | 1 + app/app.py | 18 +++++++++++++++--- app/comment.py | 14 ++++++++++++++ app/config.ini | 9 --------- app/static/style.css | 23 +++++++++++++++++++++++ app/templates/index.html | 19 ++++++++++++++++++- requirements.txt | 4 +++- 7 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 app/comment.py delete mode 100644 app/config.ini diff --git a/.gitignore b/.gitignore index 9fe92d2..8b502d0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ __pycache__ docker-compose.yaml *.sh +*.ini # Ignore images in posts *.jpg diff --git a/app/app.py b/app/app.py index 3492a78..c5509d4 100644 --- a/app/app.py +++ b/app/app.py @@ -3,13 +3,19 @@ import configparser import random import flask +import flask_wtf.csrf +import flask_session import waitress import markdown from post import Post +import comment app = flask.Flask(__name__, static_url_path='', static_folder='static') +csrf = flask_wtf.csrf.CSRFProtect() +csrf.init_app(app) + CONFIG_PATH = "./config.ini" config = configparser.ConfigParser() config.read(CONFIG_PATH) @@ -66,14 +72,20 @@ def index(): # Get posts posts = get_posts() - post_bodies = [] + # Get Comments + comments = [] + + posts_and_comments = [] for post in posts: - post_bodies.append(post.body) + posts_and_comments.append((post.body, comments)) # Get status status = get_status() - return flask.render_template('index.html', posts=post_bodies, status=status) + # Setup Comment Form + form = comment.CommentForm() + + return flask.render_template('index.html', posts=posts_and_comments, status=status, form=form) # Games Page @app.route('/games/') diff --git a/app/comment.py b/app/comment.py new file mode 100644 index 0000000..9b5d3b1 --- /dev/null +++ b/app/comment.py @@ -0,0 +1,14 @@ +import flask +import flask_session +import flask_wtf +import wtforms + + + + +class CommentForm(flask_wtf.FlaskForm): + textbox = wtforms.TextAreaField() + + + + diff --git a/app/config.ini b/app/config.ini deleted file mode 100644 index 8d00b71..0000000 --- a/app/config.ini +++ /dev/null @@ -1,9 +0,0 @@ -[POSTS] -posts_folder=./posts - -[STATUS] -status_file=./resources/status.text - -[NETWORK] -PORT=1111 -DEV=0 diff --git a/app/static/style.css b/app/static/style.css index 3ba8574..3d83e15 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -172,3 +172,26 @@ a:hover { line-height: 2.25; text-indent: 3em; } + +.comment-container { + background-color: var(--primary40); + border-style: var(--borders-style); + border-radius: var(--border-radius); + + padding: 1em; + margin: 1em; +} + +.comment-editor input { + width: 100%; + padding: 3em 0; + line-height: 140%; + + border-style: solid; + /* border-color: var(--secondary50); */ + border-radius: var(--border-radius); +} + +.comment-editor input:focus { + border: 3px solid var(--accent); +} diff --git a/app/templates/index.html b/app/templates/index.html index a5bfb0c..21daca3 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -25,7 +25,24 @@
{% for post in posts %} -
{{ post|safe }}
+
+ {{ post[0]|safe }} +
+ {% for comment in post[1] %} +
{{ comment|safe }}
+ {% endfor %} + {% if user %} +
+

{{ user }}

+
+ + {{ form.textbox }} + +
+
+ {% endif %} +
+
{% endfor %}
diff --git a/requirements.txt b/requirements.txt index bb91e2f..a8030e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ Markdown==3.5.2 Flask==2.2.3 waitress==2.1.2 -Werkzeug==2.2.3 \ No newline at end of file +Werkzeug==2.2.3 +Flask-Session==0.5.0 +Flask-WTF==1.1.1 \ No newline at end of file From 76688b2d2c1e4cc47e865a9e66d6ae3f92b0315b Mon Sep 17 00:00:00 2001 From: 0x01fe Date: Wed, 3 Apr 2024 13:13:48 -0500 Subject: [PATCH 02/11] started working on the comments feature --- .gitignore | 5 ++++- app/app.py | 21 +++++++++++++++---- app/comment.py | 16 ++++++++++---- app/post.py | 5 ++++- app/posts/POST_TEMPLATE.md | 1 + app/static/style.css | 10 ++++----- app/templates/index.html | 3 +-- app/templates/user/register.html | 36 ++++++++++++++++++++++++++++++++ app/user.py | 25 ++++++++++++++++++++++ 9 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 app/templates/user/register.html create mode 100644 app/user.py diff --git a/.gitignore b/.gitignore index 8b502d0..170b585 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,11 @@ __pycache__ docker-compose.yaml *.sh -*.ini # Ignore images in posts *.jpg *.png + +# Flask Data & Config +*.ini +data diff --git a/app/app.py b/app/app.py index c5509d4..e7d9301 100644 --- a/app/app.py +++ b/app/app.py @@ -1,6 +1,7 @@ import glob import configparser import random +import base64 import flask import flask_wtf.csrf @@ -10,12 +11,13 @@ import markdown from post import Post import comment +import user app = flask.Flask(__name__, static_url_path='', static_folder='static') +app.register_blueprint(comment.comments) +app.register_blueprint(user.user) -csrf = flask_wtf.csrf.CSRFProtect() -csrf.init_app(app) - +# CONFIG CONFIG_PATH = "./config.ini" config = configparser.ConfigParser() config.read(CONFIG_PATH) @@ -25,6 +27,17 @@ STATUS_FILE = config['STATUS']['STATUS_FILE'] PORT = int(config['NETWORK']['PORT']) DEV = int(config['NETWORK']['DEV']) + +# CSRF Protect +app.config['SECRET_KEY'] = base64.b64decode(config["FLASK"]["SECRET"]) +csrf = flask_wtf.csrf.CSRFProtect() +csrf.init_app(app) + +# Session Setup +app.config['SESSION_TYPE'] = 'filesystem' +app.config['SESSION_FILE_DIR'] = './data/.flask_session/' +flask_session.Session(app) + def get_posts(category_filter : str | None = None) -> list[Post]: post_files = glob.glob(f'{POSTS_FOLDER}/*') try: @@ -85,7 +98,7 @@ def index(): # Setup Comment Form form = comment.CommentForm() - return flask.render_template('index.html', posts=posts_and_comments, status=status, form=form) + return flask.render_template('index.html', posts=posts_and_comments, status=status, form=form, user="yes") # Games Page @app.route('/games/') diff --git a/app/comment.py b/app/comment.py index 9b5d3b1..6eaa510 100644 --- a/app/comment.py +++ b/app/comment.py @@ -1,14 +1,22 @@ +import json + import flask import flask_session -import flask_wtf +import flask_wtf.csrf import wtforms - - +comments = flask.Blueprint('comment', __name__, template_folder='./templates') class CommentForm(flask_wtf.FlaskForm): - textbox = wtforms.TextAreaField() + textbox = wtforms.TextAreaField('Input') + +@comments.route('/comment/', methods=['POST']) +def comment(): + form = CommentForm(csrf_enabled=True) + + return flask.redirect('/') +def get_comments(post_id : int) -> list[dict]: diff --git a/app/post.py b/app/post.py index 1de822a..f087a29 100644 --- a/app/post.py +++ b/app/post.py @@ -8,6 +8,7 @@ class Post: date : datetime.datetime body : str file : str + id : int def __init__(self, file_path): self.file = file_path @@ -21,5 +22,7 @@ class Post: date = lines[3].split(":")[1].strip() self.date = datetime.datetime.strptime(date, "%d-%m-%Y") - self.body = markdown.markdown(''.join(lines[6:])) + self.id = int(lines[4].split(":")[1].strip()) + + self.body = markdown.markdown(''.join(lines[7:])) diff --git a/app/posts/POST_TEMPLATE.md b/app/posts/POST_TEMPLATE.md index 7fd0fb8..fa1630e 100644 --- a/app/posts/POST_TEMPLATE.md +++ b/app/posts/POST_TEMPLATE.md @@ -2,6 +2,7 @@ category: category author: author date: date +id: post id # POST ## TITLE diff --git a/app/static/style.css b/app/static/style.css index 3d83e15..68cbe2c 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -182,16 +182,16 @@ a:hover { margin: 1em; } -.comment-editor input { +.comment-editor textarea { width: 100%; - padding: 3em 0; - line-height: 140%; + height: 6em; + padding: 0.75em; border-style: solid; - /* border-color: var(--secondary50); */ + border-color: var(--secondary50); border-radius: var(--border-radius); } -.comment-editor input:focus { +.comment-editor textarea:focus { border: 3px solid var(--accent); } diff --git a/app/templates/index.html b/app/templates/index.html index 21daca3..d349f43 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -34,8 +34,7 @@ {% if user %}

{{ user }}

-
- + {{ form.textbox }}
diff --git a/app/templates/user/register.html b/app/templates/user/register.html new file mode 100644 index 0000000..27804b0 --- /dev/null +++ b/app/templates/user/register.html @@ -0,0 +1,36 @@ + + +
+ + + 0x01fe.net - Register +
+ +
+

0x01fe.net

+ {{ status|safe }} +
+
+ + + + + +
+

Register

+
+ {{ form.username }} + {{ form.password }} + +
+
+
+ + diff --git a/app/user.py b/app/user.py new file mode 100644 index 0000000..ff7c758 --- /dev/null +++ b/app/user.py @@ -0,0 +1,25 @@ +import flask +import flask_wtf.csrf +import wtforms + +user = flask.Blueprint('user', __name__, template_folder='./templates/user') + +class RegisterUserForm(flask_wtf.FlaskForm): + username = wtforms.StringField("Username", [ + wtforms.validators.Length(min=4, max=32), + wtforms.validators.DataRequired() + ]) + password = wtforms.PasswordField("Password", [ + wtforms.validators.Length(min=8, max=64), + wtforms.validators.DataRequired() + ]) + +@user.route('/user/add/', methods=["POST"]) +def add_user(): + pass + +@user.route('/user/register/') +def register_user(): + form = RegisterUserForm() + + return flask.render_template('register.html', form=form) From 9cd68f404880784955b4344043414b80cae8edf1 Mon Sep 17 00:00:00 2001 From: 0x01fe Date: Wed, 26 Jun 2024 15:53:17 -0500 Subject: [PATCH 03/11] finished up the comments/user system --- .gitignore | 2 + app/app.py | 27 +++--- app/comment.py | 42 +++++++++- .../2029240f6d1128be89ddc32729463129 | Bin 9 -> 0 bytes .../aa43147407c8a8c678cd91f678f2a023 | Bin 90 -> 0 bytes app/data/comments.json | 8 -- app/data/users.json | 5 -- app/post.py | 5 +- app/static/style.css | 3 +- app/templates/about.html | 3 +- app/templates/games.html | 3 +- app/templates/index.html | 13 ++- app/templates/motion-pictures.html | 3 +- app/templates/music.html | 3 +- app/templates/programming.html | 3 +- app/templates/user/login.html | 40 +++++++++ app/templates/user/register.html | 24 +++--- app/user.py | 77 +++++++++++++++++- 18 files changed, 200 insertions(+), 61 deletions(-) delete mode 100644 app/data/.flask_session/2029240f6d1128be89ddc32729463129 delete mode 100644 app/data/.flask_session/aa43147407c8a8c678cd91f678f2a023 delete mode 100644 app/data/comments.json delete mode 100644 app/data/users.json create mode 100644 app/templates/user/login.html diff --git a/.gitignore b/.gitignore index 0db4e3d..8e92c84 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ docker-compose.yaml # Flask Data & Config *.ini data +.flask_session +*.json *.gif # Writing diff --git a/app/app.py b/app/app.py index b672af8..416bf54 100644 --- a/app/app.py +++ b/app/app.py @@ -116,12 +116,21 @@ def index(): # Get posts posts = get_posts() - # Get Comments - comments = [] + if 'username' in flask.session: + user = flask.session['username'] + else: + user = 'Anon' + # Get Comments posts_and_comments = [] for post in posts: - posts_and_comments.append((post.body, comments)) + + comments = comment.get_comments(post.title) + posts_and_comments.append(({ + "body" : post.body, + "title" : post.title + }, + comments)) # Get status status = get_status() @@ -129,17 +138,7 @@ def index(): # Setup Comment Form form = comment.CommentForm() - return flask.render_template('index.html', posts=posts_and_comments, status=status, form=form, user="yes") - -# Posts -@app.route('/post/') -def post(post_name: str): - - for post in get_posts(): - if post.title.replace(' ', '-') == post_name: - return flask.render_template('index.html', posts=[post.body], status=get_status()) - - flask.abort(404) + return flask.render_template('index.html', posts=posts_and_comments, status=status, form=form, user=user) # Posts @app.route('/post/') diff --git a/app/comment.py b/app/comment.py index 6eaa510..0e27ea6 100644 --- a/app/comment.py +++ b/app/comment.py @@ -1,22 +1,56 @@ import json import flask -import flask_session import flask_wtf.csrf import wtforms +COMMENTS_PATH = "./data/comments.json" comments = flask.Blueprint('comment', __name__, template_folder='./templates') class CommentForm(flask_wtf.FlaskForm): textbox = wtforms.TextAreaField('Input') -@comments.route('/comment/', methods=['POST']) -def comment(): +@comments.route('/comment/', methods=['POST']) +def comment(post_title: str): form = CommentForm(csrf_enabled=True) + save_comment(form.textbox.data, post_title) + return flask.redirect('/') +def save_comment(content: str, post_title: str): + with open(COMMENTS_PATH, 'r') as file: + comment_data = json.loads(file.read()) -def get_comments(post_id : int) -> list[dict]: + # See if user is logged in, otherwise setup as anon + if 'username' in flask.session: + username = flask.session['username'] + else: + username = 'Anon' + + comment = { + "username" : username, + "content" : content + } + + # Add comment to JSON data + if post_title in comment_data: + comment_data[post_title].append(comment) + else: + comment_data[post_title] = [comment] + + # Save JSON data + with open(COMMENTS_PATH, 'w') as file: + file.write(json.dumps(comment_data)) + +def get_comments(post_title : int) -> list[dict]: + + with open(COMMENTS_PATH, 'r') as file: + comment_data = json.loads(file.read()) + + if post_title in comment_data: + return comment_data[post_title] + else: + return [] diff --git a/app/data/.flask_session/2029240f6d1128be89ddc32729463129 b/app/data/.flask_session/2029240f6d1128be89ddc32729463129 deleted file mode 100644 index 7f5741f13017ee705ea34021d222a06a6ee2a6c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9 QcmZQzU|?uq^=8ro00XcA0RR91 diff --git a/app/data/.flask_session/aa43147407c8a8c678cd91f678f2a023 b/app/data/.flask_session/aa43147407c8a8c678cd91f678f2a023 deleted file mode 100644 index d818306312c02b6939f76f09975e7cd3b355c42e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeB))lF+)o$Ad10ku;!dbr{XQj2mE^HTFlrgZdhB^MW^#h2t~r{+!R(J-_yH#Sa5 oH8xK%HZ!wKO-(j2Ha1VQ1o9116D`cmEmPBs%u|z-rMotion Picture
Programming
Writing
- About + About
+ Login
diff --git a/app/templates/games.html b/app/templates/games.html index 0ffded7..69587d9 100644 --- a/app/templates/games.html +++ b/app/templates/games.html @@ -19,7 +19,8 @@ Motion Picture
Programming
Writing
- About + About
+ Login diff --git a/app/templates/index.html b/app/templates/index.html index 4c79bc5..f0114c4 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -19,7 +19,8 @@ Motion Picture
Programming
Writing
- About + About
+ Login @@ -27,15 +28,19 @@
{% for post in posts %}
- {{ post[0]|safe }} + {{ post[0].body|safe }}
{% for comment in post[1] %} -
{{ comment|safe }}
+
+

{{ comment.username }}

+

{{ comment.content }}

+
{% endfor %} {% if user %}

{{ user }}

-
+ + {{ form.hidden_tag() }} {{ form.textbox }}
diff --git a/app/templates/motion-pictures.html b/app/templates/motion-pictures.html index 87d36f9..ef72b79 100644 --- a/app/templates/motion-pictures.html +++ b/app/templates/motion-pictures.html @@ -19,7 +19,8 @@ Motion Picture
Programming
Writing
- About + About
+ Login
diff --git a/app/templates/music.html b/app/templates/music.html index 5ea89f3..71c868a 100644 --- a/app/templates/music.html +++ b/app/templates/music.html @@ -19,7 +19,8 @@ Motion Picture
Programming
Writing
- About + About
+ Login
diff --git a/app/templates/programming.html b/app/templates/programming.html index d146ccd..0bf970e 100644 --- a/app/templates/programming.html +++ b/app/templates/programming.html @@ -19,7 +19,8 @@ Motion Picture
Programming
Writing
- About + About
+ Login
diff --git a/app/templates/user/login.html b/app/templates/user/login.html new file mode 100644 index 0000000..9659b9c --- /dev/null +++ b/app/templates/user/login.html @@ -0,0 +1,40 @@ + + +
+ + + 0x01fe.net - Login +
+ +
+

0x01fe.net

+ {{ status|safe }} +
+
+ + + +
+

Login

+
+ {{ form.hidden_tag() }} + Username: {{ form.username }}
+ Password: {{ form.password }}
+ +
+
+ +

Need to Register?

+ Register +
+
+ + diff --git a/app/templates/user/register.html b/app/templates/user/register.html index 27804b0..b5cc4da 100644 --- a/app/templates/user/register.html +++ b/app/templates/user/register.html @@ -13,22 +13,22 @@
- -

Register

-
- {{ form.username }} - {{ form.password }} - + + {{ form.hidden_tag() }} + Username: {{ form.username }}
+ Password: {{ form.password }}
+
diff --git a/app/user.py b/app/user.py index ff7c758..6c6411b 100644 --- a/app/user.py +++ b/app/user.py @@ -1,9 +1,12 @@ +import base64 +import json + import flask import flask_wtf.csrf import wtforms user = flask.Blueprint('user', __name__, template_folder='./templates/user') - +USERS_PATH = "./data/users.json" class RegisterUserForm(flask_wtf.FlaskForm): username = wtforms.StringField("Username", [ wtforms.validators.Length(min=4, max=32), @@ -14,12 +17,80 @@ class RegisterUserForm(flask_wtf.FlaskForm): wtforms.validators.DataRequired() ]) +class LoginUserForm(flask_wtf.FlaskForm): + username = wtforms.StringField("Username", [ + wtforms.validators.DataRequired() + ]) + password = wtforms.PasswordField("Password", [ + wtforms.validators.DataRequired() + ]) + @user.route('/user/add/', methods=["POST"]) def add_user(): - pass + + # Get form data + form = RegisterUserForm(csrf_enabled=True) + + username = form.username.data + password = form.password.data + + # Read existing user data + with open(USERS_PATH, 'r') as file: + user_data = json.loads(file.read()) + + # check if user exists + if username in user_data: + flask.abort(400) + + # Store password / server side cookie + user_data[username] = base64.b64encode(password.encode()).decode() + flask.session['username'] = username + + # Write user data + with open(USERS_PATH, 'w') as file: + file.write(json.dumps(user_data)) + + flask.redirect('/') @user.route('/user/register/') -def register_user(): +def register_page(): form = RegisterUserForm() return flask.render_template('register.html', form=form) + +@user.route('/user/login/', methods=["POST"]) +def login_user(): + form = LoginUserForm(csrf_enabled=True) + + username = form.username.data + password = base64.b64encode(form.password.data.encode()).decode() + + # Read existing user data + with open(USERS_PATH, 'r') as file: + user_data = json.loads(file.read()) + + # check if user exists + if username not in user_data: + flask.abort(400) + + # Does password match? + if user_data[username] != password: + flask.abort(400) + + flask.session['username'] = username + + return flask.redirect('/') + +@user.route('/login/') +def login_page(): + form = LoginUserForm() + + return flask.render_template('login.html', form=form) + +@user.route('/logout/') +def logout_user(): + + if 'username' in flask.session: + flask.session.pop('username') + + return flask.redirect('/') From caf3a7ac3205cbb7dae12049e1980fb37b51aacf Mon Sep 17 00:00:00 2001 From: 0x01fe Date: Wed, 26 Jun 2024 15:58:05 -0500 Subject: [PATCH 04/11] menu pathing changes --- app/posts/POST_TEMPLATE.md | 1 - app/templates/about.html | 10 +++++----- app/templates/games.html | 10 +++++----- app/templates/index.html | 2 +- app/templates/motion-pictures.html | 10 +++++----- app/templates/music.html | 10 +++++----- app/templates/programming.html | 10 +++++----- 7 files changed, 26 insertions(+), 27 deletions(-) diff --git a/app/posts/POST_TEMPLATE.md b/app/posts/POST_TEMPLATE.md index fa1630e..7fd0fb8 100644 --- a/app/posts/POST_TEMPLATE.md +++ b/app/posts/POST_TEMPLATE.md @@ -2,7 +2,6 @@ category: category author: author date: date -id: post id # POST ## TITLE diff --git a/app/templates/about.html b/app/templates/about.html index 88ff118..822612a 100644 --- a/app/templates/about.html +++ b/app/templates/about.html @@ -13,11 +13,11 @@