Building a Gemini Clone with Python Flask and Firebase database & Auth
Are you interested in creating a real-time, Firebase-powered chat app using Python and Flask? This tutorial will guide you through building a Gemini clone with fully functional Firebase Authentication and Database capabilities, perfect for developers keen on crafting a responsive chat application. You’ll be able to check connectivity, manage chats, handle file inputs, and authenticate users with Firebase.
The code for this project is available on
Prerequisites
Before starting, make sure you have the following:
Project Setup
Step 1: Install Necessary Libraries
To begin, install Flask and the Firebase Admin SDK with:
pip install Flask firebase-admin
Step 2: Initialize Firebase
Create a serviceAccountKey.json file containing Firebase credentials. In main.py, we import this key to initialize the Firebase Admin SDK, which connects the app to your Firebase project.
from firebase_admin import credentials, auth, db, storage
cred = credentials.Certificate('./serviceAccountKey.json')
firebase_admin.initialize_app(cred, {
'databaseURL': 'https://your-database-url.firebaseio.com',
'storageBucket': 'your-bucket-id.appspot.com'
})
Code Breakdown
1. Home Page - User Authentication Check
The home() route checks if a user is authenticated before allowing access to the home page. It retrieves the UID (User ID) from a cookie and verifies it with Firebase Authentication.
@app.route("/")
def home():
uid = request.cookies.get('uid')
if not uid:
return redirect(url_for('login'))
try:
user = auth.get_user(uid)
except Exception:
return redirect(url_for('login'))
# If verified, access user data in Firebase Realtime Database
user_ref = db.reference(f'users/{uid}')
snapshot = user_ref.get()
if snapshot is None:
user_ref.set({'createdAt': datetime.now().isoformat()})
return render_template("index.html")
2. Login Page
This route renders the login.html page, where users can log in. The actual authentication process occurs on the client side using Firebase Authentication.
@app.route("/login")
def login():
return render_template("login.html")
3. Connection Check
The /check_connection route validates that Firebase Database and Storage are accessible. It reads a dummy reference in the database and lists files in storage to confirm connectivity, returning a JSON response indicating the status.
@app.route("/check_connection")
def check_connection():
response = {'database': 'Not connected', 'storage': 'Not connected'}
try:
ref = db.reference('/test_connection').get()
response['database'] = 'Connected'
except Exception as e:
response['database'] = f'Failed to connect: {str(e)}'
try:
bucket = storage.bucket()
blobs = bucket.list_blobs(max_results=1)
response['storage'] = 'Connected'
except Exception as e:
response['storage'] = f'Failed to connect: {str(e)}'
return jsonify(response)
4. Create a New Chat
The /createNewChat route lets users create a new chat by sending a POST request with chat text data. It checks for existing chats to avoid duplicates.
@app.route('/createNewChat', methods=['POST'])
def create_new_chat():
data = request.get_json()
text = data.get('text')
uid = request.cookies.get('uid')
ref = db.reference(f'users/{uid}/chats')
if ref.child(text).get():
return jsonify({'message': 'Chat already exists. Choose another one.'}), 400
ref.child(text).set({'createdAt': datetime.now(timezone.utc).isoformat()})
return jsonify({'message': 'Chat message added successfully.'}), 200
5. Retrieve Chat List
With the /getChats route, the user can fetch a list of existing chats. It retrieves chat names from Firebase and returns them in JSON format.
@app.route('/getChats', methods=['GET'])
def get_chats():
uid = request.cookies.get('uid')
chats_ref = db.reference(f'users/{uid}/chats')
chat_names = list(chats_ref.get().keys())
return jsonify({'chats': chat_names}), 200
6. Chat with Gemini Model
The /api route is a multi-purpose API endpoint that accepts user text input, image files, or audio files to simulate chat interactions with Gemini AI.
The function returns a response based on the input type, generating streaming responses from Gemini AI.
@app.route("/api", methods=["POST"])
def qa():
if not os.path.exists('temp'):
os.makedirs('temp')
text = request.form.get('text', '')
file = request.files.get('file')
audioFile = request.files.get('audioFile')
selectedChatName = request.form.get('selectedChatName')
response_text = ""
if file:
filename = secure_filename(file.filename)
file_path = os.path.join('temp', filename)
file.save(file_path)
# Process image and send to model
elif audioFile:
audio_filename = secure_filename(audioFile.filename)
audio_file_path = os.path.join('temp', audio_filename)
audioFile.save(audio_file_path)
# Process audio and send to model
else:
prompt = f"{text}"
response = chat.send_message(prompt, stream=True)
def generate():
for chunk in response:
yield chunk.text
return Response(generate(), content_type='text/plain')
Firebase Auth with html
Firebase Authentication for a smooth signup and login experience. The following HTML code (login.html) includes Firebase Auth configuration and functions to handle user sign-up and login, which helps manage authentication in our app seamlessly.
In the beginning, the document specifies that it's an HTML file (<!DOCTYPE html>), with the default language set to English (lang="en"). The meta tags establish the document's character set as UTF-8 and set up the viewport for mobile responsiveness, enabling the page to adjust its layout on different screen sizes, including phones and tablets.
<meta charset="UTF-8">
<title>Sign up / Login Form</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/css/login.css">
We then link a CSS file located at /static/css/login.css, which contains the styles for the login and signup forms
Firebase SDK Integration
The Firebase Authentication SDKs are added next to power user authentication. Two SDKs are specifically included:
<!-- Firebase SDKs -->
<script src="https://www.gstatic.com/firebasejs/9.6.1/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.6.1/firebase-auth-compat.js"></script>
These SDKs are hosted by Google and integrated using <script> tags. Each one is imported from Firebase’s own CDN, ensuring the latest compatible version is loaded without needing to manage versions locally.
Firebase Configuration
Following the SDK inclusion, we define the Firebase configuration in JavaScript. The firebaseConfig object contains the necessary details for initializing our Firebase app and connecting to the Firebase project
<script>
// Firebase configuration
const firebaseConfig = {
apiKey: "your-api-key",
authDomain: "your-app-id.firebaseapp.com",
projectId: "your-project-id",
storageBucket: "your-storage-bucket",
messagingSenderId: "your-messaging-sender-id",
appId: "your-app-id",
measurementId: "G-measurement-id"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
</script>
This configuration is specific to our Firebase project and can be retrieved from the Firebase console. Using firebase.initializeApp(firebaseConfig), we initialize Firebase in our application, making the Firebase Authentication object (auth) available for authentication processes throughout the app.
Sign-Up Function
The signUp() function is where the user registration process takes place. It begins by capturing the user’s email and password from input fields with name="email" and name="pswd", respectively.
<script>
// Sign Up Function
async function signUp(event) {
event.preventDefault();
const email = document.querySelector('input[name="email"]').value;
const password = document.querySelector('input[name="pswd"]').value;
try {
const userCredential = await auth.createUserWithEmailAndPassword(email, password);
const uid = userCredential.user.uid;
// Set UID cookie for session persistence
document.cookie = `uid=${uid}; max-age=${365 * 24 * 60 * 60}; path=/;`;
window.location.href = '/'; // Redirect to home after sign-up
} catch (error) {
console.error('Error during sign-up:', error.message);
}
}
</script>
This function simplifies the sign-up process, delegating security checks and error handling to Firebase, which follows best practices in security for managing sensitive data like passwords.
Login Function
The login() function follows a similar pattern to signUp(), except that it’s geared toward existing users who are logging in.
<script>
// Login Function
async function login(event) {
event.preventDefault();
const email = document.querySelector('input[name="loginemail"]').value;
const password = document.querySelector('input[name="loginpswd"]').value;
try {
const userCredential = await auth.signInWithEmailAndPassword(email, password);
const uid = userCredential.user.uid;
// Set UID cookie for session persistence
document.cookie = `uid=${uid}; max-age=${365 * 24 * 60 * 60}; path=/;`;
window.location.href = '/'; // Redirect to home after login
} catch (error) {
console.error('Error during login:', error.message);
}
}
</script>
This function ensures that only registered users can log in, with Firebase handling password verification and user management, significantly reducing the need for our app to handle sensitive data directly.
This login.html file encapsulates both the visual and functional aspects of user authentication for the Gemini Clone application. With Firebase managing the back-end logic for user authentication, this file focuses on securely handling user input, setting session cookies, and directing users to the correct location based on their actions. By leveraging Firebase, we eliminate the need for manual handling of user passwords, reducing risk and allowing Firebase’s secure infrastructure to handle user authentication.
Main index.html file
You will find this file on Github with full project code is available on
Running the Application
To start the server, run:
python main.py
You can access the app at http://localhost:5001
This project has introduced you to building a full-stack Gemini clone using Python Flask and Firebase. You learned how to set up Firebase Authentication, handle file uploads, and communicate with a model like Gemini. With this knowledge, you can enhance the app further by adding features or optimizing performance. All the source code is on GitHub, so feel free to clone and explore!
Happy coding!