# app.py from flask import ( Flask, request, redirect, render_template, session, url_for, flash, Response, ) from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from authlib.integrations.flask_client import OAuth from dotenv import load_dotenv from sqlalchemy import create_engine, text, insert, select, MetaData, Table import os import string import random import uuid import csv import json import gspread from io import StringIO from google.oauth2.service_account import Credentials import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import pickle from datetime import datetime, timedelta from zoneinfo import ZoneInfo # Load environment variables from .env file load_dotenv() app = Flask(__name__) app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY") or "your_secret_key" app.config["SQLALCHEMY_DATABASE_URI"] = ( os.environ.get("DATABASE_URI") or "postgresql://localhost/sunolinks" ) app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False # app.config['SERVER_NAME'] = os.environ.get('SERVER_NAME') or 'localhost:5000' # Set your server name here db = SQLAlchemy(app) migrate = Migrate(app, db) oauth = OAuth(app) oauth.register( name="google", client_id=os.environ.get("GOOGLE_CLIENT_ID"), client_secret=os.environ.get("GOOGLE_CLIENT_SECRET"), server_metadata_url="https://accounts.google.com/.well-known/openid-configuration", client_kwargs={"scope": "openid email profile"}, ) # Template filters @app.template_filter("eastern_time") def eastern_time_filter(utc_dt): """Convert UTC datetime to Eastern Time""" if utc_dt is None: return None # The datetime from prod database is in UTC if utc_dt.tzinfo is None: utc_dt = utc_dt.replace(tzinfo=ZoneInfo("UTC")) # Convert to Eastern Time eastern_dt = utc_dt.astimezone(ZoneInfo("America/New_York")) return eastern_dt # SMTP Configuration SMTP_SERVER = os.getenv("SMTP_SERVER", "smtp.gmail.com") SMTP_PORT = int(os.getenv("SMTP_PORT", "587")) SMTP_USERNAME = os.getenv("SMTP_USERNAME") SMTP_PASSWORD = os.getenv("SMTP_PASSWORD") FROM_EMAIL = os.getenv("FROM_EMAIL") def send_hackmit_token_email( email: str, hacker_name: str, token: str, team_name: str = "N/A", university: str = "N/A", is_existing: bool = False, ): """Send HackMIT token email to participant""" # Update message based on whether it's a new or existing token if is_existing: token_message = "Here's your existing token for the Suno HackMIT event:" else: token_message = "Here's your new token for the Suno HackMIT event:" # Extract first name from hacker_name first_name = hacker_name.split()[0] if hacker_name else "there" # Create HTML body body = f"""

Hey {first_name}! 👋

Thanks for opting in to the Suno HackMIT track! 🎶

{token_message}

Token: \"{token}\"

This is YOUR team's token to access the Suno API during the hackathon. Please don't share it with anyone not on your team. 🔒

For Suno's full API documentation, please visit suno.com/hackmit 🚀

Happy hacking! 🔥

- The Suno Team 🎶

