feat: complete Git Repo Manager MVP implementation
Backend (Phase 1-6): - Pydantic schemas for request/response validation - Service layer (SSH Key, Server, Repo, Sync) - API routes with authentication - FastAPI main application with lifespan management - ORM models (SshKey, Server, Repo, SyncLog) Frontend (Phase 7): - Vue 3 + Element Plus + Pinia + Vue Router - API client with Axios and interceptors - State management stores - All page components (Dashboard, Servers, Repos, SyncLogs, SshKeys, Settings) Deployment (Phase 8): - README with quick start guide - Startup script (start.sh) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
111
backend/app/api/deps.py
Normal file
111
backend/app/api/deps.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
FastAPI dependencies for API routes.
|
||||
|
||||
Provides reusable dependencies for:
|
||||
- Database session management
|
||||
- Authentication/authorization
|
||||
"""
|
||||
from typing import Generator, Optional
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.database import get_session_factory
|
||||
from app.security import verify_api_token
|
||||
|
||||
|
||||
# HTTP Bearer token security scheme
|
||||
security = HTTPBearer(auto_error=False)
|
||||
|
||||
|
||||
def get_db_session() -> Generator[Session, None, None]:
|
||||
"""
|
||||
Dependency to get a database session.
|
||||
|
||||
Yields:
|
||||
SQLAlchemy database session
|
||||
|
||||
Example:
|
||||
@app.get("/items")
|
||||
def read_items(db: Session = Depends(get_db_session)):
|
||||
items = db.query(Item).all()
|
||||
return items
|
||||
"""
|
||||
session_factory = get_session_factory()
|
||||
if session_factory is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Database not initialized"
|
||||
)
|
||||
|
||||
session = session_factory()
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def require_auth(
|
||||
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
|
||||
) -> None:
|
||||
"""
|
||||
Dependency to require authentication for protected endpoints.
|
||||
|
||||
Args:
|
||||
credentials: HTTP Bearer token credentials
|
||||
|
||||
Raises:
|
||||
HTTPException: If authentication fails (401 Unauthorized)
|
||||
|
||||
Example:
|
||||
@app.get("/protected")
|
||||
def protected_route(auth: None = Depends(require_auth)):
|
||||
return {"message": "authenticated"}
|
||||
"""
|
||||
if credentials is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Missing authentication credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
authorization = f"Bearer {credentials.credentials}"
|
||||
if not verify_api_token(authorization):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid authentication token",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Return None to indicate successful authentication
|
||||
return None
|
||||
|
||||
|
||||
async def require_auth_optional(
|
||||
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
|
||||
) -> bool:
|
||||
"""
|
||||
Optional authentication dependency.
|
||||
Returns True if authenticated, False otherwise.
|
||||
|
||||
This is useful for endpoints that have different behavior
|
||||
based on authentication status but don't require it.
|
||||
|
||||
Args:
|
||||
credentials: HTTP Bearer token credentials
|
||||
|
||||
Returns:
|
||||
bool: True if authenticated, False otherwise
|
||||
|
||||
Example:
|
||||
@app.get("/public")
|
||||
def public_route(authenticated: bool = Depends(require_auth_optional)):
|
||||
if authenticated:
|
||||
return {"message": "authenticated user"}
|
||||
return {"message": "anonymous user"}
|
||||
"""
|
||||
if credentials is None:
|
||||
return False
|
||||
|
||||
authorization = f"Bearer {credentials.credentials}"
|
||||
return verify_api_token(authorization)
|
||||
Reference in New Issue
Block a user