tanbushi commited on
Commit
7183ec8
·
1 Parent(s): 4cf1e7e

Sun Jun 8 16:41:37 CST 2025

Browse files
Files changed (5) hide show
  1. app/database.py +2 -2
  2. app/routers/key_management.py +74 -0
  3. app/schemas.py +74 -0
  4. main.py +5 -0
  5. sql/schema.sql +3 -0
app/database.py CHANGED
@@ -38,7 +38,7 @@ class KeyCategory(Base):
38
  name = Column(String, unique=True, index=True, nullable=False)
39
  type = Column(String, nullable=False)
40
  tags = Column(JSONText) # Stored as JSON string
41
- metadata_ = Column(JSONText, name="metadata") # Use metadata_ to avoid conflict with SQLAlchemy metadata
42
 
43
  api_keys = relationship("APIKey", back_populates="category")
44
 
@@ -55,7 +55,7 @@ class APIKey(Base):
55
  status = Column(String, default="active", nullable=False) # 'active', 'inactive'
56
  usage_count = Column(Integer, default=0, nullable=False)
57
  last_used = Column(DateTime)
58
- metadata_ = Column(JSONText, name="metadata") # Use metadata_
59
 
60
  category = relationship("KeyCategory", back_populates="api_keys")
61
 
 
38
  name = Column(String, unique=True, index=True, nullable=False)
39
  type = Column(String, nullable=False)
40
  tags = Column(JSONText) # Stored as JSON string
41
+ metadata_ = Column(JSONText, name="metadata") # Map to 'metadata' column in DB
42
 
43
  api_keys = relationship("APIKey", back_populates="category")
44
 
 
55
  status = Column(String, default="active", nullable=False) # 'active', 'inactive'
56
  usage_count = Column(Integer, default=0, nullable=False)
57
  last_used = Column(DateTime)
58
+ metadata_ = Column(JSONText, name="metadata") # Map to 'metadata' column in DB
59
 
60
  category = relationship("KeyCategory", back_populates="api_keys")
61
 
