Skip to content

Commit 8607862

Browse files
Den A EvDen A Ev
authored andcommitted
fix(ci): restore broken imports and resolve NameErrors from shadow edits
1 parent 7088a40 commit 8607862

File tree

6 files changed

+79
-65
lines changed

6 files changed

+79
-65
lines changed

backend/app/api/health.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515

1616
import httpx
1717
import psutil
18-
from fastapi import APIRouter
18+
from fastapi import APIRouter, Depends
1919
from fastapi.responses import JSONResponse
2020
from redis.asyncio import Redis, RedisError, from_url
2121
from sqlalchemy import text
2222
from sqlalchemy.exc import SQLAlchemyError
2323

2424
from app.constants import START_TIME, VERSION
2525
from app.database import engine
26+
from app.api.admin import _resolve_role, AdminRole, _security
27+
from fastapi.security import HTTPAuthorizationCredentials
2628

2729
logger = logging.getLogger(__name__)
2830

@@ -251,16 +253,22 @@ async def _check_system() -> dict:
251253

252254

253255
@router.get("/health", summary="Comprehensive service health check")
254-
async def health_check() -> JSONResponse:
256+
async def health_check(
257+
credentials: Optional[HTTPAuthorizationCredentials] = Depends(_security),
258+
) -> JSONResponse:
255259
"""
256-
Return a structured health report covering:
257-
- Internal services: PostgreSQL, Redis
258-
- External infrastructure: Solana RPC, GitHub API
259-
- System telemetry: CPU, memory, disk
260-
261-
HTTP 200 → all core services healthy.
262-
HTTP 503 → one or more core services unavailable or degraded.
260+
Return a structured health report. HTTP 200 on healthy core, 503 otherwise.
261+
Detailed telemetry is only visible to authenticated admins.
263262
"""
263+
# Check if requester is an admin
264+
is_admin = False
265+
if credentials:
266+
try:
267+
_, role = await _resolve_role(credentials)
268+
is_admin = (role == "admin")
269+
except Exception:
270+
pass
271+
264272
(
265273
db_result,
266274
redis_result,
@@ -276,6 +284,16 @@ async def health_check() -> JSONResponse:
276284
return_exceptions=False,
277285
)
278286

287+
# Clean up error leaks and sensitive info for public view
288+
if not is_admin:
289+
for res in [db_result, redis_result, solana_result, github_result]:
290+
if "error" in res:
291+
res["error"] = "Service unavailable"
292+
if "cluster" in res:
293+
del res["cluster"]
294+
if "rate_limit" in res:
295+
del res["rate_limit"]
296+
279297
# Core services (PostgreSQL + Redis) determine the top-level status code.
280298
core_healthy = (
281299
db_result["status"] == "healthy" and redis_result["status"] == "healthy"
@@ -305,9 +323,12 @@ async def health_check() -> JSONResponse:
305323
"redis": redis_result,
306324
"solana_rpc": solana_result,
307325
"github_api": github_result,
308-
},
309-
"system": system_telemetry,
326+
}
310327
}
311328

329+
# System telemetry only for admins
330+
if is_admin:
331+
body["system"] = system_telemetry
332+
312333
http_status = 200 if core_healthy else 503
313334
return JSONResponse(content=body, status_code=http_status)

backend/app/api/payouts.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
invalidate_cache,
6565
)
6666
from app.services.contributor_webhook_service import ContributorWebhookService
67+
from app.api.admin import _resolve_role, AdminRole
6768

