Files
flask_blog/app.py
2026-03-14 15:44:32 +01:00

345 lines
7.8 KiB
Python

from flask import Flask, render_template, request, redirect, url_for
# 🌟 IMPORT THE content from separate files.
from content.posts import BLOG_POSTS
from content.about_text import ABOUT, TITLE
app = Flask(__name__)
import csv
import datetime
import os, math
COMMENT_FILE = 'content/comments.csv'
MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024 # 5 Megabytes
POSTS_PER_PAGE = 5 # posts per page limit here
def get_comments_for_post(post_id):
comments = []
if os.path.exists(COMMENT_FILE) and os.path.getsize(COMMENT_FILE) > MAX_FILE_SIZE_BYTES:
return ["Comment section full.", 507]
if not os.path.exists(COMMENT_FILE):
return comments
with open(COMMENT_FILE, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
if row['post_id'] == str(post_id):
comments.append(row)
return comments
@app.route('/post/<int:post_id>/comment', methods=['POST'])
def post_comment(post_id):
# 1. Security: Check Honeypot
if request.form.get('honeypot'):
return redirect(url_for('post_detail', post_id=post_id))
# 2. Get Data
author = request.form.get('author', 'Anonymous').strip()
content = request.form.get('content', '').strip()
# 3. Validation (Keep it lightweight)
if not content or len(content) > 1000: # Max 1000 chars to save disk/RAM
return redirect(url_for('post_detail', post_id=post_id))
# 4. Save to CSV
file_exists = os.path.isfile(COMMENT_FILE)
# check max size
if file_exists and os.path.getsize(COMMENT_FILE) > MAX_FILE_SIZE_BYTES:
return
with open(COMMENT_FILE, 'a', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['post_id', 'author', 'content', 'date'])
if not file_exists:
writer.writeheader()
writer.writerow({
'post_id': post_id,
'author': author[:50], # Truncate long names
'content': content,
'date': datetime.datetime.now().strftime("%B %d, %Y at %I:%M %p")
})
return redirect(url_for('post_detail', post_id=post_id))
# --- Routes ---
@app.route('/')
def home():
page = request.args.get('page', 1, type=int)
blog_title = TITLE
# 2. Sort posts by ID newest first
all_posts = sorted(BLOG_POSTS, key=lambda x: x['id'], reverse=True)
# Calculate totals
total_posts = len(all_posts)
total_pages = math.ceil(total_posts / POSTS_PER_PAGE)
# 3. Calculate start and end indices
start = (page - 1) * POSTS_PER_PAGE
end = start + POSTS_PER_PAGE
# 4. Slice the list for the current page
posts_to_show = all_posts[start:end]
# 5. Determine if there is a next or previous page
prev_url = url_for('home', page=page - 1) if page > 1 else None
next_url = url_for('home', page=page + 1) if end < len(all_posts) else None
return render_template('index.html',
posts=posts_to_show,
prev_url=prev_url,
next_url=next_url,
current_page=page,
blog_title=blog_title,
total_pages=total_pages)
@app.route('/about')
def about():
about_txt = ABOUT
"""Renders the About page."""
return render_template('about.html', about_txt = about_txt, blog_title = TITLE)
@app.route('/post/<int:post_id>')
def post_detail(post_id):
"""Renders a single post detail page."""
# 1. Look up the post dictionary
post = next((p for p in BLOG_POSTS if p['id'] == post_id), None)
if post is None:
return "Post not found", 404
#get comments
comments = get_comments_for_post(post_id)
if post["id"] == 10:
post["template"] = "components/timeline_post.html"
post["timeline"] = """
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item1', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-13
</time>
<div id="item1" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
moved some web-based tools to localhost
</h3>
<p class="text-gray-500 mt-1">
a testing page for blog preview and some string formatter...<br>It's also my first time to correctly using git, to upload edited pages directly via rsync and synchronization via git.
</p>
</div>
</div>
</li>
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item2', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-13
</time>
<div id="item2" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
Editing
</h3>
<p class="text-gray-500 mt-1">
added a timeline component,<br>but I have to generate a timeline html and paste it as text,<br>then paste into content generation, and the upload to blog.
</p>
</div>
</div>
</li>
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item3', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-12
</time>
<div id="item3" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
Packing blog in Docker
</h3>
<p class="text-gray-500 mt-1">
uwsgi + flask -> caddy, gunicorn, flask, docker
</p>
</div>
</div>
</li>
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item4', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-12
</time>
<div id="item4" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
moved all wiki pages into one repository
</h3>
<p class="text-gray-500 mt-1">
have been using gitea's wiki page to take notes, which is a life-saver<br>I made a script to pull all wiki pages into one repository, now I have a way to save all my notes.
</p>
</div>
</div>
</li>
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item5', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-11
</time>
<div id="item5" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
/root partition is full
</h3>
<p class="text-gray-500 mt-1">
pacman -Sc doesnt work anymore<br>I am confused very confused<br>started to move gitea from /etc to /home<br>accidentally deleted all gitea data...then recovered with a previous gitea dump
</p>
</div>
</div>
</li>
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item6', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-11
</time>
<div id="item6" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
have another gitea instance, docker
</h3>
<p class="text-gray-500 mt-1">
all repositories have a mirror now
</p>
</div>
</div>
</li>
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item7', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-11
</time>
<div id="item7" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
Tailscale ip doesnt work
</h3>
<p class="text-gray-500 mt-1">
system-wide caddy worked for https://ip <br>but docked caddy didnt work for https://ip<br>I have no clue...
</p>
</div>
</div>
</li>
<li class="mb-10 ml-6">
<button onclick="toggleTimeline('item8', this)"
class="timeline-button"></button>
<div>
<time class="text-sm text-gray-400">
2026-03-11
</time>
<div id="item8" class="mt-2">
<h3 class="text-lg font-semibold text-gray-900">
language tool
</h3>
<p class="text-gray-500 mt-1">
trying to use language tool, which helps with spelling but it doesnt work well for grammar checking, also the current setting should be fixed, I am not sure if I am running locally or querying via API.
</p>
</div>
</div>
</li>
"""
# 2. Pass the post dictionary (with the raw HTML content) directly to the template
return render_template('post_detail.html', post=post, comments=comments, blog_title = TITLE)
if __name__ == '__main__':
app.run(debug=True)