samchun-gemini / utils /validators.py
JHyeok5's picture
Upload folder using huggingface_hub
0f3460d verified
"""
Input Validation Utilities
μž…λ ₯κ°’ 검증 및 sanitization
SQL Injection λ°©μ§€λ₯Ό μœ„ν•œ μΆ”κ°€ 검증 λ ˆμ΄μ–΄
"""
import re
from typing import Any, List, Optional
from fastapi import HTTPException
# UUID v4 μ •κ·œμ‹
UUID_PATTERN = re.compile(
r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
re.IGNORECASE
)
def validate_uuid(value: str, field_name: str = "id") -> str:
"""
UUID ν˜•μ‹ 검증
Args:
value: 검증할 κ°’
field_name: ν•„λ“œ 이름 (μ—λŸ¬ λ©”μ‹œμ§€μš©)
Returns:
κ²€μ¦λœ UUID λ¬Έμžμ—΄
Raises:
HTTPException: UUID ν˜•μ‹μ΄ μ•„λ‹Œ 경우
"""
if not value or not isinstance(value, str):
raise HTTPException(
status_code=400,
detail=f"{field_name} must be a valid UUID"
)
value = value.strip().lower()
if not UUID_PATTERN.match(value):
raise HTTPException(
status_code=400,
detail=f"{field_name} must be a valid UUID format"
)
return value
def validate_user_id(user_id: str) -> str:
"""
μ‚¬μš©μž ID 검증 (UUID ν˜•μ‹)
Args:
user_id: μ‚¬μš©μž ID
Returns:
κ²€μ¦λœ user_id
"""
return validate_uuid(user_id, "user_id")
def validate_ids(ids: List[str], field_name: str = "ids") -> List[str]:
"""
μ—¬λŸ¬ UUID 검증
Args:
ids: UUID 리슀트
field_name: ν•„λ“œ 이름
Returns:
κ²€μ¦λœ UUID 리슀트
"""
if not ids or not isinstance(ids, list):
raise HTTPException(
status_code=400,
detail=f"{field_name} must be a non-empty list"
)
if len(ids) > 100:
raise HTTPException(
status_code=400,
detail=f"{field_name} list too large (max 100)"
)
return [validate_uuid(id_val, field_name) for id_val in ids]
def sanitize_string(value: str, max_length: int = 1000) -> str:
"""
λ¬Έμžμ—΄ sanitization
Args:
value: μž…λ ₯ λ¬Έμžμ—΄
max_length: μ΅œλŒ€ 길이
Returns:
μ •μ œλœ λ¬Έμžμ—΄
"""
if not value or not isinstance(value, str):
return ""
# μ•žλ’€ 곡백 제거
value = value.strip()
# 길이 μ œν•œ
if len(value) > max_length:
value = value[:max_length]
# μ œμ–΄ 문자 제거 (μ€„λ°”κΏˆ, νƒ­ μ œμ™Έ)
value = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', value)
return value
def validate_pagination(
limit: int,
offset: int,
max_limit: int = 100
) -> tuple[int, int]:
"""
νŽ˜μ΄μ§€λ„€μ΄μ…˜ νŒŒλΌλ―Έν„° 검증
Args:
limit: 쑰회 개수
offset: μ‹œμž‘ μœ„μΉ˜
max_limit: μ΅œλŒ€ limit κ°’
Returns:
(limit, offset) νŠœν”Œ
"""
if limit < 1:
limit = 10
if limit > max_limit:
limit = max_limit
if offset < 0:
offset = 0
return limit, offset
def validate_enum(
value: str,
allowed_values: List[str],
field_name: str = "value"
) -> str:
"""
Enum κ°’ 검증
Args:
value: 검증할 κ°’
allowed_values: ν—ˆμš©λœ κ°’ λͺ©λ‘
field_name: ν•„λ“œ 이름
Returns:
κ²€μ¦λœ κ°’
"""
if not value or value not in allowed_values:
raise HTTPException(
status_code=400,
detail=f"{field_name} must be one of: {', '.join(allowed_values)}"
)
return value
def validate_coordinates(lat: float, lng: float) -> tuple[float, float]:
"""
μ’Œν‘œ μœ νš¨μ„± 검증
Args:
lat: μœ„λ„
lng: 경도
Returns:
(lat, lng) νŠœν”Œ
"""
if not isinstance(lat, (int, float)) or not isinstance(lng, (int, float)):
raise HTTPException(
status_code=400,
detail="Coordinates must be numeric"
)
if not -90 <= lat <= 90:
raise HTTPException(
status_code=400,
detail="Latitude must be between -90 and 90"
)
if not -180 <= lng <= 180:
raise HTTPException(
status_code=400,
detail="Longitude must be between -180 and 180"
)
return lat, lng