Backend:
- AppUser model with email, name, role (viewer/editor/admin), azure_oid
- Users API: GET /users/me (current user + role), GET /users (admin: list all),
PUT /users/{id}/role (admin: change role)
- Auto-create user on first login: first user = admin, rest = editor
- get_or_create_user helper for role lookup
- require_role helper for permission checks
Frontend:
- UserRoleContext provides role to all components
- useUserRole() hook: isAdmin, isEditor, isViewer
- Nav items filtered by role: GMAL Editor + Users only for admin
- Dashboard: Ingest button admin-only, New Project editor-only
- User Management page: list all users, change roles via dropdown
- Role badges: admin (red), editor (gold), viewer (grey)
Roles:
- Viewer: view projects, download exports
- Editor: create/edit projects, upload, match, build ratecards
- Admin: all + GMAL Editor, data ingest, user management
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
59 lines
2 KiB
Python
59 lines
2 KiB
Python
import logging
|
|
|
|
from fastapi import FastAPI, Depends
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
# Enable app-level logging
|
|
logging.basicConfig(level=logging.INFO, format="%(levelname)s [%(name)s] %(message)s")
|
|
|
|
from app.api import gmal, ingest, projects, matching, ratecard, efficiency, users
|
|
from app.middleware.auth import get_current_user
|
|
|
|
app = FastAPI(title="Scope Builder", version="1.0.0")
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[
|
|
"http://localhost:3000",
|
|
"http://localhost:3001",
|
|
"http://localhost:3010",
|
|
"https://optical-dev.oliver.solutions",
|
|
],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
_auth = Depends(get_current_user)
|
|
|
|
app.include_router(gmal.router, prefix="/api/gmal", tags=["GMAL"], dependencies=[_auth])
|
|
app.include_router(ingest.router, prefix="/api/gmal", tags=["Ingest"], dependencies=[_auth])
|
|
app.include_router(projects.router, prefix="/api/projects", tags=["Projects"], dependencies=[_auth])
|
|
app.include_router(matching.router, prefix="/api/projects", tags=["Matching"], dependencies=[_auth])
|
|
app.include_router(ratecard.router, prefix="/api/projects", tags=["Ratecard"], dependencies=[_auth])
|
|
app.include_router(efficiency.router, prefix="/api/efficiency", tags=["Efficiency"], dependencies=[_auth])
|
|
app.include_router(users.router, prefix="/api/users", tags=["Users"], dependencies=[_auth])
|
|
|
|
|
|
@app.get("/api/health")
|
|
async def health():
|
|
return {"status": "ok"}
|
|
|
|
|
|
@app.get("/api/ai/usage", dependencies=[_auth])
|
|
async def ai_usage():
|
|
from app.utils.claude_client import get_usage_stats
|
|
return get_usage_stats()
|
|
|
|
|
|
@app.post("/api/ai/usage/reset", dependencies=[_auth])
|
|
async def ai_usage_reset():
|
|
from app.utils.claude_client import reset_usage_stats
|
|
reset_usage_stats()
|
|
return {"detail": "Usage stats reset"}
|
|
|
|
|
|
@app.get("/api/ai/debug", dependencies=[_auth])
|
|
async def ai_debug():
|
|
from app.utils.claude_client import get_debug_log
|
|
return get_debug_log()
|