Skip to content

Commit 629ce1f

Browse files
committed
Format source
1 parent c3ab628 commit 629ce1f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1200
-798
lines changed

backend/app/admin_auth.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ async def create_admin_session(
2323
) -> str:
2424
"""Create a new admin session for a GitHub user."""
2525
session_token = secrets.token_urlsafe(48)
26-
expires_at = datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=duration_hours)
27-
26+
expires_at = datetime.now(UTC).replace(tzinfo=None) + timedelta(
27+
hours=duration_hours
28+
)
29+
2830
admin_session = AdminSession(
2931
session_token=session_token,
3032
github_user_id=github_user.id,
@@ -34,11 +36,11 @@ async def create_admin_session(
3436
github_avatar_url=github_user.avatar_url,
3537
expires_at=expires_at,
3638
)
37-
39+
3840
db.add(admin_session)
3941
await db.commit()
4042
await db.refresh(admin_session)
41-
43+
4244
return session_token
4345

4446

@@ -80,10 +82,10 @@ async def cleanup_expired_sessions(db: AsyncSession) -> None:
8082
)
8183
)
8284
expired_sessions = result.scalars().all()
83-
85+
8486
for session in expired_sessions:
8587
session.is_active = False
86-
88+
8789
if expired_sessions:
8890
await db.commit()
8991

