“shubhamdhamal”
commited on
Commit
·
a08ee99
1
Parent(s):
36c67d1
Revert to simple SQLite - remove PostgreSQL complexity
Browse files- DEPLOYMENT_STATUS.md +63 -0
- Dockerfile +7 -3
- start.sh +66 -0
- web_app/templates/redirect.html +28 -0
DEPLOYMENT_STATUS.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,7 +33,10 @@ 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 |
# Hugging Face Spaces requires port 7860
|
| 39 |
EXPOSE 7860
|
|
@@ -42,6 +45,7 @@ EXPOSE 7860
|
|
| 42 |
ENV PORT=7860
|
| 43 |
ENV FLASK_ENV=production
|
| 44 |
ENV PYTHONUNBUFFERED=1
|
|
|
|
| 45 |
|
| 46 |
-
# Run the
|
| 47 |
-
CMD ["
|
|
|
|
| 33 |
COPY --chown=user . .
|
| 34 |
|
| 35 |
# Create necessary directories with proper permissions
|
| 36 |
+
RUN mkdir -p vector_db cache learning_paths instance
|
| 37 |
+
|
| 38 |
+
# Make startup script executable
|
| 39 |
+
RUN chmod +x start.sh
|
| 40 |
|
| 41 |
# Hugging Face Spaces requires port 7860
|
| 42 |
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 startup script (initializes DB then starts gunicorn)
|
| 51 |
+
CMD ["bash", "start.sh"]
|
start.sh
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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>
|