from flask import Flask, request, jsonify, render_template from flask_cors import CORS import sqlite3 import uuid import time import base64 import os from dotenv import load_dotenv from captcha import generate_captcha_image load_dotenv() app = Flask(__name__) CORS(app) DB_FILE = os.getenv("DB_FILE", "captchas.db") CAPTCHA_DIR = os.getenv("CAPTCHA_DIR", "data/captcha_data") FONT_PATH = os.getenv("FONT_PATH", "captcha.ttf") EXPIRY_SECONDS = int(os.getenv("CAPTCHA_EXPIRY_SECONDS", "90")) def init_db(): with sqlite3.connect(DB_FILE) as conn: conn.execute(''' CREATE TABLE IF NOT EXISTS captchas ( token TEXT PRIMARY KEY, answers TEXT, expires_at REAL ) ''') init_db() @app.route('/') def index(): return render_template("index.html") @app.route('/generate', methods=['GET', 'POST']) def generate(): try: positions, blob = generate_captcha_image( captcha_dir=CAPTCHA_DIR, font_path=FONT_PATH ) except Exception as e: return jsonify({"error": str(e)}), 500 token = str(uuid.uuid4()) answers_str = ",".join(map(str, sorted(positions))) expires_at = time.time() + EXPIRY_SECONDS with sqlite3.connect(DB_FILE) as conn: conn.execute('INSERT INTO captchas (token, answers, expires_at) VALUES (?, ?, ?)', (token, answers_str, expires_at)) b64_img = base64.b64encode(blob).decode('utf-8') return jsonify({ "token": token, "image": f"data:image/jpeg;base64,{b64_img}", "expires_in": EXPIRY_SECONDS }) @app.route('/verify', methods=['POST']) def verify(): data = request.json if not data or 'token' not in data or 'answers' not in data: return jsonify({"error": "Missing token or answers"}), 400 token = data['token'] user_answers = data['answers'] if not isinstance(user_answers, list): return jsonify({"error": "answers must be a list of integers"}), 400 with sqlite3.connect(DB_FILE) as conn: cursor = conn.cursor() conn.execute('pragma journal_mode=wal') cursor.execute('SELECT answers, expires_at FROM captchas WHERE token = ?', (token,)) row = cursor.fetchone() if not row: return jsonify({"error": "Invalid token"}), 404 answers_str, expires_at = row cursor.execute('DELETE FROM captchas WHERE token = ?', (token,)) if time.time() > expires_at: return jsonify({"error": "Captcha expired"}), 400 correct_answers = [int(x) for x in answers_str.split(',')] user_answers_sorted = sorted([int(x) for x in user_answers]) if correct_answers == user_answers_sorted: return jsonify({"success": True}) else: return jsonify({"success": False, "error": "Incorrect answers"}) if __name__ == '__main__': app.run(debug=True, port=int(os.getenv("PORT", 5000)))