Compare commits

..

18 Commits
main ... v2

Author SHA1 Message Date
c2f829d39b new css stuff 2024-09-17 10:16:26 -05:00
654a87b877 gitignore update 2024-09-17 09:50:03 -05:00
0x01FE
ad2175e6cb added all my images and stuff 2024-09-17 09:45:25 -05:00
0x01FE
637e7bd9ab starting new css (again) 2024-09-14 17:01:26 -05:00
0x01fe
c3f7cd5ac7 forgot to remove the endpoint 2024-07-11 15:35:23 -05:00
0x01fe
9cdd5f3da4 removed writing page 2024-07-11 15:34:59 -05:00
0x01fe
abc4574988 refactored how comments are handled to fit in with the posts class 2024-07-01 10:30:42 -05:00
0x01fe
8712b8734e fixed most endpoints 2024-07-01 09:52:38 -05:00
0x01fe
2542b6862c added some checks for user/comments files 2024-07-01 09:41:30 -05:00
0x01fe
724e0ac3d9 consolidated some pages 2024-07-01 09:35:53 -05:00
0x01fe
8d49357074 various changes making the website look better 2024-06-28 15:40:53 -05:00
0x01FE
022f41a883 fixed some bugs that i found when deploying 2024-06-26 18:03:12 -05:00
0x01fe
e69ec565f7 fixed post links and added comment stuff to posts page 2024-06-26 16:03:47 -05:00
0x01fe
caf3a7ac32 menu pathing changes 2024-06-26 15:58:05 -05:00
0x01fe
9cd68f4048 finished up the comments/user system 2024-06-26 15:53:17 -05:00
0x01fe
ce9c995a50 Merge branch 'main' into comments 2024-06-26 14:10:02 -05:00
0x01fe
76688b2d2c started working on the comments feature 2024-04-03 13:13:48 -05:00
0x01fe
f569620fce started adding the comment thing 2024-03-27 14:50:43 -05:00
48 changed files with 604 additions and 289 deletions

41
.gitignore vendored
View File

@ -1,18 +1,23 @@
__pycache__ __pycache__
*.md *.md
!POST_TEMPLATE.md !POST_TEMPLATE.md
!README.md !README.md
docker-compose.yaml docker-compose.yaml
*.sh *.sh
*.ini *.ini
# Ignore images in posts # Ignore images in posts
*.jpg
*.png
*.gif # Flask Data & Config
*.ini
# Writing data
writing .flask_session
*.json
# Writing
writing

View File

@ -4,8 +4,6 @@ FROM python:3.12.2-slim-bookworm
RUN apt-get update && apt-get upgrade -y RUN apt-get update && apt-get upgrade -y
RUN groupadd -r app && useradd -r -g app app
COPY . . COPY . .
RUN python3 -m pip install --upgrade pip RUN python3 -m pip install --upgrade pip
@ -13,6 +11,4 @@ RUN python3 -m pip install -r requirements.txt
WORKDIR ./app WORKDIR ./app
USER app
CMD ["python3", "-u", "app.py"] CMD ["python3", "-u", "app.py"]

View File

