NikaMimi commited on
Commit
080cd1e
·
verified ·
1 Parent(s): 5f35238

Upload 8 files

Browse files
Files changed (2) hide show
  1. README.md +2 -0
  2. app.py +84 -10
README.md CHANGED
@@ -20,6 +20,7 @@ A modern web interface for managing Discord bot AI model configurations stored i
20
  3. Configure environment variables in Space settings:
21
  - `FIREBASE_URL`: Your Firebase Realtime Database URL
22
  - `FIREBASE_SERVICE_ACCOUNT_B64`: Base64-encoded service account JSON
 
23
  4. The app will build automatically and be available on port 7860
24
 
25
  ### Local Development:
@@ -47,6 +48,7 @@ A modern web interface for managing Discord bot AI model configurations stored i
47
 
48
  ## ✨ Features
49
 
 
50
  - **Model Management**: Add, edit, delete AI models by category
51
  - **Image Previews**: Thumbnail carousels from Civitai API with metadata
52
  - **Advanced Filtering**: Sort by reactions/comments/newest, NSFW levels, time periods
 
20
  3. Configure environment variables in Space settings:
21
  - `FIREBASE_URL`: Your Firebase Realtime Database URL
22
  - `FIREBASE_SERVICE_ACCOUNT_B64`: Base64-encoded service account JSON
23
+ - `ADMIN_KEY`: Secret admin key for authentication (set this to a secure password)
24
  4. The app will build automatically and be available on port 7860
25
 
26
  ### Local Development:
 
48
 
49
  ## ✨ Features
50
 
51
+ - **🔐 Admin Authentication**: Secure login with admin key protection
52
  - **Model Management**: Add, edit, delete AI models by category
53
  - **Image Previews**: Thumbnail carousels from Civitai API with metadata
54
  - **Advanced Filtering**: Sort by reactions/comments/newest, NSFW levels, time periods
app.py CHANGED
@@ -1,6 +1,6 @@
1
- from fastapi import FastAPI, HTTPException, Request
2
  from fastapi.staticfiles import StaticFiles
3
- from fastapi.responses import HTMLResponse, FileResponse
4
  from pydantic import BaseModel
5
  import os
6
  import json
@@ -8,6 +8,8 @@ import firebase_admin
8
  from firebase_admin import credentials, db
9
  import base64
10
  import time
 
 
11
  from dotenv import load_dotenv
12
  from typing import List, Dict, Any, Optional
13
 
@@ -16,6 +18,33 @@ load_dotenv()
16
 
17
  app = FastAPI(title="CatGPT Model Manager", description="Modern web interface for managing Discord bot AI models")
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  class FirebaseManager:
20
  def __init__(self):
21
  self.app = None
@@ -223,12 +252,57 @@ class ModelUpdate(BaseModel):
223
  field: str
224
  value: Any
225
 
226
- # API Routes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  @app.get("/", response_class=HTMLResponse)
228
- async def read_root():
229
- """Serve the main HTML page"""
 
 
230
  return FileResponse('index.html')
231
 
 
 
 
 
 
232
  @app.get("/api/models")
233
  async def get_all_models():
234
  """Get all models from all categories"""
@@ -252,7 +326,7 @@ async def get_models_by_category(category: str):
252
  }
253
 
254
  @app.post("/api/models/{category}")
255
- async def add_model(category: str, model: ModelCreate):
256
  """Add a new model to a category"""
257
  if category not in ["pony", "illustrious", "sdxl"]:
258
  raise HTTPException(status_code=400, detail="Invalid category")
@@ -274,7 +348,7 @@ async def add_model(category: str, model: ModelCreate):
274
  return {"message": message}
275
 
276
  @app.put("/api/models/{category}/{model_id}")
277
- async def update_model(category: str, model_id: str, update: ModelUpdate):
278
  """Update a specific field of a model"""
279
  if category not in ["pony", "illustrious", "sdxl"]:
280
  raise HTTPException(status_code=400, detail="Invalid category")
@@ -287,7 +361,7 @@ async def update_model(category: str, model_id: str, update: ModelUpdate):
287
  return {"message": message}
288
 
289
  @app.delete("/api/models/{category}/{model_id}")
290
- async def delete_model(category: str, model_id: str):
291
  """Delete a model"""
292
  if category not in ["pony", "illustrious", "sdxl"]:
293
  raise HTTPException(status_code=400, detail="Invalid category")
