Spaces:
Sleeping
Sleeping
βshubhamdhamalβ
commited on
Commit
Β·
36c67d1
1
Parent(s):
b217afb
reverted all changes to old
Browse files- DEPLOYMENT_STATUS.md +0 -63
- Dockerfile +3 -7
- SETUP_DATABASE.md +0 -77
- encode_password.py +0 -57
- start.sh +0 -66
- web_app/templates/redirect.html +0 -28
DEPLOYMENT_STATUS.md
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
# HF Spaces Deployment Summary
|
| 2 |
-
|
| 3 |
-
## β
Completed
|
| 4 |
-
1. **Dockerized Flask app** for Hugging Face Spaces
|
| 5 |
-
2. **Database initialization** - auto-creates tables on startup
|
| 6 |
-
3. **Login/Registration** - fully functional
|
| 7 |
-
4. **Secrets configuration** - SECRET_KEY set in `.env`
|
| 8 |
-
5. **Logging added** - debug auth routes
|
| 9 |
-
|
| 10 |
-
## π§ Current Configuration
|
| 11 |
-
- **App URL**: https://crackbit-ai-learning-path-generator.hf.space
|
| 12 |
-
- **Port**: 7860 (HF Spaces default)
|
| 13 |
-
- **Workers**: 1 worker + 4 threads (prevents CSRF issues)
|
| 14 |
-
- **Database**: SQLite (ephemeral - resets on restart)
|
| 15 |
-
- **Session**: Secure cookies disabled for HF internal HTTP
|
| 16 |
-
- **CSRF**: Temporarily disabled (WTF_CSRF_ENABLED = False)
|
| 17 |
-
|
| 18 |
-
## π Testing Checklist
|
| 19 |
-
- [ ] Register new user β Should redirect to home with success message
|
| 20 |
-
- [ ] Login with registered user β Should redirect to home with greeting
|
| 21 |
-
- [ ] Generate learning path β Should work without login (guest mode)
|
| 22 |
-
- [ ] Check logs for login/registration messages
|
| 23 |
-
|
| 24 |
-
## β οΈ Known Issues
|
| 25 |
-
1. **Database is ephemeral** - User data resets on Space restart
|
| 26 |
-
- Fix: Use external database (Supabase, Neon, etc.)
|
| 27 |
-
2. **CSRF disabled** - For security, should be re-enabled with session backend
|
| 28 |
-
- Fix: Use Flask-Session with Redis or database backend
|
| 29 |
-
|
| 30 |
-
## π Security TODOs
|
| 31 |
-
1. Remove/regenerate API keys from `.env` (currently in git)
|
| 32 |
-
- Move to HF Space Repository Secrets instead
|
| 33 |
-
2. Regenerate these keys:
|
| 34 |
-
- `OPENROUTER_API_KEY`
|
| 35 |
-
- `PERPLEXITY_API_KEY`
|
| 36 |
-
3. Re-enable CSRF with persistent session backend
|
| 37 |
-
|
| 38 |
-
## π File Structure
|
| 39 |
-
```
|
| 40 |
-
hf-space/
|
| 41 |
-
βββ Dockerfile # HF Spaces compatible
|
| 42 |
-
βββ start.sh # Database init + gunicorn startup
|
| 43 |
-
βββ requirements.txt # All Python dependencies
|
| 44 |
-
βββ .env # Environment variables (move to secrets!)
|
| 45 |
-
βββ config.py # Flask configuration
|
| 46 |
-
βββ run.py # App entry point
|
| 47 |
-
βββ web_app/
|
| 48 |
-
β βββ __init__.py # App factory
|
| 49 |
-
β βββ auth_routes.py # Login/registration (with logging)
|
| 50 |
-
β βββ models.py # Database models
|
| 51 |
-
β βββ templates/ # HTML templates
|
| 52 |
-
β βββ static/ # CSS, JS
|
| 53 |
-
βββ src/ # Learning path generation
|
| 54 |
-
βββ backend/ # API routes
|
| 55 |
-
βββ migrations/ # Database migrations
|
| 56 |
-
```
|
| 57 |
-
|
| 58 |
-
## π Next Steps
|
| 59 |
-
1. Test login/registration functionality
|
| 60 |
-
2. For persistent data, configure external database
|
| 61 |
-
3. Move secrets from .env to HF Spaces Repository Secrets
|
| 62 |
-
4. Re-enable CSRF with Flask-Session backend
|
| 63 |
-
5. Add monitoring/error tracking (Sentry, etc.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
CHANGED
|
@@ -33,10 +33,7 @@ RUN pip install --no-cache-dir --upgrade pip && \
|
|
| 33 |
COPY --chown=user . .
|
| 34 |
|
| 35 |
# Create necessary directories with proper permissions
|
| 36 |
-
RUN mkdir -p vector_db cache learning_paths
|
| 37 |
-
|
| 38 |
-
# Make startup script executable
|
| 39 |
-
RUN chmod +x start.sh
|
| 40 |
|
| 41 |
# Hugging Face Spaces requires port 7860
|
| 42 |
EXPOSE 7860
|
|
@@ -45,7 +42,6 @@ EXPOSE 7860
|
|
| 45 |
ENV PORT=7860
|
| 46 |
ENV FLASK_ENV=production
|
| 47 |
ENV PYTHONUNBUFFERED=1
|
| 48 |
-
ENV FLASK_APP=web_app:create_app
|
| 49 |
|
| 50 |
-
# Run the
|
| 51 |
-
CMD ["
|
|
|
|
| 33 |
COPY --chown=user . .
|
| 34 |
|
| 35 |
# Create necessary directories with proper permissions
|
| 36 |
+
RUN mkdir -p vector_db cache learning_paths
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
# Hugging Face Spaces requires port 7860
|
| 39 |
EXPOSE 7860
|
|
|
|
| 42 |
ENV PORT=7860
|
| 43 |
ENV FLASK_ENV=production
|
| 44 |
ENV PYTHONUNBUFFERED=1
|
|
|
|
| 45 |
|
| 46 |
+
# Run the Flask app with gunicorn
|
| 47 |
+
CMD ["gunicorn", "run:app", "--bind", "0.0.0.0:7860", "--workers", "2", "--timeout", "120"]
|
SETUP_DATABASE.md
DELETED
|
@@ -1,77 +0,0 @@
|
|
| 1 |
-
# Setup Persistent PostgreSQL Database (FREE)
|
| 2 |
-
|
| 3 |
-
Your user database is currently wiped on every HF Space rebuild. Here's how to fix it:
|
| 4 |
-
|
| 5 |
-
## β οΈ Supabase Issue: IPv6 Only (Won't Work on HF Spaces)
|
| 6 |
-
Supabase free tier only provides IPv6 connections, but HF Spaces doesn't support IPv6. Use **Neon.tech instead**.
|
| 7 |
-
|
| 8 |
-
## Option 1: Neon.tech (Recommended - IPv4 + Free)
|
| 9 |
-
|
| 10 |
-
1. **Create Free Account**: Go to [neon.tech](https://neon.tech) and sign up with GitHub/email
|
| 11 |
-
|
| 12 |
-
2. **Create New Project**:
|
| 13 |
-
- Click "Create a project"
|
| 14 |
-
- Choose a name (e.g., "learning-path-db")
|
| 15 |
-
- Select region: **US East (Ohio)** or closest to you
|
| 16 |
-
- Click "Create project"
|
| 17 |
-
|
| 18 |
-
3. **Get Connection String**:
|
| 19 |
-
- After project creation, you'll see **"Connection Details"**
|
| 20 |
-
- Copy the **"Connection string"** (the one that starts with `postgresql://`)
|
| 21 |
-
- It looks like: `postgresql://username:password@ep-xxx.us-east-2.aws.neon.tech/neondb?sslmode=require`
|
| 22 |
-
- **Important**: This connection string uses **IPv4** and works with HF Spaces!
|
| 23 |
-
|
| 24 |
-
4. **Add to Hugging Face**:
|
| 25 |
-
- Go to your HF Space: https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE
|
| 26 |
-
- Click "Settings" tab
|
| 27 |
-
- Scroll to "Repository secrets"
|
| 28 |
-
- Click "Add a secret"
|
| 29 |
-
- Name: `DATABASE_URL`
|
| 30 |
-
- Value: Paste the Neon connection string
|
| 31 |
-
- Click "Add secret"
|
| 32 |
-
|
| 33 |
-
5. **Restart Your Space**:
|
| 34 |
-
- Go back to "Files" tab
|
| 35 |
-
- Space will auto-rebuild with new database
|
| 36 |
-
- Users will persist across rebuilds! β
|
| 37 |
-
|
| 38 |
-
**Free tier limits**: 512 MB storage, 3 GB data transfer/month - plenty for personal use!
|
| 39 |
-
|
| 40 |
-
## Option 2: Supabase (Requires IPv4 Addon - $4/month)
|
| 41 |
-
|
| 42 |
-
**Skip this** - Supabase free tier won't work due to IPv6-only connections.
|
| 43 |
-
If you upgrade to IPv4 addon ($4/month), follow original instructions above.
|
| 44 |
-
|
| 45 |
-
## Option 3: ElephantSQL (Traditional)
|
| 46 |
-
|
| 47 |
-
1. Go to [elephantsql.com](https://www.elephantsql.com)
|
| 48 |
-
2. Sign up and create "Tiny Turtle" plan (free)
|
| 49 |
-
3. Copy the URL
|
| 50 |
-
4. Add as `DATABASE_URL` secret in HF Spaces
|
| 51 |
-
|
| 52 |
-
---
|
| 53 |
-
|
| 54 |
-
## Current Setup
|
| 55 |
-
|
| 56 |
-
Your app already supports PostgreSQL - it will automatically:
|
| 57 |
-
- Detect `SUPABASE_DB_URL` or `DATABASE_URL` environment variable
|
| 58 |
-
- Use PostgreSQL if found, otherwise fallback to SQLite
|
| 59 |
-
- Create all tables on first run
|
| 60 |
-
|
| 61 |
-
## Verify It Works
|
| 62 |
-
|
| 63 |
-
After adding the database secret:
|
| 64 |
-
1. Wait for Space rebuild (1-2 minutes)
|
| 65 |
-
2. Create a new user account
|
| 66 |
-
3. Restart your Space manually
|
| 67 |
-
4. Try logging in with same credentials
|
| 68 |
-
5. If login works, database is persistent! π
|
| 69 |
-
|
| 70 |
-
---
|
| 71 |
-
|
| 72 |
-
**Note**: The free tier limits:
|
| 73 |
-
- Supabase: 500MB database, 2GB bandwidth/month
|
| 74 |
-
- Neon: 512MB storage, shared CPU
|
| 75 |
-
- ElephantSQL: 20MB database, 5 concurrent connections
|
| 76 |
-
|
| 77 |
-
All are plenty for learning path app with personal use.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
encode_password.py
DELETED
|
@@ -1,57 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
URL-encode your Supabase password for connection string
|
| 4 |
-
"""
|
| 5 |
-
from urllib.parse import quote_plus
|
| 6 |
-
|
| 7 |
-
print("=" * 60)
|
| 8 |
-
print("Supabase Password URL Encoder")
|
| 9 |
-
print("=" * 60)
|
| 10 |
-
print()
|
| 11 |
-
|
| 12 |
-
# Example connection string format
|
| 13 |
-
print("Your connection string looks like:")
|
| 14 |
-
print("postgresql://postgres:YOUR_PASSWORD@db.xxx.supabase.co:5432/postgres")
|
| 15 |
-
print()
|
| 16 |
-
|
| 17 |
-
# Get password from user
|
| 18 |
-
password = input("Enter your Supabase database password: ")
|
| 19 |
-
|
| 20 |
-
# URL encode it
|
| 21 |
-
encoded_password = quote_plus(password)
|
| 22 |
-
|
| 23 |
-
print()
|
| 24 |
-
print("=" * 60)
|
| 25 |
-
print(f"Original password: {password}")
|
| 26 |
-
print(f"Encoded password: {encoded_password}")
|
| 27 |
-
print("=" * 60)
|
| 28 |
-
print()
|
| 29 |
-
|
| 30 |
-
# Get the full connection string
|
| 31 |
-
print("Now paste your FULL connection string from Supabase:")
|
| 32 |
-
conn_string = input().strip()
|
| 33 |
-
|
| 34 |
-
# Replace the password
|
| 35 |
-
if '@' in conn_string:
|
| 36 |
-
# Extract parts
|
| 37 |
-
parts = conn_string.split('@')
|
| 38 |
-
prefix = parts[0] # postgresql://postgres:PASSWORD
|
| 39 |
-
suffix = '@'.join(parts[1:]) # db.xxx.supabase.co:5432/postgres
|
| 40 |
-
|
| 41 |
-
# Replace password in prefix
|
| 42 |
-
if ':' in prefix:
|
| 43 |
-
protocol_user = ':'.join(prefix.split(':')[:-1]) # postgresql://postgres
|
| 44 |
-
new_conn_string = f"{protocol_user}:{encoded_password}@{suffix}"
|
| 45 |
-
|
| 46 |
-
print()
|
| 47 |
-
print("=" * 60)
|
| 48 |
-
print("β
ENCODED CONNECTION STRING (copy this):")
|
| 49 |
-
print("=" * 60)
|
| 50 |
-
print(new_conn_string)
|
| 51 |
-
print()
|
| 52 |
-
print("Add this to HF Spaces as SUPABASE_DB_URL secret")
|
| 53 |
-
print("=" * 60)
|
| 54 |
-
else:
|
| 55 |
-
print("β Invalid connection string format")
|
| 56 |
-
else:
|
| 57 |
-
print("β Invalid connection string - no @ symbol found")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start.sh
DELETED
|
@@ -1,66 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
set -e
|
| 3 |
-
|
| 4 |
-
echo "=== Starting AI Learning Path Generator ==="
|
| 5 |
-
|
| 6 |
-
# Set Flask app for migrations
|
| 7 |
-
export FLASK_APP=web_app:create_app
|
| 8 |
-
|
| 9 |
-
# Debug: Check if SECRET_KEY is set
|
| 10 |
-
if [ -z "$SECRET_KEY" ]; then
|
| 11 |
-
echo "WARNING: SECRET_KEY is not set! Sessions/CSRF will not work properly."
|
| 12 |
-
else
|
| 13 |
-
echo "SECRET_KEY is configured (length: ${#SECRET_KEY})"
|
| 14 |
-
fi
|
| 15 |
-
|
| 16 |
-
# Initialize database if it doesn't exist
|
| 17 |
-
echo "Initializing database..."
|
| 18 |
-
python -c "
|
| 19 |
-
from web_app import create_app
|
| 20 |
-
from web_app.models import db, User
|
| 21 |
-
from config import Config
|
| 22 |
-
from sqlalchemy import text
|
| 23 |
-
|
| 24 |
-
print(f'SECRET_KEY set: {bool(Config.SECRET_KEY)}')
|
| 25 |
-
print(f'IS_PRODUCTION: {Config.IS_PRODUCTION}')
|
| 26 |
-
print(f'SESSION_COOKIE_SECURE: {Config.SESSION_COOKIE_SECURE}')
|
| 27 |
-
print(f'Database URI: {Config.SQLALCHEMY_DATABASE_URI[:50]}...')
|
| 28 |
-
|
| 29 |
-
app = create_app()
|
| 30 |
-
with app.app_context():
|
| 31 |
-
try:
|
| 32 |
-
# Try to query users table - if this works, DB is fine
|
| 33 |
-
user_count = User.query.count()
|
| 34 |
-
print(f'β
Database working ({user_count} users)')
|
| 35 |
-
except Exception as check_error:
|
| 36 |
-
print(f'Database needs setup: {str(check_error)[:100]}')
|
| 37 |
-
|
| 38 |
-
# Nuclear option: drop and recreate entire public schema
|
| 39 |
-
try:
|
| 40 |
-
print('Performing complete database reset...')
|
| 41 |
-
with db.engine.connect() as conn:
|
| 42 |
-
conn.execute(text('DROP SCHEMA public CASCADE'))
|
| 43 |
-
conn.commit()
|
| 44 |
-
print(' Schema dropped')
|
| 45 |
-
|
| 46 |
-
conn.execute(text('CREATE SCHEMA public'))
|
| 47 |
-
conn.commit()
|
| 48 |
-
print(' Schema recreated')
|
| 49 |
-
|
| 50 |
-
# Now create all tables
|
| 51 |
-
db.create_all()
|
| 52 |
-
print('β
Database schema created successfully!')
|
| 53 |
-
|
| 54 |
-
# Verify it works
|
| 55 |
-
user_count = User.query.count()
|
| 56 |
-
print(f'β
Verified: Database has {user_count} users')
|
| 57 |
-
|
| 58 |
-
except Exception as create_error:
|
| 59 |
-
print(f'β Database reset failed: {create_error}')
|
| 60 |
-
raise
|
| 61 |
-
"
|
| 62 |
-
|
| 63 |
-
echo "Starting gunicorn server..."
|
| 64 |
-
# Use 1 worker to avoid CSRF token mismatch between workers
|
| 65 |
-
# Use threads instead for concurrency
|
| 66 |
-
exec gunicorn run:app --bind 0.0.0.0:7860 --workers 1 --threads 4 --timeout 120
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
web_app/templates/redirect.html
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8">
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>Success!</title>
|
| 7 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
-
<link rel="stylesheet" href="{{ url_for('static', filename='css/glassmorphic.css') }}">
|
| 9 |
-
</head>
|
| 10 |
-
<body class="grid-background min-h-screen flex items-center justify-center px-6">
|
| 11 |
-
<div class="glass-card p-8 max-w-md text-center">
|
| 12 |
-
<div class="mb-6">
|
| 13 |
-
<svg class="w-20 h-20 mx-auto text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 14 |
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
| 15 |
-
</svg>
|
| 16 |
-
</div>
|
| 17 |
-
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-4">
|
| 18 |
-
Welcome! π
|
| 19 |
-
</h1>
|
| 20 |
-
<p class="text-gray-600 dark:text-gray-400 mb-8">
|
| 21 |
-
You're successfully logged in!
|
| 22 |
-
</p>
|
| 23 |
-
<a href="{{ redirect_url }}" class="block w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-lg transition">
|
| 24 |
-
Go to Dashboard β
|
| 25 |
-
</a>
|
| 26 |
-
</div>
|
| 27 |
-
</body>
|
| 28 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|