from flask import Flask, render_template, request, redirect, url_for, flash, session from flask_sqlalchemy import SQLAlchemy from datetime import date import gnupg import secrets app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://love:love@localhost:3309/lovedb' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SECRET_KEY'] = 'random' gpg = gnupg.GPG() db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(128), unique=True, nullable=False) pgp = db.Column(db.String(8128), nullable=False) firstname = db.Column(db.String(128), nullable=False) lastname = db.Column(db.String(128), nullable=False) sex = db.Column(db.Enum('male', 'female'), nullable=False) date_of_birth = db.Column(db.Date, nullable=False) profile_picture = db.Column(db.String(200), nullable=False) pictures = db.Column(db.JSON, nullable=True) country = db.Column(db.String(128), nullable=False) city = db.Column(db.String(128), nullable=True) height = db.Column(db.Float, nullable=True) weight = db.Column(db.Integer, nullable=True) race = db.Column(db.String(20), nullable=True) prefered_age_range = db.Column(db.String(20), nullable=True) likes = db.Column(db.JSON, nullable=True) dislikes = db.Column(db.JSON, nullable=True) xmpp = db.Column(db.String(128), unique=True, nullable=False) email = db.Column(db.String(128), unique=True, nullable=True) phone = db.Column(db.String(20), unique=True, nullable=True) is_verified = db.Column(db.Boolean, default=False) @app.route("/") def home(): return render_template("index.html") @app.route("/register", methods=["GET", "POST"]) def register(): if request.method == "POST": username = request.form.get("username") pgp = request.form.get("pgp") firstname = request.form.get("firstname") lastname = request.form.get("lastname") sex = request.form.get("sex") date_of_birth = request.form.get("date_of_birth") profile_picture = request.form.get("profile_picture") country = request.form.get("country") xmpp = request.form.get("xmpp") email = request.form.get("email") phone = request.form.get("phone") city = request.form.get("city") height = request.form.get("height") weight = request.form.get("weight") race = request.form.get("race") prefered_age_range = request.form.get("prefered_age_range") if not all([username, pgp, firstname, lastname, sex, date_of_birth, profile_picture, country, xmpp]): flash("Please fill all required fields.") return redirect(url_for("register")) if User.query.filter_by(username=username).first(): flash("Username already exists.") return redirect(url_for("register")) if User.query.filter_by(xmpp=xmpp).first(): flash("XMPP already exists.") return redirect(url_for("register")) if email and User.query.filter_by(email=email).first(): flash("Email already exists.") return redirect(url_for("register")) if phone and User.query.filter_by(phone=phone).first(): flash("Phone already exists.") return redirect(url_for("register")) try: dob = date.fromisoformat(date_of_birth) except ValueError: flash("Invalid date format.") return redirect(url_for("register")) today = date.today() age = today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day)) if age < 18: flash("You must be at least 18 years old to register.") return redirect(url_for("register")) session["pending_user"] = { "username": username, "pgp": pgp, "firstname": firstname, "lastname": lastname, "sex": sex, "date_of_birth": date_of_birth, "profile_picture": profile_picture, "country": country, "xmpp": xmpp, "email": email, "phone": phone, "city": city, "height": height, "weight": weight, "race": race, "prefered_age_range": prefered_age_range } import_result = gpg.import_keys(pgp) if not import_result.fingerprints: flash("Invalid PGP key. Make sure you pasted the full ASCII-armored public key.") return redirect(url_for("register")) fingerprint = import_result.fingerprints[0] random_string = secrets.token_hex(16) challenge_phrase = f"this is the unencrypted string: {random_string}" encrypted_data = gpg.encrypt( challenge_phrase, recipients=[fingerprint] ) if not encrypted_data.ok: flash("Failed to encrypt challenge. Check your PGP key.") return redirect(url_for("register")) session["pgp_expected_phrase"] = challenge_phrase return render_template( "verify.html", encrypted_message=str(encrypted_data) ) return render_template("register.html") @app.route("/verify", methods=["POST"]) def verify(): expected_phrase = session.get("pgp_expected_phrase") data = session.get("pending_user") if not data or not expected_phrase: flash("Session expired.") return redirect(url_for("register")) submitted = request.form.get("decrypted_message") if not submitted: flash("You must paste the decrypted message.") return redirect(url_for("register")) if submitted.strip() == expected_phrase: dob = date.fromisoformat(data["date_of_birth"]) new_user = User( username=data["username"], pgp=data["pgp"], firstname=data["firstname"], lastname=data["lastname"], sex=data["sex"], date_of_birth=dob, profile_picture=data["profile_picture"], country=data["country"], xmpp=data["xmpp"], email=data["email"] or None, phone=data["phone"] or None, city=data["city"] or None, height=float(data["height"]) if data["height"] else None, weight=int(data["weight"]) if data["weight"] else None, race=data["race"] or None, prefered_age_range=data["prefered_age_range"] or None, is_verified=False ) db.session.add(new_user) db.session.commit() session['user_id'] = new_user.id session['username'] = new_user.username session.pop("pending_user", None) session.pop("pgp_expected_phrase", None) flash("PGP verification successful!") return redirect(url_for("home")) else: flash("Verification failed. Account not created.") return redirect(url_for("register")) @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": username = request.form.get("username") pgp = request.form.get("pgp") if not username or not pgp: flash("Please enter both username and PGP key.") return redirect(url_for("login")) user = User.query.filter_by(username=username).first() if not user: flash("User not found.") return redirect(url_for("login")) import_result = gpg.import_keys(pgp) if not import_result.fingerprints: flash("Invalid PGP key.") return redirect(url_for("login")) fingerprint = import_result.fingerprints[0] random_string = secrets.token_hex(16) challenge_phrase = f"this is the unencrypted string: {random_string}" encrypted_data = gpg.encrypt( challenge_phrase, recipients=[fingerprint] ) if not encrypted_data.ok: flash("Failed to encrypt challenge.") return redirect(url_for("login")) session["login_user_id"] = user.id session["login_expected_phrase"] = challenge_phrase return render_template( "login_verify.html", encrypted_message=str(encrypted_data) ) return render_template("login.html") @app.route("/login_verify", methods=["POST"]) def login_verify(): user_id = session.get("login_user_id") expected_phrase = session.get("login_expected_phrase") if not user_id or not expected_phrase: flash("Login session expired") return redirect(url_for("login")) submitted = request.form.get("decrypted_message") if not submitted: flash("You must paste the decrypted message") return redirect(url_for("login")) if submitted.strip() == expected_phrase: user = User.query.get(user_id) session['user_id'] = user.id session['username'] = user.username session.pop("login_user_id", None) session.pop("login_expected_phrase", None) flash("Logged in successfully") return redirect(url_for("home")) else: flash("Verification failed") return redirect(url_for("login")) @app.route("/logout") def logout(): session.pop('user_id', None) session.pop('username', None) flash("Logged out successfully") return redirect(url_for("home")) if __name__ == "__main__": with app.app_context(): db.create_all() app.run(debug=True)