jebin2 commited on
Commit
661c02e
·
1 Parent(s): fc638eb

credit api

Browse files
Files changed (2) hide show
  1. app.py +2 -1
  2. routers/credits.py +121 -0
app.py CHANGED
@@ -12,7 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
12
  from fastapi.responses import JSONResponse
13
 
14
  from core.database import init_db
15
- from routers import auth, blink, general, gemini
16
  from services.drive_service import DriveService
17
 
18
  # Configure logging
@@ -86,6 +86,7 @@ app.include_router(general.router)
86
  app.include_router(auth.router)
87
  app.include_router(blink.router)
88
  app.include_router(gemini.router)
 
89
 
90
 
91
  @app.exception_handler(Exception)
 
12
  from fastapi.responses import JSONResponse
13
 
14
  from core.database import init_db
15
+ from routers import auth, blink, credits, general, gemini
16
  from services.drive_service import DriveService
17
 
18
  # Configure logging
 
86
  app.include_router(auth.router)
87
  app.include_router(blink.router)
88
  app.include_router(gemini.router)
89
+ app.include_router(credits.router)
90
 
91
 
92
  @app.exception_handler(Exception)
routers/credits.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Credits Router - API endpoints for credit management.
3
+
4
+ Provides endpoints for checking credit balance and viewing credit history.
5
+ """
6
+ from fastapi import APIRouter, Depends, Query
7
+ from pydantic import BaseModel
8
+ from typing import List, Optional
9
+ from datetime import datetime
10
+ from sqlalchemy.ext.asyncio import AsyncSession
11
+ from sqlalchemy import select, desc
12
+
13
+ from core.database import get_db
14
+ from core.models import User, GeminiJob
15
+ from dependencies import get_current_user
16
+
17
+ router = APIRouter(prefix="/credits", tags=["credits"])
18
+
19
+
20
+ # Response Models
21
+ class CreditBalanceResponse(BaseModel):
22
+ """Response for credit balance endpoint."""
23
+ user_id: str
24
+ credits: int
25
+ last_used_at: Optional[str] = None
26
+
27
+
28
+ class CreditHistoryItem(BaseModel):
29
+ """Single item in credit history."""
30
+ job_id: str
31
+ job_type: str
32
+ status: str
33
+ credits_reserved: int
34
+ credits_refunded: bool
35
+ error_message: Optional[str] = None
36
+ created_at: str
37
+ completed_at: Optional[str] = None
38
+
39
+
40
+ class CreditHistoryResponse(BaseModel):
41
+ """Response for credit history endpoint."""
42
+ user_id: str
43
+ current_balance: int
44
+ history: List[CreditHistoryItem]
45
+ total_count: int
46
+ page: int
47
+ limit: int
48
+
49
+
50
+ @router.get("", response_model=CreditBalanceResponse)
51
+ async def get_credits(
52
+ user: User = Depends(get_current_user)
53
+ ):
54
+ """
55
+ Get current credit balance.
56
+
57
+ Returns the user's current credit balance and last usage time.
58
+ """
59
+ return CreditBalanceResponse(
60
+ user_id=user.user_id,
61
+ credits=user.credits,
62
+ last_used_at=user.last_used_at.isoformat() if user.last_used_at else None
63
+ )
64
+
65
+
66
+ @router.get("/history", response_model=CreditHistoryResponse)
67
+ async def get_credit_history(
68
+ user: User = Depends(get_current_user),
69
+ db: AsyncSession = Depends(get_db),
70
+ page: int = Query(1, ge=1, description="Page number"),
71
+ limit: int = Query(20, ge=1, le=100, description="Items per page")
72
+ ):
73
+ """
74
+ Get credit usage history.
75
+
76
+ Returns a paginated list of jobs with credit transactions,
77
+ showing which jobs used credits and which were refunded.
78
+
79
+ Only includes jobs where credits were reserved (credits_reserved > 0).
80
+ """
81
+ offset = (page - 1) * limit
82
+
83
+ # Query jobs with credit transactions
84
+ query = select(GeminiJob).where(
85
+ GeminiJob.user_id == user.user_id,
86
+ GeminiJob.credits_reserved > 0 # Only jobs that had credits reserved
87
+ ).order_by(desc(GeminiJob.created_at)).offset(offset).limit(limit)
88
+
89
+ result = await db.execute(query)
90
+ jobs = result.scalars().all()
91
+
92
+ # Get total count
93
+ count_query = select(GeminiJob).where(
94
+ GeminiJob.user_id == user.user_id,
95
+ GeminiJob.credits_reserved > 0
96
+ )
97
+ count_result = await db.execute(count_query)
98
+ total_count = len(count_result.scalars().all())
99
+
100
+ # Build history items
101
+ history = []
102
+ for job in jobs:
103
+ history.append(CreditHistoryItem(
104
+ job_id=job.job_id,
105
+ job_type=job.job_type,
106
+ status=job.status,
107
+ credits_reserved=job.credits_reserved,
108
+ credits_refunded=job.credits_refunded or False,
109
+ error_message=job.error_message,
110
+ created_at=job.created_at.isoformat() if job.created_at else None,
111
+ completed_at=job.completed_at.isoformat() if job.completed_at else None
112
+ ))
113
+
114
+ return CreditHistoryResponse(
115
+ user_id=user.user_id,
116
+ current_balance=user.credits,
117
+ history=history,
118
+ total_count=total_count,
119
+ page=page,
120
+ limit=limit
121
+ )