codeshift 0.3.7__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codeshift/__init__.py +2 -2
- codeshift/cli/__init__.py +1 -1
- codeshift/cli/commands/__init__.py +1 -1
- codeshift/cli/commands/auth.py +5 -5
- codeshift/cli/commands/scan.py +2 -5
- codeshift/cli/commands/upgrade.py +2 -7
- codeshift/cli/commands/upgrade_all.py +1 -1
- codeshift/cli/main.py +2 -2
- codeshift/migrator/llm_migrator.py +8 -12
- codeshift/utils/__init__.py +1 -1
- codeshift/utils/api_client.py +11 -11
- codeshift/utils/cache.py +1 -1
- {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/METADATA +2 -17
- {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/RECORD +18 -34
- {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/licenses/LICENSE +1 -1
- codeshift/api/__init__.py +0 -1
- codeshift/api/auth.py +0 -182
- codeshift/api/config.py +0 -73
- codeshift/api/database.py +0 -215
- codeshift/api/main.py +0 -103
- codeshift/api/models/__init__.py +0 -55
- codeshift/api/models/auth.py +0 -108
- codeshift/api/models/billing.py +0 -92
- codeshift/api/models/migrate.py +0 -42
- codeshift/api/models/usage.py +0 -116
- codeshift/api/routers/__init__.py +0 -5
- codeshift/api/routers/auth.py +0 -440
- codeshift/api/routers/billing.py +0 -395
- codeshift/api/routers/migrate.py +0 -304
- codeshift/api/routers/usage.py +0 -291
- codeshift/api/routers/webhooks.py +0 -289
- {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/WHEEL +0 -0
- {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/entry_points.txt +0 -0
- {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/top_level.txt +0 -0
codeshift/api/config.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"""API configuration settings."""
|
|
2
|
-
|
|
3
|
-
from functools import lru_cache
|
|
4
|
-
|
|
5
|
-
from pydantic_settings import BaseSettings
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class APISettings(BaseSettings):
|
|
9
|
-
"""Configuration settings for the Codeshift API."""
|
|
10
|
-
|
|
11
|
-
# Supabase
|
|
12
|
-
supabase_url: str = ""
|
|
13
|
-
supabase_anon_key: str = ""
|
|
14
|
-
supabase_service_role_key: str = ""
|
|
15
|
-
|
|
16
|
-
# Stripe
|
|
17
|
-
stripe_secret_key: str = ""
|
|
18
|
-
stripe_webhook_secret: str = ""
|
|
19
|
-
stripe_price_id_pro: str = ""
|
|
20
|
-
stripe_price_id_unlimited: str = ""
|
|
21
|
-
|
|
22
|
-
# Anthropic (for server-side LLM calls)
|
|
23
|
-
anthropic_api_key: str = ""
|
|
24
|
-
|
|
25
|
-
# API settings
|
|
26
|
-
codeshift_api_url: str = "https://py-resolve.replit.app"
|
|
27
|
-
api_key_prefix: str = "cs_"
|
|
28
|
-
|
|
29
|
-
# Tier quotas
|
|
30
|
-
tier_free_files: int = 100
|
|
31
|
-
tier_free_llm_calls: int = 50
|
|
32
|
-
tier_pro_files: int = 1000
|
|
33
|
-
tier_pro_llm_calls: int = 500
|
|
34
|
-
tier_unlimited_files: int = 999999999
|
|
35
|
-
tier_unlimited_llm_calls: int = 999999999
|
|
36
|
-
|
|
37
|
-
# Environment
|
|
38
|
-
environment: str = "development"
|
|
39
|
-
|
|
40
|
-
model_config = {
|
|
41
|
-
"env_prefix": "",
|
|
42
|
-
"env_file": ".env",
|
|
43
|
-
"extra": "ignore",
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def is_production(self) -> bool:
|
|
48
|
-
"""Check if running in production."""
|
|
49
|
-
return self.environment == "production"
|
|
50
|
-
|
|
51
|
-
def get_tier_limits(self, tier: str) -> dict[str, int]:
|
|
52
|
-
"""Get quota limits for a tier."""
|
|
53
|
-
limits = {
|
|
54
|
-
"free": {
|
|
55
|
-
"files_per_month": self.tier_free_files,
|
|
56
|
-
"llm_calls_per_month": self.tier_free_llm_calls,
|
|
57
|
-
},
|
|
58
|
-
"pro": {
|
|
59
|
-
"files_per_month": self.tier_pro_files,
|
|
60
|
-
"llm_calls_per_month": self.tier_pro_llm_calls,
|
|
61
|
-
},
|
|
62
|
-
"unlimited": {
|
|
63
|
-
"files_per_month": self.tier_unlimited_files,
|
|
64
|
-
"llm_calls_per_month": self.tier_unlimited_llm_calls,
|
|
65
|
-
},
|
|
66
|
-
}
|
|
67
|
-
return limits.get(tier, limits["free"])
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@lru_cache
|
|
71
|
-
def get_settings() -> APISettings:
|
|
72
|
-
"""Get cached API settings."""
|
|
73
|
-
return APISettings()
|
codeshift/api/database.py
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
"""Supabase database client and operations."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
from typing import Any, Optional, cast
|
|
5
|
-
|
|
6
|
-
from codeshift.api.config import get_settings
|
|
7
|
-
from supabase import Client as SupabaseClient
|
|
8
|
-
from supabase import create_client
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def get_supabase_client() -> "SupabaseClient":
|
|
12
|
-
"""Get a Supabase client instance."""
|
|
13
|
-
settings = get_settings()
|
|
14
|
-
return create_client(
|
|
15
|
-
settings.supabase_url,
|
|
16
|
-
settings.supabase_service_role_key,
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def get_supabase_anon_client() -> "SupabaseClient":
|
|
21
|
-
"""Get a Supabase client with anon key (for user-facing operations)."""
|
|
22
|
-
settings = get_settings()
|
|
23
|
-
return create_client(
|
|
24
|
-
settings.supabase_url,
|
|
25
|
-
settings.supabase_anon_key,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class Database:
|
|
30
|
-
"""Database operations wrapper."""
|
|
31
|
-
|
|
32
|
-
def __init__(self, client: Optional["SupabaseClient"] = None):
|
|
33
|
-
"""Initialize with optional client, otherwise use service role client."""
|
|
34
|
-
self._client = client
|
|
35
|
-
|
|
36
|
-
@property
|
|
37
|
-
def client(self) -> "SupabaseClient":
|
|
38
|
-
"""Get or create the Supabase client."""
|
|
39
|
-
if self._client is None:
|
|
40
|
-
self._client = get_supabase_client()
|
|
41
|
-
return self._client
|
|
42
|
-
|
|
43
|
-
# Profile operations
|
|
44
|
-
def get_profile_by_id(self, user_id: str) -> dict | None:
|
|
45
|
-
"""Get a user profile by ID."""
|
|
46
|
-
result = self.client.table("profiles").select("*").eq("id", user_id).execute()
|
|
47
|
-
return result.data[0] if result.data else None
|
|
48
|
-
|
|
49
|
-
def get_profile_by_email(self, email: str) -> dict | None:
|
|
50
|
-
"""Get a user profile by email."""
|
|
51
|
-
result = self.client.table("profiles").select("*").eq("email", email).execute()
|
|
52
|
-
return result.data[0] if result.data else None
|
|
53
|
-
|
|
54
|
-
def update_profile(self, user_id: str, data: dict) -> dict | None:
|
|
55
|
-
"""Update a user profile."""
|
|
56
|
-
result = self.client.table("profiles").update(data).eq("id", user_id).execute()
|
|
57
|
-
return result.data[0] if result.data else None
|
|
58
|
-
|
|
59
|
-
def update_profile_tier(
|
|
60
|
-
self, user_id: str, tier: str, stripe_customer_id: str | None = None
|
|
61
|
-
) -> dict | None:
|
|
62
|
-
"""Update a user's tier and optionally their Stripe customer ID."""
|
|
63
|
-
data = {"tier": tier, "updated_at": datetime.now(timezone.utc).isoformat()}
|
|
64
|
-
if stripe_customer_id:
|
|
65
|
-
data["stripe_customer_id"] = stripe_customer_id
|
|
66
|
-
return self.update_profile(user_id, data)
|
|
67
|
-
|
|
68
|
-
# API key operations
|
|
69
|
-
def get_api_key_by_hash(self, key_hash: str) -> dict | None:
|
|
70
|
-
"""Get an API key by its hash."""
|
|
71
|
-
result = (
|
|
72
|
-
self.client.table("api_keys")
|
|
73
|
-
.select("*, profiles(*)")
|
|
74
|
-
.eq("key_hash", key_hash)
|
|
75
|
-
.eq("revoked", False)
|
|
76
|
-
.execute()
|
|
77
|
-
)
|
|
78
|
-
return result.data[0] if result.data else None
|
|
79
|
-
|
|
80
|
-
def get_api_key_by_prefix(self, key_prefix: str) -> dict | None:
|
|
81
|
-
"""Get an API key by its prefix."""
|
|
82
|
-
result = (
|
|
83
|
-
self.client.table("api_keys")
|
|
84
|
-
.select("*, profiles(*)")
|
|
85
|
-
.eq("key_prefix", key_prefix)
|
|
86
|
-
.eq("revoked", False)
|
|
87
|
-
.execute()
|
|
88
|
-
)
|
|
89
|
-
return result.data[0] if result.data else None
|
|
90
|
-
|
|
91
|
-
def create_api_key(
|
|
92
|
-
self,
|
|
93
|
-
user_id: str,
|
|
94
|
-
key_prefix: str,
|
|
95
|
-
key_hash: str,
|
|
96
|
-
name: str = "CLI Key",
|
|
97
|
-
scopes: list[str] | None = None,
|
|
98
|
-
) -> dict[str, Any]:
|
|
99
|
-
"""Create a new API key."""
|
|
100
|
-
data = {
|
|
101
|
-
"user_id": user_id,
|
|
102
|
-
"key_prefix": key_prefix,
|
|
103
|
-
"key_hash": key_hash,
|
|
104
|
-
"name": name,
|
|
105
|
-
"scopes": scopes or ["read", "write"],
|
|
106
|
-
}
|
|
107
|
-
result = self.client.table("api_keys").insert(data).execute()
|
|
108
|
-
return cast(dict[str, Any], result.data[0])
|
|
109
|
-
|
|
110
|
-
def revoke_api_key(self, key_id: str) -> bool:
|
|
111
|
-
"""Revoke an API key."""
|
|
112
|
-
result = (
|
|
113
|
-
self.client.table("api_keys")
|
|
114
|
-
.update({"revoked": True, "revoked_at": datetime.now(timezone.utc).isoformat()})
|
|
115
|
-
.eq("id", key_id)
|
|
116
|
-
.execute()
|
|
117
|
-
)
|
|
118
|
-
return bool(result.data)
|
|
119
|
-
|
|
120
|
-
def update_api_key_last_used(self, key_id: str) -> None:
|
|
121
|
-
"""Update the last_used_at timestamp for an API key."""
|
|
122
|
-
self.client.table("api_keys").update(
|
|
123
|
-
{"last_used_at": datetime.now(timezone.utc).isoformat()}
|
|
124
|
-
).eq("id", key_id).execute()
|
|
125
|
-
|
|
126
|
-
# Usage event operations
|
|
127
|
-
def record_usage_event(
|
|
128
|
-
self,
|
|
129
|
-
user_id: str,
|
|
130
|
-
event_type: str,
|
|
131
|
-
library: str | None = None,
|
|
132
|
-
quantity: int = 1,
|
|
133
|
-
metadata: dict[str, Any] | None = None,
|
|
134
|
-
) -> dict[str, Any]:
|
|
135
|
-
"""Record a usage event."""
|
|
136
|
-
now = datetime.now(timezone.utc)
|
|
137
|
-
data = {
|
|
138
|
-
"user_id": user_id,
|
|
139
|
-
"event_type": event_type,
|
|
140
|
-
"library": library,
|
|
141
|
-
"quantity": quantity,
|
|
142
|
-
"metadata": metadata or {},
|
|
143
|
-
"billing_period": now.strftime("%Y-%m"),
|
|
144
|
-
"created_at": now.isoformat(),
|
|
145
|
-
}
|
|
146
|
-
result = self.client.table("usage_events").insert(data).execute()
|
|
147
|
-
return cast(dict[str, Any], result.data[0])
|
|
148
|
-
|
|
149
|
-
def get_usage_for_period(
|
|
150
|
-
self, user_id: str, billing_period: str | None = None
|
|
151
|
-
) -> dict[str, int]:
|
|
152
|
-
"""Get usage summary for a billing period."""
|
|
153
|
-
if billing_period is None:
|
|
154
|
-
billing_period = datetime.now(timezone.utc).strftime("%Y-%m")
|
|
155
|
-
|
|
156
|
-
result = (
|
|
157
|
-
self.client.table("usage_events")
|
|
158
|
-
.select("event_type, quantity")
|
|
159
|
-
.eq("user_id", user_id)
|
|
160
|
-
.eq("billing_period", billing_period)
|
|
161
|
-
.execute()
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
# Aggregate by event type
|
|
165
|
-
usage: dict[str, int] = {}
|
|
166
|
-
for event in result.data:
|
|
167
|
-
event_type = event["event_type"]
|
|
168
|
-
usage[event_type] = usage.get(event_type, 0) + event["quantity"]
|
|
169
|
-
|
|
170
|
-
return usage
|
|
171
|
-
|
|
172
|
-
def get_usage_events(
|
|
173
|
-
self,
|
|
174
|
-
user_id: str,
|
|
175
|
-
billing_period: str | None = None,
|
|
176
|
-
event_type: str | None = None,
|
|
177
|
-
limit: int = 100,
|
|
178
|
-
) -> list[dict[str, Any]]:
|
|
179
|
-
"""Get detailed usage events."""
|
|
180
|
-
if billing_period is None:
|
|
181
|
-
billing_period = datetime.now(timezone.utc).strftime("%Y-%m")
|
|
182
|
-
|
|
183
|
-
query = (
|
|
184
|
-
self.client.table("usage_events")
|
|
185
|
-
.select("*")
|
|
186
|
-
.eq("user_id", user_id)
|
|
187
|
-
.eq("billing_period", billing_period)
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
if event_type:
|
|
191
|
-
query = query.eq("event_type", event_type)
|
|
192
|
-
|
|
193
|
-
result = query.order("created_at", desc=True).limit(limit).execute()
|
|
194
|
-
return cast(list[dict[str, Any]], result.data)
|
|
195
|
-
|
|
196
|
-
def get_user_quota(self, user_id: str) -> dict[str, int] | None:
|
|
197
|
-
"""Get quota information for a user.
|
|
198
|
-
|
|
199
|
-
Returns:
|
|
200
|
-
Dict with llm_calls and file_migrated counts, or None if error.
|
|
201
|
-
"""
|
|
202
|
-
billing_period = datetime.now(timezone.utc).strftime("%Y-%m")
|
|
203
|
-
return self.get_usage_for_period(user_id, billing_period)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
# Singleton instance
|
|
207
|
-
_db: Database | None = None
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
def get_database() -> Database:
|
|
211
|
-
"""Get the database singleton."""
|
|
212
|
-
global _db
|
|
213
|
-
if _db is None:
|
|
214
|
-
_db = Database()
|
|
215
|
-
return _db
|
codeshift/api/main.py
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"""FastAPI application for Codeshift billing API."""
|
|
2
|
-
|
|
3
|
-
from collections.abc import AsyncGenerator
|
|
4
|
-
from contextlib import asynccontextmanager
|
|
5
|
-
|
|
6
|
-
from fastapi import FastAPI, Request
|
|
7
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
8
|
-
from fastapi.responses import JSONResponse
|
|
9
|
-
|
|
10
|
-
from codeshift import __version__
|
|
11
|
-
from codeshift.api.config import get_settings
|
|
12
|
-
from codeshift.api.routers import auth, billing, migrate, usage, webhooks
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@asynccontextmanager
|
|
16
|
-
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
17
|
-
"""Application lifespan handler."""
|
|
18
|
-
# Startup
|
|
19
|
-
settings = get_settings()
|
|
20
|
-
print(f"Codeshift API starting (environment: {settings.environment})")
|
|
21
|
-
yield
|
|
22
|
-
# Shutdown
|
|
23
|
-
print("Codeshift API shutting down")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
app = FastAPI(
|
|
27
|
-
title="Codeshift API",
|
|
28
|
-
description="Billing and authentication API for Codeshift CLI",
|
|
29
|
-
version=__version__,
|
|
30
|
-
lifespan=lifespan,
|
|
31
|
-
docs_url="/docs" if not get_settings().is_production else None,
|
|
32
|
-
redoc_url="/redoc" if not get_settings().is_production else None,
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
# CORS configuration
|
|
36
|
-
app.add_middleware(
|
|
37
|
-
CORSMiddleware,
|
|
38
|
-
allow_origins=[
|
|
39
|
-
"https://codeshift.dev",
|
|
40
|
-
"https://www.codeshift.dev",
|
|
41
|
-
"http://localhost:3000", # Local development
|
|
42
|
-
],
|
|
43
|
-
allow_credentials=True,
|
|
44
|
-
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
45
|
-
allow_headers=["*"],
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# Include routers
|
|
50
|
-
app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
|
|
51
|
-
app.include_router(billing.router, prefix="/billing", tags=["Billing"])
|
|
52
|
-
app.include_router(migrate.router, tags=["Migration"])
|
|
53
|
-
app.include_router(usage.router, prefix="/usage", tags=["Usage"])
|
|
54
|
-
app.include_router(webhooks.router, prefix="/webhooks", tags=["Webhooks"])
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@app.get("/")
|
|
58
|
-
async def root() -> dict:
|
|
59
|
-
"""API root endpoint."""
|
|
60
|
-
return {
|
|
61
|
-
"name": "Codeshift API",
|
|
62
|
-
"version": __version__,
|
|
63
|
-
"status": "healthy",
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@app.get("/health")
|
|
68
|
-
async def health_check() -> dict:
|
|
69
|
-
"""Health check endpoint."""
|
|
70
|
-
return {"status": "healthy", "version": __version__}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
@app.exception_handler(Exception)
|
|
74
|
-
async def global_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
|
75
|
-
"""Global exception handler."""
|
|
76
|
-
settings = get_settings()
|
|
77
|
-
|
|
78
|
-
# Log the error
|
|
79
|
-
print(f"Unhandled exception: {exc}")
|
|
80
|
-
|
|
81
|
-
# Return appropriate response based on environment
|
|
82
|
-
if settings.is_production:
|
|
83
|
-
return JSONResponse(
|
|
84
|
-
status_code=500,
|
|
85
|
-
content={"detail": "Internal server error"},
|
|
86
|
-
)
|
|
87
|
-
else:
|
|
88
|
-
return JSONResponse(
|
|
89
|
-
status_code=500,
|
|
90
|
-
content={"detail": str(exc), "type": type(exc).__name__},
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# For running with uvicorn directly
|
|
95
|
-
if __name__ == "__main__":
|
|
96
|
-
import uvicorn
|
|
97
|
-
|
|
98
|
-
uvicorn.run(
|
|
99
|
-
"codeshift.api.main:app",
|
|
100
|
-
host="0.0.0.0",
|
|
101
|
-
port=8000,
|
|
102
|
-
reload=True,
|
|
103
|
-
)
|
codeshift/api/models/__init__.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""Pydantic models for the PyResolve API."""
|
|
2
|
-
|
|
3
|
-
from codeshift.api.models.auth import (
|
|
4
|
-
APIKey,
|
|
5
|
-
APIKeyCreate,
|
|
6
|
-
APIKeyResponse,
|
|
7
|
-
TokenResponse,
|
|
8
|
-
UserInfo,
|
|
9
|
-
)
|
|
10
|
-
from codeshift.api.models.billing import (
|
|
11
|
-
CheckoutSessionRequest,
|
|
12
|
-
CheckoutSessionResponse,
|
|
13
|
-
PortalSessionResponse,
|
|
14
|
-
SubscriptionInfo,
|
|
15
|
-
TierInfo,
|
|
16
|
-
)
|
|
17
|
-
from codeshift.api.models.migrate import (
|
|
18
|
-
ExplainChangeRequest,
|
|
19
|
-
ExplainChangeResponse,
|
|
20
|
-
MigrateCodeRequest,
|
|
21
|
-
MigrateCodeResponse,
|
|
22
|
-
)
|
|
23
|
-
from codeshift.api.models.usage import (
|
|
24
|
-
QuotaInfo,
|
|
25
|
-
UsageEvent,
|
|
26
|
-
UsageEventCreate,
|
|
27
|
-
UsageResponse,
|
|
28
|
-
UsageSummary,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
__all__ = [
|
|
32
|
-
# Auth models
|
|
33
|
-
"APIKey",
|
|
34
|
-
"APIKeyCreate",
|
|
35
|
-
"APIKeyResponse",
|
|
36
|
-
"TokenResponse",
|
|
37
|
-
"UserInfo",
|
|
38
|
-
# Billing models
|
|
39
|
-
"CheckoutSessionRequest",
|
|
40
|
-
"CheckoutSessionResponse",
|
|
41
|
-
"PortalSessionResponse",
|
|
42
|
-
"SubscriptionInfo",
|
|
43
|
-
"TierInfo",
|
|
44
|
-
# Migrate models
|
|
45
|
-
"ExplainChangeRequest",
|
|
46
|
-
"ExplainChangeResponse",
|
|
47
|
-
"MigrateCodeRequest",
|
|
48
|
-
"MigrateCodeResponse",
|
|
49
|
-
# Usage models
|
|
50
|
-
"QuotaInfo",
|
|
51
|
-
"UsageEvent",
|
|
52
|
-
"UsageEventCreate",
|
|
53
|
-
"UsageResponse",
|
|
54
|
-
"UsageSummary",
|
|
55
|
-
]
|
codeshift/api/models/auth.py
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"""Authentication models for the PyResolve API."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, EmailStr, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class UserInfo(BaseModel):
|
|
9
|
-
"""User profile information."""
|
|
10
|
-
|
|
11
|
-
id: str
|
|
12
|
-
email: EmailStr
|
|
13
|
-
full_name: str | None = None
|
|
14
|
-
tier: str = "free"
|
|
15
|
-
stripe_customer_id: str | None = None
|
|
16
|
-
created_at: datetime
|
|
17
|
-
|
|
18
|
-
class Config:
|
|
19
|
-
from_attributes = True
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class APIKeyCreate(BaseModel):
|
|
23
|
-
"""Request to create a new API key."""
|
|
24
|
-
|
|
25
|
-
name: str = Field(default="CLI Key", min_length=1, max_length=100)
|
|
26
|
-
scopes: list[str] = Field(default=["read", "write"])
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class APIKey(BaseModel):
|
|
30
|
-
"""API key information (without the secret)."""
|
|
31
|
-
|
|
32
|
-
id: str
|
|
33
|
-
name: str
|
|
34
|
-
key_prefix: str
|
|
35
|
-
scopes: list[str]
|
|
36
|
-
revoked: bool = False
|
|
37
|
-
last_used_at: datetime | None = None
|
|
38
|
-
expires_at: datetime | None = None
|
|
39
|
-
created_at: datetime
|
|
40
|
-
|
|
41
|
-
class Config:
|
|
42
|
-
from_attributes = True
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class APIKeyResponse(BaseModel):
|
|
46
|
-
"""Response when creating a new API key (includes full key once)."""
|
|
47
|
-
|
|
48
|
-
id: str
|
|
49
|
-
name: str
|
|
50
|
-
key: str # Full API key - only shown once
|
|
51
|
-
key_prefix: str
|
|
52
|
-
scopes: list[str]
|
|
53
|
-
created_at: datetime
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class TokenResponse(BaseModel):
|
|
57
|
-
"""Response for authentication token."""
|
|
58
|
-
|
|
59
|
-
access_token: str
|
|
60
|
-
token_type: str = "bearer"
|
|
61
|
-
expires_in: int
|
|
62
|
-
refresh_token: str | None = None
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class LoginRequest(BaseModel):
|
|
66
|
-
"""Request for CLI login."""
|
|
67
|
-
|
|
68
|
-
email: EmailStr
|
|
69
|
-
password: str = Field(min_length=6)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class LoginResponse(BaseModel):
|
|
73
|
-
"""Response for CLI login."""
|
|
74
|
-
|
|
75
|
-
api_key: str
|
|
76
|
-
user: UserInfo
|
|
77
|
-
message: str = "Login successful"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class DeviceCodeRequest(BaseModel):
|
|
81
|
-
"""Request to initiate device code flow."""
|
|
82
|
-
|
|
83
|
-
client_id: str = "codeshift-cli"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class DeviceCodeResponse(BaseModel):
|
|
87
|
-
"""Response with device code for authentication."""
|
|
88
|
-
|
|
89
|
-
device_code: str
|
|
90
|
-
user_code: str
|
|
91
|
-
verification_uri: str
|
|
92
|
-
expires_in: int = 900 # 15 minutes
|
|
93
|
-
interval: int = 5 # Polling interval in seconds
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class DeviceTokenRequest(BaseModel):
|
|
97
|
-
"""Request to exchange device code for token."""
|
|
98
|
-
|
|
99
|
-
device_code: str
|
|
100
|
-
client_id: str = "codeshift-cli"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
class RegisterRequest(BaseModel):
|
|
104
|
-
"""Request for new user registration."""
|
|
105
|
-
|
|
106
|
-
email: EmailStr
|
|
107
|
-
password: str = Field(min_length=8, description="Password must be at least 8 characters")
|
|
108
|
-
full_name: str | None = Field(default=None, max_length=100)
|
codeshift/api/models/billing.py
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"""Billing models for the PyResolve API."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TierInfo(BaseModel):
|
|
9
|
-
"""Information about a pricing tier."""
|
|
10
|
-
|
|
11
|
-
name: str
|
|
12
|
-
display_name: str
|
|
13
|
-
price_monthly: int # In cents
|
|
14
|
-
files_per_month: int
|
|
15
|
-
llm_calls_per_month: int
|
|
16
|
-
features: list[str]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class SubscriptionInfo(BaseModel):
|
|
20
|
-
"""Current subscription information."""
|
|
21
|
-
|
|
22
|
-
tier: str
|
|
23
|
-
status: str # active, canceled, past_due, etc.
|
|
24
|
-
stripe_subscription_id: str | None = None
|
|
25
|
-
current_period_start: datetime | None = None
|
|
26
|
-
current_period_end: datetime | None = None
|
|
27
|
-
cancel_at_period_end: bool = False
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class CheckoutSessionRequest(BaseModel):
|
|
31
|
-
"""Request to create a Stripe checkout session."""
|
|
32
|
-
|
|
33
|
-
tier: str = Field(..., pattern="^(pro|unlimited)$")
|
|
34
|
-
success_url: str | None = None
|
|
35
|
-
cancel_url: str | None = None
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class CheckoutSessionResponse(BaseModel):
|
|
39
|
-
"""Response with Stripe checkout session."""
|
|
40
|
-
|
|
41
|
-
checkout_url: str
|
|
42
|
-
session_id: str
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class PortalSessionResponse(BaseModel):
|
|
46
|
-
"""Response with Stripe billing portal URL."""
|
|
47
|
-
|
|
48
|
-
portal_url: str
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class PriceInfo(BaseModel):
|
|
52
|
-
"""Stripe price information."""
|
|
53
|
-
|
|
54
|
-
id: str
|
|
55
|
-
product_id: str
|
|
56
|
-
unit_amount: int
|
|
57
|
-
currency: str
|
|
58
|
-
recurring_interval: str
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class InvoiceInfo(BaseModel):
|
|
62
|
-
"""Invoice information."""
|
|
63
|
-
|
|
64
|
-
id: str
|
|
65
|
-
status: str
|
|
66
|
-
amount_due: int
|
|
67
|
-
amount_paid: int
|
|
68
|
-
currency: str
|
|
69
|
-
created: datetime
|
|
70
|
-
due_date: datetime | None = None
|
|
71
|
-
hosted_invoice_url: str | None = None
|
|
72
|
-
pdf_url: str | None = None
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class PaymentMethodInfo(BaseModel):
|
|
76
|
-
"""Payment method information."""
|
|
77
|
-
|
|
78
|
-
id: str
|
|
79
|
-
type: str # card, bank_account, etc.
|
|
80
|
-
card_brand: str | None = None
|
|
81
|
-
card_last4: str | None = None
|
|
82
|
-
card_exp_month: int | None = None
|
|
83
|
-
card_exp_year: int | None = None
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class BillingOverview(BaseModel):
|
|
87
|
-
"""Complete billing overview for a user."""
|
|
88
|
-
|
|
89
|
-
subscription: SubscriptionInfo
|
|
90
|
-
tier_info: TierInfo
|
|
91
|
-
payment_method: PaymentMethodInfo | None = None
|
|
92
|
-
upcoming_invoice: InvoiceInfo | None = None
|
codeshift/api/models/migrate.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"""Migration models for the PyResolve API."""
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class MigrateCodeRequest(BaseModel):
|
|
9
|
-
"""Request to migrate code using LLM."""
|
|
10
|
-
|
|
11
|
-
code: str = Field(..., description="Source code to migrate")
|
|
12
|
-
library: str = Field(..., description="Library being upgraded (e.g., 'pydantic')")
|
|
13
|
-
from_version: str = Field(..., description="Current version (e.g., '1.10.0')")
|
|
14
|
-
to_version: str = Field(..., description="Target version (e.g., '2.5.0')")
|
|
15
|
-
context: str | None = Field(None, description="Optional context about the migration")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class MigrateCodeResponse(BaseModel):
|
|
19
|
-
"""Response from LLM migration."""
|
|
20
|
-
|
|
21
|
-
success: bool
|
|
22
|
-
migrated_code: str
|
|
23
|
-
original_code: str
|
|
24
|
-
error: str | None = None
|
|
25
|
-
usage: dict[str, Any] = Field(default_factory=dict)
|
|
26
|
-
cached: bool = False
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class ExplainChangeRequest(BaseModel):
|
|
30
|
-
"""Request to explain a migration change."""
|
|
31
|
-
|
|
32
|
-
original_code: str = Field(..., description="Original code before migration")
|
|
33
|
-
transformed_code: str = Field(..., description="Transformed code after migration")
|
|
34
|
-
library: str = Field(..., description="Library being upgraded")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class ExplainChangeResponse(BaseModel):
|
|
38
|
-
"""Response with explanation of changes."""
|
|
39
|
-
|
|
40
|
-
success: bool
|
|
41
|
-
explanation: str | None = None
|
|
42
|
-
error: str | None = None
|