diff --git a/config.py b/config.py index 06cc772..0879261 100644 --- a/config.py +++ b/config.py @@ -17,7 +17,8 @@ def get_config(): # Configuração Padrão return { "active_provider": "gemini", - "gemini_api_key": "" + "gemini_api_key": "", + "web_password": "@@Gi05Br;;" } def save_config(cfg): diff --git a/main.py b/main.py index 6b7fcc4..2441c14 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,16 @@ import os import psutil -from fastapi import FastAPI, Request +import subprocess +import time +import json +from fastapi import FastAPI, Request, Header, Depends, HTTPException, status from fastapi.responses import HTMLResponse, JSONResponse -from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from dotenv import load_dotenv +from starlette.concurrency import run_in_threadpool + from ai_agent import query_agent +from config import get_config, save_config # Carrega as variáveis do .env load_dotenv() @@ -13,22 +18,37 @@ load_dotenv() app = FastAPI(title="VpsTelegramBot API") # Configura templates HTML -# Certifique-se de que a pasta 'templates' existe e tem o index.html templates = Jinja2Templates(directory="templates") -@app.get("/favicon.ico", include_in_schema=False) -async def favicon(): - return JSONResponse(content={"status": "ok"}) +# --- SEGURANÇA --- +async def verify_password(x_web_password: str = Header(None)): + cfg = get_config() + saved_pwd = cfg.get("web_password", "@@Gi05Br;;") + if not x_web_password or x_web_password != saved_pwd: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Senha Web inválida ou ausente." + ) + return True +# --- ROTAS PÚBLICAS --- @app.get("/", response_class=HTMLResponse) async def read_root(request: Request): """Renderiza o Dashboard Web.""" return templates.TemplateResponse("index.html", {"request": request}) -from starlette.concurrency import run_in_threadpool +@app.get("/favicon.ico", include_in_schema=False) +async def favicon(): + """Favicon dummy para evitar 404.""" + return JSONResponse(content={"status": "ok"}) + +# --- ROTAS PROTEGIDAS (API) --- +@app.get("/api/login") +async def check_login(is_auth: bool = Depends(verify_password)): + return {"status": "success"} @app.get("/api/status") -async def get_system_status(): +async def get_system_status(is_auth: bool = Depends(verify_password)): """Retorna o status do sistema (CPU, RAM, Disco) sem travar o loop.""" def get_stats(): cpu_percent = psutil.cpu_percent(interval=0.1) @@ -47,24 +67,20 @@ async def get_system_status(): "percent": disk.percent } } - data = await run_in_threadpool(get_stats) return JSONResponse(content=data) -import subprocess -from config import get_config, save_config - @app.get("/api/config") -async def read_configuration(): +async def read_configuration(is_auth: bool = Depends(verify_password)): return JSONResponse(content=get_config()) @app.post("/api/config") -async def update_configuration(req: dict): +async def update_configuration(req: dict, is_auth: bool = Depends(verify_password)): save_config(req) return JSONResponse(content={"status": "success"}) @app.post("/api/action") -async def execute_smart_action(action: dict): +async def execute_smart_action(action: dict, is_auth: bool = Depends(verify_password)): """Executa ações predefinidas no servidor (Smart Actions da Web UI).""" action_type = action.get("type") @@ -72,37 +88,31 @@ async def execute_smart_action(action: dict): return JSONResponse(content={"status": "success", "message": "Pong! Servidor online e responsivo."}) elif action_type == "restart_bot": - # Dá um pequeno delay e depois reinicia o próprio container a partir de fora (pelo host docker) subprocess.Popen("sleep 1 && docker restart vps-ai-agent", shell=True) - return JSONResponse(content={"status": "success", "message": "Reboot do Agente autorizado. Estará de volta em instantes!"}) + return JSONResponse(content={"status": "success", "message": "Reboot do Agente autorizado."}) elif action_type == "clear_cache": - # Roda um docker prune para deletar volumes perdidos e todos containers parados (limpeza profunda) subprocess.Popen("docker system prune -af --volumes", shell=True) - return JSONResponse(content={"status": "success", "message": "Limpando caches obsoletos em background! Verifique o gráfico de disco em instantes."}) + return JSONResponse(content={"status": "success", "message": "Limpando caches obsoletos em background!"}) elif action_type == "reboot_vps": - # Hacker trick: Roda um container hiper-privilegiado descartável pra entrar no espaço do host (PID 1) e emitir comando de REBOOT físico subprocess.Popen("sleep 2 && docker run --rm --privileged --pid=host alpine nsenter -t 1 -m -u -n -i reboot", shell=True) - return JSONResponse(content={"status": "success", "message": "🚨 O REBOOT CRÍTICO COMEÇOU. A VPS inteira desligará e religará agora."}) + return JSONResponse(content={"status": "success", "message": "🚨 O REBOOT CRÍTICO COMEÇOU."}) return JSONResponse(content={"status": "error", "message": "Ação desconhecida."}, status_code=400) @app.post("/api/chat") -async def web_chat(message: dict): +async def web_chat(message: dict, is_auth: bool = Depends(verify_password)): """Endpoint para interagir com a IA via Web UI.""" user_text = message.get("text", "") if not user_text: return JSONResponse(content={"reply": "Por favor, digite um comando válido."}) - # Executa a IA em uma thread separada para não travar a UI/API de status reply = await run_in_threadpool(query_agent, prompt=user_text) return JSONResponse(content={"reply": reply}) -import time - @app.get("/api/test_llm") -async def test_llm_speed(): +async def test_llm_speed(is_auth: bool = Depends(verify_password)): """Mede a velocidade de resposta da IA ativa.""" start_time = time.time() try: @@ -116,11 +126,9 @@ async def test_llm_speed(): async def telegram_webhook(request: Request): """Recebe as atualizações (mensagens) do Telegram.""" update = await request.json() - # O bot_logic.py lidará com o 'update' no futuro print("Update recebido do Telegram:", update) return {"ok": True} if __name__ == "__main__": import uvicorn - # Executa o servidor na porta 8000 acessível de qualquer lugar na rede uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) diff --git a/templates/index.html b/templates/index.html index 732c3b9..16d47dd 100644 --- a/templates/index.html +++ b/templates/index.html @@ -457,9 +457,58 @@ grid-template-columns: 1fr; } } + + /* Login Overlay */ + #login-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--bg-base); + display: flex; + align-items: center; + justify-content: center; + z-index: 2000; + backdrop-filter: blur(20px); + transition: opacity 0.5s ease, visibility 0.5s; + } + + #login-overlay.hidden { + opacity: 0; + visibility: hidden; + } + + .login-card { + width: 90%; + max-width: 400px; + padding: 2.5rem; + text-align: center; + border: 1px solid var(--accent); + box-shadow: 0 0 30px var(--accent-glow); + } + + .login-card h2 { + margin-bottom: 1rem; + font-size: 1.5rem; + background: linear-gradient(135deg, var(--accent), #8b5cf6); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + }
+Esta VPS está protegida. Insira a senha mestra para gerenciar o Agente.
+ + + +