diff --git a/.env.example b/.env.example index 178641c..dd73be9 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,6 @@ LVT_ALLOWED_ORIGINS=* # Google Gemini API Key for Jules AI advice GEMINI_API_KEY=your_gemini_api_key_here + +# Staff access password for the private bunker panel +STAFF_PASSWORD=your_staff_password_here diff --git a/backend/__pycache__/jules_engine.cpython-312.pyc b/backend/__pycache__/jules_engine.cpython-312.pyc deleted file mode 100644 index e245657..0000000 Binary files a/backend/__pycache__/jules_engine.cpython-312.pyc and /dev/null differ diff --git a/backend/__pycache__/main.cpython-312.pyc b/backend/__pycache__/main.cpython-312.pyc deleted file mode 100644 index 326c467..0000000 Binary files a/backend/__pycache__/main.cpython-312.pyc and /dev/null differ diff --git a/backend/__pycache__/models.cpython-312.pyc b/backend/__pycache__/models.cpython-312.pyc deleted file mode 100644 index a1357dc..0000000 Binary files a/backend/__pycache__/models.cpython-312.pyc and /dev/null differ diff --git a/backend/__pycache__/test_jules.cpython-312-pytest-9.0.2.pyc b/backend/__pycache__/test_jules.cpython-312-pytest-9.0.2.pyc deleted file mode 100644 index a7209d8..0000000 Binary files a/backend/__pycache__/test_jules.cpython-312-pytest-9.0.2.pyc and /dev/null differ diff --git a/backend/main.py b/backend/main.py index fb88c85..fad0bf9 100644 --- a/backend/main.py +++ b/backend/main.py @@ -7,7 +7,7 @@ from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from dotenv import load_dotenv -from models import UserScan, SHOPIFY_INVENTORY +from models import UserScan, StaffLogin, SHOPIFY_INVENTORY from jules_engine import get_jules_advice # Load .env file @@ -28,6 +28,7 @@ # 🛡️ Configuración Maestra (abvetos.com) - Secrets moved to environment variables SECRET_KEY = os.getenv("LVT_SECRET_KEY", "DEVELOPMENT_SECRET_DO_NOT_USE_IN_PROD") +STAFF_PASSWORD = os.getenv("STAFF_PASSWORD") PATENT = "PCT/EP2025/067317" def verify_auth(user_id: str, token: str) -> bool: @@ -49,6 +50,21 @@ def calculate_fit(user_waist: float, item_id: str): is_perfect = 0.95 <= fit_index <= 1.05 return is_perfect, round(fit_index, 3), item +@app.post("/api/verify-staff") +def verify_staff(login: StaffLogin): + """🛡️ Secure staff password verification via backend.""" + # Ensure staff authentication is properly configured before comparing. + if not STAFF_PASSWORD: + # Fail closed with a clear server-side configuration error. + raise HTTPException( + status_code=500, + detail="STAFF_PASSWORD is not configured on the server.", + ) + if hmac.compare_digest(login.password, STAFF_PASSWORD): + return {"status": "SUCCESS", "message": "Acceso concedido al búnker."} + else: + raise HTTPException(status_code=401, detail="Credencial de acceso denegada.") + @app.post("/api/recommend") async def recommend_garment(scan: UserScan, garment_id: str = "BALMAIN_SS26_SLIM"): # 1. Seguridad y Handshake diff --git a/backend/models.py b/backend/models.py index 652f4cd..46626de 100644 --- a/backend/models.py +++ b/backend/models.py @@ -16,6 +16,9 @@ class Garment(BaseModel): price: str variant_id: str +class StaffLogin(BaseModel): + password: str + # 👗 Catálogo Shopify (Divineo Bunker) SHOPIFY_INVENTORY = { "BALMAIN_SS26_SLIM": { diff --git a/backend/tests/__pycache__/test_main.cpython-312-pytest-9.0.2.pyc b/backend/tests/__pycache__/test_main.cpython-312-pytest-9.0.2.pyc deleted file mode 100644 index 8ee3541..0000000 Binary files a/backend/tests/__pycache__/test_main.cpython-312-pytest-9.0.2.pyc and /dev/null differ diff --git a/backend/tests/test_staff.py b/backend/tests/test_staff.py new file mode 100644 index 0000000..e88db18 --- /dev/null +++ b/backend/tests/test_staff.py @@ -0,0 +1,28 @@ +from fastapi.testclient import TestClient +from backend.main import app +import backend.main + +TEST_STAFF_PASSWORD = "test_staff_password_123" + +client = TestClient(app) + +def test_verify_staff_success(monkeypatch): + """Test successful staff verification with correct password.""" + monkeypatch.setattr(backend.main, "STAFF_PASSWORD", TEST_STAFF_PASSWORD) + response = client.post("/api/verify-staff", json={"password": TEST_STAFF_PASSWORD}) + assert response.status_code == 200 + assert response.json() == {"status": "SUCCESS", "message": "Acceso concedido al búnker."} + +def test_verify_staff_failure(monkeypatch): + """Test failed staff verification with incorrect password.""" + monkeypatch.setattr(backend.main, "STAFF_PASSWORD", TEST_STAFF_PASSWORD) + response = client.post("/api/verify-staff", json={"password": "WRONG_PASSWORD"}) + assert response.status_code == 401 + assert response.json() == {"detail": "Credencial de acceso denegada."} + +def test_verify_staff_empty(monkeypatch): + """Test staff verification with empty password.""" + monkeypatch.setattr(backend.main, "STAFF_PASSWORD", TEST_STAFF_PASSWORD) + response = client.post("/api/verify-staff", json={"password": ""}) + assert response.status_code == 401 + assert response.json() == {"detail": "Credencial de acceso denegada."} diff --git a/js/main.js b/js/main.js index 56a1cb6..0d33ca1 100644 --- a/js/main.js +++ b/js/main.js @@ -238,14 +238,27 @@ class TryOnYouBunker { modal.style.display = 'none'; } - verifyPrivatePass() { + async verifyPrivatePass() { const input = document.getElementById('private-pass-input'); - if (input.value === "SAC_MUSEUM_2026") { - this.showNotification('ACCESO CONCEDIDO', 'success'); - setTimeout(() => { window.location.href = "/staff-dashboard"; }, 1500); - } else { - this.showNotification('ACCESO DENEGADO', 'error'); - input.value = ""; + const password = input.value; + + try { + const response = await fetch('/api/verify-staff', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ password }) + }); + + if (response.ok) { + this.showNotification('ACCESO CONCEDIDO', 'success'); + setTimeout(() => { window.location.href = "/staff-dashboard"; }, 1500); + } else { + this.showNotification('ACCESO DENEGADO', 'error'); + input.value = ""; + } + } catch (error) { + this.showNotification('SISTEMA OFFLINE', 'error'); + console.error("Staff verification error:", error); } } } diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..7af6018 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + server: { + proxy: { + '/api': { + target: 'http://localhost:8000', + changeOrigin: true, + }, + }, + }, +});