@ -2,32 +2,54 @@ import os
import glob import glob
import configparser import configparser
import random import random
import base64
import datetime import datetime
import requests import requests
import flask import flask
import flask_wtf.csrf
import flask_session
import waitress import waitress
import markdown import markdown
from post import Post from post import Post
import comment
import user
app = flask.Flask(__name__, static_url_path='', static_folder='static') app = flask.Flask(__name__, static_url_path='', static_folder='static')
app.register_blueprint(comment.comments)
app.register_blueprint(user.user)
# CONFIG
CONFIG_PATH = "./config.ini" CONFIG_PATH = "./config.ini"
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(CONFIG_PATH) config.read(CONFIG_PATH)
WRITING_FOLDER = 'static/writing/'
POSTS_FOLDER = config['POSTS']['POSTS_FOLDER'] POSTS_FOLDER = config['POSTS']['POSTS_FOLDER']
STATUS_FILE = config['STATUS']['STATUS_FILE'] STATUS_FILE = config['STATUS']['STATUS_FILE']
PORT = int(config['NETWORK']['PORT']) PORT = int(config['NETWORK']['PORT'])
DEV = int(config['NETWORK']['DEV']) 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)
MUSIC_API_TOKEN = config['AUTH']['MUSIC_API_TOKEN'] MUSIC_API_TOKEN = config['AUTH']['MUSIC_API_TOKEN']
MUSIC_API_URL = config['NETWORK']['MUSIC_API_URL'] MUSIC_API_URL = config['NETWORK']['MUSIC_API_URL']
statuses = {} statuses = {}
header_background_images = [
"canal-banner2.jpg",
"real-greensilt-banner.png"
]
def get_posts(category_filter : str | None = None) -> list[Post]: def get_posts(category_filter : str | None = None) -> list[tuple[dict, list]]:
post_files = glob.glob(f'{POSTS_FOLDER}/*') post_files = glob.glob(f'{POSTS_FOLDER}/*')
try: try:
post_files.remove(f'{POSTS_FOLDER}/POST_TEMPLATE.md') post_files.remove(f'{POSTS_FOLDER}/POST_TEMPLATE.md')
@ -57,7 +79,12 @@ def get_posts(category_filter : str | None = None) -> list[Post]:
ordered_posts.append(most_recent) ordered_posts.append(most_recent)
posts.remove(most_recent) posts.remove(most_recent)
return reversed(ordered_posts) # Convert to dict
posts = []
for post in reversed(ordered_posts):
posts.append(post.__dict__)
return posts
def read_status_file() -> dict: def read_status_file() -> dict:
with open(STATUS_FILE, 'r', encoding='utf-8') as file: with open(STATUS_FILE, 'r', encoding='utf-8') as file:
@ -90,6 +117,9 @@ def get_status() -> str:
return f'<div title="{selected_key}">{markdown.markdown(selected_status)}</div>' return f'<div title="{selected_key}">{markdown.markdown(selected_status)}</div>'
def get_header_image() -> str:
return header_background_images[random.randint(0, len(header_background_images) - 1)]
# Main Page # Main Page
@app.route('/') @app.route('/')
def index(): def index():
@ -97,40 +127,59 @@ def index():
# Get posts # Get posts
posts = get_posts() posts = get_posts()
post_bodies = [] if 'username' in flask.session:
for post in posts: user = flask.session['username']
post_bodies.append(post.body) else:
user = 'Anon'
# Get status # Get status
status = get_status() status = get_status()
return flask.render_template('index.html', posts=post_bodies, status=status) # Setup Comment Form
form = comment.CommentForm()
img = get_header_image()
return flask.render_template('index.html', posts=posts, status=status, form=form, user=user, title='0x01fe.net', header_background_image=img)
# Posts # Posts
@app.route('/post/<string:post_name>') @app.route('/post/<string:post_name>')
def post(post_name: str): def post(post_name: str):
for post in get_posts(): for post in get_posts():
if post.title.replace(' ', '-') == post_name: if post['title'] == post_name:
return flask.render_template('index.html', posts=[post.body], status=get_status())
if 'username' in flask.session:
user = flask.session['username']
else:
user = 'Anon'
# Setup Comment Form
form = comment.CommentForm()
return flask.render_template('index.html', posts=[post], status=get_status(), form=form, user=user, title='0x01fe.net')
flask.abort(404) flask.abort(404)
# Games Page # Category's Endpoint
@app.route('/games/') @app.route('/category/<string:category>/')
def games(): def category_filter(category: str):
# Get posts # Get posts
posts = get_posts(category_filter="games") posts = get_posts(category_filter=category)
post_bodies = [] if 'username' in flask.session:
for post in posts: user = flask.session['username']
post_bodies.append(post.body) else:
user = 'Anon'
# Get status # Get status
status = get_status() status = get_status()
return flask.render_template('games.html', posts=post_bodies, status=status) # Setup Comment Form
form = comment.CommentForm()
return flask.render_template('index.html', posts=posts, status=status, form=form, user=user, title=category.replace('-', ' '))
# Music Page # Music Page
@app.route('/music/') @app.route('/music/')
@ -139,9 +188,10 @@ def music():
# Get posts # Get posts
posts = get_posts(category_filter="music") posts = get_posts(category_filter="music")
post_bodies = [] if 'username' in flask.session:
for post in posts: user = flask.session['username']
post_bodies.append(post.body) else:
user = 'Anon'
# Get status # Get status
status = get_status() status = get_status()
@ -164,64 +214,30 @@ def music():
top_albums[album_index]['listen_time'] = hours top_albums[album_index]['listen_time'] = hours
return flask.render_template('music.html', posts=post_bodies, status=status, top_albums=top_albums) # Setup Comment Form
form = comment.CommentForm()
# Motion Pictures Page return flask.render_template('music.html', posts=posts, status=status, top_albums=top_albums, form=form, user=user)
@app.route('/motion-pictures/')
def motion_pictures():
# Get posts
posts = get_posts(category_filter="motion-pictures")
post_bodies = []
for post in posts:
post_bodies.append(post.body)
# Get status
status = get_status()
return flask.render_template('motion-pictures.html', posts=post_bodies, status=status)
# Programming Page # Programming Page
@app.route('/programming/') @app.route('/programming/')
def programming(): def programming():
# Get posts # Get posts
posts = get_posts(category_filter="programming") posts_and_comments = get_posts(category_filter="programming")
post_bodies = [] if 'username' in flask.session:
for post in posts: user = flask.session['username']
post_bodies.append(post.body) else:
user = 'Anon'
# Get status # Get status
status = get_status() status = get_status()
return flask.render_template('programming.html', posts=post_bodies, status=status) # Setup Comment Form
form = comment.CommentForm()
@app.route('/writing/')
def writing():
works = []
# Get all works in writing folder
files = glob.glob(WRITING_FOLDER + '*')
for path in files:
date: str = datetime.datetime.fromtimestamp(os.path.getctime(path)).strftime("%B %d, %Y")
name: str = path.split('/')[-1]
works.append({
'date' : date,
'name' : name,
'path' : path
})
return flask.render_template('writing.html', works=works)
return flask.render_template('programming.html', posts=posts_and_comments, form=form, user=user, status=status)
# About Page # About Page
@app.route('/about/') @app.route('/about/')

