Personal site — Quart + nginx + Redis
[← Back to Portfolio]My personal site, styled after the terminal interfaces from SIGNALIS. Everything on it was designed and built entirely by me with no AI assistance. It runs behind nginx on a VPS, with the application layer written in Python using Quart — an async port of Flask. Redis handles caching and session state.
The site has a handful of distinct systems, each living in its own Quart blueprint: an interactive terminal emulator, a live Last.fm music tracker, a daily fractal generator, a guestbook with rate limiting, a writing archive, a collaborative pixel canvas called Paper Lily Place, and a budget tracker I built for myself.
from fractals import fractal_bp from music import music_bp from musings import musings_bp from paperlilyplace import place_bp from budget_tracker import budget_bp app.register_blueprint(fractal_bp) app.register_blueprint(music_bp) app.register_blueprint(musings_bp) app.register_blueprint(place_bp) app.register_blueprint(budget_bp)
CACHE_KEY = "lastfm:recent_tracks" CACHE_TTL = 25 # slightly less than the 30s client poll interval async def now_playing(): cached = redis_client.get(CACHE_KEY) if cached: return jsonify(json.loads(cached)) data = await fetch_lastfm('user.getrecenttracks', limit=5) result = build_result(data) redis_client.setex(CACHE_KEY, CACHE_TTL, json.dumps(result)) return jsonify(result)
def set_pixel(x, y, colour_idx): redis_client.setrange("place:canvas", y * WIDTH + x, bytes([colour_idx])) @place_bp.route('/place/api/events') async def sse(): q = asyncio.Queue() _subscribers.append(q) async def generate(): try: while True: event = await q.get() yield f"data: {json.dumps(event)}\n\n" finally: _subscribers.remove(q) return app.response_class(generate(), mimetype='text/event-stream')
if redis_client.get(f"guestbook:ratelimit:{ip}"): return jsonify({'error': 'One entry per hour.'}), 429 # store hashed IP — not reversible ip_hash = hashlib.sha256(ip.encode()).hexdigest() conn.execute( 'INSERT INTO guestbook (name, message, ip_hash) VALUES (?,?,?)', (name, message, ip_hash) ) redis_client.setex(f"guestbook:ratelimit:{ip}", 3600, 1)