""" try: # Create message msg = MIMEMultipart("alternative") msg["Subject"] = "Your Suno HackMIT Token!" msg["From"] = f"Suno Hackathons <{FROM_EMAIL}>" msg["To"] = email # Create HTML part html_part = MIMEText(body, "html") msg.attach(html_part) # Connect to server and send email server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) server.starttls() # Enable TLS encryption if SMTP_USERNAME and SMTP_PASSWORD: server.login(SMTP_USERNAME, SMTP_PASSWORD) # Send email text = msg.as_string() server.sendmail(FROM_EMAIL, email, text) server.quit() print(f"✅ Email sent successfully to {email}") return {"success": True, "message": "Email sent successfully"} except Exception as e: print(f"❌ Failed to send email to {email}: {str(e)}") return {"success": False, "error": str(e)} # Define file paths for pickled data EMPS_FILE = "emps_data.pkl" TEAMS_FILE = "teams_data.pkl" CACHE_DURATION = timedelta(hours=24) # Cache data for 24 hours def load_or_fetch_data(override_cache=False): current_time = datetime.now() # Check if cached data exists and is fresh if os.path.exists(EMPS_FILE) and os.path.exists(TEAMS_FILE) and not override_cache: emps_mtime = datetime.fromtimestamp(os.path.getmtime(EMPS_FILE)) teams_mtime = datetime.fromtimestamp(os.path.getmtime(TEAMS_FILE)) if current_time - emps_mtime < CACHE_DURATION and current_time - teams_mtime < CACHE_DURATION: try: with open(EMPS_FILE, "rb") as f: emps = pickle.load(f) with open(TEAMS_FILE, "rb") as f: teams = pickle.load(f) print("Loaded employee data from cache") return emps, teams except Exception as e: print(f"Error loading cached data: {e}") # If cache doesn't exist or is stale, fetch fresh data try: creds_dict = json.loads(os.getenv("GOOGLE_CREDENTIALS_JSON")) # Set scopes SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"] # Load credentials from dict credentials = Credentials.from_service_account_info(creds_dict, scopes=SCOPES) # Create gspread client gc = gspread.authorize(credentials) sheet = gc.open_by_url( "https://docs.google.com/spreadsheets/d/1jZqj4UyTTWqt3RsvjWG1Ifhi_pnNwUPhVnK8IcnN2_I/edit" ).worksheet("emps") emps = sheet.get_all_records() sheet = gc.open_by_url( "https://docs.google.com/spreadsheets/d/1jZqj4UyTTWqt3RsvjWG1Ifhi_pnNwUPhVnK8IcnN2_I/edit" ).worksheet("teams") teams = sheet.get_all_records() for emp in emps: emp["ldap"] = emp["Email"].split("@")[0] emp["profile_url"] = f"employees/{emp['ldap']}" # Save to cache with open(EMPS_FILE, "wb") as f: pickle.dump(emps, f) with open(TEAMS_FILE, "wb") as f: pickle.dump(teams, f) print("Fetched and cached fresh employee data") return emps, teams except Exception as e: print(f"Error fetching data: {e}") # If fetch fails and cache exists, try to use stale cache if os.path.exists(EMPS_FILE) and os.path.exists(TEAMS_FILE): with open(EMPS_FILE, "rb") as f: emps = pickle.load(f) with open(TEAMS_FILE, "rb") as f: teams = pickle.load(f) print("Using stale cached data due to fetch error") return emps, teams raise # Load the data emps, teams = load_or_fetch_data() # Initialize global variables emps_lookup = None managers_with_reports = None emp_dict = None ceo = None def process_employee_data(): global emps_lookup, managers_with_reports, emp_dict, ceo emps_lookup = {emp["Name"]: emp["Email"] for emp in emps} managers_with_reports = {} emp_dict = {emp["Email"]: emp for emp in emps} ceo = None for emp in emp_dict.values(): emp["Reports"] = [] for emp in emp_dict.values(): if emp["Name"] == "Mikey Shulman": ceo = emp continue manager_name = emp.get("Corrected Manager") if manager_name and manager_name in emps_lookup: manager_email = emps_lookup[manager_name] if manager_email: emp_dict[manager_email]["Reports"].append(emp) # Process the initial data process_employee_data() def count_all_reports(emp): """Recursively count all reports under an employee""" direct_count = len(emp["Reports"]) total_count = direct_count for report in emp["Reports"]: total_count += count_all_reports(report) return total_count # Add cumulative report counts to each employee for emp in emp_dict.values(): emp["CumulativeReports"] = count_all_reports(emp) # Database models class URL(db.Model): id = db.Column(db.Integer, primary_key=True) short_code = db.Column(db.String(10), unique=True, nullable=False) original_url = db.Column(db.Text, nullable=False) user_email = db.Column(db.String(255), nullable=False) count = db.Column(db.Integer, default=0) class Event(db.Model): id = db.Column(db.Integer, primary_key=True) event_type = db.Column(db.String(50), nullable=False) datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) user_email = db.Column(db.String(255), nullable=False) url_id = db.Column(db.Integer, nullable=True) note = db.Column(db.Text, nullable=True) # Ensure the database is created within the application context with app.app_context(): db.create_all() # Helper function to generate a short code def generate_short_code(length=6): return "".join(random.choices(string.ascii_letters + string.digits, k=length)) # Google OAuth login @app.route("/login") def login(): redirect_uri = url_for("authorized", _external=True) nonce = str(uuid.uuid4()) session["nonce"] = nonce return oauth.google.authorize_redirect(redirect_uri, nonce=nonce) @app.route("/logout") def logout(): session.pop("user_email", None) flash("You have been logged out.", "info") return redirect(url_for("index")) @app.route("/login/authorized") def authorized(): token = oauth.google.authorize_access_token() nonce = session.pop("nonce", None) try: user_info = oauth.google.parse_id_token(token, nonce=nonce) session["user_email"] = user_info["email"] flash("You have been logged in.", "success") except Exception as e: flash(f"Login failed: {str(e)}", "error") return redirect(url_for("index")) # Add a new URL @app.route("/add_url", methods=["POST"]) def add_url(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) original_url = request.form.get("original_url") short_code = request.form.get("short_code") or generate_short_code() if URL.query.filter_by(short_code=short_code).first(): flash("Short code already exists. Please choose another.", "error") return redirect(url_for("index")) url_entry = URL( short_code=short_code, original_url=original_url, user_email=session["user_email"], ) db.session.add(url_entry) db.session.commit() flash("URL added successfully!", "success") return redirect(url_for("index")) # Delete a URL @app.route("/delete_url/", methods=["POST"]) def delete_url(url_id): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) url_entry = URL.query.get(url_id) if url_entry and url_entry.user_email == session["user_email"]: # Create a delete event delete_event = Event( event_type="delete", user_email=session["user_email"], url_id=url_entry.id, note=f"{url_entry.short_code},{url_entry.original_url}", ) db.session.add(delete_event) db.session.delete(url_entry) db.session.commit() flash("URL deleted successfully!", "success") else: flash("Unauthorized action.", "error") return redirect(url_for("index")) # Display all URLs @app.route("/") def index(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("login")) # Sort emps by surname (last word in Name field) def get_surname(emp): name = emp.get("Name", "").strip() if name: return name.split()[-1].lower() return "" sorted_emps = sorted(emps, key=get_surname) # Get all unique teams for the filter dropdown all_teams = set() for emp in emps: if emp.get("Teams"): teams = emp["Teams"].split(",") for team in teams: all_teams.add(team.strip()) # Get all unique offices for the filter dropdown all_offices = set() for emp in emps: if emp.get("Office"): all_offices.add(emp["Office"].strip()) return render_template( "club47/employees_list.html", employees=sorted_emps, all_teams=sorted(all_teams), all_offices=sorted(all_offices), ) # Display all URLs @app.route("/golinks") def golinks(): if "user_email" in session: urls = URL.query.all() else: flash("You need to login first.", "error") urls = [] return render_template("golinks.html", urls=urls) # Redirect short URL to original URL @app.route("/") def redirect_url(short_code): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) url_entry = URL.query.filter_by(short_code=short_code).first() if url_entry: url_entry.count += 1 click_event = Event(event_type="click", user_email=session["user_email"], url_id=url_entry.id) db.session.add(click_event) db.session.commit() return redirect(url_entry.original_url) return "URL not found", 404 @app.route("/download_csv") def download_csv(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) # Create a StringIO object to write the CSV data si = StringIO() cw = csv.writer(si) # Write the header cw.writerow(["id", "short_code", "original_url", "user_email", "count"]) # Query all URLs and write them to the CSV urls = URL.query.all() for url in urls: cw.writerow([url.id, url.short_code, url.original_url, url.user_email, url.count]) # Seek to the start of the StringIO object si.seek(0) # Create a response with the CSV data response = Response(si.getvalue(), mimetype="text/csv") response.headers["Content-Disposition"] = "attachment; filename=data.csv" click_event = Event(event_type="download", user_email=session["user_email"], url_id=None) db.session.add(click_event) db.commit() return response def _copy_song(direction, clip_id, user_email): print(f"Copying song {clip_id} from {direction} to {user_email}") staging_engine = create_engine(os.getenv("STAGING_DATABASE")) prod_engine = create_engine(os.getenv("PROD_DATABASE")) if direction == "staging_to_prod": print("Copying from staging to prod") source_engine = staging_engine target_engine = prod_engine elif direction == "prod_to_staging": print("Copying from prod to staging") source_engine = prod_engine target_engine = staging_engine else: print("Invalid direction") return False try: metadata = MetaData() clip_table = Table("bots_generatedclip", metadata, autoload_with=source_engine) # Get row from source with source_engine.connect() as conn: result = conn.execute(select(clip_table).where(clip_table.c.id == clip_id)) row = result.fetchone() if not row: print(f"No clip found with ID {clip_id}") return False # Convert row to dictionary using column names clip_data = {column.name: getattr(row, column.name) for column in clip_table.columns} clip_data["request_id"] = None clip_data["model_name"] = "copy-tool" # Insert into target with target_engine.connect() as conn: # Get user ID for brad@suno.com user_result = conn.execute(text(f"select id from auth_user where email='{user_email}'")) user_id = user_result.fetchone()[0] print(f"User ID: {user_id}") # Update clip_data with user_id clip_data["user_id"] = user_id # Insert the clip with updated user_id conn.execute(insert(clip_table), clip_data) conn.commit() print(f"Successfully copied clip {clip_id}") except Exception as e: print(f"Error: {e}") return False return True @app.route("/tools/copy-song", methods=["GET", "POST"]) def copy_song(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) if request.method == "POST": direction = request.form.get("direction") clip_id = request.form.get("clip_id") user_email = request.form.get("user_email") print("Form submitted with values:") print(f"Direction: {direction}") print(f"Clip ID: {clip_id}") print(f"User Email: {user_email}") status = _copy_song(direction, clip_id, user_email) if status: return redirect(url_for("success", clip_id=clip_id)) else: flash("Failed to copy song", "error") return redirect(url_for("copy_song")) return render_template("copy_song.html") @app.route("/tools/copy-song/success") def success(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) clip_id = request.args.get("clip_id") return render_template("success.html", clip_id=clip_id) @app.route("/employees/") def employee_detail(ldap): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) employee = next((emp for emp in emps if emp["Email"] == ldap + "@suno.com"), None) print(employee) if not employee: return "Employee not found", 404 # Find manager info manager_info = None if employee.get("Corrected Manager") and employee["Corrected Manager"] in emps_lookup: manager_email = emps_lookup[employee["Corrected Manager"]] manager_info = emp_dict.get(manager_email) manager_name = employee.get("Corrected Manager") manager_email = emps_lookup.get(manager_name) return render_template( "club47/employee_profile.html", emp=employee, reports=emp_dict[employee["Email"]]["Reports"], manager_name=manager_name, manager_email=manager_email, ) @app.route("/employees/list") def employees_list(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) # Get all unique teams for the filter dropdown all_teams = set() for emp in emps: if emp.get("Teams"): teams = emp["Teams"].split(",") for team in teams: all_teams.add(team.strip()) # Get all unique offices for the filter dropdown all_offices = set() for emp in emps: if emp.get("Office"): all_offices.add(emp["Office"].strip()) return render_template( "club47/employees_list.html", employees=emps, all_teams=sorted(all_teams), all_offices=sorted(all_offices), ) @app.route("/home") def home(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) employee = next((emp for emp in emps if emp["Email"] == session["user_email"]), None) employee["Handle"] = "buno" return render_template( "club47/base.html", emp=employee, reports=emp_dict[employee["Email"]]["Reports"] ) @app.route("/org") def fullorg(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) return render_template("club47/org.html", org=emp_dict, ceo=emp_dict["mikey@suno.com"]) @app.route("/reload") def reload(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) print("Refreshing employee data...") # Fetch fresh data from Google Sheets global emps, teams emps, teams = load_or_fetch_data(override_cache=True) # Reprocess all the derived data structures process_employee_data() # Recalculate cumulative report counts for emp in emp_dict.values(): emp["CumulativeReports"] = count_all_reports(emp) print("Employee data refreshed successfully") flash("Employee data has been refreshed from Google Sheets", "success") return redirect(url_for("fullorg")) @app.route("/hack") def hack(): if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("index")) prod_engine = create_engine(os.getenv("PROD_DATABASE")) with prod_engine.connect() as conn: result = conn.execute( text( f"select token from clips_usertoken ut where ut.user_id=(select id from auth_user where email='{session['user_email']}')" ) ) tokens = result.fetchall() print(f"Tokens: {tokens}") # If no tokens found, generate a new one if not tokens: # Generate UUID v4 and remove dashes new_token = str(uuid.uuid4()).replace("-", "") # Get user ID user_result = conn.execute( text(f"select id from auth_user where email='{session['user_email']}'") ) user_id = user_result.fetchone()[0] # Insert the new token conn.execute( text( f"INSERT INTO clips_usertoken (user_id, token, is_active) VALUES ({user_id}, '{new_token}', true)" ) ) conn.commit() # Return the newly created token tokens.append((new_token,)) print(f"Generated new token: {new_token}") return render_template("hack.html", email=session["user_email"], tokens=tokens) # HackMIT Token Management Routes @app.route("/hackmit") def hackmit_dashboard(): """HackMIT token booth dashboard""" # Check staff authorization if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("login")) try: engine = create_engine(os.getenv("PROD_DATABASE")) with engine.connect() as conn: # Get stats total_tokens_result = conn.execute(text("SELECT COUNT(*) FROM hackmit_tokens")) total_tokens = total_tokens_result.scalar() active_tokens_result = conn.execute( text(""" SELECT COUNT(*) FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id WHERE ut.is_active = true """) ) active_tokens = active_tokens_result.scalar() # Get recent tokens (last 10) recent_tokens_result = conn.execute( text(""" SELECT ht.id, ht.hacker_name, ht.email_address, ht.team_name, ht.university, ht.created_at, ht.updated_at, ut.is_active, ut.token, au.email as user_email FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id JOIN auth_user au ON ut.user_id = au.id ORDER BY ht.created_at DESC LIMIT 10 """) ) recent_tokens = recent_tokens_result.fetchall() # Get recent activity (last 24 hours) - use created_at since tracking is commented out recent_activity_result = conn.execute( text(""" SELECT COUNT(*) FROM hackmit_tokens WHERE created_at >= NOW() - INTERVAL '24 hours' """) ) recent_activity = recent_activity_result.scalar() stats = { "total_tokens": total_tokens, "active_tokens": active_tokens, "revoked_tokens": total_tokens - active_tokens, "recent_activity": recent_activity, } # Get staff name if "user_email" in session: staff_name = session["user_email"].split("@")[0] else: staff_name = "api-staff" return render_template( "hackmit/dashboard.html", stats=stats, recent_tokens=recent_tokens, staff_name=staff_name, ) except Exception as e: flash(f"Database error: {str(e)}", "error") # Get staff name even if database fails staff_name = ( session.get("user_email", "api-staff").split("@")[0] if session.get("user_email") else "api-staff" ) return render_template( "hackmit/dashboard.html", stats={}, recent_tokens=[], staff_name=staff_name ) @app.route("/hackmit/issue", methods=["GET", "POST"]) def hackmit_issue_token(): """Issue a new HackMIT token""" # Check staff authorization if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("login")) if request.method == "POST": hacker_name = request.form.get("hacker_name", "").strip() email_address = request.form.get("email_address", "").strip() team_name = request.form.get("team_name", "").strip() university = request.form.get("university", "").strip() notes = request.form.get("notes", "").strip() # Validate required fields if not hacker_name: flash("Hacker name is required.", "error") return render_template("hackmit/issue_token.html") if not email_address: flash("Email address is required.", "error") return render_template("hackmit/issue_token.html") if not team_name: flash("Team name is required.", "error") return render_template("hackmit/issue_token.html") try: engine = create_engine(os.getenv("PROD_DATABASE")) with engine.connect() as conn: # Check if user exists in Suno system user_check = conn.execute( text(""" SELECT id FROM auth_user WHERE email = :email """), {"email": email_address}, ) user_row = user_check.fetchone() if not user_row: flash( f"❌ {email_address} does not have a Suno account. " f"Please sign up at https://accounts.suno.com/sign-up first, " f"then return to get an API token for HackMIT!", "error", ) return render_template("hackmit/issue_token.html") user_id = user_row[0] # Check for existing active token existing_token_check = conn.execute( text(""" SELECT ht.id, ut.token FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id WHERE ut.user_id = :user_id AND ut.is_active = true """), {"user_id": user_id}, ) existing_token = existing_token_check.fetchone() if existing_token: # Send email with existing token email_result = send_hackmit_token_email( email=email_address, hacker_name=hacker_name, token=existing_token[1], team_name=team_name, university=university, is_existing=True, ) if email_result["success"]: flash( f"🎵 {email_address} already has an active HackMIT token: {existing_token[1][:8]}... " f"Your songs are already linked to your Suno account! 📧 Reminder email sent to {email_address}", "warning", ) else: flash( f"🎵 {email_address} already has an active HackMIT token: {existing_token[1][:8]}... " f"Your songs are already linked to your Suno account! ⚠️ Email failed to send: {email_result.get('error', 'Unknown error')}", "warning", ) return redirect(url_for("hackmit_token_details", token_id=existing_token[0])) # Create UserToken first new_token = str(uuid.uuid4()).replace("-", "") user_token_result = conn.execute( text(""" INSERT INTO clips_usertoken (user_id, token, is_active) VALUES (:user_id, :token, true) RETURNING id """), {"user_id": user_id, "token": new_token}, ) user_token_id = user_token_result.scalar() # Create HackMIT token hackmit_token_uuid = str(uuid.uuid4()) hackmit_result = conn.execute( text(""" INSERT INTO hackmit_tokens (id, user_token_id, hacker_name, email_address, team_name, university, notes, generations_used, created_at, updated_at) VALUES (:id, :user_token_id, :hacker_name, :email_address, :team_name, :university, :notes, 0, NOW(), NOW()) RETURNING id """), { "id": hackmit_token_uuid, "user_token_id": user_token_id, "hacker_name": hacker_name, "email_address": email_address, "team_name": team_name, "university": university or None, "notes": notes or None, }, ) hackmit_token_id = hackmit_result.scalar() conn.commit() # Send email notification to the participant email_result = send_hackmit_token_email( email=email_address, hacker_name=hacker_name, token=new_token, team_name=team_name, university=university, is_existing=False, ) if email_result["success"]: flash( f"🎉 Token issued successfully! Token: {new_token[:8]}... " f"✨ {email_address} is a Suno user, so all API-generated songs will appear in your regular Suno dashboard! " f"📧 Email sent to {email_address}", "success", ) else: flash( f"🎉 Token issued successfully! Token: {new_token[:8]}... " f"✨ {email_address} is a Suno user, so all API-generated songs will appear in your regular Suno dashboard! " f"⚠️ Email failed to send: {email_result.get('error', 'Unknown error')}", "warning", ) return redirect(url_for("hackmit_token_details", token_id=hackmit_token_id)) except Exception as e: flash(f"Error creating token: {str(e)}", "error") return render_template("hackmit/issue_token.html") @app.route("/hackmit/tokens") def hackmit_list_tokens(): """List all HackMIT tokens with search and filtering""" # Check staff authorization if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("login")) try: engine = create_engine(os.getenv("PROD_DATABASE")) with engine.connect() as conn: # Base query query = """ SELECT ht.id, ht.hacker_name, ht.email_address, ht.team_name, ht.university, ht.created_at, ht.updated_at, ht.generations_used, ut.is_active, ut.token, au.email as user_email FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id JOIN auth_user au ON ut.user_id = au.id """ # Apply filters conditions = [] params = {} status_filter = request.args.get("status") if status_filter == "active": conditions.append("ut.is_active = true") elif status_filter == "revoked": conditions.append("ut.is_active = false") search = request.args.get("search") if search: conditions.append(""" (ht.email_address ILIKE :search OR ht.hacker_name ILIKE :search OR ht.team_name ILIKE :search) """) params["search"] = f"%{search}%" if conditions: query += " WHERE " + " AND ".join(conditions) query += " ORDER BY ht.created_at DESC LIMIT 50" tokens_result = conn.execute(text(query), params) tokens = tokens_result.fetchall() # Get summary stats stats_result = conn.execute( text(""" SELECT COUNT(*) as total_tokens, COUNT(CASE WHEN ut.is_active = true THEN 1 END) as active_tokens FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id """) ) stats_row = stats_result.fetchone() stats = { "total_tokens": stats_row[0], "active_tokens": stats_row[1], "revoked_tokens": stats_row[0] - stats_row[1], } return render_template( "hackmit/token_list.html", tokens=tokens, stats=stats, status_filter=status_filter, search=search or "", ) except Exception as e: flash(f"Database error: {str(e)}", "error") return render_template( "hackmit/token_list.html", tokens=[], stats={}, status_filter="", search="" ) @app.route("/hackmit/tokens/") def hackmit_token_details(token_id): """Show details for a specific token""" # Check staff authorization if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("login")) try: engine = create_engine(os.getenv("PROD_DATABASE")) with engine.connect() as conn: token_result = conn.execute( text(""" SELECT ht.id, ht.hacker_name, ht.email_address, ht.team_name, ht.university, ht.notes, ht.created_at, ht.updated_at, ht.generations_used, ut.is_active, ut.token, au.email as user_email, au.id as user_id FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id JOIN auth_user au ON ut.user_id = au.id WHERE ht.id = :token_id """), {"token_id": token_id}, ) token = token_result.fetchone() if not token: flash("Token not found.", "error") return redirect(url_for("hackmit_dashboard")) return render_template("hackmit/token_details.html", token=token) except Exception as e: flash(f"Database error: {str(e)}", "error") return redirect(url_for("hackmit_dashboard")) @app.route("/hackmit/tokens/revoke-all", methods=["POST"]) def hackmit_revoke_all_tokens(): """Revoke all active tokens""" # Check staff authorization if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("login")) try: engine = create_engine(os.getenv("PROD_DATABASE")) with engine.connect() as conn: # Get count of active tokens first active_count_result = conn.execute( text(""" SELECT COUNT(*) FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id WHERE ut.is_active = true """) ) active_count = active_count_result.scalar() if active_count == 0: flash("No active tokens to revoke.", "info") return redirect(url_for("hackmit_dashboard")) # Get the user_token_ids that are currently active (before revoking) active_token_ids_result = conn.execute( text(""" SELECT ht.user_token_id FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id WHERE ut.is_active = true """) ) active_token_ids = [row[0] for row in active_token_ids_result.fetchall()] if not active_token_ids: flash("No active tokens to revoke.", "info") return redirect(url_for("hackmit_dashboard")) # Revoke those specific active tokens conn.execute( text(""" UPDATE clips_usertoken SET is_active = false WHERE id = ANY(:token_ids) """), {"token_ids": active_token_ids}, ) # Update the HackMIT tokens we just revoked conn.execute( text(""" UPDATE hackmit_tokens SET updated_at = NOW() WHERE user_token_id = ANY(:token_ids) """), {"token_ids": active_token_ids}, ) conn.commit() flash( f"🚨 Revoke all successful! {active_count} active tokens revoked.", "success", ) except Exception as e: flash(f"Error revoking all tokens: {str(e)}", "error") return redirect(url_for("hackmit_dashboard")) @app.route("/hackmit/tokens//revoke", methods=["POST"]) def hackmit_revoke_token(token_id): """Revoke a token""" # Check staff authorization if "user_email" not in session: flash("You need to login first.", "error") return redirect(url_for("login")) try: engine = create_engine(os.getenv("PROD_DATABASE")) with engine.connect() as conn: # Get token info first token_check = conn.execute( text(""" SELECT ht.id, ut.token, ut.is_active FROM hackmit_tokens ht JOIN clips_usertoken ut ON ht.user_token_id = ut.id WHERE ht.id = :token_id """), {"token_id": token_id}, ) token = token_check.fetchone() if not token: flash("Token not found.", "error") return redirect(url_for("hackmit_dashboard")) if not token[2]: # is_active flash("Token is already revoked.", "warning") else: # Revoke the token conn.execute( text(""" UPDATE clips_usertoken SET is_active = false WHERE id = (SELECT user_token_id FROM hackmit_tokens WHERE id = :token_id) """), {"token_id": token_id}, ) # Update HackMIT token updated_at timestamp conn.execute( text(""" UPDATE hackmit_tokens SET updated_at = NOW() WHERE id = :token_id """), {"token_id": token_id}, ) conn.commit() flash(f"Token {token[1][:8]}... revoked successfully.", "success") next_url = request.form.get("next") or url_for("hackmit_dashboard") return redirect(next_url) except Exception as e: flash(f"Error revoking token: {str(e)}", "error") return redirect(url_for("hackmit_dashboard")) @app.route("/hello") def hello(): return "Hello, World!" if __name__ == "__main__": app.run(debug=False)