64
app/comment.py Normal file
View File

@ -0,0 +1,64 @@
import json
import os
import flask
import flask_wtf.csrf
import wtforms
COMMENTS_PATH = "./data/comments.json"
if not os.path.exists('./data/'):
os.mkdir('./data/')
if not os.path.exists(COMMENTS_PATH):
with open(COMMENTS_PATH, 'w+') as file:
file.write('{}')
comments = flask.Blueprint('comment', __name__, template_folder='./templates')
class CommentForm(flask_wtf.FlaskForm):
textbox = wtforms.TextAreaField('Input')
@comments.route('/comment/<string:post_title>', 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())
# 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 []

View File

@ -1,8 +0,0 @@
{
"2" : [
{
"username" : "0x01FE",
"content" : "Hello, this is an example comment!"
}
]
}

View File

@ -1,5 +0,0 @@
{
"users" : {
"0x01FE" : "cGFzc3dvcmQ="
}
}

View File

@ -1,15 +1,19 @@
import markdown import markdown
import datetime import datetime
import comment
class Post: class Post:
category : str category : str
author : str author : str
date : datetime.datetime date : datetime.datetime
date_str : str
body : str body : str
file : str file : str
title : str title : str
url : str url : str
comments : list[dict]
def __init__(self, file_path): def __init__(self, file_path):
self.file = file_path self.file = file_path
@ -19,11 +23,12 @@ class Post:
self.category = lines[1].split(":")[1].strip() self.category = lines[1].split(":")[1].strip()
self.author = lines[2].split(":")[1].strip() self.author = lines[2].split(":")[1].strip()
self.title = lines[6][2:-1] self.title = lines[6].replace('#', '').strip()
self.url = '/post/' + self.title.replace(' ', '-') self.url = '/post/' + self.title
date = lines[3].split(":")[1].strip() date = lines[3].split(":")[1].strip()
self.date = datetime.datetime.strptime(date, "%d-%m-%Y") self.date = datetime.datetime.strptime(date, "%d-%m-%Y")
self.date_str = self.date.strftime("%B %d, %Y")
self.body = markdown.markdown(f'# [{self.title}]({self.url})\n' + ''.join(lines[7:])) self.body = markdown.markdown(f'# [{self.title}]({self.url})\n' + ''.join(lines[7:]), extensions=['footnotes'])
self.comments = comment.get_comments(self.title)