@@ -315,6 +389,6 @@ app.add_middleware(
315
 
316
  if __name__ == "__main__":
317
  import uvicorn
318
- port = int(os.getenv('PORT', 8000))
319
- print(f"Starting CatGPT Model Manager on http://127.0.0.1:{port}")
320
  uvicorn.run(app, host="0.0.0.0", port=port)
 
1
+ from fastapi import FastAPI, HTTPException, Request, Depends, Cookie
2
  from fastapi.staticfiles import StaticFiles
3
+ from fastapi.responses import HTMLResponse, FileResponse, RedirectResponse, JSONResponse
4
  from pydantic import BaseModel
5
  import os
6
  import json
 
8
  from firebase_admin import credentials, db
9
  import base64
10
  import time
11
+ import hashlib
12
+ import secrets
13
  from dotenv import load_dotenv
14
  from typing import List, Dict, Any, Optional
15
 
 
18
 
19
  app = FastAPI(title="CatGPT Model Manager", description="Modern web interface for managing Discord bot AI models")
20
 
21
+ # Authentication Configuration
22
+ ADMIN_KEY = os.getenv('ADMIN_KEY')
23
+ 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
+
30
+ # Pydantic models for authentication
31
+ class LoginRequest(BaseModel):
32
+ admin_key: str
33
+
34
+ def create_session_token() -> str:
35
+ """Generate a secure session token"""
36
+ return secrets.token_urlsafe(32)
37
+
38
+ def verify_session(session_token: Optional[str] = Cookie(None)) -> bool:
39
+ """Verify if session token is valid"""
40
+ return session_token in active_sessions
41
+
42
+ def require_auth(authenticated: bool = Depends(verify_session)):
43
+ """Dependency to require authentication"""
44
+ if not authenticated:
45
+ raise HTTPException(status_code=401, detail="Authentication required")
46
+ return True
47
+
48
  class FirebaseManager:
49
  def __init__(self):
50
  self.app = None
 
252
  field: str
253
  value: Any
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=True,
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")
278
+ async def logout(session_token: Optional[str] = Cookie(None)):
279
+ """Logout and invalidate session"""
280
+ if session_token and session_token in active_sessions:
281
+ active_sessions.remove(session_token)
282
+
283
+ response = {"success": True, "message": "Logged out successfully"}
284
+ response_obj = JSONResponse(response)
285
+ response_obj.delete_cookie("session_token")
286
+ return response_obj
287
+
288
+ @app.get("/api/auth-status")
289
+ 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)):
296
+ """Serve the main HTML page or login page"""
297
+ if not authenticated:
298
+ return FileResponse('login.html')
299
  return FileResponse('index.html')
300
 
301
+ @app.get("/login", response_class=HTMLResponse)
302
+ async def login_page():
303
+ """Serve the login page"""
304
+ return FileResponse('login.html')
305
+
306
  @app.get("/api/models")
307
  async def get_all_models():
308
  """Get all models from all categories"""
 
326
  }
327
 
328
  @app.post("/api/models/{category}")
329
+ async def add_model(category: str, model: ModelCreate, _: bool = Depends(require_auth)):
330
  """Add a new model to a category"""
331
  if category not in ["pony", "illustrious", "sdxl"]:
332
  raise HTTPException(status_code=400, detail="Invalid category")
 
348
  return {"message": message}
349
 
350
  @app.put("/api/models/{category}/{model_id}")
351
+ async def update_model(category: str, model_id: str, update: ModelUpdate, _: bool = Depends(require_auth)):
352
  """Update a specific field of a model"""
353
  if category not in ["pony", "illustrious", "sdxl"]:
354
  raise HTTPException(status_code=400, detail="Invalid category")
 
361
  return {"message": message}
362
 
363
  @app.delete("/api/models/{category}/{model_id}")
364
+ async def delete_model(category: str, model_id: str, _: bool = Depends(require_auth)):
365
  """Delete a model"""
366
  if category not in ["pony", "illustrious", "sdxl"]:
367
  raise HTTPException(status_code=400, detail="Invalid category")
 
389
 
390
  if __name__ == "__main__":
391
  import uvicorn
392
+ port = int(os.getenv('PORT', 7860))
393
+ print(f"Starting CatGPT Model Manager on http://0.0.0.0:{port}")
394
  uvicorn.run(app, host="0.0.0.0", port=port)