cinchdb 0.1.0__py3-none-any.whl → 0.1.2__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.
- cinchdb/cli/commands/branch.py +9 -0
- cinchdb/cli/commands/database.py +9 -0
- cinchdb/cli/commands/tenant.py +17 -0
- cinchdb/managers/branch.py +5 -0
- cinchdb/managers/tenant.py +13 -0
- cinchdb/models/branch.py +9 -1
- cinchdb/models/database.py +9 -1
- cinchdb/models/tenant.py +9 -1
- cinchdb/utils/__init__.py +11 -1
- cinchdb/utils/name_validator.py +129 -0
- {cinchdb-0.1.0.dist-info → cinchdb-0.1.2.dist-info}/METADATA +17 -19
- {cinchdb-0.1.0.dist-info → cinchdb-0.1.2.dist-info}/RECORD +15 -30
- {cinchdb-0.1.0.dist-info → cinchdb-0.1.2.dist-info}/entry_points.txt +0 -1
- cinchdb/api/__init__.py +0 -5
- cinchdb/api/app.py +0 -76
- cinchdb/api/auth.py +0 -290
- cinchdb/api/main.py +0 -137
- cinchdb/api/routers/__init__.py +0 -25
- cinchdb/api/routers/auth.py +0 -135
- cinchdb/api/routers/branches.py +0 -368
- cinchdb/api/routers/codegen.py +0 -164
- cinchdb/api/routers/columns.py +0 -290
- cinchdb/api/routers/data.py +0 -479
- cinchdb/api/routers/databases.py +0 -177
- cinchdb/api/routers/projects.py +0 -133
- cinchdb/api/routers/query.py +0 -156
- cinchdb/api/routers/tables.py +0 -349
- cinchdb/api/routers/tenants.py +0 -216
- cinchdb/api/routers/views.py +0 -219
- {cinchdb-0.1.0.dist-info → cinchdb-0.1.2.dist-info}/WHEEL +0 -0
- {cinchdb-0.1.0.dist-info → cinchdb-0.1.2.dist-info}/licenses/LICENSE +0 -0
cinchdb/api/auth.py
DELETED
@@ -1,290 +0,0 @@
|
|
1
|
-
"""Authentication and API key management for CinchDB API."""
|
2
|
-
|
3
|
-
import uuid
|
4
|
-
from datetime import datetime, timezone
|
5
|
-
from typing import Optional, List, Literal
|
6
|
-
from pathlib import Path
|
7
|
-
|
8
|
-
from fastapi import HTTPException, Security, Depends, Query
|
9
|
-
from fastapi.security import APIKeyHeader
|
10
|
-
from pydantic import BaseModel, Field
|
11
|
-
|
12
|
-
from cinchdb.config import Config
|
13
|
-
from cinchdb.core.path_utils import get_project_root
|
14
|
-
|
15
|
-
|
16
|
-
# API Key header
|
17
|
-
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
18
|
-
|
19
|
-
|
20
|
-
class APIKey(BaseModel):
|
21
|
-
"""API key model."""
|
22
|
-
|
23
|
-
key: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
24
|
-
name: str
|
25
|
-
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
26
|
-
permissions: Literal["read", "write"] = "read"
|
27
|
-
branches: Optional[List[str]] = None # None means all branches
|
28
|
-
active: bool = True
|
29
|
-
|
30
|
-
|
31
|
-
class APIKeyManager:
|
32
|
-
"""Manages API keys in the project configuration."""
|
33
|
-
|
34
|
-
def __init__(self, project_dir: Optional[Path] = None):
|
35
|
-
"""Initialize API key manager.
|
36
|
-
|
37
|
-
Args:
|
38
|
-
project_dir: Project directory path
|
39
|
-
"""
|
40
|
-
self.project_dir = project_dir or Path.cwd()
|
41
|
-
self.config = Config(self.project_dir)
|
42
|
-
|
43
|
-
def create_key(
|
44
|
-
self,
|
45
|
-
name: str,
|
46
|
-
permissions: Literal["read", "write"] = "read",
|
47
|
-
branches: Optional[List[str]] = None,
|
48
|
-
) -> APIKey:
|
49
|
-
"""Create a new API key.
|
50
|
-
|
51
|
-
Args:
|
52
|
-
name: Name for the API key
|
53
|
-
permissions: Permission level (read or write)
|
54
|
-
branches: List of allowed branches (None for all)
|
55
|
-
|
56
|
-
Returns:
|
57
|
-
Created API key
|
58
|
-
"""
|
59
|
-
api_key = APIKey(name=name, permissions=permissions, branches=branches)
|
60
|
-
|
61
|
-
# Load config and add key
|
62
|
-
config_data = self.config.load()
|
63
|
-
if not config_data.api_keys:
|
64
|
-
config_data.api_keys = {}
|
65
|
-
|
66
|
-
config_data.api_keys[api_key.key] = {
|
67
|
-
"name": api_key.name,
|
68
|
-
"created_at": api_key.created_at.isoformat(),
|
69
|
-
"permissions": api_key.permissions,
|
70
|
-
"branches": api_key.branches,
|
71
|
-
"active": api_key.active,
|
72
|
-
}
|
73
|
-
|
74
|
-
self.config.save(config_data)
|
75
|
-
return api_key
|
76
|
-
|
77
|
-
def get_key(self, key: str) -> Optional[APIKey]:
|
78
|
-
"""Get an API key by its value.
|
79
|
-
|
80
|
-
Args:
|
81
|
-
key: API key string
|
82
|
-
|
83
|
-
Returns:
|
84
|
-
API key if found and active, None otherwise
|
85
|
-
"""
|
86
|
-
config_data = self.config.load()
|
87
|
-
if not config_data.api_keys or key not in config_data.api_keys:
|
88
|
-
return None
|
89
|
-
|
90
|
-
key_data = config_data.api_keys[key]
|
91
|
-
if not key_data.get("active", True):
|
92
|
-
return None
|
93
|
-
|
94
|
-
return APIKey(
|
95
|
-
key=key,
|
96
|
-
name=key_data["name"],
|
97
|
-
created_at=datetime.fromisoformat(key_data["created_at"]),
|
98
|
-
permissions=key_data.get("permissions", "read"),
|
99
|
-
branches=key_data.get("branches"),
|
100
|
-
active=key_data.get("active", True),
|
101
|
-
)
|
102
|
-
|
103
|
-
def list_keys(self) -> List[APIKey]:
|
104
|
-
"""List all API keys.
|
105
|
-
|
106
|
-
Returns:
|
107
|
-
List of API keys
|
108
|
-
"""
|
109
|
-
config_data = self.config.load()
|
110
|
-
if not config_data.api_keys:
|
111
|
-
return []
|
112
|
-
|
113
|
-
keys = []
|
114
|
-
for key, key_data in config_data.api_keys.items():
|
115
|
-
keys.append(
|
116
|
-
APIKey(
|
117
|
-
key=key,
|
118
|
-
name=key_data["name"],
|
119
|
-
created_at=datetime.fromisoformat(key_data["created_at"]),
|
120
|
-
permissions=key_data.get("permissions", "read"),
|
121
|
-
branches=key_data.get("branches"),
|
122
|
-
active=key_data.get("active", True),
|
123
|
-
)
|
124
|
-
)
|
125
|
-
|
126
|
-
return keys
|
127
|
-
|
128
|
-
def revoke_key(self, key: str) -> bool:
|
129
|
-
"""Revoke an API key.
|
130
|
-
|
131
|
-
Args:
|
132
|
-
key: API key to revoke
|
133
|
-
|
134
|
-
Returns:
|
135
|
-
True if revoked, False if not found
|
136
|
-
"""
|
137
|
-
config_data = self.config.load()
|
138
|
-
if not config_data.api_keys or key not in config_data.api_keys:
|
139
|
-
return False
|
140
|
-
|
141
|
-
config_data.api_keys[key]["active"] = False
|
142
|
-
self.config.save(config_data)
|
143
|
-
return True
|
144
|
-
|
145
|
-
def delete_key(self, key: str) -> bool:
|
146
|
-
"""Delete an API key.
|
147
|
-
|
148
|
-
Args:
|
149
|
-
key: API key to delete
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
True if deleted, False if not found
|
153
|
-
"""
|
154
|
-
config_data = self.config.load()
|
155
|
-
if not config_data.api_keys or key not in config_data.api_keys:
|
156
|
-
return False
|
157
|
-
|
158
|
-
del config_data.api_keys[key]
|
159
|
-
self.config.save(config_data)
|
160
|
-
return True
|
161
|
-
|
162
|
-
|
163
|
-
class AuthContext(BaseModel):
|
164
|
-
"""Authentication context for requests."""
|
165
|
-
|
166
|
-
api_key: APIKey
|
167
|
-
project_dir: Path
|
168
|
-
|
169
|
-
|
170
|
-
async def get_current_project(project_dir: Optional[str] = None) -> Path:
|
171
|
-
"""Get the current project directory.
|
172
|
-
|
173
|
-
Args:
|
174
|
-
project_dir: Optional project directory path
|
175
|
-
|
176
|
-
Returns:
|
177
|
-
Project directory path
|
178
|
-
|
179
|
-
Raises:
|
180
|
-
HTTPException: If project not found
|
181
|
-
"""
|
182
|
-
if project_dir:
|
183
|
-
path = Path(project_dir)
|
184
|
-
if not path.exists() or not (path / ".cinchdb").exists():
|
185
|
-
raise HTTPException(status_code=404, detail="Project not found")
|
186
|
-
return path
|
187
|
-
|
188
|
-
# Try to find project from current directory
|
189
|
-
project_root = get_project_root(Path.cwd())
|
190
|
-
if not project_root:
|
191
|
-
raise HTTPException(
|
192
|
-
status_code=404,
|
193
|
-
detail="No CinchDB project found. Specify project_dir parameter.",
|
194
|
-
)
|
195
|
-
|
196
|
-
return project_root
|
197
|
-
|
198
|
-
|
199
|
-
async def verify_api_key(
|
200
|
-
api_key_header: Optional[str] = Security(api_key_header),
|
201
|
-
api_key_query: Optional[str] = Query(None, alias="api_key"),
|
202
|
-
project_dir: Path = Depends(get_current_project),
|
203
|
-
) -> AuthContext:
|
204
|
-
"""Verify API key and return auth context.
|
205
|
-
|
206
|
-
Args:
|
207
|
-
api_key_header: API key from header
|
208
|
-
api_key_query: API key from query parameter
|
209
|
-
project_dir: Project directory
|
210
|
-
|
211
|
-
Returns:
|
212
|
-
Authentication context
|
213
|
-
|
214
|
-
Raises:
|
215
|
-
HTTPException: If authentication fails
|
216
|
-
"""
|
217
|
-
# Check header first, then query parameter
|
218
|
-
api_key = api_key_header or api_key_query
|
219
|
-
|
220
|
-
if not api_key:
|
221
|
-
raise HTTPException(
|
222
|
-
status_code=401,
|
223
|
-
detail="API key required",
|
224
|
-
headers={"WWW-Authenticate": "ApiKey"},
|
225
|
-
)
|
226
|
-
|
227
|
-
manager = APIKeyManager(project_dir)
|
228
|
-
key_obj = manager.get_key(api_key)
|
229
|
-
|
230
|
-
if not key_obj:
|
231
|
-
raise HTTPException(
|
232
|
-
status_code=401,
|
233
|
-
detail="Invalid API key",
|
234
|
-
headers={"WWW-Authenticate": "ApiKey"},
|
235
|
-
)
|
236
|
-
|
237
|
-
return AuthContext(api_key=key_obj, project_dir=project_dir)
|
238
|
-
|
239
|
-
|
240
|
-
async def require_write_permission(
|
241
|
-
auth: AuthContext = Depends(verify_api_key), branch: Optional[str] = None
|
242
|
-
) -> AuthContext:
|
243
|
-
"""Require write permission for the request.
|
244
|
-
|
245
|
-
Args:
|
246
|
-
auth: Authentication context
|
247
|
-
branch: Optional branch name to check
|
248
|
-
|
249
|
-
Returns:
|
250
|
-
Authentication context
|
251
|
-
|
252
|
-
Raises:
|
253
|
-
HTTPException: If permission denied
|
254
|
-
"""
|
255
|
-
if auth.api_key.permissions != "write":
|
256
|
-
raise HTTPException(status_code=403, detail="Write permission required")
|
257
|
-
|
258
|
-
# Check branch-specific permissions
|
259
|
-
if branch and auth.api_key.branches and branch not in auth.api_key.branches:
|
260
|
-
raise HTTPException(
|
261
|
-
status_code=403, detail=f"Access denied for branch '{branch}'"
|
262
|
-
)
|
263
|
-
|
264
|
-
return auth
|
265
|
-
|
266
|
-
|
267
|
-
async def require_read_permission(
|
268
|
-
auth: AuthContext = Depends(verify_api_key), branch: Optional[str] = None
|
269
|
-
) -> AuthContext:
|
270
|
-
"""Require read permission for the request.
|
271
|
-
|
272
|
-
Args:
|
273
|
-
auth: Authentication context
|
274
|
-
branch: Optional branch name to check
|
275
|
-
|
276
|
-
Returns:
|
277
|
-
Authentication context
|
278
|
-
|
279
|
-
Raises:
|
280
|
-
HTTPException: If permission denied
|
281
|
-
"""
|
282
|
-
# All valid API keys have at least read permission
|
283
|
-
|
284
|
-
# Check branch-specific permissions
|
285
|
-
if branch and auth.api_key.branches and branch not in auth.api_key.branches:
|
286
|
-
raise HTTPException(
|
287
|
-
status_code=403, detail=f"Access denied for branch '{branch}'"
|
288
|
-
)
|
289
|
-
|
290
|
-
return auth
|
cinchdb/api/main.py
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
"""Main entry point for CinchDB API server."""
|
2
|
-
|
3
|
-
import uvicorn
|
4
|
-
from pathlib import Path
|
5
|
-
from typing import Optional
|
6
|
-
|
7
|
-
import typer
|
8
|
-
from rich.console import Console
|
9
|
-
|
10
|
-
from cinchdb.api.auth import APIKeyManager
|
11
|
-
from cinchdb.core.path_utils import get_project_root
|
12
|
-
|
13
|
-
|
14
|
-
cli = typer.Typer(
|
15
|
-
name="cinch-server",
|
16
|
-
help="CinchDB API server",
|
17
|
-
add_completion=False,
|
18
|
-
)
|
19
|
-
console = Console()
|
20
|
-
|
21
|
-
|
22
|
-
@cli.command()
|
23
|
-
def serve(
|
24
|
-
host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
|
25
|
-
port: int = typer.Option(8000, "--port", "-p", help="Port to bind to"),
|
26
|
-
reload: bool = typer.Option(False, "--reload", "-r", help="Enable auto-reload"),
|
27
|
-
project_dir: Optional[Path] = typer.Option(
|
28
|
-
None, "--project-dir", "-d", help="Project directory"
|
29
|
-
),
|
30
|
-
create_key: bool = typer.Option(
|
31
|
-
False, "--create-key", help="Create an API key on startup"
|
32
|
-
),
|
33
|
-
):
|
34
|
-
"""Start the CinchDB API server."""
|
35
|
-
# Find project directory
|
36
|
-
if project_dir:
|
37
|
-
project_path = Path(project_dir)
|
38
|
-
else:
|
39
|
-
project_path = get_project_root(Path.cwd())
|
40
|
-
|
41
|
-
if not project_path or not (project_path / ".cinchdb").exists():
|
42
|
-
console.print("[red]❌ No CinchDB project found[/red]")
|
43
|
-
console.print("[yellow]Run 'cinch init' to create a project[/yellow]")
|
44
|
-
raise typer.Exit(1)
|
45
|
-
|
46
|
-
console.print("[green]Starting CinchDB API server[/green]")
|
47
|
-
console.print(f"Project: {project_path}")
|
48
|
-
console.print(f"Host: {host}:{port}")
|
49
|
-
|
50
|
-
# Create API key if requested
|
51
|
-
if create_key:
|
52
|
-
manager = APIKeyManager(project_path)
|
53
|
-
api_key = manager.create_key("Initial API Key", permissions="write")
|
54
|
-
console.print("\n[bold green]Created API key:[/bold green]")
|
55
|
-
console.print(f"[yellow]Key: {api_key.key}[/yellow]")
|
56
|
-
console.print(f"[yellow]Permissions: {api_key.permissions}[/yellow]")
|
57
|
-
console.print("\n[bold]Save this key - it won't be shown again![/bold]\n")
|
58
|
-
|
59
|
-
# Start server
|
60
|
-
uvicorn.run(
|
61
|
-
"cinchdb.api.app:app", host=host, port=port, reload=reload, log_level="info"
|
62
|
-
)
|
63
|
-
|
64
|
-
|
65
|
-
@cli.command()
|
66
|
-
def create_key(
|
67
|
-
name: str = typer.Argument(..., help="Name for the API key"),
|
68
|
-
permissions: str = typer.Option(
|
69
|
-
"read", "--permissions", "-p", help="Permissions (read/write)"
|
70
|
-
),
|
71
|
-
project_dir: Optional[Path] = typer.Option(
|
72
|
-
None, "--project-dir", "-d", help="Project directory"
|
73
|
-
),
|
74
|
-
):
|
75
|
-
"""Create a new API key."""
|
76
|
-
# Find project directory
|
77
|
-
if project_dir:
|
78
|
-
project_path = Path(project_dir)
|
79
|
-
else:
|
80
|
-
project_path = get_project_root(Path.cwd())
|
81
|
-
|
82
|
-
if not project_path or not (project_path / ".cinchdb").exists():
|
83
|
-
console.print("[red]❌ No CinchDB project found[/red]")
|
84
|
-
raise typer.Exit(1)
|
85
|
-
|
86
|
-
if permissions not in ["read", "write"]:
|
87
|
-
console.print("[red]❌ Invalid permissions. Must be 'read' or 'write'[/red]")
|
88
|
-
raise typer.Exit(1)
|
89
|
-
|
90
|
-
manager = APIKeyManager(project_path)
|
91
|
-
api_key = manager.create_key(name, permissions=permissions)
|
92
|
-
|
93
|
-
console.print(f"[green]✅ Created API key '{name}'[/green]")
|
94
|
-
console.print(f"[yellow]Key: {api_key.key}[/yellow]")
|
95
|
-
console.print(f"[yellow]Permissions: {api_key.permissions}[/yellow]")
|
96
|
-
console.print("\n[bold]Save this key - it won't be shown again![/bold]")
|
97
|
-
|
98
|
-
|
99
|
-
@cli.command()
|
100
|
-
def list_keys(
|
101
|
-
project_dir: Optional[Path] = typer.Option(
|
102
|
-
None, "--project-dir", "-d", help="Project directory"
|
103
|
-
),
|
104
|
-
):
|
105
|
-
"""List all API keys."""
|
106
|
-
# Find project directory
|
107
|
-
if project_dir:
|
108
|
-
project_path = Path(project_dir)
|
109
|
-
else:
|
110
|
-
project_path = get_project_root(Path.cwd())
|
111
|
-
|
112
|
-
if not project_path or not (project_path / ".cinchdb").exists():
|
113
|
-
console.print("[red]❌ No CinchDB project found[/red]")
|
114
|
-
raise typer.Exit(1)
|
115
|
-
|
116
|
-
manager = APIKeyManager(project_path)
|
117
|
-
keys = manager.list_keys()
|
118
|
-
|
119
|
-
if not keys:
|
120
|
-
console.print("[yellow]No API keys found[/yellow]")
|
121
|
-
return
|
122
|
-
|
123
|
-
console.print("\n[bold]API Keys:[/bold]")
|
124
|
-
for key in keys:
|
125
|
-
status = "[green]Active[/green]" if key.active else "[red]Revoked[/red]"
|
126
|
-
branches = (
|
127
|
-
f"Branches: {', '.join(key.branches)}" if key.branches else "All branches"
|
128
|
-
)
|
129
|
-
console.print(f"\n{key.name} - {status}")
|
130
|
-
console.print(f" Key: {key.key}")
|
131
|
-
console.print(f" Permissions: {key.permissions}")
|
132
|
-
console.print(f" {branches}")
|
133
|
-
console.print(f" Created: {key.created_at}")
|
134
|
-
|
135
|
-
|
136
|
-
if __name__ == "__main__":
|
137
|
-
cli()
|
cinchdb/api/routers/__init__.py
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
"""API routers package."""
|
2
|
-
|
3
|
-
from cinchdb.api.routers import (
|
4
|
-
auth,
|
5
|
-
projects,
|
6
|
-
databases,
|
7
|
-
branches,
|
8
|
-
tenants,
|
9
|
-
tables,
|
10
|
-
columns,
|
11
|
-
views,
|
12
|
-
query,
|
13
|
-
)
|
14
|
-
|
15
|
-
__all__ = [
|
16
|
-
"auth",
|
17
|
-
"projects",
|
18
|
-
"databases",
|
19
|
-
"branches",
|
20
|
-
"tenants",
|
21
|
-
"tables",
|
22
|
-
"columns",
|
23
|
-
"views",
|
24
|
-
"query",
|
25
|
-
]
|
cinchdb/api/routers/auth.py
DELETED
@@ -1,135 +0,0 @@
|
|
1
|
-
"""Authentication router for CinchDB API."""
|
2
|
-
|
3
|
-
from typing import List
|
4
|
-
from fastapi import APIRouter, Depends, HTTPException
|
5
|
-
from pydantic import BaseModel
|
6
|
-
|
7
|
-
from cinchdb.api.auth import (
|
8
|
-
APIKeyManager,
|
9
|
-
AuthContext,
|
10
|
-
verify_api_key,
|
11
|
-
get_current_project,
|
12
|
-
)
|
13
|
-
from pathlib import Path
|
14
|
-
|
15
|
-
|
16
|
-
router = APIRouter()
|
17
|
-
|
18
|
-
|
19
|
-
class CreateAPIKeyRequest(BaseModel):
|
20
|
-
"""Request model for creating API key."""
|
21
|
-
|
22
|
-
name: str
|
23
|
-
permissions: str = "read" # "read" or "write"
|
24
|
-
branches: List[str] = None
|
25
|
-
|
26
|
-
|
27
|
-
class APIKeyResponse(BaseModel):
|
28
|
-
"""Response model for API key."""
|
29
|
-
|
30
|
-
key: str
|
31
|
-
name: str
|
32
|
-
created_at: str
|
33
|
-
permissions: str
|
34
|
-
branches: List[str] = None
|
35
|
-
active: bool
|
36
|
-
|
37
|
-
|
38
|
-
@router.post("/keys", response_model=APIKeyResponse)
|
39
|
-
async def create_api_key(
|
40
|
-
request: CreateAPIKeyRequest,
|
41
|
-
project_dir: Path = Depends(get_current_project),
|
42
|
-
auth: AuthContext = Depends(verify_api_key),
|
43
|
-
):
|
44
|
-
"""Create a new API key (requires existing API key with write permission)."""
|
45
|
-
# Only write permission can create new keys
|
46
|
-
if auth.api_key.permissions != "write":
|
47
|
-
raise HTTPException(
|
48
|
-
status_code=403, detail="Write permission required to create API keys"
|
49
|
-
)
|
50
|
-
|
51
|
-
manager = APIKeyManager(project_dir)
|
52
|
-
|
53
|
-
# Validate permissions
|
54
|
-
if request.permissions not in ["read", "write"]:
|
55
|
-
raise HTTPException(
|
56
|
-
status_code=400, detail="Invalid permissions. Must be 'read' or 'write'"
|
57
|
-
)
|
58
|
-
|
59
|
-
api_key = manager.create_key(
|
60
|
-
name=request.name, permissions=request.permissions, branches=request.branches
|
61
|
-
)
|
62
|
-
|
63
|
-
return APIKeyResponse(
|
64
|
-
key=api_key.key,
|
65
|
-
name=api_key.name,
|
66
|
-
created_at=api_key.created_at.isoformat(),
|
67
|
-
permissions=api_key.permissions,
|
68
|
-
branches=api_key.branches,
|
69
|
-
active=api_key.active,
|
70
|
-
)
|
71
|
-
|
72
|
-
|
73
|
-
@router.get("/keys", response_model=List[APIKeyResponse])
|
74
|
-
async def list_api_keys(
|
75
|
-
project_dir: Path = Depends(get_current_project),
|
76
|
-
auth: AuthContext = Depends(verify_api_key),
|
77
|
-
):
|
78
|
-
"""List all API keys (requires write permission)."""
|
79
|
-
# Only write permission can list keys
|
80
|
-
if auth.api_key.permissions != "write":
|
81
|
-
raise HTTPException(
|
82
|
-
status_code=403, detail="Write permission required to list API keys"
|
83
|
-
)
|
84
|
-
|
85
|
-
manager = APIKeyManager(project_dir)
|
86
|
-
keys = manager.list_keys()
|
87
|
-
|
88
|
-
return [
|
89
|
-
APIKeyResponse(
|
90
|
-
key=k.key,
|
91
|
-
name=k.name,
|
92
|
-
created_at=k.created_at.isoformat(),
|
93
|
-
permissions=k.permissions,
|
94
|
-
branches=k.branches,
|
95
|
-
active=k.active,
|
96
|
-
)
|
97
|
-
for k in keys
|
98
|
-
]
|
99
|
-
|
100
|
-
|
101
|
-
@router.delete("/keys/{key}")
|
102
|
-
async def revoke_api_key(
|
103
|
-
key: str,
|
104
|
-
project_dir: Path = Depends(get_current_project),
|
105
|
-
auth: AuthContext = Depends(verify_api_key),
|
106
|
-
):
|
107
|
-
"""Revoke an API key (requires write permission)."""
|
108
|
-
# Only write permission can revoke keys
|
109
|
-
if auth.api_key.permissions != "write":
|
110
|
-
raise HTTPException(
|
111
|
-
status_code=403, detail="Write permission required to revoke API keys"
|
112
|
-
)
|
113
|
-
|
114
|
-
# Cannot revoke your own key
|
115
|
-
if auth.api_key.key == key:
|
116
|
-
raise HTTPException(status_code=400, detail="Cannot revoke your own API key")
|
117
|
-
|
118
|
-
manager = APIKeyManager(project_dir)
|
119
|
-
if not manager.revoke_key(key):
|
120
|
-
raise HTTPException(status_code=404, detail="API key not found")
|
121
|
-
|
122
|
-
return {"message": "API key revoked"}
|
123
|
-
|
124
|
-
|
125
|
-
@router.get("/me")
|
126
|
-
async def get_current_key_info(auth: AuthContext = Depends(verify_api_key)):
|
127
|
-
"""Get information about the current API key."""
|
128
|
-
return APIKeyResponse(
|
129
|
-
key=auth.api_key.key,
|
130
|
-
name=auth.api_key.name,
|
131
|
-
created_at=auth.api_key.created_at.isoformat(),
|
132
|
-
permissions=auth.api_key.permissions,
|
133
|
-
branches=auth.api_key.branches,
|
134
|
-
active=auth.api_key.active,
|
135
|
-
)
|