View File

@ -4,6 +4,6 @@ author: author
date: date date: date
# POST # POST
## TITLE # TITLE
### DATE OR SUBTITLE ### DATE OR SUBTITLE
POST TEXT POST TEXT

View File

@ -101,6 +101,7 @@ Buildings that you know // People come and go // Are a certain way
And then you get the news that obliterates your view // Amputate your truths // The signifiance has changed And then you get the news that obliterates your view // Amputate your truths // The signifiance has changed
Hospital inane // Meaningless and grey // But lie within the walls and the signifiance will change Hospital inane // Meaningless and grey // But lie within the walls and the signifiance will change
What would it take for us to change the game? // Maybe our existence is signifiance in vain What would it take for us to change the game? // Maybe our existence is signifiance in vain
Is that what we consider changing? // Ah, save me
# Candles by King Gizzard & The Lizard Wizard # Candles by King Gizzard & The Lizard Wizard
This little man is too hot to handle // Set the funeral on fire with the candles This little man is too hot to handle // Set the funeral on fire with the candles
@ -110,3 +111,11 @@ My little heart, as cold as a morgue // Let the weight of it drop like an iceber
# Presumptuous by King Gizzard & The Lizard Wizard # Presumptuous by King Gizzard & The Lizard Wizard
Eggshell, landmine, stepping stones // Kindred spirits at a crossroads Eggshell, landmine, stepping stones // Kindred spirits at a crossroads
The world we built is on a tilt // Bottled up inside and filled with guilt The world we built is on a tilt // Bottled up inside and filled with guilt
# Exctintion by King Gizzard & The Lizard Wizard
I see sullied, toxic seas, poisoned soil and felled trees / Once paradise, now wasteland / Shadow on the moon expands
Set tries chaos, but too late to to unseal our futures fate
Magenta beckons like a lighthouse / Hypnotised and pulled into a pulsar
Mirage city on the ridge / Beowulf, can you land deadstick?
Pilgrims with burnt offerings / Spacesick for the whole voyage
Metal horses on the flight/ Together, transcend this life

View File

