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
12 changes: 6 additions & 6 deletions backend/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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")
Expand Down
78 changes: 46 additions & 32 deletions backend/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -74,36 +74,43 @@ 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}")
raise


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}")
Expand Down Expand Up @@ -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
Expand All @@ -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),
Expand All @@ -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,
Expand All @@ -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,
Expand Down
101 changes: 90 additions & 11 deletions scripts/start_server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}"
Expand All @@ -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"
Expand All @@ -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