@@ -103,7 +105,7 @@ async def require_admin_auth(
103105
detail="Admin authentication required",
104106
headers={"WWW-Authenticate": "Bearer"},
105107
)
106-
108+
107109
try:
108110
session = await get_admin_session(db, admin_session_token)
109111
except Exception as e:
@@ -114,14 +116,14 @@ async def require_admin_auth(
114116
detail="Authentication service unavailable",
115117
headers={"WWW-Authenticate": "Bearer"},
116118
)
117-
119+
118120
if not session:
119121
raise HTTPException(
120122
status_code=status.HTTP_401_UNAUTHORIZED,
121123
detail="Invalid or expired admin session",
122124
headers={"WWW-Authenticate": "Bearer"},
123125
)
124-
126+
125127
# Check if user is still an admin (for existing sessions, we need a valid token)
126128
# Note: This check is disabled for existing sessions as we don't store the access token
127129
# Team membership is verified during initial login only
@@ -131,7 +133,7 @@ async def require_admin_auth(
131133
# status_code=status.HTTP_403_FORBIDDEN,
132134
# detail="Admin privileges revoked",
133135
# )
134-
136+
135137
return session
136138

137139

@@ -146,7 +148,7 @@ async def optional_admin_auth(
146148
"""
147149
if not admin_session_token:
148150
return None
149-
151+
150152
try:
151153
return await require_admin_auth(request, admin_session_token, db)
152154
except HTTPException:

backend/app/auth.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ async def get_current_token(
4848
if not auth_token:
4949
# Only log token length and first/last 4 characters for security
5050
masked_token = f"{token[:4]}...{token[-4:]}" if len(token) > 8 else "***"
51-
logger.warning(f"Authentication failed: Invalid token {masked_token} (length: {len(token)})")
51+
logger.warning(
52+
f"Authentication failed: Invalid token {masked_token} (length: {len(token)})"
53+
)
5254
raise authentication_failed("Invalid or expired token")
5355

5456
# Set user context for logging
@@ -58,4 +60,4 @@ async def get_current_token(
5860
await crud.update_token_last_used(db, token)
5961

6062
logger.info(f"Authentication successful for token: {auth_token.name}")
61-
return auth_token
63+
return auth_token

backend/app/config.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ class Settings(BaseSettings):
2626
database_pool_recycle: int = 3600
2727
database_echo: bool = False
2828

29-
# CORS Configuration
29+
# CORS Configuration
3030
cors_origins: str = "" # Comma-separated string, will be parsed to list
3131
cors_allow_credentials: bool = True
3232
cors_allow_methods: List[str] = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
33-
cors_allow_headers: List[str] = ["Authorization", "Content-Type", "Accept", "Origin", "X-Requested-With"]
33+
cors_allow_headers: List[str] = [
34+
"Authorization",
35+
"Content-Type",
36+
"Accept",
37+
"Origin",
38+
"X-Requested-With",
39+
]
3440

3541
# Pagination Configuration
3642
default_page_size: int = 100
@@ -41,18 +47,20 @@ class Settings(BaseSettings):
4147
# Authentication Configuration
4248
token_length: int = 32 # in bytes, results in 64 hex characters
4349
token_cleanup_days: int = 90
44-
50+
4551
# GitHub OAuth Configuration
4652
github_client_id: str = ""
4753
github_client_secret: str = ""
4854
oauth_redirect_uri: str = "http://localhost:3000/admin/auth/callback"
4955
oauth_state_secret: str = "your-secret-key-change-me"
50-
56+
5157
# Admin authorization via GitHub usernames
5258
admin_initial_username: str = "" # Initial admin username (e.g., "pablogsal")
5359
# Legacy team-based auth (deprecated)
54-
admin_github_org: str = "" # GitHub organization name (e.g., "python")
55-
admin_github_teams: str = "" # Comma-separated list of team slugs (e.g., "memory-python-org")
60+
admin_github_org: str = "" # GitHub organization name (e.g., "python")
61+
admin_github_teams: str = (
62+
"" # Comma-separated list of team slugs (e.g., "memory-python-org")
63+
)
5664

5765
# Performance Configuration
5866
top_functions_limit: int = 10
@@ -78,14 +86,18 @@ def cors_origins_list(self) -> List[str]:
7886
"""Parse CORS origins string into a list."""
7987
if not self.cors_origins.strip():
8088
return []
81-
return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
82-
89+
return [
90+
origin.strip() for origin in self.cors_origins.split(",") if origin.strip()
91+
]
92+
8393
@property
8494
def admin_github_teams_list(self) -> List[str]:
8595
"""Parse admin GitHub teams string into a list."""
8696
if not self.admin_github_teams.strip():
8797
return []
88-
return [team.strip() for team in self.admin_github_teams.split(",") if team.strip()]
98+
return [
99+
team.strip() for team in self.admin_github_teams.split(",") if team.strip()
100+
]
89101

90102

91103
@lru_cache()

backend/app/crud.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ async def create_commit(
9999
timestamp = commit.timestamp
100100
if timestamp.tzinfo is not None:
101101
timestamp = timestamp.replace(tzinfo=None)
102-
102+
103103
db_commit = models.Commit(
104104
sha=commit.sha,
105105
timestamp=timestamp,
@@ -118,8 +118,7 @@ async def create_commit(
118118
async def get_binaries(db: AsyncSession) -> List[models.Binary]:
119119
result = await db.execute(
120120
select(models.Binary).order_by(
121-
models.Binary.display_order.asc(),
122-
models.Binary.name.asc()
121+
models.Binary.display_order.asc(), models.Binary.name.asc()
123122
)
124123
)
125124
return result.scalars().all()
@@ -217,7 +216,7 @@ async def get_runs_with_commits(
217216

218217
if commit_sha:
219218
# Use prefix matching (starts with) for commit SHA
220-
query = query.where(models.Run.commit_sha.ilike(f'{commit_sha}%'))
219+
query = query.where(models.Run.commit_sha.ilike(f"{commit_sha}%"))
221220
if binary_id:
222221
query = query.where(models.Run.binary_id == binary_id)
223222
if environment_id:
@@ -239,7 +238,7 @@ async def count_runs(
239238

240239
if commit_sha:
241240
# Use prefix matching (starts with) for commit SHA
242-
query = query.where(models.Run.commit_sha.ilike(f'{commit_sha}%'))
241+
query = query.where(models.Run.commit_sha.ilike(f"{commit_sha}%"))
243242
if binary_id:
244243
query = query.where(models.Run.binary_id == binary_id)
245244
if environment_id:
@@ -254,7 +253,7 @@ async def create_run(db: AsyncSession, run: schemas.RunCreate) -> models.Run:
254253
timestamp = run.timestamp
255254
if timestamp.tzinfo is not None:
256255
timestamp = timestamp.replace(tzinfo=None)
257-
256+
258257
db_run = models.Run(
259258
run_id=run.run_id,
260259
commit_sha=run.commit_sha,
@@ -467,9 +466,7 @@ async def create_auth_token(
467466
db: AsyncSession, token: str, name: str, description: str = None
468467
) -> models.AuthToken:
469468
"""Create a new auth token."""
470-
db_token = models.AuthToken(
471-
token=token, name=name, description=description
472-
)
469+
db_token = models.AuthToken(token=token, name=name, description=description)
473470
db.add(db_token)
474471
await db.commit()
475472
await db.refresh(db_token)
@@ -512,30 +509,34 @@ async def deactivate_auth_token(db: AsyncSession, token_id: int) -> bool:
512509
async def get_admin_users(db: AsyncSession) -> List[models.AdminUser]:
513510
"""Get all admin users."""
514511
result = await db.execute(
515-
select(models.AdminUser).where(models.AdminUser.is_active == True).order_by(models.AdminUser.added_at)
512+
select(models.AdminUser)
513+
.where(models.AdminUser.is_active == True)
514+
.order_by(models.AdminUser.added_at)
516515
)
517516
return result.scalars().all()
518517

519518

520-
async def get_admin_user_by_username(db: AsyncSession, username: str) -> Optional[models.AdminUser]:
519+
async def get_admin_user_by_username(
520+
db: AsyncSession, username: str
521+
) -> Optional[models.AdminUser]:
521522
"""Get admin user by GitHub username."""
522523
result = await db.execute(
523524
select(models.AdminUser).where(
524525
and_(
525526
models.AdminUser.github_username == username,
526-
models.AdminUser.is_active == True
527+
models.AdminUser.is_active == True,
527528
)
528529
)
529530
)
530531
return result.scalars().first()
531532

532533

533-
async def create_admin_user(db: AsyncSession, username: str, added_by: str, notes: Optional[str] = None) -> models.AdminUser:
534+
async def create_admin_user(
535+
db: AsyncSession, username: str, added_by: str, notes: Optional[str] = None
536+
) -> models.AdminUser:
534537
"""Create a new admin user."""
535538
admin_user = models.AdminUser(
536-
github_username=username,
537-
added_by=added_by,
538-
notes=notes
539+
github_username=username, added_by=added_by, notes=notes
539540
)
540541
db.add(admin_user)
541542
await db.commit()
@@ -566,10 +567,12 @@ async def ensure_initial_admin(db: AsyncSession, username: str) -> None:
566567
"""Ensure the initial admin user exists."""
567568
if not username:
568569
return
569-
570+
570571
existing = await get_admin_user_by_username(db, username)
571572
if not existing:
572573
logger.info(f"Creating initial admin user: {username}")
573-
await create_admin_user(db, username, "system", "Initial admin from environment variable")
574+
await create_admin_user(
575+
db, username, "system", "Initial admin from environment variable"
576+
)
574577
else:
575578
logger.info(f"Initial admin user already exists: {username}")

backend/app/database.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77

88
logger = logging.getLogger(__name__)
99

10+
1011
def create_database_engine():
1112
"""Create database engine with proper configuration."""
1213
settings = get_settings()
13-
14+
1415
engine_kwargs = {
1516
"echo": settings.database_echo,
1617
"pool_size": settings.database_pool_size,
@@ -20,7 +21,7 @@ def create_database_engine():
2021
# Add connection timeout for production
2122
"connect_args": {"timeout": 30} if "sqlite" in settings.database_url else {},
2223
}
23-
24+
2425
# Add additional PostgreSQL/MySQL specific optimizations
2526
if "postgresql" in settings.database_url:
2627
engine_kwargs["connect_args"] = {
@@ -34,7 +35,7 @@ def create_database_engine():
3435
"charset": "utf8mb4",
3536
"autocommit": False,
3637
}
37-
38+
3839
logger.info(
3940
f"Creating database engine: {settings.database_url.split('@')[-1] if '@' in settings.database_url else settings.database_url}"
4041
)
@@ -43,9 +44,10 @@ def create_database_engine():
4344
f"max_overflow={settings.database_max_overflow}, "
4445
f"recycle={settings.database_pool_recycle}s"
4546
)
46-
47+
4748
return create_async_engine(settings.database_url, **engine_kwargs)
4849

50+
4951
# Create engine using factory function
5052
engine = create_database_engine()
5153
AsyncSessionLocal = async_sessionmaker(
@@ -87,6 +89,7 @@ async def drop_tables():
8789
from contextlib import asynccontextmanager
8890
from typing import AsyncGenerator
8991

92+
9093
@asynccontextmanager
9194
async def transaction_scope() -> AsyncGenerator[AsyncSession, None]:
9295
"""

0 commit comments

Comments
 (0)