@ -1,19 +1,19 @@
.albums { .albums {
height: 100%; height: 100%;
line-height: 0; line-height: 0;
font-size: 0; font-size: 0;
} }
.albums img { .albums img {
padding: 0 0; padding: 0 0;
margin: 0 0; margin: 0 0;
border: 0 0; border: 0 0;
} }
.body { .body {
height: 100%; height: 100%;
padding: 0 0; padding: 0 0;
margin: 0 0; margin: 0 0;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@ -1,12 +1,12 @@
EOF Mark, EOF Mark,
Glassit-VSC, Glassit-VSC,
JSON formatter, JSON formatter,
Pylance, Pylance,
Remote - SSH, Remote - SSH,
Synthwave '84 Blues, Synthwave '84 Blues,
Trailing Spaces, Trailing Spaces,
Helm Intellisense, Helm Intellisense,
background, background,
Helium Icon Theme, Helium Icon Theme,
SQLite Viewer, SQLite Viewer,
Docker Docker

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

BIN
app/static/render.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
app/static/roland_think.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View File

@ -1,25 +1,20 @@
/* Global Stuff */ /* Global Stuff */
@import url('https://fonts.googleapis.com/css?family=PT%20Mono:700|PT%20Mono:400'); @import url('https://fonts.googleapis.com/css?family=PT%20Mono:700|PT%20Mono:400');
body {
font-family: 'PT Mono';
font-weight: 400;
}
h1, h2, h3, h4, h5 {
font-family: 'PT Mono';
font-weight: 700;
}
html { html {
--text: hsl(224, 83%, 91%); --text: black;
--background: hsl(224, 31%, 23%); --background: white;
--primary: hsl(229, 81%, 73%); /* --background: #2b2d31; */
--primary10: hsla(209, 61%, 71%, 10%); --primary: hsl(135, 100%, 15%);
--primary10: hsla(0, 0%, 53%, 0.1);
--primary20: hsla(209, 61%, 71%, 20%); --primary20: hsla(209, 61%, 71%, 20%);
--primary40: hsla(209, 61%, 71%, 40%); --primary40: hsla(0, 0%, 53%, 0.4);
--secondary: hsl(277, 81%, 33%); --secondary: hsl(277, 81%, 33%);
--secondary50: hsla(277, 81%, 33%, 50%); --secondary10: hsla(277, 81%, 33%, 10%);
--secondary20: hsla(277, 81%, 33%, 20%);
--secondary30: hsla(277, 81%, 33%, 30%);
--secondary40: hsla(277, 81%, 33%, 40%);
--secondary50: hsla(0, 0%, 21%, 0.5);
--accent: hsl(291, 81%, 60%); --accent: hsl(291, 81%, 60%);
--accent75: hsla(291, 81%, 60%, 75%); --accent75: hsla(291, 81%, 60%, 75%);
--accent50: hsla(291, 81%, 60%, 50%); --accent50: hsla(291, 81%, 60%, 50%);
@ -43,21 +38,43 @@ a:hover {
color: var(--accent); color: var(--accent);
} }
blockquote {
background-color: var(--secondary10);
border-style: var(--borders-style);
border-radius: 7.5px;
padding: 0.25em;
}
.post blockquote p {
text-indent: 1.5em;
}
.post blockquote li p {
text-indent: 0;
}
li {
margin-left: 4em;
}
/* Other */ /* Other */
.header { .header {
font-family: 'PT Mono';
background-size: cover;
text-align: center; text-align: center;
margin: 0 30%; margin: 2em 25%;
padding: 2em; padding: 3em;
border-style: var(--borders-style); border-style: var(--borders-style);
border-color: #1E2022; border-color: #1E2022;
background-color: var(--background); background-color: var(--background);
border-radius: var(--border-radius); border-radius: var(--border-radius);
color: white;
} }
.header a { .header a {
@ -70,7 +87,7 @@ a:hover {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin: 0px 5%; margin: 0px 20%;
border-style: var(--borders-style); border-style: var(--borders-style);
border-color: #1E2022; border-color: #1E2022;
@ -99,8 +116,9 @@ a:hover {
/* box-shadow: -10px 10px var(--accent); */ /* box-shadow: -10px 10px var(--accent); */
/* Text Settings */ /* Text Settings */
font-weight: bold; /* font-weight: bold; */
line-height: 30pt; line-height: 30pt;
font-family: Verdana, Geneva, Tahoma, sans-serif;
} }
.sidebar a { .sidebar a {
@ -202,10 +220,55 @@ a:hover {
} }
.post p { .post p {
font-family: Verdana, Geneva, sans-serif;
line-height: 2.25; line-height: 2.25;
text-indent: 3em; text-indent: 3em;
} }
.post-date {
float: right;
}
.comment-container {
background-color: var(--primary40);
border-style: var(--borders-style);
border-radius: var(--border-radius);
padding: 1em;
margin: 1em;
}
.comment {
background-color: var(--primary40);
border-style: var(--borders-style);
border-radius: 5px;
margin: 0.25em;
padding: 0.25em;
}
.comment h4 {
margin: 0.25em;
}
.comment p {
margin: 0.25em;
text-indent: 1em;
}
.comment-editor textarea {
width: 80%;
height: 6em;
padding: 0.75em;
border-style: solid;
border-color: var(--secondary50);
}
.comment-editor textarea:focus {
border: 3px solid var(--accent);
}
/* MUSIC */ /* MUSIC */
.albums { .albums {
height: fit-content; height: fit-content;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

BIN
app/static/yosemite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 MiB

View File

@ -13,13 +13,13 @@
<div class="container"> <div class="container">
<!-- Sidebar --> <!-- Sidebar -->
<div class="sidebar"> <div class="sidebar">
<a href="..">Home</a><br> <a href="/"><u>Home</u></a><br>
<a href="../games/">Games</a><br> <a href="/category/games/">Games</a><br>
<a href="../music/">Music</a><br> <a href="/music/">Music</a><br>
<a href="../motion-pictures/">Motion Picture</a><br> <a href="/category/motion-pictures/">Motion Picture</a><br>
<a href="../programming/">Programming</a><br> <a href="/programming/">Programming</a><br>
<a href="/writing/">Writing</a><br> <a href="/about/">About</a><br>
<a href="."><u>About</u></a> <a href="/login/">Login</a>
</div> </div>
<!-- Main Page --> <!-- Main Page -->

View File

@ -1,20 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<header> <header>
<link rel="shortcut icon" href="index_favicon.ico"> <link rel="shortcut icon" href="index_favicon.ico">
<link rel="stylesheet" href="{{ url_for('static', filename='albumsquare.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='albumsquare.css') }}">
<title>Album Square</title> <title>Album Square</title>
</header> </header>
<body> <body>
<div class="albums"> <div class="albums">
{% for album in top_albums %} {% for album in top_albums %}
<a title="{{ album.album_name}} by {{ album.artist_name }} - {{ album.listen_time }} Hours"> <a title="{{ album.album_name}} by {{ album.artist_name }} - {{ album.listen_time }} Hours">
<img src="{{ album.album_cover }}" style="width: {{ res }}px; height: {{ res }}px;"> <img src="{{ album.album_cover }}" style="width: {{ res }}px; height: {{ res }}px;">
</a> </a>
{% if loop.index % limit == 0 %} {% if loop.index % limit == 0 %}
<br> <br>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<header>
<link rel="shortcut icon" href="index_favicon.ico">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>0x01fe.net - Games</title>
</header>
<body>
<div class="header">
<h1>Games</h1>
{{ status|safe }}
</div>
<div class="container">
<!-- Sidebar -->
<div class="sidebar">
<a href="..">Home</a><br>
<a href="."><u>Games</u></a><br>
<a href="../music/">Music</a><br>
<a href="../motion-pictures/">Motion Picture</a><br>
<a href="../programming/">Programming</a><br>
<a href="/writing/">Writing</a><br>
<a href="../about/">About</a>
</div>
<!-- Main Page -->
<!-- Get it? D-Log? Like digital log? -->
<div class="dlog">
{% for post in posts %}
<div class="post">{{ post|safe }}</div>
{% endfor %}
</div>
</div>
</body>
</html>

View File

@ -3,30 +3,54 @@
<header> <header>
<link rel="shortcut icon" href="index_favicon.ico"> <link rel="shortcut icon" href="index_favicon.ico">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>0x01fe.net</title> <title>0x01fe.net - {{ title }}</title>
</header> </header>
<body> <body>
<div class="header"> <div style="background-image: url({{ header_background_image }});" class="header">
<h1>0x01fe.net</h1> <h1>{{ title }}</h1>
{{ status|safe }} {{ status|safe }}
</div> </div>
<div class="container"> <div class="container">
<!-- Sidebar --> <!-- Sidebar -->
<div class="sidebar"> <div class="sidebar">
<a href="."><u>Home</u></a><br> <a href="/"><u>Home</u></a><br>
<a href="/games/">Games</a><br> <a href="/category/games/">Games</a><br>
<a href="/music/">Music</a><br> <a href="/music/">Music</a><br>
<a href="/motion-pictures/">Motion Picture</a><br> <a href="/category/motion-pictures/">Motion Picture</a><br>
<a href="/programming/">Programming</a><br> <a href="/programming/">Programming</a><br>
<a href="/writing/">Writing</a><br> <a href="/about/">About</a><br>
<a href="/about/">About</a> <a href="/login/">Login</a>
</div> </div>
<!-- Main Page --> <!-- Main Page -->
<!-- Get it? D-Log? Like digital log? --> <!-- Get it? D-Log? Like digital log? -->
<div class="dlog"> <div class="dlog">
{% for post in posts %} {% for post in posts %}
<div class="post">{{ post|safe }}</div> <div class="post">
<div class="post-date">
{{ post.date_str }}
</div>
{{ post.body|safe }}
<div class="comment-container">
<h2>Comments</h2>
{% for comment in post.comments %}
<div class="comment">
<h4>{{ comment.username }}</h4>
<p>{{ comment.content }}</p>
</div>
{% endfor %}
{% if user %}
<div class="comment-editor">
<h4>{{ user }}</h4>
<form method="post" action="/comment/{{ post.title }}">
{{ form.hidden_tag() }}
{{ form.textbox }}
<input type="submit" value="Save">
</form>
</div>
{% endif %}
</div>
</div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<header>
<link rel="shortcut icon" href="index_favicon.ico">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>0x01fe.net - Motion Pictures</title>
</header>
<body>
<div class="header">
<h1>Motion Pictures</h1>
{{ status|safe }}
</div>
<div class="container">
<!-- Sidebar -->
<div class="sidebar">
<a href="..">Home</a><br>
<a href="../games/">Games</a><br>
<a href="../music/">Music</a><br>
<a href="."><u>Motion Picture</u></a><br>
<a href="../programming/">Programming</a><br>
<a href="/writing/">Writing</a><br>
<a href="../about/">About</a>
</div>
<!-- Main Page -->
<!-- Get it? D-Log? Like digital log? -->
<div class="dlog">
{% for post in posts %}
<div class="post">{{ post|safe }}</div>
{% endfor %}
</div>
</div>
</body>
</html>

View File

@ -13,20 +13,44 @@
<div class="container"> <div class="container">
<!-- Sidebar --> <!-- Sidebar -->
<div class="sidebar"> <div class="sidebar">
<a href="..">Home</a><br> <a href="/"><u>Home</u></a><br>
<a href="../games/">Games</a><br> <a href="/category/games/">Games</a><br>
<a href="."><u>Music</u></a><br> <a href="/music/">Music</a><br>
<a href="../motion-pictures/">Motion Picture</a><br> <a href="/category/motion-pictures/">Motion Picture</a><br>
<a href="../programming/">Programming</a><br> <a href="/programming/">Programming</a><br>
<a href="/writing/">Writing</a><br> <a href="/about/">About</a><br>
<a href="../about/">About</a> <a href="/login/">Login</a>
</div> </div>
<!-- Main Page --> <!-- Main Page -->
<!-- Get it? D-Log? Like digital log? --> <!-- Get it? D-Log? Like digital log? -->
<div class="dlog"> <div class="dlog">
{% for post in posts %} {% for post in posts %}
<div class="post">{{ post|safe }}</div> <div class="post">
<div class="post-date">
{{ post.date_str }}
</div>
{{ post.body|safe }}
<div class="comment-container">
<h2>Comments</h2>
{% for comment in post.comments %}
<div class="comment">
<h4>{{ comment.username }}</h4>
<p>{{ comment.content }}</p>
</div>
{% endfor %}
{% if user %}
<div class="comment-editor">
<h4>{{ user }}</h4>
<form method="post" action="/comment/{{ post.title }}">
{{ form.hidden_tag() }}
{{ form.textbox }}
<input type="submit" value="Save">
</form>
</div>
{% endif %}
</div>
</div>
{% endfor %} {% endfor %}
</div> </div>

View File

@ -13,20 +13,44 @@
<div class="container"> <div class="container">
<!-- Sidebar --> <!-- Sidebar -->
<div class="sidebar"> <div class="sidebar">
<a href="..">Home</a><br> <a href="/"><u>Home</u></a><br>
<a href="../games/">Games</a><br> <a href="/category/games/">Games</a><br>
<a href="../music/">Music</a><br> <a href="/music/">Music</a><br>
<a href="../motion-pictures/">Motion Picture</a><br> <a href="/category/motion-pictures/">Motion Picture</a><br>
<a href="."><u>Programming</u></a><br> <a href="/programming/">Programming</a><br>
<a href="/writing/">Writing</a><br> <a href="/about/">About</a><br>
<a href="../about/">About</a> <a href="/login/">Login</a>
</div> </div>
<!-- Main Page --> <!-- Main Page -->
<!-- Get it? D-Log? Like digital log? --> <!-- Get it? D-Log? Like digital log? -->
<div class="dlog"> <div class="dlog">
{% for post in posts %} {% for post in posts %}
<div class="post">{{ post|safe }}</div> <div class="post">
<div class="post-date">
{{ post.date_str }}
</div>
{{ post.body|safe }}
<div class="comment-container">
<h2>Comments</h2>
{% for comment in post.comments %}
<div class="comment">
<h4>{{ comment.username }}</h4>
<p>{{ comment.content }}</p>
</div>
{% endfor %}
{% if user %}
<div class="comment-editor">
<h4>{{ user }}</h4>
<form method="post" action="/comment/{{ post.title }}">
{{ form.hidden_tag() }}
{{ form.textbox }}
<input type="submit" value="Save">
</form>
</div>
{% endif %}
</div>
</div>
{% endfor %} {% endfor %}
</div> </div>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<header>
<link rel="shortcut icon" href="index_favicon.ico">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>0x01fe.net - Login</title>
</header>
<body>
<div class="header">
<h1>0x01fe.net</h1>
{{ status|safe }}
</div>
<div class="container">
<!-- Sidebar -->
<div class="sidebar">
<a href="/"><u>Home</u></a><br>
<a href="/games/">Games</a><br>
<a href="/music/">Music</a><br>
<a href="/motion-pictures/">Motion Picture</a><br>
<a href="/programming/">Programming</a><br>
<a href="/about/">About</a><br>
<a href="/login/">Login</a>
</div>
<div class="dlog">
<h1>Login</h1>
<form method="post" action="/user/login/">
{{ form.hidden_tag() }}
Username: {{ form.username }} <br>
Password: {{ form.password }} <br>
<input type="submit" value="Login">
</form>
<br>
<h1>Need to Register?</h1>
<a href="/user/register/">Register</a>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<header>
<link rel="shortcut icon" href="index_favicon.ico">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>0x01fe.net - Register</title>
</header>
<body>
<div class="header">
<h1>0x01fe.net</h1>
{{ status|safe }}
</div>
<div class="container">
<!-- Sidebar -->
<div class="sidebar">
<a href="/"><u>Home</u></a><br>
<a href="/games/">Games</a><br>
<a href="/music/">Music</a><br>
<a href="/motion-pictures/">Motion Picture</a><br>
<a href="/programming/">Programming</a><br>
<a href="/about/">About</a><br>
<a href="/login/">Login</a>
</div>
<div class="dlog">
<h1>Register</h1>
<form method="post" action="/user/add/">
{{ form.hidden_tag() }}
Username: {{ form.username }} <br>
Password: {{ form.password }} <br>
<input type="submit" value="Register">
</form>
</div>
</div>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<body>
<a href="/">home</a>
<p>
This is just a little page where I post stuff I write. I was kind of unsure if I wanted this stuff out here but I think to those who know me it could serve as an interesting view into some thoughts that I don't often express elsewhere.
</p>
<br>
<br>
{% for work in works %}
<a href="/{{ work.path }}">{{ work.name }}</a> {{ work.date }}
{% endfor %}
</body>
</html>

102
app/user.py Normal file
View File

@ -0,0 +1,102 @@
import base64
import json
import os
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),
wtforms.validators.DataRequired()
])
password = wtforms.PasswordField("Password", [
wtforms.validators.Length(min=8, max=64),
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():
# 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:
return 'ERROR PROCESSING REQUEST - That user already exists'
# 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))
return flask.redirect('/')
@user.route('/user/register/')
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:
return 'ERROR PROCESSING REQUEST - Bad username OR password'
# Does password match?
if user_data[username] != password:
return 'ERROR PROCESSING REQUEST - Bad username OR password'
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('/')
# Check User file exists
if not os.path.exists(USERS_PATH):
with open(USERS_PATH, 'w+') as file:
file.write('{}')

View File

@ -2,4 +2,6 @@ Markdown==3.5.2
Flask==2.2.3 Flask==2.2.3
waitress==2.1.2 waitress==2.1.2
Werkzeug==2.2.3 Werkzeug==2.2.3
Flask-Session==0.5.0
Flask-WTF==1.1.1
requests==2.31.0 requests==2.31.0