diff options
| author | Pinapelz <yukais@pinapelz.com> | 2026-05-28 17:27:49 -0700 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2026-05-28 17:27:49 -0700 |
| commit | f64d5d678bc69fa429e99eab87abca787f10a9ca (patch) | |
| tree | d3d949f3e930d8e33b8c0b510a1fa2e146474433 /server.py | |
init commitmain
Diffstat (limited to 'server.py')
| -rw-r--r-- | server.py | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/server.py b/server.py new file mode 100644 index 0000000..1d277ab --- /dev/null +++ b/server.py @@ -0,0 +1,94 @@ +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))) |
