Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CEREBRAS_API_KEY=your_cerebras_api_key_here
MONGO_URI=mongodb+srv://username:password@cluster.mongodb.net/database_name?retryWrites=true&w=majority

# Google OAuth Configuration (optional - leave empty to disable OAuth)
GOOGLE_CLIENT_ID=your_google_client_id_here
245 changes: 175 additions & 70 deletions backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,169 @@
from datetime import datetime
from dotenv import load_dotenv
from llm_service import LLM
from auth import SimpleAuth

# Load environment variables
load_dotenv()

app = Flask(__name__)
CORS(app)

# Initialize LLM service
# Initialize services
llm_service = LLM()
auth_service = SimpleAuth()

# In-memory storage for demo purposes (use a database in production)
# Data structure: users -> sessions -> quizzes
users = {} # user_id -> user_data
sessions = {} # session_id -> session_data
quizzes = {} # quiz_id -> quizzes

# Add test data
def initialize_test_data():
"""Initialize test data for development and testing"""
global users, sessions, quizzes

# Test user
users['1'] = {
'id': '1',
'username': 'alice',
'created_at': '2024-01-15T10:30:00',
'sessions': ['session1', 'session2']
}

# Test sessions
sessions['session1'] = {
'id': 'session1',
'user_id': '1',
'created_at': '2024-01-15T10:30:00',
'title': 'React component',
'messages': [
{
'id': 'msg1',
'user_message': 'How to create a button component?',
'chat_response': 'Here is how to create a button component...',
'timestamp': '2024-01-15T10:30:00'
}
],
'quizzes': [],
'conversation_history': [
{"role": "user", "content": "How to create a button component?"},
{"role": "assistant", "content": "Here is how to create a button component..."}
]
}

sessions['session2'] = {
'id': 'session2',
'user_id': '1',
'created_at': '2024-01-14T15:45:00',
'title': 'JavaScript patterns',
'messages': [
{
'id': 'msg2',
'user_message': 'What are async/await best practices?',
'chat_response': 'Async/await best practices include...',
'timestamp': '2024-01-14T15:45:00'
}
],
'quizzes': [],
'conversation_history': [
{"role": "user", "content": "What are async/await best practices?"},
{"role": "assistant", "content": "Async/await best practices include..."}
]
}

# Initialize test data
initialize_test_data()

# Authentication Routes
@app.route('/api/auth/google', methods=['POST'])
def google_oauth():
"""Handle Google OAuth authentication"""
try:
data = request.get_json()
id_token = data.get('id_token')

if not id_token:
return jsonify({'error': 'ID token is required'}), 400

# Verify Google token
user_info = auth_service.verify_google_token(id_token)
if not user_info:
return jsonify({'error': 'Invalid Google token or OAuth not configured'}), 401

# Check if user exists, create if not
user_id = user_info['id']
if user_id not in users:
users[user_id] = {
'id': user_id,
'username': user_info['name'],
'email': user_info['email'],
'picture': user_info.get('picture', ''),
'is_guest': False,
'created_at': datetime.now().isoformat(),
'sessions': []
}

return jsonify({
'user': {
'id': user_id,
'username': user_info['name'],
'email': user_info['email'],
'picture': user_info.get('picture', ''),
'is_guest': False
}
})

except Exception as e:
return jsonify({'error': str(e)}), 500

@app.route('/api/auth/guest', methods=['POST'])
def guest_login():
"""Handle guest login with just a username"""
try:
data = request.get_json()
username = data.get('username', '').strip()

if not username:
return jsonify({'error': 'Username is required'}), 400

# Create guest user
user_info = auth_service.create_guest_user(username)
user_id = user_info['id']

# Store guest user
users[user_id] = {
'id': user_id,
'username': user_info['name'],
'email': '',
'picture': '',
'is_guest': True,
'created_at': datetime.now().isoformat(),
'sessions': []
}

return jsonify({
'user': {
'id': user_id,
'username': user_info['name'],
'email': '',
'picture': '',
'is_guest': True
}
})

except Exception as e:
return jsonify({'error': str(e)}), 500

@app.route('/api/auth/config', methods=['GET'])
def auth_config():
"""Get authentication configuration"""
return jsonify({
'google_oauth_enabled': bool(auth_service.google_client_id),
'guest_enabled': True
})

@app.route('/api/users', methods=['POST'])
def create_user():
"""Create a new user"""
Expand Down Expand Up @@ -320,52 +467,34 @@ def continue_quiz_conversation(quiz_id):
except Exception as e:
return jsonify({'error': str(e)}), 500

# @app.route('/api/session/<session_id>', methods=['GET'])
# def get_session(session_id):
# """Get session history"""
# try:
# if session_id not in sessions:
# return jsonify({'error': 'Session not found'}), 404

