fix: Applied fix for name, the oracle team sharing, sentinel client list visibility

This commit is contained in:
Sagnik
2026-04-19 17:07:12 +05:30
parent 269591a3cc
commit d886e4a669
20 changed files with 940 additions and 109 deletions

View File

@@ -11,15 +11,50 @@ import os
import json
import asyncio
import logging
import re
from contextlib import asynccontextmanager
from datetime import UTC, datetime
from pathlib import Path
from typing import Set
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, UploadFile, File
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from dotenv import load_dotenv
def _load_velocity_env() -> None:
repo_root = Path(__file__).resolve().parent.parent
backend_root = repo_root / "backend"
explicit_env = os.getenv("VELOCITY_ENV_FILE", "").strip()
candidate_paths = []
if explicit_env:
candidate_paths.append(Path(explicit_env))
candidate_paths.extend(
[
backend_root / ".env",
repo_root / ".env",
]
)
loaded_any = False
seen: set[Path] = set()
for candidate in candidate_paths:
resolved = candidate.resolve()
if resolved in seen or not candidate.exists():
continue
load_dotenv(candidate, override=not loaded_any)
loaded_any = True
seen.add(resolved)
if not loaded_any:
load_dotenv()
_load_velocity_env()
from backend.api.routes_catalyst import router as catalyst_router
from backend.api.routes_crm import crm_router, analytics_router
from backend.api.routes_oracle import router as oracle_helper_router
@@ -39,8 +74,6 @@ from backend.routers.videos import router as videos_router
from backend.routers.vault import router as vault_router
from backend.routers.sentinel import router as sentinel_router, broadcast_sentinel_event
load_dotenv()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("velocity.main")
@@ -91,6 +124,11 @@ ASSET_DIR = os.getenv("VELOCITY_ASSET_DIR", "/opt/dlami/nvme/assets")
if os.path.isdir(ASSET_DIR):
app.mount("/assets", StaticFiles(directory=ASSET_DIR), name="assets")
def _sanitize_filename(value: str) -> str:
cleaned = re.sub(r"[^A-Za-z0-9._-]+", "_", value).strip("._")
return cleaned or "upload"
# ── Routers ───────────────────────────────────────────────────────────────────
app.include_router(catalyst_router, prefix="/api/catalyst", tags=["Catalyst"])
@@ -160,7 +198,7 @@ async def me(user: UserPrincipal = Depends(get_current_user)):
async with pool.acquire() as conn:
row = await conn.fetchrow(
"""
SELECT full_name, email
SELECT full_name, email, avatar_url
FROM users_and_roles
WHERE id = $1::uuid
""",
@@ -172,9 +210,85 @@ async def me(user: UserPrincipal = Depends(get_current_user)):
"role": user.role,
"full_name": row["full_name"] if row else None,
"email": row["email"] if row else None,
"avatar_url": row["avatar_url"] if row else None,
}
@app.get("/api/auth/users", tags=["Auth"])
async def list_auth_users(_: UserPrincipal = Depends(get_current_user)):
pool = app.state.db_pool
if pool is None:
raise HTTPException(status_code=503, detail="Database unavailable.")
async with pool.acquire() as conn:
rows = await conn.fetch(
"""
SELECT
id::text AS user_id,
role,
full_name,
email,
avatar_url
FROM users_and_roles
WHERE is_active = TRUE
ORDER BY
COALESCE(NULLIF(full_name, ''), email, id::text) ASC
"""
)
return [
{
"user_id": row["user_id"],
"role": row["role"],
"full_name": row["full_name"],
"email": row["email"],
"avatar_url": row["avatar_url"],
}
for row in rows
]
@app.post("/api/auth/profile/avatar", tags=["Auth"])
async def upload_profile_avatar(
file: UploadFile = File(...),
user: UserPrincipal = Depends(get_current_user),
):
pool = app.state.db_pool
if pool is None:
raise HTTPException(status_code=503, detail="Database unavailable.")
allowed = {"image/png", "image/jpeg", "image/jpg", "image/webp"}
if file.content_type not in allowed:
raise HTTPException(status_code=400, detail="Unsupported avatar format.")
extension = Path(file.filename or "avatar.png").suffix.lower() or ".png"
if extension not in {".png", ".jpg", ".jpeg", ".webp"}:
extension = ".png"
avatar_dir = Path(ASSET_DIR) / "profile_avatars"
avatar_dir.mkdir(parents=True, exist_ok=True)
filename = f"{user.user_id}_{_sanitize_filename(Path(file.filename or 'avatar').stem)}_{int(datetime.now(UTC).timestamp())}{extension}"
destination = avatar_dir / filename
contents = await file.read()
destination.write_bytes(contents)
avatar_url = f"/assets/profile_avatars/{filename}"
async with pool.acquire() as conn:
await conn.execute(
"""
UPDATE users_and_roles
SET avatar_url = $2
WHERE id = $1::uuid
""",
user.user_id,
avatar_url,
)
return {"avatar_url": avatar_url}
# ── Catalyst WebSocket (preserved from v1) ────────────────────────────────────
class _CatalystManager: