Iacode / ide_completa.py
Lucasidcloned's picture
Create ide_completa.py
02dd5cc verified
Raw
History Blame Contribute Delete
4.75 kB
# ============= ide_completa.py =============
from fastapi import APIRouter, HTTPException
from fastapi.responses import FileResponse
from pydantic import BaseModel
import requests
import zipfile
import io
from typing import Dict, List
router = APIRouter()
# MEMÓRIA COMPARTILHADA DO PROJETO
projeto_atual = {
"url": "",
"arquivos": {},
"estrutura": []
}
class LinkGitHub(BaseModel):
url: str
class EditarArquivo(BaseModel):
caminho: str
novo_conteudo: str
def baixar_github(url: str) -> tuple[Dict[str, str], List[str]]:
"""Baixa TODOS os arquivos de um repo GitHub público usando a API do GitHub"""
# Limpa e extrai owner/repo
url = url.rstrip('/')
if url.endswith('.git'):
url = url[:-4]
parts = url.replace('https://github.com/', '').split('/')
if len(parts) < 2:
raise ValueError("URL inválida")
owner, repo = parts[0], parts[1]
# Tenta main primeiro, depois master
for branch in ['main', 'master']:
api_url = f"https://api.github.com/repos/{owner}/{repo}/git/trees/{branch}?recursive=1"
resp = requests.get(api_url)
if resp.status_code == 200:
break
else:
raise ValueError("Repositório não encontrado ou privado")
tree = resp.json().get('tree', [])
arquivos = {}
estrutura = []
for item in tree:
path = item['path']
estrutura.append(path)
# Ignora pastas desnecessárias
if any(x in path for x in ['node_modules', '.git/', 'build/', '.gradle', '.idea']):
continue
if item['type'] == 'blob':
# Baixa conteúdo do arquivo via raw.githubusercontent.com
raw_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}"
try:
file_resp = requests.get(raw_url, timeout=5)
if file_resp.status_code == 200:
try:
# Tenta decodificar como texto UTF-8
arquivos[path] = file_resp.content.decode('utf-8')
except:
# Arquivo binário - ignora
pass
except:
pass
return arquivos, estrutura
@router.post("/carregar-projeto")
async def carregar_projeto(data: LinkGitHub):
"""Carrega projeto completo do GitHub incluindo pasta lib"""
try:
arquivos, estrutura = baixar_github(data.url)
projeto_atual['url'] = data.url
projeto_atual['arquivos'] = arquivos
projeto_atual['estrutura'] = estrutura
return {
"status": "sucesso",
"total_arquivos": len(arquivos),
"estrutura": estrutura
}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@router.get("/estrutura")
def get_estrutura():
"""Retorna estrutura completa de pastas e arquivos"""
return {"estrutura": projeto_atual['estrutura']}
@router.get("/arquivo/{caminho:path}")
def get_arquivo(caminho: str):
"""Obtém conteúdo de um arquivo específico"""
if caminho in projeto_atual['arquivos']:
return {"conteudo": projeto_atual['arquivos'][caminho]}
raise HTTPException(status_code=404, detail="Arquivo não encontrado")
@router.post("/editar-arquivo")
def editar_arquivo(data: EditarArquivo):
"""Salva edição de arquivo no projeto em memória"""
projeto_atual['arquivos'][data.caminho] = data.novo_conteudo
return {"status": "salvo", "arquivo": data.caminho}
@router.get("/baixar-projeto")
def baixar_projeto():
"""Gera ZIP do projeto completo com todas as modificações"""
if not projeto_atual['arquivos']:
raise HTTPException(status_code=400, detail="Nenhum projeto carregado")
# Cria ZIP em memória
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for caminho, conteudo in projeto_atual['arquivos'].items():
zip_file.writestr(caminho, conteudo)
zip_buffer.seek(0)
# Salva temporariamente para download
with open('/tmp/projeto.zip', 'wb') as f:
f.write(zip_buffer.getvalue())
return FileResponse(
'/tmp/projeto.zip',
media_type='application/zip',
filename='projeto_modificado.zip'
)
@router.get("/status-projeto")
def status_projeto():
"""Status atual do projeto carregado"""
return {
"carregado": bool(projeto_atual['arquivos']),
"url": projeto_atual['url'],
"total_arquivos": len(projeto_atual['arquivos']),
"total_pastas": len(projeto_atual['estrutura'])
}