Spaces:
Running
Running
Upload 9 files
Browse files- app.py +32 -2
- login.html +73 -3
- static/smile.png +0 -0
app.py
CHANGED
|
@@ -24,6 +24,10 @@ if not ADMIN_KEY:
|
|
| 24 |
print("WARNING: ADMIN_KEY not set. Using default key for development.")
|
| 25 |
ADMIN_KEY = "dev-admin-key-please-change"
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
# Store active sessions (in production, use Redis or database)
|
| 28 |
active_sessions = set()
|
| 29 |
|
|
@@ -254,24 +258,37 @@ class ModelUpdate(BaseModel):
|
|
| 254 |
|
| 255 |
# Authentication Routes
|
| 256 |
@app.post("/api/login")
|
| 257 |
-
async def login(request: LoginRequest):
|
| 258 |
"""Authenticate with admin key"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
if request.admin_key == ADMIN_KEY:
|
| 260 |
session_token = create_session_token()
|
| 261 |
active_sessions.add(session_token)
|
| 262 |
|
|
|
|
|
|
|
| 263 |
response = {"success": True, "message": "Authenticated successfully"}
|
| 264 |
response_obj = JSONResponse(response)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
response_obj.set_cookie(
|
| 266 |
key="session_token",
|
| 267 |
value=session_token,
|
| 268 |
httponly=True,
|
| 269 |
-
secure=
|
| 270 |
samesite="lax",
|
| 271 |
max_age=86400 # 24 hours
|
| 272 |
)
|
| 273 |
return response_obj
|
| 274 |
else:
|
|
|
|
| 275 |
raise HTTPException(status_code=401, detail="Invalid admin key")
|
| 276 |
|
| 277 |
@app.post("/api/logout")
|
|
@@ -290,6 +307,19 @@ async def auth_status(authenticated: bool = Depends(verify_session)):
|
|
| 290 |
"""Check if user is authenticated"""
|
| 291 |
return {"authenticated": authenticated}
|
| 292 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
# Main Routes
|
| 294 |
@app.get("/", response_class=HTMLResponse)
|
| 295 |
async def read_root(authenticated: bool = Depends(verify_session)):
|
|
|
|
| 24 |
print("WARNING: ADMIN_KEY not set. Using default key for development.")
|
| 25 |
ADMIN_KEY = "dev-admin-key-please-change"
|
| 26 |
|
| 27 |
+
print(f"π ADMIN_KEY configured: {'Yes' if ADMIN_KEY else 'No'}")
|
| 28 |
+
print(f"π ADMIN_KEY length: {len(ADMIN_KEY) if ADMIN_KEY else 0} characters")
|
| 29 |
+
print(f"π ADMIN_KEY starts with: {ADMIN_KEY[:3]}... (hidden for security)")
|
| 30 |
+
|
| 31 |
# Store active sessions (in production, use Redis or database)
|
| 32 |
active_sessions = set()
|
| 33 |
|
|
|
|
| 258 |
|
| 259 |
# Authentication Routes
|
| 260 |
@app.post("/api/login")
|
| 261 |
+
async def login(request: LoginRequest, req: Request):
|
| 262 |
"""Authenticate with admin key"""
|
| 263 |
+
print(f"π Login attempt with key length: {len(request.admin_key)}")
|
| 264 |
+
print(f"π Expected key length: {len(ADMIN_KEY)}")
|
| 265 |
+
print(f"π Keys match: {request.admin_key == ADMIN_KEY}")
|
| 266 |
+
print(f"π Request from: {req.client.host if req.client else 'unknown'}")
|
| 267 |
+
|
| 268 |
if request.admin_key == ADMIN_KEY:
|
| 269 |
session_token = create_session_token()
|
| 270 |
active_sessions.add(session_token)
|
| 271 |
|
| 272 |
+
print(f"β
Login successful, session token created: {session_token[:8]}...")
|
| 273 |
+
|
| 274 |
response = {"success": True, "message": "Authenticated successfully"}
|
| 275 |
response_obj = JSONResponse(response)
|
| 276 |
+
|
| 277 |
+
# Try different cookie settings for HuggingFace
|
| 278 |
+
is_hf = "hf.space" in str(req.headers.get("host", ""))
|
| 279 |
+
print(f"π Detected Hugging Face: {is_hf}")
|
| 280 |
+
|
| 281 |
response_obj.set_cookie(
|
| 282 |
key="session_token",
|
| 283 |
value=session_token,
|
| 284 |
httponly=True,
|
| 285 |
+
secure=is_hf, # Only secure on HTTPS (HF Spaces)
|
| 286 |
samesite="lax",
|
| 287 |
max_age=86400 # 24 hours
|
| 288 |
)
|
| 289 |
return response_obj
|
| 290 |
else:
|
| 291 |
+
print(f"β Login failed - invalid key")
|
| 292 |
raise HTTPException(status_code=401, detail="Invalid admin key")
|
| 293 |
|
| 294 |
@app.post("/api/logout")
|
|
|
|
| 307 |
"""Check if user is authenticated"""
|
| 308 |
return {"authenticated": authenticated}
|
| 309 |
|
| 310 |
+
@app.get("/api/debug")
|
| 311 |
+
async def debug_info(req: Request):
|
| 312 |
+
"""Debug endpoint to check configuration"""
|
| 313 |
+
return {
|
| 314 |
+
"admin_key_set": bool(ADMIN_KEY),
|
| 315 |
+
"admin_key_length": len(ADMIN_KEY) if ADMIN_KEY else 0,
|
| 316 |
+
"active_sessions_count": len(active_sessions),
|
| 317 |
+
"host": req.headers.get("host"),
|
| 318 |
+
"user_agent": req.headers.get("user-agent"),
|
| 319 |
+
"is_hf": "hf.space" in str(req.headers.get("host", "")),
|
| 320 |
+
"environment": "production" if "hf.space" in str(req.headers.get("host", "")) else "development"
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
# Main Routes
|
| 324 |
@app.get("/", response_class=HTMLResponse)
|
| 325 |
async def read_root(authenticated: bool = Depends(verify_session)):
|
login.html
CHANGED
|
@@ -141,6 +141,31 @@
|
|
| 141 |
from { transform: rotate(0deg); }
|
| 142 |
to { transform: rotate(360deg); }
|
| 143 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
</style>
|
| 145 |
</head>
|
| 146 |
<body>
|
|
@@ -198,6 +223,7 @@
|
|
| 198 |
hideError();
|
| 199 |
|
| 200 |
try {
|
|
|
|
| 201 |
const response = await fetch('/api/login', {
|
| 202 |
method: 'POST',
|
| 203 |
headers: {
|
|
@@ -206,17 +232,59 @@
|
|
| 206 |
body: JSON.stringify({ admin_key: adminKey })
|
| 207 |
});
|
| 208 |
|
|
|
|
|
|
|
| 209 |
if (response.ok) {
|
| 210 |
-
|
| 211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
} else {
|
|
|
|
| 213 |
const error = await response.json();
|
|
|
|
| 214 |
showError(error.detail || 'Invalid admin key');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
}
|
| 216 |
} catch (error) {
|
| 217 |
-
console.error('Login error:', error);
|
| 218 |
showError('Network error. Please try again.');
|
| 219 |
} finally {
|
|
|
|
| 220 |
setLoading(false);
|
| 221 |
}
|
| 222 |
});
|
|
@@ -239,6 +307,7 @@
|
|
| 239 |
loginBtn.disabled = false;
|
| 240 |
loginBtn.classList.remove('loading');
|
| 241 |
loginBtn.innerHTML = '<i class="fas fa-sign-in-alt"></i><span>Login</span>';
|
|
|
|
| 242 |
}
|
| 243 |
}
|
| 244 |
|
|
@@ -248,6 +317,7 @@
|
|
| 248 |
loginForm.dispatchEvent(new Event('submit'));
|
| 249 |
}
|
| 250 |
});
|
|
|
|
| 251 |
</script>
|
| 252 |
</body>
|
| 253 |
</html>
|
|
|
|
| 141 |
from { transform: rotate(0deg); }
|
| 142 |
to { transform: rotate(360deg); }
|
| 143 |
}
|
| 144 |
+
|
| 145 |
+
@keyframes shake {
|
| 146 |
+
0%, 100% { transform: translateX(0); }
|
| 147 |
+
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
|
| 148 |
+
20%, 40%, 60%, 80% { transform: translateX(5px); }
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
.success {
|
| 152 |
+
background: #22c55e !important;
|
| 153 |
+
border-color: #22c55e !important;
|
| 154 |
+
color: white !important;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
.success:hover {
|
| 158 |
+
background: #16a34a !important;
|
| 159 |
+
border-color: #16a34a !important;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.success:disabled {
|
| 163 |
+
background: #22c55e !important;
|
| 164 |
+
border-color: #22c55e !important;
|
| 165 |
+
color: white !important;
|
| 166 |
+
opacity: 1 !important;
|
| 167 |
+
cursor: default !important;
|
| 168 |
+
}
|
| 169 |
</style>
|
| 170 |
</head>
|
| 171 |
<body>
|
|
|
|
| 223 |
hideError();
|
| 224 |
|
| 225 |
try {
|
| 226 |
+
console.log('π Sending login request...');
|
| 227 |
const response = await fetch('/api/login', {
|
| 228 |
method: 'POST',
|
| 229 |
headers: {
|
|
|
|
| 232 |
body: JSON.stringify({ admin_key: adminKey })
|
| 233 |
});
|
| 234 |
|
| 235 |
+
console.log('π‘ Response received:', response.status, response.statusText);
|
| 236 |
+
|
| 237 |
if (response.ok) {
|
| 238 |
+
console.log('β
Login successful, showing success feedback...');
|
| 239 |
+
|
| 240 |
+
// Show success feedback IMMEDIATELY - before parsing response
|
| 241 |
+
setLoading(false); // Stop loading first
|
| 242 |
+
console.log('π¨ Updating button appearance...');
|
| 243 |
+
loginBtn.innerHTML = '<i class="fas fa-check"></i><span>Success! Redirecting...</span>';
|
| 244 |
+
loginBtn.style.background = '#22c55e !important';
|
| 245 |
+
loginBtn.style.borderColor = '#22c55e !important';
|
| 246 |
+
loginBtn.disabled = true;
|
| 247 |
+
loginBtn.classList.add('success');
|
| 248 |
+
|
| 249 |
+
// Hide any error messages
|
| 250 |
+
hideError();
|
| 251 |
+
|
| 252 |
+
// Force a repaint to ensure UI updates
|
| 253 |
+
loginBtn.offsetHeight; // This forces a reflow
|
| 254 |
+
|
| 255 |
+
console.log('π¨ Button should be green now');
|
| 256 |
+
console.log('π¨ Button innerHTML:', loginBtn.innerHTML);
|
| 257 |
+
console.log('π¨ Button background:', loginBtn.style.background);
|
| 258 |
+
|
| 259 |
+
// Parse response data after UI update
|
| 260 |
+
const data = await response.json();
|
| 261 |
+
console.log('π Response data:', data);
|
| 262 |
+
|
| 263 |
+
console.log('β³ Starting redirect timer (2 seconds)...');
|
| 264 |
+
// Redirect after showing success feedback
|
| 265 |
+
setTimeout(() => {
|
| 266 |
+
console.log('π Redirecting to main app...');
|
| 267 |
+
window.location.href = '/';
|
| 268 |
+
}, 2000); // 2 seconds to see the feedback
|
| 269 |
+
|
| 270 |
+
return; // Don't execute the finally block
|
| 271 |
} else {
|
| 272 |
+
console.error('β Login failed with status:', response.status);
|
| 273 |
const error = await response.json();
|
| 274 |
+
console.error('β Error details:', error);
|
| 275 |
showError(error.detail || 'Invalid admin key');
|
| 276 |
+
|
| 277 |
+
// Shake the form to indicate error
|
| 278 |
+
loginForm.style.animation = 'shake 0.5s';
|
| 279 |
+
setTimeout(() => {
|
| 280 |
+
loginForm.style.animation = '';
|
| 281 |
+
}, 500);
|
| 282 |
}
|
| 283 |
} catch (error) {
|
| 284 |
+
console.error('π₯ Login error:', error);
|
| 285 |
showError('Network error. Please try again.');
|
| 286 |
} finally {
|
| 287 |
+
console.log('π Setting loading to false...');
|
| 288 |
setLoading(false);
|
| 289 |
}
|
| 290 |
});
|
|
|
|
| 307 |
loginBtn.disabled = false;
|
| 308 |
loginBtn.classList.remove('loading');
|
| 309 |
loginBtn.innerHTML = '<i class="fas fa-sign-in-alt"></i><span>Login</span>';
|
| 310 |
+
loginBtn.style.background = ''; // Reset any custom background
|
| 311 |
}
|
| 312 |
}
|
| 313 |
|
|
|
|
| 317 |
loginForm.dispatchEvent(new Event('submit'));
|
| 318 |
}
|
| 319 |
});
|
| 320 |
+
|
| 321 |
</script>
|
| 322 |
</body>
|
| 323 |
</html>
|
static/smile.png
ADDED
|