import os from datetime import datetime, timedelta from flask import Flask, request, jsonify import jwt from supabase import create_client, Client app = Flask(__name__) # ── Configuration ──────────────────────────────────────────────────────── SUPABASE_URL = os.getenv('SUPABASE_URL', 'https://hzvpbysektyrztlpvbhu.supabase.co') SUPABASE_KEY = os.getenv('SUPABASE_KEY', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imh6dnBieXNla3R5cnp0bHB2Ymh1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njg5NjY3MDIsImV4cCI6MjA4NDU0MjcwMn0.lIAECydBZjNQOLNG748KmCdWOfTMfUIIW2yd_6xCEQw') supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) SECRET_KEY = os.getenv('SECRET_KEY', 'aymmm-change-this-in-production') # ── Health check ───────────────────────────────────────────────────────── @app.route('/', methods=['GET']) def home(): return jsonify({ "status": "running", "message": "Custom JWT + Supabase demo (UUID-based profile fetch)", "endpoints": { "POST /signup": "Create user profile", "POST /login": "Authenticate and get JWT", "GET /profile": "Get profile (requires Bearer token)" } }) # ── Signup ─────────────────────────────────────────────────────────────── @app.route('/signup', methods=['POST']) def signup(): data = request.get_json() required = ['name', 'email', 'password', 'department'] if not data or not all(k in data for k in required): return jsonify({'message': 'Missing required fields'}), 400 email = data['email'].strip().lower() name = data['name'].strip() department = data['department'].strip() password = data['password'] # plain text – demo only # Check if email exists existing = supabase.table('profiles') \ .select('email') \ .eq('email', email) \ .execute() if existing.data: return jsonify({'message': 'User already exists'}), 409 # Insert new profile insert_data = { 'name': name, 'email': email, 'department': department # password NOT stored – add hashing later } result = supabase.table('profiles').insert(insert_data).execute() if not result.data: return jsonify({'message': 'Failed to create user'}), 500 return jsonify({'message': 'User created successfully'}), 201 # ── Login ──────────────────────────────────────────────────────────────── @app.route('/login', methods=['POST']) def login(): data = request.get_json() if not data or 'email' not in data or 'password' not in data: return jsonify({'message': 'Missing email or password'}), 400 email = data['email'].strip().lower() password = data['password'] user_response = supabase.table('profiles') \ .select('id, name, email, department') \ .eq('email', email) \ .maybe_single() \ .execute() if user_response.data is None: return jsonify({'message': 'User not found'}), 404 user = user_response.data # TODO: Replace with real password verification (bcrypt, etc.) # Right now: insecure – any password works if email exists payload = { 'sub': email, 'uid': str(user['id']), 'name': user['name'], 'department': user['department'], 'iat': datetime.utcnow(), 'exp': datetime.utcnow() + timedelta(minutes=60) } token = jwt.encode(payload, SECRET_KEY, algorithm='HS256') return jsonify({'token': token}) # ── Profile ────────────────────────────────────────────────────────────── @app.route('/profile', methods=['GET']) def profile(): auth_header = request.headers.get('Authorization') if not auth_header or not auth_header.startswith('Bearer '): return jsonify({'message': 'Missing or invalid Authorization header'}), 401 token = auth_header.split(' ')[1] try: decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256']) user_id = decoded.get('uid') if not user_id: return jsonify({'message': 'Token missing user identifier'}), 401 user_response = supabase.table('profiles') \ .select('name, email, department') \ .eq('id', user_id) \ .maybe_single() \ .execute() if user_response.data is None: return jsonify({'message': 'User not found'}), 404 user = user_response.data return jsonify({ 'name': user['name'], 'email': user['email'], 'department': user['department'] }) except jwt.ExpiredSignatureError: return jsonify({'message': 'Token has expired'}), 401 except jwt.InvalidTokenError: return jsonify({'message': 'Invalid token'}), 401 except Exception as e: return jsonify({'message': f'Server error: {str(e)}'}), 500 if __name__ == '__main__': port = int(os.environ.get('PORT', 7860)) app.run(host='0.0.0.0', port=port, debug=False)