{{ post.text | e }}
{% if 'date' in post and post.date %}No posts available
import flask
import shutil
import json
import os
import datetime
import random
import string
import uuid
from werkzeug.utils import secure_filename
from markupsafe import escape
import xml.etree.ElementTree as ET
from werkzeug.security import generate_password_hash, check_password_hash
from flask_wtf.csrf import CSRFProtect, generate_csrf
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import magic
app = flask.Flask(__name__)
app.config['DATA_FILE'] = 'data.json'
app.config['BACKUP_DIR'] = 'backups'
app.config['STATIC_FOLDER'] = 'static'
app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.config['CODE_FILE'] = 'code.txt'
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}
app.config['SITE_URL'] = 'https://posts.halhadus.rocks'
app.config['MAX_CONTENT_LENGTH'] = 32 * 1024 * 1024
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'set-your-stupid-secret-key')
app.config['WTF_CSRF_ENABLED'] = True
os.makedirs(app.config['STATIC_FOLDER'], exist_ok=True)
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['BACKUP_DIR'], exist_ok=True)
csrf = CSRFProtect(app)
limiter = Limiter(app=app, key_func=get_remote_address)
def load_data():
try:
with open(app.config['DATA_FILE'], 'r', encoding='utf-8') as f:
data = json.load(f)
sorted_items = sorted(
data.items(),
key=lambda x: x[1].get('date', 0),
reverse=True
)
return sorted_items
except (FileNotFoundError, json.JSONDecodeError):
return []
def format_timestamp(ts):
if not ts:
return ""
try:
dt = datetime.datetime.fromtimestamp(ts, datetime.timezone.utc)
return dt.strftime("%Y-%m-%d %H:%M UTC")
except:
return ""
def format_timestamp_rss(ts):
if not ts:
return ""
try:
datetime.datetime.utcfromtimestamp(ts)
return dt.strftime("%a, %d %b %Y %H:%M:%S GMT")
except:
return ""
def get_upload_code():
code_file = app.config['CODE_FILE']
if os.path.exists(code_file):
with open(code_file, 'r') as f:
return f.read().strip()
else:
code = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
hashed_code = generate_password_hash(code)
with open(code_file, 'w') as f:
f.write(hashed_code)
print(f"Generated upload code (SAVE THIS, WON'T BE SHOWN AGAIN): {code}")
return hashed_code
def verify_upload_code(entered_code):
stored_hash = get_upload_code()
return check_password_hash(stored_hash, entered_code)
def create_backup():
source = app.config['DATA_FILE']
if not os.path.exists(source):
return None
backup_dir = app.config['BACKUP_DIR']
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = os.path.join(backup_dir, f"data_backup_{timestamp}.json")
shutil.copy2(source, backup_file)
return backup_file
def cleanup_old_backups(max_backups=5):
backups = sorted(os.listdir(app.config['BACKUP_DIR']))
if len(backups) > max_backups:
for old_backup in backups[:-max_backups]:
os.remove(os.path.join(app.config['BACKUP_DIR'], old_backup))
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
def validate_file(file_stream):
try:
header = file_stream.read(2048)
file_stream.seek(0)
mime_type = magic.from_buffer(header, mime=True)
return mime_type in ['image/png', 'image/jpeg', 'image/gif']
except Exception as e:
print(f"File validation error: {str(e)}")
return False
def generate_sitemap():
base_url = app.config['SITE_URL']
posts = load_data()
urlset = ET.Element('urlset', xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
url = ET.SubElement(urlset, 'url')
ET.SubElement(url, 'loc').text = base_url
ET.SubElement(url, 'lastmod').text = datetime.datetime.utcnow().strftime("%Y-%m-%d")
ET.SubElement(url, 'changefreq').text = 'daily'
ET.SubElement(url, 'priority').text = '1.0'
for post_id, post in posts:
url = ET.SubElement(urlset, 'url')
ET.SubElement(url, 'loc').text = f"{base_url}/#{post_id}"
if 'date' in post:
dt = datetime.datetime.utcfromtimestamp(post['date'])
ET.SubElement(url, 'lastmod').text = dt.strftime("%Y-%m-%d")
ET.SubElement(url, 'changefreq').text = 'monthly'
ET.SubElement(url, 'priority').text = '0.8'
return '' + ET.tostring(urlset, encoding='unicode')
def generate_rss():
base_url = app.config['SITE_URL']
posts = load_data()
rss = ET.Element('rss', version="2.0")
channel = ET.SubElement(rss, 'channel')
ET.SubElement(channel, 'title').text = "Halhadus' Posts"
ET.SubElement(channel, 'link').text = base_url
ET.SubElement(channel, 'description').text = "Personal posts and updates from Halhadus"
ET.SubElement(channel, 'language').text = "en-us"
ET.SubElement(channel, 'lastBuildDate').text = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")
ET.SubElement(channel, 'generator').text = "Halhadus Posts RSS Generator"
for post_id, post in posts:
item = ET.SubElement(channel, 'item')
title = post['text'].strip().split('\n')[0][:50] + ('...' if len(post['text']) > 50 else '')
ET.SubElement(item, 'title').text = title
post_url = f"{base_url}/#{post_id}"
ET.SubElement(item, 'link').text = post_url
ET.SubElement(item, 'guid').text = post_url
description_content = f"{post['text']}"
if 'image' in post and post['image']:
img_url = flask.url_for('static', filename=post['image'], _external=True)
description_content += f''
description = ET.SubElement(item, 'description')
description.text = description_content
if 'date' in post and post['date']:
pub_date = format_timestamp_rss(post['date'])
ET.SubElement(item, 'pubDate').text = pub_date
return '' + ET.tostring(rss, encoding='unicode')
@app.route('/')
def index():
data = load_data()
return flask.render_template_string('''
{{ post.text | e }}
{% if 'date' in post and post.date %}No posts available