Skip to content

Commit 492ab25

Browse files
committed
refactor: repository -> service -> api & 의존성 주입
1 parent f8f1927 commit 492ab25

7 files changed

Lines changed: 88 additions & 52 deletions

File tree

app/api/api_key_api.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
from fastapi import APIRouter
1+
from fastapi import APIRouter, Depends
22

33
from app.core.response import ResponseMessage
44
from app.core.status import CommonCode
55
from app.schemas.api_key.create_model import APIKeyCreate
66
from app.schemas.api_key.response_model import APIKeyResponse
7-
from app.services import api_key_service
7+
from app.services.api_key_service import APIKeyService, api_key_service
8+
9+
api_key_service_dependency = Depends(lambda: api_key_service)
810

911
router = APIRouter()
1012

@@ -15,12 +17,14 @@
1517
summary="API KEY 저장 (처음 한 번)",
1618
description="외부 AI 서비스의 API Key를 암호화하여 로컬 데이터베이스에 저장합니다.",
1719
)
18-
def store_api_key(credential: APIKeyCreate) -> ResponseMessage[APIKeyResponse]:
20+
def store_api_key(
21+
credential: APIKeyCreate, service: APIKeyService = api_key_service_dependency
22+
) -> ResponseMessage[APIKeyResponse]:
1923
"""
2024
- **service_name**: API Key가 사용될 외부 서비스 이름 (예: "OpenAI")
2125
- **api_key**: 암호화하여 저장할 실제 API Key (예: "sk-***..")
2226
"""
23-
created_credential = api_key_service.store_api_key(credential)
27+
created_credential = service.store_api_key(credential)
2428

2529
response_data = APIKeyResponse(
2630
id=created_credential.id,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import sqlite3
2+
3+
from app.core.utils import get_db_path
4+
from app.schemas.api_key.db_model import APIKeyInDB
5+
6+
7+
class APIKeyRepository:
8+
def create_api_key(self, new_id: str, service_name: str, encrypted_key: str) -> APIKeyInDB:
9+
"""
10+
암호화된 API Key 정보를 받아 데이터베이스에 저장하고,
11+
저장된 객체를 반환합니다.
12+
"""
13+
db_path = get_db_path()
14+
conn = None
15+
try:
16+
conn = sqlite3.connect(str(db_path), timeout=10)
17+
conn.row_factory = sqlite3.Row
18+
cursor = conn.cursor()
19+
20+
cursor.execute(
21+
"""
22+
INSERT INTO ai_credential (id, service_name, api_key)
23+
VALUES (?, ?, ?)
24+
""",
25+
(new_id, service_name, encrypted_key),
26+
)
27+
conn.commit()
28+
29+
cursor.execute("SELECT * FROM ai_credential WHERE id = ?", (new_id,))
30+
created_row = cursor.fetchone()
31+
32+
if not created_row:
33+
return None
34+
35+
return APIKeyInDB.model_validate(dict(created_row))
36+
37+
finally:
38+
if conn:
39+
conn.close()
40+
41+
42+
api_key_repository = APIKeyRepository()

app/schemas/api_key/base_model.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# app/schemas/api_key/base_model.py
21
from pydantic import BaseModel, Field
32

43
from app.core.enum.llm_service import LLMServiceEnum

app/schemas/api_key/create_model.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# app/schemas/api_key/create_model.py
21
from pydantic import Field, field_validator
32

43
from app.schemas.api_key.base_model import APIKeyBase

app/schemas/api_key/db_model.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# app/schemas/api_key/db_model.py
21
from datetime import datetime
32

43
from app.schemas.api_key.base_model import APIKeyBase

app/schemas/api_key/response_model.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# app/schemas/api_key/response_model.py
21
from datetime import datetime
32

43
from pydantic import Field

app/services/api_key_service.py

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,47 @@
11
import sqlite3
22

3+
from fastapi import Depends
4+
35
from app.core.exceptions import APIException
46
from app.core.security import AES256
57
from app.core.status import CommonCode
6-
from app.core.utils import generate_prefixed_uuid, get_db_path
8+
from app.core.utils import generate_prefixed_uuid
9+
from app.repository.api_key_repository import APIKeyRepository, api_key_repository
710
from app.schemas.api_key.create_model import APIKeyCreate
811
from app.schemas.api_key.db_model import APIKeyInDB
912

13+
api_key_repository_dependency = Depends(lambda: api_key_repository)
14+
15+
16+
class APIKeyService:
17+
def store_api_key(
18+
self, credential_data: APIKeyCreate, repository: APIKeyRepository = api_key_repository
19+
) -> APIKeyInDB:
20+
"""API_KEY를 암호화하고 repository를 통해 데이터베이스에 저장합니다."""
21+
try:
22+
encrypted_key = AES256.encrypt(credential_data.api_key)
23+
new_id = generate_prefixed_uuid("QGENIE")
24+
25+
created_row = repository.create_api_key(
26+
new_id=new_id,
27+
service_name=credential_data.service_name.value,
28+
encrypted_key=encrypted_key,
29+
)
30+
31+
if not created_row:
32+
raise APIException(CommonCode.FAIL_TO_VERIFY_CREATION)
33+
34+
return created_row
35+
36+
except sqlite3.IntegrityError as e:
37+
# UNIQUE 제약 조건 위반 (service_name)
38+
raise APIException(CommonCode.DUPLICATION) from e
39+
except sqlite3.Error as e:
40+
# "database is locked" 오류를 명시적으로 처리
41+
if "database is locked" in str(e):
42+
raise APIException(CommonCode.DB_BUSY) from e
43+
# 기타 모든 sqlite3 오류
44+
raise APIException(CommonCode.FAIL) from e
45+
1046

11-
def store_api_key(credential_data: APIKeyCreate) -> APIKeyInDB:
12-
"""API_KEY를 암호화하여 데이터베이스에 저장합니다."""
13-
14-
encrypted_key = AES256.encrypt(credential_data.api_key)
15-
new_id = generate_prefixed_uuid("QGENIE")
16-
17-
db_path = get_db_path()
18-
conn = None
19-
try:
20-
# timeout을 10초로 설정하여 BUSY 상태에서 대기하도록 함
21-
conn = sqlite3.connect(str(db_path), timeout=10)
22-
conn.row_factory = sqlite3.Row
23-
cursor = conn.cursor()
24-
25-
cursor.execute(
26-
"""
27-
INSERT INTO ai_credential (id, service_name, api_key)
28-
VALUES (?, ?, ?)
29-
""",
30-
(new_id, credential_data.service_name, encrypted_key),
31-
)
32-
conn.commit()
33-
34-
cursor.execute("SELECT * FROM ai_credential WHERE id = ?", (new_id,))
35-
created_row = cursor.fetchone()
36-
37-
if not created_row:
38-
raise APIException(CommonCode.FAIL_TO_VERIFY_CREATION)
39-
40-
return APIKeyInDB.model_validate(dict(created_row))
41-
42-
except sqlite3.IntegrityError as e:
43-
# UNIQUE 제약 조건 위반 (service_name)
44-
raise APIException(CommonCode.DUPLICATION) from e
45-
except sqlite3.Error as e:
46-
# "database is locked" 오류를 명시적으로 처리
47-
if "database is locked" in str(e):
48-
raise APIException(CommonCode.DB_BUSY) from e
49-
# 기타 모든 sqlite3 오류
50-
raise APIException(CommonCode.FAIL) from e
51-
finally:
52-
if conn:
53-
conn.close()
47+
api_key_service = APIKeyService()

0 commit comments

Comments
 (0)