Spaces:
Runtime error
Runtime error
| from flask import Blueprint, render_template, request, redirect, url_for, flash, session, abort, jsonify | |
| # from .models import User | |
| # from . import db | |
| from .email_utils import send_verification_email | |
| from .code_generator import generate_ranker_code | |
| from .utils import rank_resumes_by_similarity | |
| from werkzeug.utils import secure_filename | |
| from .ranker import rank_resumes_by_semantic_similarity | |
| import traceback | |
| from werkzeug.security import generate_password_hash, check_password_hash | |
| from .supabase_client import get_user_by_email, create_user | |
| import uuid | |
| from .supabase_config import SUPABASE_URL, SUPABASE_API_KEY | |
| import secrets | |
| import os | |
| import requests | |
| api = Blueprint('api', __name__) | |
| SUPABASE_URL = os.getenv("SUPABASE_URL") | |
| SUPABASE_API_KEY = os.getenv("SUPABASE_API_KEY") | |
| HEADERS = { | |
| "apikey": SUPABASE_API_KEY, | |
| "Authorization": f"Bearer {SUPABASE_API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| MOCK_USER = { | |
| 'username': 'Alice', | |
| 'api_key': 'rk_DEMO_KEY_1a2b3c4d5e6f7g8h', | |
| } | |
| MOCK_ANALYTICS = { | |
| 'average_score': '85%', | |
| 'top_resume': 'Software Engineer - Jane Doe.pdf', | |
| 'last_upload_date': '2025-07-07', | |
| } | |
| MOCK_ALL_UPLOADS = [ # More data for the full rankings page | |
| {'id': 1, 'name': 'John_Doe_CV.pdf', 'score': '92%', 'uploaded_date': '2025-07-06', 'job_description_summary': 'Software Engineer at Google...'}, | |
| {'id': 2, 'name': 'Software_Dev_Resume.docx', 'score': '88%', 'uploaded_date': '2025-07-05', 'job_description_summary': 'Backend Developer for Fintech...'}, | |
| {'id': 3, 'name': 'Project_Manager_Exp.pdf', 'score': '75%', 'uploaded_date': '2025-07-04', 'job_description_summary': 'Lead project teams for Agile...'}, | |
| {'id': 4, 'name': 'Marketing_Specialist.pdf', 'score': '65%', 'uploaded_date': '2025-07-03', 'job_description_summary': 'Digital Marketing Expert...'}, | |
| {'id': 5, 'name': 'Data_Analyst_Resume.docx', 'score': '80%', 'uploaded_date': '2025-07-02', 'job_description_summary': 'Analyze large datasets...'}, | |
| ] | |
| auth = Blueprint('auth', __name__) | |
| def landing(): | |
| if 'user_id' in session: | |
| return redirect(url_for('auth.dashboard')) | |
| return render_template('landing.html') | |
| # @api.route('/rank-resumes', methods=['GET', 'POST']) | |
| # def rank_resumes(): | |
| # if request.method == 'GET': | |
| # return render_template("upload_rank_form.html") # Optional: A form to test this route manually | |
| # # 1. Extract and validate API key | |
| # auth_header = request.headers.get('Authorization') | |
| # if not auth_header or not auth_header.startswith("Bearer "): | |
| # return render_template("error.html", error="Missing or invalid Authorization header"), 401 | |
| # api_key = auth_header.split(" ")[1] | |
| # # 2. Query Supabase to validate API key | |
| # response = requests.get( | |
| # f"{SUPABASE_URL}/rest/v1/users?api_key=eq.{api_key}", | |
| # headers=HEADERS | |
| # ) | |
| # if response.status_code != 200 or not response.json(): | |
| # return render_template("error.html", error="Invalid API key"), 403 | |
| # # 3. Parse form data | |
| # job_description = request.form.get('job_description') | |
| # resume_files = request.files.getlist('resumes') | |
| # if not job_description: | |
| # return render_template("error.html", error="Missing job description"), 400 | |
| # if not resume_files: | |
| # return render_template("error.html", error="No resumes uploaded"), 400 | |
| # # 4. Rank resumes | |
| # try: | |
| # results = rank_resumes_by_semantic_similarity(resume_files, job_description) | |
| # return render_template( | |
| # "resume_rankings.html", | |
| # job_description=job_description, | |
| # ranked_resumes=results | |
| # ) | |
| # except Exception as e: | |
| # return render_template("error.html", error=f"Error processing resumes: {str(e)}"), 500 | |
| def rank_resumes(): | |
| api_key = request.form.get('api_key') | |
| job_description = request.form.get('job_description') | |
| resume_files = request.files.getlist('resumes') | |
| if not api_key or not job_description or not resume_files: | |
| flash("All fields are required.") | |
| return redirect(url_for('auth.rank_resumes')) | |
| # Validate API key with Supabase | |
| response = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?api_key=eq.{api_key}", | |
| headers=HEADERS | |
| ) | |
| if response.status_code != 200 or not response.json(): | |
| flash("Invalid API key.") | |
| return redirect(url_for('auth.rank_resumes')) | |
| try: | |
| rankings = rank_resumes_by_semantic_similarity(resume_files, job_description) | |
| except Exception as e: | |
| flash(f"Ranking error: {str(e)}") | |
| return redirect(url_for('auth.rank_resumes')) | |
| return render_template('resume_rankings.html', rankings=rankings, job_description=job_description) | |
| def register(): | |
| if request.method == 'POST': | |
| username = request.form.get('username') | |
| email = request.form.get('email') | |
| password = request.form.get('password') | |
| if not username or not email or not password: | |
| flash('Please fill all fields.', 'error') | |
| return render_template("register.html") | |
| # Check if email already exists | |
| check_response = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?email=eq.{email}", | |
| headers=HEADERS | |
| ) | |
| if check_response.status_code == 200 and check_response.json(): | |
| flash('Email already registered. Please log in.', 'warning') | |
| return redirect(url_for('auth.login')) | |
| hashed_password = generate_password_hash(password) | |
| token = str(uuid.uuid4()) | |
| payload = { | |
| "username": username, | |
| "email": email, | |
| "password": hashed_password, | |
| "email_token": token, | |
| "is_verified": False | |
| } | |
| response = requests.post(f"{SUPABASE_URL}/rest/v1/users", json=payload, headers=HEADERS) | |
| if response.status_code == 201: | |
| try: | |
| send_verification_email(email, username, token) | |
| flash('Account created. Check your email to verify.', 'success') | |
| except Exception as e: | |
| print("Email sending failed:", e) | |
| flash('Registered, but email failed to send.', 'warning') | |
| return redirect(url_for('auth.login')) | |
| else: | |
| print("Supabase error:", response.text) | |
| flash('Failed to register. Try again.', 'error') | |
| return render_template("register.html") | |
| def login(): | |
| if 'user_id' in session: | |
| return redirect(url_for('auth.dashboard')) | |
| if request.method == 'POST': | |
| email = request.form['email'] | |
| password = request.form['password'] | |
| # Query Supabase for user by email | |
| headers = { | |
| "apikey": SUPABASE_API_KEY, | |
| "Authorization": f"Bearer {SUPABASE_API_KEY}" | |
| } | |
| response = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?email=eq.{email}", | |
| headers=headers | |
| ) | |
| if response.status_code != 200 or not response.json(): | |
| flash('Invalid email or password.') | |
| return redirect(url_for('auth.login')) | |
| user_data = response.json()[0] | |
| # Check password | |
| if not check_password_hash(user_data['password'], password): | |
| flash('Invalid email or password.') | |
| return redirect(url_for('auth.login')) | |
| # Check if user is verified | |
| if not user_data.get('is_verified', False): | |
| flash('Please verify your email before logging in.') | |
| return redirect(url_for('auth.login')) | |
| # Login successful | |
| session['user_id'] = user_data['id'] | |
| session['username'] = user_data['username'] | |
| flash('Logged in successfully.') | |
| return redirect(url_for('auth.dashboard')) | |
| return render_template('login.html') | |
| def resend_verification(): | |
| if request.method == 'POST': | |
| try: | |
| email = request.form['email'] | |
| # Fetch user by email | |
| headers = { | |
| "apikey": SUPABASE_API_KEY, | |
| "Authorization": f"Bearer {SUPABASE_API_KEY}" | |
| } | |
| res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?email=eq.{email}&select=*", | |
| headers=headers | |
| ) | |
| if res.status_code != 200 or not res.json(): | |
| flash('No account found with that email.') | |
| return redirect(url_for('auth.login')) | |
| user = res.json()[0] | |
| if user.get("is_verified") == True: | |
| flash('Your email is already verified. You can log in.') | |
| return redirect(url_for('auth.login')) | |
| # Generate new token | |
| new_token = str(uuid.uuid4()) | |
| # Update `email_token` in Supabase | |
| patch_res = requests.patch( | |
| f"{SUPABASE_URL}/rest/v1/users?email=eq.{email}", | |
| headers={**headers, "Content-Type": "application/json"}, | |
| json={"email_token": new_token} | |
| ) | |
| if patch_res.status_code != 204: | |
| print("Failed to update token:", patch_res.text) | |
| return jsonify({"error": "Failed to update verification token."}), 500 | |
| # Send the verification email | |
| result = send_verification_email(user['email'], user['username'], new_token) | |
| if result is True: | |
| flash('Verification email resent. Please check your inbox.') | |
| else: | |
| print("Email error from resend:", result) | |
| return jsonify(result), 500 | |
| return redirect(url_for('auth.login')) | |
| except Exception as e: | |
| print("Resend error:", e) | |
| traceback.print_exc() | |
| return jsonify({"error": str(e)}), 500 | |
| return render_template('resend_verification.html') | |
| def dashboard(): | |
| if 'user_id' not in session: | |
| flash('Please log in to access the dashboard.') | |
| return redirect(url_for('auth.login')) | |
| user_id = session['user_id'] | |
| # Fetch user info | |
| user_res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}&select=*", | |
| headers=HEADERS | |
| ) | |
| if user_res.status_code != 200 or not user_res.json(): | |
| flash('User not found.') | |
| return redirect(url_for('auth.login')) | |
| user = user_res.json()[0] | |
| # Fetch user's resume uploads | |
| uploads_res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/resume-api?user_id=eq.{user_id}&select=*", | |
| headers=HEADERS | |
| ) | |
| analytics = { | |
| 'average_score': None, | |
| 'top_resume': None, | |
| 'last_upload_date': None | |
| } | |
| if uploads_res.status_code == 200: | |
| uploads = uploads_res.json() | |
| if uploads: | |
| scores = [u['score'] for u in uploads if u.get('score') is not None] | |
| if scores: | |
| analytics['average_score'] = round(sum(scores) / len(scores), 2) | |
| top = max(uploads, key=lambda x: x.get('score', 0)) | |
| analytics['top_resume'] = top.get('resume_name', 'N/A') | |
| last_upload = max(uploads, key=lambda x: x.get('upload_date', '')) | |
| analytics['last_upload_date'] = last_upload.get('upload_date', '')[:16] # YYYY-MM-DD HH:MM | |
| return render_template('dashboard.html', user=user, analytics=analytics) | |
| def generate_api(): | |
| if 'user_id' not in session: | |
| flash('Please log in to access the API generator.') | |
| return redirect(url_for('auth.login')) | |
| if request.method == 'POST': | |
| flask_code, html_code = generate_ranker_code() | |
| return render_template( | |
| 'generated_code.html', | |
| flask_code=flask_code, | |
| html_code=html_code, | |
| title="Resume Ranker API Code" | |
| ) | |
| return render_template('generate_api.html', title="Generate Resume Ranker API") | |
| def generate_api_key(): | |
| return f"rk_live_{secrets.token_urlsafe(24)}" | |
| def generate_initial_api_key(): | |
| if 'user_id' not in session: | |
| flash('Please log in.') | |
| return redirect(url_for('auth.login')) | |
| user_id = session['user_id'] | |
| new_key = generate_api_key() | |
| res = requests.patch( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}", | |
| headers={**HEADERS, "Content-Type": "application/json"}, | |
| json={"api_key": new_key} | |
| ) | |
| if res.status_code == 200 or res.status_code == 204: | |
| flash("API key generated successfully.") | |
| else: | |
| flash("Failed to generate API key. Please try again.") | |
| return redirect(url_for('auth.api_keys')) | |
| def regenerate_api_key(): | |
| if 'user_id' not in session: | |
| flash('Please log in.') | |
| return redirect(url_for('auth.login')) | |
| user_id = session['user_id'] | |
| new_key = generate_api_key() | |
| res = requests.patch( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}", | |
| headers={**HEADERS, "Content-Type": "application/json"}, | |
| json={"api_key": new_key} | |
| ) | |
| if res.status_code == 200 or res.status_code == 204: | |
| flash("API key regenerated successfully.") | |
| else: | |
| flash("Failed to regenerate API key. Please try again.") | |
| return redirect(url_for('auth.api_keys')) | |
| def logout(): | |
| session.clear() | |
| flash('You have been logged out.') | |
| return redirect(url_for('auth.login')) | |
| def api_keys(): | |
| if 'user_id' not in session: | |
| flash('Please log in to view your API key.') | |
| return redirect(url_for('auth.login')) | |
| user_id = session['user_id'] | |
| res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}&select=*", | |
| headers=HEADERS | |
| ) | |
| if res.status_code != 200 or not res.json(): | |
| flash('Failed to fetch your account.') | |
| return redirect(url_for('auth.dashboard')) | |
| user = res.json()[0] | |
| return render_template('api_keys.html', user=user, title="My API Keys") | |
| def verify_email(token): | |
| res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?email_token=eq.{token}&select=*", | |
| headers=HEADERS | |
| ) | |
| if res.status_code != 200 or not res.json(): | |
| flash('Invalid or expired verification link.') | |
| return redirect(url_for('auth.register')) | |
| user = res.json()[0] | |
| email = user["email"] | |
| patch = requests.patch( | |
| f"{SUPABASE_URL}/rest/v1/users?email=eq.{email}", | |
| headers=HEADERS, | |
| json={"is_verified": True, "email_token": None} | |
| ) | |
| if patch.status_code == 204: | |
| flash('Email verified! You can now log in.') | |
| return redirect(url_for('auth.login')) | |
| else: | |
| flash('Verification failed.') | |
| return redirect(url_for('auth.register')) | |
| def api_docs(): | |
| if 'user_id' not in session: | |
| flash('Please log in to view the API documentation.') | |
| return redirect(url_for('auth.login')) | |
| # Optionally fetch user info (for personalization in template) | |
| user_id = session['user_id'] | |
| user_res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}&select=*", | |
| headers=HEADERS | |
| ) | |
| user = user_res.json()[0] if user_res.status_code == 200 and user_res.json() else {} | |
| return render_template('api_docs.html', title="API Documentation", user=user) | |
| def settings(): | |
| if 'user_id' not in session: | |
| flash('Please log in to access settings.') | |
| return redirect(url_for('auth.login')) | |
| user_id = session['user_id'] | |
| user_res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}&select=*", | |
| headers=HEADERS | |
| ) | |
| user = user_res.json()[0] if user_res.status_code == 200 and user_res.json() else {} | |
| return render_template('settings.html', title="User Settings", user=user) | |
| # @auth.route('/docs') | |
| # def docs(): | |
| # return render_template('docs.html', response=None) | |
| # allowed_extensions = {'pdf', 'docx'} | |
| # def allowed_file(filename): | |
| # return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions | |
| # @auth.route('/try-api', methods=['GET', 'POST']) | |
| # def try_api(): | |
| # api_key = request.form.get('api_key') | |
| # resumes = request.files.getlist('resumes') | |
| # job_description = request.form.get('job_description') | |
| # # Validate form fields | |
| # if not api_key: | |
| # flash("API key is required.") | |
| # return redirect(url_for('auth.docs')) | |
| # if not resumes: | |
| # flash("Please upload at least one resume.") | |
| # return redirect(url_for('auth.docs')) | |
| # if not job_description: | |
| # flash("Job description is required.") | |
| # return redirect(url_for('auth.docs')) | |
| # # Validate API key from Supabase | |
| # res = requests.get( | |
| # f"{SUPABASE_URL}/rest/v1/users?api_key=eq.{api_key}&select=id", | |
| # headers=HEADERS | |
| # ) | |
| # if res.status_code != 200 or not res.json(): | |
| # flash("Invalid API key.") | |
| # return redirect(url_for('auth.docs')) | |
| # # Validate file types | |
| # for resume in resumes: | |
| # if not allowed_file(resume.filename): | |
| # flash(f"Invalid file type: {resume.filename}. Only PDF or DOCX allowed.") | |
| # return redirect(url_for('auth.docs')) | |
| # resume.filename = secure_filename(resume.filename) | |
| # # Run actual ranking logic | |
| # try: | |
| # ranked = rank_resumes_by_semantic_similarity(resumes, job_description) | |
| # except Exception as e: | |
| # flash(f"Error processing resumes: {str(e)}") | |
| # return redirect(url_for('auth.docs')) | |
| # # Render results | |
| # return render_template('docs.html', response={ | |
| # "job_description": job_description, | |
| # "ranked_resumes": ranked # List of dicts with filename and score | |
| # }) | |
| def try_api(): | |
| if 'user_id' not in session: | |
| flash('Please log in to test the API.') | |
| return redirect(url_for('auth.login')) | |
| user_id = session['user_id'] | |
| # Fetch user info | |
| user_res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}&select=*", | |
| headers=HEADERS | |
| ) | |
| if user_res.status_code != 200 or not user_res.json(): | |
| flash('User not found.') | |
| return redirect(url_for('auth.login')) | |
| user = user_res.json()[0] | |
| if request.method == 'GET': | |
| return render_template('try_api.html', title="Try the ResumeRanker API", user=user) | |
| # POST logic | |
| api_key = request.form.get('api_key') | |
| resumes = request.files.getlist('resumes') | |
| job_description = request.form.get('job_description') | |
| if not api_key: | |
| flash("API key is required.") | |
| return redirect(url_for('auth.try_api')) | |
| if not resumes: | |
| flash("Please upload at least one resume.") | |
| return redirect(url_for('auth.try_api')) | |
| if not job_description: | |
| flash("Job description is required.") | |
| return redirect(url_for('auth.try_api')) | |
| res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?api_key=eq.{api_key}&select=id", | |
| headers=HEADERS | |
| ) | |
| if res.status_code != 200 or not res.json(): | |
| flash("Invalid API key.") | |
| return redirect(url_for('auth.try_api')) | |
| for resume in resumes: | |
| if not allowed_file(resume.filename): | |
| flash(f"Invalid file type: {resume.filename}. Only PDF or DOCX allowed.") | |
| return redirect(url_for('auth.try_api')) | |
| resume.filename = secure_filename(resume.filename) | |
| try: | |
| ranked = rank_resumes_by_semantic_similarity(resumes, job_description) | |
| except Exception as e: | |
| flash(f"Error processing resumes: {str(e)}") | |
| return redirect(url_for('auth.try_api')) | |
| return render_template('try_api.html', user=user, response={ | |
| "job_description": job_description, | |
| "ranked_resumes": ranked | |
| }) | |
| def ping_db(): | |
| try: | |
| res = requests.get( | |
| f"{SUPABASE_URL}/rest/v1/users?select=id", | |
| headers=HEADERS | |
| ) | |
| if res.status_code == 200: | |
| count = len(res.json()) | |
| return f" Connected to Supabase. Users count: {count}" | |
| else: | |
| return f" Failed to fetch users: {res.text}" | |
| except Exception as e: | |
| return f" DB Error: {e}" | |
| # @auth.route('/settings') | |
| # def settings(): | |
| # user, redirect_response = get_current_user() | |
| # if redirect_response: | |
| # return redirect_response | |
| # return render_template('settings.html', user=user) | |
| def update_profile(): | |
| user, redirect_response = get_current_user() | |
| if redirect_response: | |
| return redirect_response | |
| new_email = request.form.get('email') | |
| # username = request.form.get('username') # Username is read-only, not updated here | |
| if not new_email: | |
| flash('Email cannot be empty.', 'error') | |
| return redirect(url_for('auth.settings')) | |
| # Update email in Supabase. Assuming 'users' table or Supabase Auth update. | |
| # For Supabase Auth, you'd use the client library or the /auth/v1/users endpoint | |
| # For a custom 'users' table, it would be a PATCH request: | |
| update_payload = {"email": new_email} | |
| update_res = requests.patch( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user['id']}", | |
| headers=HEADERS, | |
| json=update_payload | |
| ) | |
| if update_res.status_code == 200 or update_res.status_code == 204: # 200 OK or 204 No Content | |
| flash('Profile updated successfully!', 'success') | |
| else: | |
| flash(f'Failed to update profile. Error: {update_res.text}', 'error') | |
| return redirect(url_for('auth.settings')) | |
| def change_password(): | |
| user, redirect_response = get_current_user() | |
| if redirect_response: | |
| return redirect_response | |
| current_password = request.form.get('current_password') | |
| new_password = request.form.get('new_password') | |
| confirm_new_password = request.form.get('confirm_new_password') | |
| if not current_password or not new_password or not confirm_new_password: | |
| flash('All password fields are required.', 'error') | |
| return redirect(url_for('auth.settings')) | |
| if new_password != confirm_new_password: | |
| flash('New password and confirmation do not match.', 'error') | |
| return redirect(url_for('auth.settings')) | |
| # In a real app, you would verify current_password first: | |
| # 1. Fetch user from Supabase Auth based on their email or ID. | |
| # 2. Use `check_password_hash(user.hashed_password, current_password)` | |
| # If you're using Supabase's built-in auth, you'd typically re-authenticate | |
| # or use their client's `updateUser` function which handles password changes. | |
| # Direct patching to a 'users' table password field is generally insecure | |
| # unless you are doing the hashing yourself. | |
| # Example using a mock check for demonstration | |
| if current_password != 'TestPass123': # Replace with actual password verification | |
| flash('Incorrect current password.', 'error') | |
| return redirect(url_for('auth.settings')) | |
| # Update password in Supabase Auth (conceptual) | |
| # This typically involves a call to Supabase's auth client or API endpoint for password change. | |
| # E.g., `supabase.auth.updateUser({ password: new_password })` | |
| # For direct database table if not using Supabase Auth for passwords: | |
| hashed_new_password = generate_password_hash(new_password) | |
| update_payload = {"password_hash": hashed_new_password} # Assuming a column for hashed password | |
| update_res = requests.patch( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user['id']}", | |
| headers=HEADERS, | |
| json=update_payload | |
| ) | |
| if update_res.status_code == 200 or update_res.status_code == 204: | |
| flash('Password changed successfully!', 'success') | |
| else: | |
| flash(f'Failed to change password. Error: {update_res.text}', 'error') | |
| flash('Password change simulated successfully!', 'success') # Placeholder | |
| return redirect(url_for('auth.settings')) | |
| def delete_account(): | |
| user, redirect_response = get_current_user() | |
| if redirect_response: | |
| return redirect_response | |
| # In a real app, confirm with a password or MFA | |
| # Then delete the user from Supabase Auth and any related tables (via RLS or triggers) | |
| # Example for deleting from a custom 'users' table: | |
| delete_res = requests.delete( | |
| f"{SUPABASE_URL}/rest/v1/users?id=eq.{user['id']}", | |
| headers=HEADERS | |
| ) | |
| if delete_res.status_code == 204: # 204 No Content for successful delete | |
| session.pop('user_id', None) # Clear session after account deletion | |
| flash('Your account has been successfully deleted.', 'success') | |
| return redirect(url_for('auth.login')) | |
| else: | |
| flash(f'Failed to delete account. Error: {delete_res.text}', 'error') | |
| return redirect(url_for('auth.settings')) | |