app/routers/key_management.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Depends, HTTPException, Query
2
+ from sqlalchemy.orm import Session
3
+ from typing import List, Optional # Import Optional
4
+
5
+ from .. import crud, schemas # Use relative imports
6
+ from ..database import get_db # Use relative import
7
+
8
+ router = APIRouter()
9
+
10
+ @router.get("/categories/", response_model=List[schemas.KeyCategory])
11
+ def read_key_categories(
12
+ skip: int = Query(0, ge=0),
13
+ limit: int = Query(100, ge=0, le=100),
14
+ db: Session = Depends(get_db)
15
+ ):
16
+ """
17
+ Retrieve a list of key categories.
18
+ """
19
+ categories = crud.get_key_categories(db, skip=skip, limit=limit)
20
+ return categories
21
+
22
+ @router.post("/categories/", response_model=schemas.KeyCategory)
23
+ def create_key_category(
24
+ category: schemas.KeyCategoryCreate,
25
+ db: Session = Depends(get_db)
26
+ ):
27
+ """
28
+ Create a new key category.
29
+ """
30
+ # Check if category with the same name already exists
31
+ db_category = crud.get_key_category_by_name(db, name=category.name)
32
+ if db_category:
33
+ raise HTTPException(status_code=400, detail="Key Category with this name already exists")
34
+
35
+ # Create the category using the CRUD function
36
+ return crud.create_key_category(db=db, name=category.name, type=category.type, tags=category.tags, metadata=category.metadata)
37
+
38
+ @router.get("/instances/", response_model=List[schemas.APIKey])
39
+ def read_api_keys(
40
+ category_id: Optional[int] = Query(None, ge=1), # Optional filter by category ID
41
+ status: Optional[str] = Query(None, pattern="^(active|inactive)$"), # Optional filter by status
42
+ skip: int = Query(0, ge=0),
43
+ limit: int = Query(100, ge=0, le=100),
44
+ db: Session = Depends(get_db)
45
+ ):
46
+ """
47
+ Retrieve a list of API key instances, optionally filtered by category or status.
48
+ """
49
+ keys = crud.get_api_keys(db, category_id=category_id, status=status, skip=skip, limit=limit)
50
+ return keys
51
+
52
+ @router.post("/instances/", response_model=schemas.APIKey)
53
+ def create_api_key(
54
+ api_key: schemas.APIKeyCreate,
55
+ db: Session = Depends(get_db)
56
+ ):
57
+ """
58
+ Create a new API key instance under a specific category.
59
+ """
60
+ # Check if the category_id exists
61
+ category = crud.get_key_category(db, category_id=api_key.category_id)
62
+ if not category:
63
+ raise HTTPException(status_code=404, detail=f"Key Category with ID {api_key.category_id} not found")
64
+
65
+ # Create the API key instance using the CRUD function
66
+ return crud.create_api_key(
67
+ db=db,
68
+ value=api_key.value,
69
+ category_id=api_key.category_id,
70
+ status=api_key.status,
71
+ metadata=api_key.metadata
72
+ )
73
+
74
+ # TODO: Add other CRUD endpoints for key categories and API keys (GET by ID, PUT, DELETE)
app/schemas.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional, Dict, Any
2
+ from pydantic import BaseModel, Field # Import Field
3
+ from datetime import datetime # Import datetime
4
+
5
+ # Pydantic models for reading data from the database
6
+
7
+ class KeyCategoryBase(BaseModel):
8
+ name: str
9
+ type: str
10
+ tags: Optional[List[str]] = None
11
+ # metadata: Optional[Dict[str, Any]] = None # Removed from base
12
+
13
+ class KeyCategory(KeyCategoryBase):
14
+ id: int
15
+ # Map metadata_ from SQLAlchemy model to metadata in schema
16
+ metadata: Optional[Dict[str, Any]] = Field(None, alias="metadata_")
17
+ created_at: Optional[datetime] = None # Use datetime type
18
+ updated_at: Optional[datetime] = None # Use datetime type
19
+
20
+ class Config:
21
+ orm_mode = True # Enable ORM mode
22
+ by_alias = True # Use aliases for serialization
23
+ # Configure JSON encoding for datetime objects if needed (orm_mode might handle this)
24
+ # json_encoders = {
25
+ # datetime: lambda v: v.isoformat() if v else None
26
+ # }
27
+
28
+
29
+ class APIKeyBase(BaseModel):
30
+ value: str
31
+ category_id: int
32
+ status: str = "active"
33
+ # metadata: Optional[Dict[str, Any]] = None # Removed from base
34
+
35
+ class APIKey(APIKeyBase):
36
+ id: int
37
+ usage_count: int
38
+ last_used: Optional[datetime] = None # Use datetime type
39
+ # Map metadata_ from SQLAlchemy model to metadata in schema
40
+ metadata: Optional[Dict[str, Any]] = Field(None, alias="metadata_")
41
+ created_at: Optional[datetime] = None # Use datetime type
42
+ updated_at: Optional[datetime] = None # Use datetime type
43
+
44
+ class Config:
45
+ orm_mode = True # Enable ORM mode
46
+ by_alias = True # Use aliases for serialization
47
+ # Configure JSON encoding for datetime objects if needed
48
+ # json_encoders = {
49
+ # datetime: lambda v: v.isoformat() if v else None
50
+ # }
51
+
52
+
53
+ # Pydantic models for creating data (less fields, e.g., no ID, timestamps)
54
+
55
+ class KeyCategoryCreate(KeyCategoryBase):
56
+ metadata: Optional[Dict[str, Any]] = None # Add metadata back to create schema
57
+
58
+ class APIKeyCreate(APIKeyBase):
59
+ metadata: Optional[Dict[str, Any]] = None # Add metadata back to create schema
60
+
61
+
62
+ # Pydantic models for updating data (all fields optional)
63
+
64
+ class KeyCategoryUpdate(BaseModel):
65
+ name: Optional[str] = None
66
+ type: Optional[str] = None
67
+ tags: Optional[List[str]] = None
68
+ metadata: Optional[Dict[str, Any]] = None
69
+
70
+ class APIKeyUpdate(BaseModel):
71
+ value: Optional[str] = None
72
+ category_id: Optional[int] = None
73
+ status: Optional[str] = None
74
+ metadata: Optional[Dict[str, Any]] = None
main.py CHANGED
@@ -6,6 +6,8 @@ import os, json
6
  import re # 用于URL路径处理
7
  from key_selector import KeySelector # 自动选择key
8
 
 
 
9
  def get_target_url(url: str) -> str:
10
  """将url参数变了转换为合法的目标url;from http/ or https/ to http:// or https://"""
11
  url = re.sub(r"^http/", "http://", url)
@@ -14,6 +16,9 @@ def get_target_url(url: str) -> str:
14
 
15
  app = FastAPI()
16
 
 
 
 
17
  # 配置日志
18
  logging.basicConfig(level=logging.INFO)
19
  logger = logging.getLogger("uvicorn.error")
 
6
  import re # 用于URL路径处理
7
  from key_selector import KeySelector # 自动选择key
8
 
9
+ from app.routers import key_management # Import the new router
10
+
11
  def get_target_url(url: str) -> str:
12
  """将url参数变了转换为合法的目标url;from http/ or https/ to http:// or https://"""
13
  url = re.sub(r"^http/", "http://", url)
 
16
 
17
  app = FastAPI()
18
 
19
+ # Include the new key management router
20
+ app.include_router(key_management.router, prefix="/api/keys", tags=["Key Management"])
21
+
22
  # 配置日志
23
  logging.basicConfig(level=logging.INFO)
24
  logger = logging.getLogger("uvicorn.error")
sql/schema.sql ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ CREATE TABLE key_categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, type TEXT NOT NULL, tags TEXT, metadata TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL );
2
+ CREATE TABLE sqlite_sequence(name,seq);
3
+ CREATE TABLE api_keys ( id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT NOT NULL, category_id INTEGER NOT NULL, status TEXT DEFAULT 'active' NOT NULL, usage_count INTEGER DEFAULT 0 NOT NULL, last_used DATETIME, metadata TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, FOREIGN KEY (category_id) REFERENCES key_categories (id) ON DELETE CASCADE );