# session = sessions[session_id]
# session_quizzes = []

# for quiz_id in session['quizzes']:
# if quiz_id in quizzes:
# quiz = quizzes[quiz_id]
# session_quizzes.append({
# 'quiz_id': quiz_id,
# 'completed': quiz['completed']
# })

# return jsonify({
# 'session_id': session_id,
# 'user_id': session['user_id'],
# 'created_at': session['created_at'],
# 'messages': session['messages'],
# 'quizzes': session_quizzes
# })

# except Exception as e:
# return jsonify({'error': str(e)}), 500

def get_all_conversations():
"""Get all conversations with id, lastMessage, and timestamp"""
def get_all_conversations(user_id):
"""Get all conversations for a specific user with id, lastMessage, and timestamp"""
try:
conversations = []

for session_id, session_data in sessions.items():
# Get user's sessions directly
if user_id not in users:
return []

user_sessions = users[user_id].get('sessions', [])

for session_id in user_sessions:
if session_id not in sessions:
continue

session_data = sessions[session_id]

# Get the last message from conversation history
last_message = ""
timestamp = session_data.get('created_at', '')

# Check if there are messages in the session
if session_data.get('messages'):
if session_data.get('messages') and len(session_data['messages']) > 0:
# Get the last message from the messages array
last_message_obj = session_data['messages'][-1]
last_message = last_message_obj.get('user_message', '')
timestamp = last_message_obj.get('timestamp', timestamp)
elif session_data.get('conversation_history'):
elif session_data.get('conversation_history') and len(session_data['conversation_history']) > 0:
# Fallback to conversation history if no messages array
# Find the last user message
for msg in reversed(session_data['conversation_history']):
Expand All @@ -375,8 +504,10 @@ def get_all_conversations():

conversations.append({
'id': session_id,
'title': session_data.get('title', ''),
'lastMessage': last_message,
'timestamp': timestamp
'timestamp': timestamp,
'isActive': False
})

# Sort by timestamp (most recent first)
Expand All @@ -385,47 +516,21 @@ def get_all_conversations():
return conversations

except Exception as e:
print(f"Error getting all conversations: {e}")
print(f"Error getting conversations for user {user_id}: {e}")
return []

@app.route('/api/conversations', methods=['GET'])
def get_all_conversations_endpoint():
"""Get all conversations endpoint"""
@app.route('/api/users/<user_id>/conversations', methods=['GET'])
def get_all_conversations_endpoint(user_id):
"""Get all conversations for a specific user"""
try:
conversations = get_all_conversations()
if user_id not in users:
return jsonify({'error': 'User not found'}), 404

conversations = get_all_conversations(user_id)
return jsonify(conversations)

except Exception as e:
return jsonify({'error': str(e)}), 500

# @app.route('/api/users/<user_id>/sessions', methods=['GET'])
# def get_user_sessions(user_id):
# """Get all sessions for a user"""
# try:
# if user_id not in users:
# return jsonify({'error': 'User not found'}), 404

# user = users[user_id]
# user_sessions = []

# for session_id in user['sessions']:
# if session_id in sessions:
# session = sessions[session_id]
# user_sessions.append({
# 'session_id': session_id,
# 'created_at': session['created_at'],
# 'message_count': len(session['messages']),
# 'quiz_count': len(session['quizzes'])
# })

# return jsonify({
# 'user_id': user_id,
# 'username': user['username'],
# 'sessions': user_sessions
# })

# except Exception as e:
# return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
45 changes: 45 additions & 0 deletions backend/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import os
import uuid
from datetime import datetime
from google.auth.transport import requests as google_requests
from google.oauth2 import id_token

class SimpleAuth:
def __init__(self):
self.google_client_id = os.getenv('GOOGLE_CLIENT_ID')

def verify_google_token(self, token):
"""Verify Google OAuth token and return user info"""
try:
if not self.google_client_id:
return None

# Verify the token with Google
idinfo = id_token.verify_oauth2_token(
token, google_requests.Request(), self.google_client_id
)

# Check if token is from correct issuer
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise ValueError('Wrong issuer.')

return {
'id': idinfo['sub'],
'email': idinfo['email'],
'name': idinfo.get('name', ''),
'picture': idinfo.get('picture', ''),
'verified_email': idinfo.get('email_verified', False)
}
except Exception as e:
print(f"Token verification failed: {e}")
return None

def create_guest_user(self, username):
"""Create a guest user with just a username"""
return {
'id': str(uuid.uuid4()),
'name': username,
'email': '',
'picture': '',
'is_guest': True
}
Loading