diff --git a/backend/api.py b/backend/api.py index 3dbdfba..804fe51 100644 --- a/backend/api.py +++ b/backend/api.py @@ -40,7 +40,7 @@ api_router = APIRouter(prefix="/api") # Serve minimal frontend scaffold -app.mount("/static", StaticFiles(directory="/home/davi/topicos/frontend"), name="static") +app.mount("/static", StaticFiles(directory="/workspaces/DiabetesAI/frontend"), name="static") # Enable CORS (allows frontend to call the API) app.add_middleware( @@ -235,32 +235,32 @@ class ChatResponse(BaseModel): @app.get("/ui") async def serve_ui(): """Serve the frontend index page which redirects based on auth status""" - return FileResponse("/home/davi/topicos/frontend/index.html") + return FileResponse("/workspaces/DiabetesAI/frontend/index.html") @app.get("/login") @app.get("/login.html") async def serve_login(): - return FileResponse("/home/davi/topicos/frontend/login.html") + return FileResponse("/workspaces/DiabetesAI/frontend/login.html") @app.get("/register") @app.get("/register.html") async def serve_register(): - return FileResponse("/home/davi/topicos/frontend/register.html") + return FileResponse("/workspaces/DiabetesAI/frontend/register.html") @app.get("/home") @app.get("/home.html") async def serve_home(): - return FileResponse("/home/davi/topicos/frontend/home.html") + return FileResponse("/workspaces/DiabetesAI/frontend/home.html") @app.get("/onboarding") @app.get("/onboarding.html") async def serve_onboarding(): """Serve onboarding page for first-time users""" - return FileResponse("/home/davi/topicos/frontend/onboarding.html") + return FileResponse("/workspaces/DiabetesAI/frontend/onboarding.html") @app.on_event("startup") diff --git a/backend/storage.py b/backend/storage.py index 4781173..1c0ea15 100644 --- a/backend/storage.py +++ b/backend/storage.py @@ -15,18 +15,18 @@ logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) -# Database configuration - PostgreSQL only +# Database configuration - PostgreSQL or SQLite PROJECT_ROOT = Path(__file__).parent.absolute() DATA_DIR = PROJECT_ROOT / "data" -# PostgreSQL database URL (required) +# Database URL (PostgreSQL or SQLite) DATABASE_URL = os.getenv( "DATABASE_URL", - "postgresql+psycopg2://diabetes_user:diabetes123@localhost:5432/diabetesai", + "sqlite:///./data/diabetesai.db", ) -if not DATABASE_URL.startswith("postgresql"): - raise ValueError("DATABASE_URL must be a PostgreSQL connection string") +if not (DATABASE_URL.startswith("postgresql") or DATABASE_URL.startswith("sqlite")): + raise ValueError("DATABASE_URL must be a PostgreSQL or SQLite connection string") # Ensure data directory exists for other data files DATA_DIR.mkdir(parents=True, exist_ok=True) @@ -74,21 +74,28 @@ class ConsumedMealRecord(Base): def _engine(): - """Create and return a PostgreSQL SQLAlchemy engine.""" + """Create and return a SQLAlchemy engine for PostgreSQL or SQLite.""" try: - engine_kwargs = { - "future": True, - "pool_pre_ping": True, # Test connections before using them - "echo": False, # Set to True for SQL logging during development - "pool_size": 10, # Connection pool size - "max_overflow": 20, # Max overflow connections - "pool_timeout": 30, # Connection timeout - "pool_recycle": 3600, # Recycle connections after 1 hour - } + if DATABASE_URL.startswith("sqlite"): + engine_kwargs = { + "future": True, + "echo": False, # Set to True for SQL logging during development + } + logger.info("🔧 Using SQLite database configuration") + else: + engine_kwargs = { + "future": True, + "pool_pre_ping": True, # Test connections before using them + "echo": False, # Set to True for SQL logging during development + "pool_size": 10, # Connection pool size + "max_overflow": 20, # Max overflow connections + "pool_timeout": 30, # Connection timeout + "pool_recycle": 3600, # Recycle connections after 1 hour + } + logger.info("🔧 Using PostgreSQL database configuration") - logger.info("🔧 Using PostgreSQL database configuration") engine = create_engine(DATABASE_URL, **engine_kwargs) - logger.info("✅ PostgreSQL database engine created successfully") + logger.info("✅ Database engine created successfully") return engine except Exception as e: logger.error(f"❌ Failed to create database engine: {e}") @@ -96,14 +103,14 @@ def _engine(): def init_db() -> None: - """Initialize PostgreSQL database tables.""" + """Initialize database tables.""" try: - logger.info("Initializing PostgreSQL database...") + logger.info("Initializing database...") engine = _engine() # Create all tables Base.metadata.create_all(engine) - logger.info("✅ PostgreSQL database tables created successfully") + logger.info("✅ Database tables created successfully") except Exception as e: logger.error(f"Failed to initialize database: {e}") @@ -138,10 +145,11 @@ def save_plan(request_payload: Dict[str, Any], response_payload: Dict[str, Any]) def check_database_health() -> Dict[str, Any]: - """Check PostgreSQL database health and connectivity.""" + """Check database health and connectivity.""" try: - logger.debug("Checking PostgreSQL database health...") + logger.debug("Checking database health...") engine = _engine() + db_type = "PostgreSQL" if DATABASE_URL.startswith("postgresql") else "SQLite" with Session(engine) as session: # Test basic connectivity @@ -153,15 +161,21 @@ def check_database_health() -> Dict[str, Any]: auth_users_count = session.query(AuthUserRecord).count() consumed_meals_count = session.query(ConsumedMealRecord).count() - # Get database size from PostgreSQL - db_size_result = session.execute( - text("SELECT pg_database_size(current_database())") - ).scalar() - db_size = db_size_result if db_size_result else 0 + # Get database size + if db_type == "PostgreSQL": + db_size_result = session.execute( + text("SELECT pg_database_size(current_database())") + ).scalar() + db_size = db_size_result if db_size_result else 0 + else: # SQLite + # For SQLite, get file size + import os + db_path = DATABASE_URL.replace("sqlite:///", "") + db_size = os.path.getsize(db_path) if os.path.exists(db_path) else 0 health_info = { "status": "healthy", - "database_type": "PostgreSQL", + "database_type": db_type, "database_url": DATABASE_URL.split('@')[1] if '@' in DATABASE_URL else "localhost", "database_size_bytes": db_size, "database_size_mb": round(db_size / (1024 * 1024), 2), @@ -174,14 +188,14 @@ def check_database_health() -> Dict[str, Any]: "last_check": datetime.utcnow().isoformat(), } - logger.info(f"PostgreSQL health check passed. Tables: plans={plans_count}, users={users_count}") + logger.info(f"{db_type} health check passed. Tables: plans={plans_count}, users={users_count}") return health_info except SQLAlchemyError as e: - logger.error(f"PostgreSQL health check failed: {e}") + logger.error(f"{db_type} health check failed: {e}") return { "status": "unhealthy", - "database_type": "PostgreSQL", + "database_type": db_type, "database_url": DATABASE_URL.split('@')[1] if '@' in DATABASE_URL else "localhost", "database_size_bytes": 0, "database_size_mb": 0, @@ -194,7 +208,7 @@ def check_database_health() -> Dict[str, Any]: logger.error(f"Unexpected error during health check: {e}") return { "status": "unhealthy", - "database_type": "PostgreSQL", + "database_type": db_type, "database_url": DATABASE_URL.split('@')[1] if '@' in DATABASE_URL else "localhost", "database_size_bytes": 0, "database_size_mb": 0, diff --git a/scripts/start_server.sh b/scripts/start_server.sh index aaa88df..56e2824 100755 --- a/scripts/start_server.sh +++ b/scripts/start_server.sh @@ -44,21 +44,33 @@ fi # Parâmetros padrão (podem ser sobrescritos pelo .env) export LLM_PROVIDER=${LLM_PROVIDER:-gemini} -export GEMINI_MODEL=${GEMINI_MODEL:-gemini-2.5-flash} +export GEMINI_MODEL=${GEMINI_MODEL:-gemini-flash-latest} export EMBEDDING_DEVICE=${EMBEDDING_DEVICE:-cpu} -# Verificar se a API key está configurada +# Verificar dependências Python críticas +echo -e "${YELLOW}🔍 Verificando dependências Python...${NC}" +python -c " +try: + import fastapi, uvicorn, crewai, chromadb, sentence_transformers + print('✅ Dependências principais OK') +except ImportError as e: + print(f'❌ Erro de importação: {e}') + print(' Execute: pip install -r requirements.txt') + exit(1) +" if [ -z "$GEMINI_API_KEY" ]; then echo -e "${RED}❌ Erro: GEMINI_API_KEY não configurada!${NC}" echo " Configure no arquivo .env: GEMINI_API_KEY=AIzaSy..." exit 1 fi -# Verificar formato da API key -if [[ ! "$GEMINI_API_KEY" =~ ^AIza ]]; then - echo -e "${YELLOW}⚠️ AVISO: API Key não começa com 'AIza'${NC}" - echo " A chave pode não ser válida (formato esperado: AIzaSy...)" - echo -e "${YELLOW} Continuando mesmo assim...${NC}\n" +# Verificar se ngrok está instalado e configurado +if command -v ngrok >/dev/null 2>&1; then + echo -e "${GREEN}✅ ngrok encontrado${NC}" + NGROK_AVAILABLE=true +else + echo -e "${YELLOW}⚠️ ngrok não encontrado (opcional)${NC}" + NGROK_AVAILABLE=false fi # Verificar e limpar portas ocupadas @@ -96,9 +108,17 @@ MODE="${1:-foreground}" if [ "$MODE" = "background" ] || [ "$MODE" = "bg" ]; then # Executar em background - echo -e "${YELLOW}🚀 Iniciando servidor em background...${NC}" + echo -e "${YELLOW}🚀 Iniciando servidor em background com multithread...${NC}" + echo -e "${YELLOW} Configuração: 4 workers = suporte a múltiplas sessões simultâneas${NC}" LOG_FILE="/tmp/api_server_$(date +%Y%m%d_%H%M%S).log" - nohup uvicorn backend.api:app --host 0.0.0.0 --port 8000 > "$LOG_FILE" 2>&1 & + nohup uvicorn backend.api:app \ + --host 0.0.0.0 \ + --port 8000 \ + --workers 4 \ + --loop uvloop \ + --http httptools \ + --access-log \ + --log-level info > "$LOG_FILE" 2>&1 & SERVER_PID=$! echo -e "${GREEN}✅ Servidor iniciado!${NC}" @@ -115,6 +135,30 @@ if [ "$MODE" = "background" ] || [ "$MODE" = "bg" ]; then if ps -p $SERVER_PID > /dev/null; then if curl -s http://localhost:8000/api/health > /dev/null 2>&1; then echo -e "\n${GREEN}✅ Servidor está respondendo!${NC}" + + # Iniciar ngrok se disponível + if [ "$NGROK_AVAILABLE" = true ]; then + echo -e "${YELLOW}🚀 Iniciando ngrok tunnel...${NC}" + NGROK_LOG="/tmp/ngrok_$(date +%Y%m%d_%H%M%S).log" + nohup ngrok http 8000 > "$NGROK_LOG" 2>&1 & + NGROK_PID=$! + echo -e "${GREEN}✅ ngrok iniciado!${NC}" + echo -e " PID: $NGROK_PID" + echo -e " Log: $NGROK_LOG" + echo -e " Aguarde alguns segundos para o URL aparecer..." + sleep 5 + if ps -p $NGROK_PID > /dev/null; then + NGROK_URL=$(curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*' | grep -o 'https://[^"]*') + if [ -n "$NGROK_URL" ]; then + echo -e " URL Pública: $NGROK_URL" + else + echo -e "${YELLOW}⚠️ ngrok iniciado mas URL ainda não disponível${NC}" + echo -e " Verifique: curl http://localhost:4040/api/tunnels" + fi + else + echo -e "${RED}❌ ngrok não iniciou${NC}" + fi + fi else echo -e "\n${YELLOW}⚠️ Servidor iniciado mas ainda não está respondendo${NC}" echo -e " Verifique os logs: tail -f $LOG_FILE" @@ -126,9 +170,44 @@ if [ "$MODE" = "background" ] || [ "$MODE" = "bg" ]; then fi else # Executar em foreground - echo -e "${YELLOW}🚀 Iniciando servidor em foreground...${NC}" + echo -e "${YELLOW}🚀 Iniciando servidor em foreground com multithread...${NC}" + echo -e "${YELLOW} Configuração: 4 workers = suporte a múltiplas sessões simultâneas${NC}" echo -e "${GREEN}✅ Servidor rodando em: http://localhost:8000${NC}" echo -e "${YELLOW} Pressione Ctrl+C para parar${NC}\n" - uvicorn backend.api:app --host 0.0.0.0 --port 8000 + # Iniciar ngrok se disponível + if [ "$NGROK_AVAILABLE" = true ]; then + echo -e "${YELLOW}🚀 Iniciando ngrok tunnel em background...${NC}" + NGROK_LOG="/tmp/ngrok_$(date +%Y%m%d_%H%M%S).log" + nohup ngrok http 8000 > "$NGROK_LOG" 2>&1 & + NGROK_PID=$! + echo -e "${GREEN}✅ ngrok iniciado!${NC}" + echo -e " PID: $NGROK_PID" + echo -e " Log: $NGROK_LOG" + echo -e " Aguarde alguns segundos para o URL aparecer..." + sleep 5 + if ps -p $NGROK_PID > /dev/null; then + NGROK_URL=$(curl -s http://localhost:4040/api/tunnels | grep -o '"public_url":"[^"]*' | grep -o 'https://[^"]*') + if [ -n "$NGROK_URL" ]; then + echo -e "${GREEN}🌐 URL Pública: $NGROK_URL${NC}" + echo -e "${GREEN}🔗 Acesse sua aplicação remotamente!${NC}\n" + else + echo -e "${YELLOW}⚠️ ngrok iniciado mas URL ainda não disponível${NC}" + echo -e " Verifique: curl http://localhost:4040/api/tunnels" + fi + else + echo -e "${RED}❌ ngrok não iniciou${NC}" + fi + else + echo -e "${YELLOW}⚠️ ngrok não disponível - apenas acesso local${NC}" + fi + + uvicorn backend.api:app \ + --host 0.0.0.0 \ + --port 8000 \ + --workers 4 \ + --loop uvloop \ + --http httptools \ + --access-log \ + --log-level info fi