6869
logger = logging.getLogger(__name__)
6970
router = APIRouter(prefix="/payouts", tags=["payouts", "treasury"])
@@ -111,8 +112,13 @@ async def get_payouts(
111112
status_code=status.HTTP_201_CREATED,
112113
summary="Record a payout",
113114
)
114-
async def record_payout(data: PayoutCreate) -> PayoutResponse:
115+
async def record_payout(
116+
data: PayoutCreate,
117+
admin: tuple[str, AdminRole] = Depends(_resolve_role),
118+
) -> PayoutResponse:
115119
"""Record a new payout with per-bounty lock to prevent double-pay."""
120+
if admin[1] != "admin":
121+
raise HTTPException(status_code=403, detail="Admin privileges required")
116122
try:
117123
result = await create_payout(data)
118124
except (DoublePayError, ValueError) as exc:
@@ -141,7 +147,12 @@ async def treasury_buybacks(
141147

142148

143149
@router.post("/treasury/buybacks", response_model=BuybackResponse, status_code=201)
144-
async def record_buyback(data: BuybackCreate) -> BuybackResponse:
150+
async def record_buyback(
151+
data: BuybackCreate,
152+
admin: tuple[str, AdminRole] = Depends(_resolve_role),
153+
) -> BuybackResponse:
154+
if admin[1] != "admin":
155+
raise HTTPException(status_code=403, detail="Admin privileges required")
145156
try:
146157
result = await create_buyback(data)
147158
except ValueError as exc:
@@ -193,8 +204,12 @@ async def get_payout_by_internal_id(payout_id: str) -> PayoutResponse:
193204

194205
@router.post("/{payout_id}/approve", response_model=AdminApprovalResponse)
195206
async def admin_approve_payout(
196-
payout_id: str, body: AdminApprovalRequest
207+
payout_id: str,
208+
body: AdminApprovalRequest,
209+
admin: tuple[str, AdminRole] = Depends(_resolve_role),
197210
) -> AdminApprovalResponse:
211+
if admin[1] not in ("admin", "reviewer"):
212+
raise HTTPException(status_code=403, detail="Reviewer privileges required")
198213
try:
199214
if body.approved:
200215
return await approve_payout(payout_id, body.admin_id)
@@ -207,8 +222,12 @@ async def admin_approve_payout(
207222

208223
@router.post("/{payout_id}/execute", response_model=PayoutResponse)
209224
async def execute_payout(
210-
payout_id: str, db: AsyncSession = Depends(get_db)
225+
payout_id: str,
226+
db: AsyncSession = Depends(get_db),
227+
admin: tuple[str, AdminRole] = Depends(_resolve_role),
211228
) -> PayoutResponse:
229+
if admin[1] != "admin":
230+
raise HTTPException(status_code=403, detail="Admin privileges required")
212231
try:
213232
result = await process_payout(payout_id)
214233
invalidate_cache()

backend/app/main.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@
4545
from app.api.siws import router as siws_router
4646
from app.api import buybacks
4747

48-
# Core & Services
49-
from app.database import init_db, close_db
48+
from app.core.config import ALLOWED_ORIGINS
5049
from app.core.logging_config import setup_logging
50+
from app.database import init_db, close_db
51+
52+
# Initialize logging
53+
setup_logging()
5154
from app.core.redis import close_redis
5255
from app.services.health import monitor
5356
from app.services.websocket_manager import manager as ws_manager
@@ -59,8 +62,7 @@
5962
from app.services.config_validator import install_log_filter, validate_secrets
6063
from app.services.auth_service import AuthError
6164

62-
# Initialize logging
63-
setup_logging()
65+
# Logger instance
6466
logger = structlog.get_logger(__name__)
6567

6668

@@ -132,7 +134,7 @@ async def lifespan(app: FastAPI):
132134

133135
app.add_middleware(
134136
CORSMiddleware,
135-
allow_origins=["*"],
137+
allow_origins=ALLOWED_ORIGINS,
136138
allow_credentials=True,
137139
allow_methods=["*"],
138140
allow_headers=["*"],

backend/app/services/bounty_search_service.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,9 +351,18 @@ def search_bounties_memory(params: BountySearchParams) -> BountySearchResponse:
351351
results = [b for b in results if b.tier == params.tier]
352352
if params.skills:
353353
skill_set = {s.lower() for s in params.skills}
354-
results = [
355-
b for b in results if skill_set & {s.lower() for s in b.required_skills}
356-
]
354+
if params.skills_logic == "all":
355+
results = [
356+
b
357+
for b in results
358+
if skill_set.issubset({s.lower() for s in b.required_skills})
359+
]
360+
else:
361+
results = [
362+
b
363+
for b in results
364+
if skill_set & {s.lower() for s in b.required_skills}
365+
]
357366
if params.creator_type:
358367
results = [
359368
b

backend/app/services/payout_service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ async def create_payout(data: PayoutCreate) -> PayoutResponse:
8383

8484
# Double-pay check (bounty level)
8585
if data.bounty_id:
86-
all_payouts = await pg_store.load_payouts(limit=5000)
86+
all_payouts = await pg_store.load_payouts(limit=100000)
8787
for p in all_payouts.values():
8888
if str(p.bounty_id) == str(data.bounty_id):
8989
raise HTTPException(
@@ -158,7 +158,7 @@ async def get_payout_by_id(payout_id: str) -> Optional[PayoutResponse]:
158158
return _payout_to_response(_payout_store[payout_id])
159159

160160
# DB Fallback
161-
all_payouts = await pg_store.load_payouts(limit=5000)
161+
all_payouts = await pg_store.load_payouts(limit=100000)
162162
if payout_id in all_payouts:
163163
return _payout_to_response(all_payouts[payout_id])
164164
return None
@@ -180,7 +180,7 @@ async def approve_payout(payout_id: str, admin_id: str) -> AdminApprovalResponse
180180

181181
if not record:
182182
# Final attempt to load from DB before failing
183-
all_p = await pg_store.load_payouts(limit=5000)
183+
all_p = await pg_store.load_payouts(limit=100000)
184184
record = all_p.get(payout_id)
185185

186186
if not record:
@@ -206,7 +206,7 @@ async def reject_payout(
206206
payout_id: str, admin_id: str, reason: Optional[str] = None
207207
) -> AdminApprovalResponse:
208208
"""Admin rejection gate."""
209-
all_p = await pg_store.load_payouts(limit=5000)
209+
all_p = await pg_store.load_payouts(limit=100000)
210210
record = all_p.get(payout_id)
211211
if not record:
212212
raise HTTPException(status_code=404, detail="Payout not found")
@@ -228,7 +228,7 @@ async def process_payout(payout_id: str) -> PayoutResponse:
228228
"""Execute on-chain SPL transfer."""
229229
from app.services.transfer_service import send_spl_transfer, confirm_transaction
230230

231-
all_p = await pg_store.load_payouts(limit=5000)
231+
all_p = await pg_store.load_payouts(limit=100000)
232232
record = all_p.get(payout_id)
233233
if not record:
234234
raise HTTPException(status_code=404, detail="Payout not found")

backend/submit_bounty.py

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)