diff --git a/.env.example b/.env.example index 178641c..734270a 100644 --- a/.env.example +++ b/.env.example @@ -7,5 +7,8 @@ LVT_SECRET_KEY=your_secure_secret_here # Allowed CORS origins (comma-separated list, e.g., https://yourdomain.com,http://localhost:3000) LVT_ALLOWED_ORIGINS=* +# Staff password for private pass verification +STAFF_PASSWORD=SAC_MUSEUM_2026 + # Google Gemini API Key for Jules AI advice GEMINI_API_KEY=your_gemini_api_key_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..9a008c9 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", "SAC_MUSEUM_2026") PATENT = "PCT/EP2025/067317" def verify_auth(user_id: str, token: str) -> bool: @@ -49,6 +50,14 @@ 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") +async def verify_staff(login: StaffLogin): + # 🛡️ Secure comparison using hmac.compare_digest to prevent timing attacks + if hmac.compare_digest(login.password, STAFF_PASSWORD): + return {"status": "SUCCESS", "message": "ACCESO CONCEDIDO"} + else: + raise HTTPException(status_code=401, detail="ACCESO DENEGADO") + @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..8a33bba 100644 --- a/backend/models.py +++ b/backend/models.py @@ -7,6 +7,9 @@ class UserScan(BaseModel): waist: float event_type: str # e.g., 'Gala', 'Business', 'Cocktail' +class StaffLogin(BaseModel): + password: str + class Garment(BaseModel): id: str name: str 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..20a68d3 --- /dev/null +++ b/backend/tests/test_staff.py @@ -0,0 +1,40 @@ +import pytest +from fastapi.testclient import TestClient +from backend.main import app +import os + +client = TestClient(app) + +def test_verify_staff_success(): + """Test successful staff verification.""" + # The default password in main.py if STAFF_PASSWORD is not set is 'SAC_MUSEUM_2026' + payload = {"password": "SAC_MUSEUM_2026"} + response = client.post("/api/verify-staff", json=payload) + assert response.status_code == 200 + assert response.json() == {"status": "SUCCESS", "message": "ACCESO CONCEDIDO"} + +def test_verify_staff_failure(): + """Test failed staff verification with wrong password.""" + payload = {"password": "WRONG_PASSWORD"} + response = client.post("/api/verify-staff", json=payload) + assert response.status_code == 401 + assert response.json()["detail"] == "ACCESO DENEGADO" + +def test_verify_staff_empty(): + """Test staff verification with empty password.""" + payload = {"password": ""} + response = client.post("/api/verify-staff", json=payload) + assert response.status_code == 401 + assert response.json()["detail"] == "ACCESO DENEGADO" + +def test_verify_staff_custom_env(monkeypatch): + """Test staff verification with a custom environment variable password.""" + monkeypatch.setenv("STAFF_PASSWORD", "CUSTOM_SECRET_123") + # We need to reload the app or at least the STAFF_PASSWORD variable in main.py + # Since STAFF_PASSWORD is a module-level variable in main.py, we patch it directly + monkeypatch.setattr("backend.main.STAFF_PASSWORD", "CUSTOM_SECRET_123") + + payload = {"password": "CUSTOM_SECRET_123"} + response = client.post("/api/verify-staff", json=payload) + assert response.status_code == 200 + assert response.json() == {"status": "SUCCESS", "message": "ACCESO CONCEDIDO"} diff --git a/js/main.js b/js/main.js index 56a1cb6..67a8dee 100644 --- a/js/main.js +++ b/js/main.js @@ -238,14 +238,28 @@ 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('SYSTEM OFFLINE', '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, + }, + }, + }, +});