import os | |
import zipfile | |
import uuid | |
from flask import Flask, request, jsonify, render_template, session, send_from_directory | |
from flask_session import Session | |
from dotenv import load_dotenv | |
import google.generativeai as genai | |
# Load env | |
load_dotenv() | |
app = Flask(__name__) | |
app.config["SECRET_KEY"] = os.getenv("FLASK_SECRET_KEY", "insecure") | |
app.config["SESSION_TYPE"] = "filesystem" | |
app.config["SESSION_FILE_DIR"] = './flask_session' | |
app.config["SESSION_PERMANENT"] = False | |
Session(app) | |
UPLOAD_FOLDER = 'uploads' | |
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER | |
os.makedirs(UPLOAD_FOLDER, exist_ok=True) | |
os.makedirs(app.config['SESSION_FILE_DIR'], exist_ok=True) | |
# Configure Gemini | |
gemini_api_key = os.getenv("GEMINI_API_KEY") | |
model = None | |
if gemini_api_key: | |
genai.configure(api_key=gemini_api_key) | |
model = genai.GenerativeModel('gemini-2.0-flash') | |
def get_project_files(project_path): | |
files = {} | |
for root, _, filenames in os.walk(project_path): | |
for file in filenames: | |
if file.startswith('.'): continue | |
rel_path = os.path.relpath(os.path.join(root, file), project_path) | |
try: | |
with open(os.path.join(root, file), 'r', encoding='utf-8', errors='ignore') as f: | |
files[rel_path] = f.read() | |
except Exception as e: | |
files[rel_path] = f"Error reading: {e}" | |
return files | |
def index(): | |
if 'project_id' not in session: | |
session['project_id'] = str(uuid.uuid4()) | |
session['chat_history'] = [] | |
return render_template('index.html') | |
def upload(): | |
if 'project_zip' not in request.files: | |
return jsonify({'error': 'No file uploaded'}), 400 | |
file = request.files['project_zip'] | |
if not file.filename.endswith('.zip'): | |
return jsonify({'error': 'Only .zip supported'}), 400 | |
project_id = session['project_id'] | |
project_path = os.path.join(app.config['UPLOAD_FOLDER'], project_id) | |
if os.path.exists(project_path): | |
for root, dirs, files in os.walk(project_path, topdown=False): | |
for f in files: | |
os.remove(os.path.join(root, f)) | |
for d in dirs: | |
os.rmdir(os.path.join(root, d)) | |
os.makedirs(project_path, exist_ok=True) | |
zip_path = os.path.join(project_path, file.filename) | |
file.save(zip_path) | |
with zipfile.ZipFile(zip_path, 'r') as z: | |
z.extractall(project_path) | |
os.remove(zip_path) | |
files = get_project_files(project_path) | |
context = "User uploaded a project:\n" | |
for path, content in files.items(): | |
context += f"**File:** `{path}`\n```\n{content[:2000]}\n```\n\n" # clip long content | |
session['chat_history'] = [ | |
{"role": "user", "parts": [context]}, | |
{"role": "model", "parts": ["Project loaded. Ready to help."]} | |
] | |
session.modified = True | |
return jsonify({ | |
"message": "Project uploaded.", | |
"file_tree": list(files.keys()), | |
"chat_history": [ | |
{"role": "user", "content": "Project context sent to AI."}, | |
{"role": "assistant", "content": "Project loaded. Ready to help."} | |
] | |
}) | |
def chat(): | |
if not model: | |
return jsonify({"error": "Gemini API not configured"}), 500 | |
msg = request.json.get("message") | |
if not msg: | |
return jsonify({"error": "Empty message"}), 400 | |
session['chat_history'].append({"role": "user", "parts": [msg]}) | |
try: | |
chat = model.start_chat(history=session['chat_history']) | |
reply = chat.send_message(msg).text | |
session['chat_history'].append({"role": "model", "parts": [reply]}) | |
return jsonify({"reply": reply}) | |
except Exception as e: | |
return jsonify({"error": str(e)}), 500 | |
def file_tree(): | |
pid = session.get('project_id') | |
if not pid: | |
return jsonify({"file_tree": []}) | |
path = os.path.join(app.config['UPLOAD_FOLDER'], pid) | |
return jsonify({"file_tree": list(get_project_files(path).keys())}) | |
def file_content(): | |
pid = session.get('project_id') | |
filepath = request.args.get('path') | |
full = os.path.join(app.config['UPLOAD_FOLDER'], pid, filepath) | |
try: | |
with open(full, 'r', encoding='utf-8', errors='ignore') as f: | |
return jsonify({"content": f.read()}) | |
except Exception as e: | |
return jsonify({"error": str(e)}) | |
def download(): | |
pid = session.get('project_id') | |
if not pid: | |
return "No project uploaded", 404 | |
path = os.path.join(app.config['UPLOAD_FOLDER'], pid) | |
zpath = os.path.join(app.config['UPLOAD_FOLDER'], f"{pid}.zip") | |
with zipfile.ZipFile(zpath, 'w') as zipf: | |
for root, _, files in os.walk(path): | |
for file in files: | |
fpath = os.path.join(root, file) | |
zipf.write(fpath, os.path.relpath(fpath, path)) | |
return send_from_directory(app.config['UPLOAD_FOLDER'], f"{pid}.zip", as_attachment=True) | |
if __name__ == "__main__": | |
app.run(host='0.0.0.0', port=7860) | |