from python_core.config import settings import httpx from python_core.utils.logger import setup_logger from typing import Dict logger = setup_logger(__name__) class Kv: kv: Dict[str, str] def __init__(self): self.cf_account_id = settings.cloudflare_account_id self.cf_kv_api_token = settings.cloudflare_api_key self.cf_kv_id = settings.cloudflare_kv_id self.base_url = f"https://api.cloudflare.com/client/v4/accounts/{self.cf_account_id}/storage/kv/namespaces/{self.cf_kv_id}" self.headers = {"Authorization": f"Bearer {self.cf_kv_api_token}"} def get(self, key: str, default=None): """ 获取单个键的值 Args: key: 要获取的键 default: 如果键不存在时返回的默认值 Returns: 键对应的值,如果不存在则返回default """ try: with httpx.Client() as client: response = client.get( f"{self.base_url}/values/{key}", headers=self.headers ) if response.status_code == 404: return default response.raise_for_status() return response.text except httpx.RequestError as e: logger.error(f"An error occurred while getting key '{key}' from cloudflare: {e}") raise e except httpx.HTTPStatusError as e: if e.response.status_code == 404: return default logger.error(f"HTTP error occurred while getting key '{key}' from cloudflare: {e}") raise e except Exception as e: logger.error(f"An unexpected error occurred while getting key '{key}': {e}") raise e def set(self, key: str, value: str): """ 设置单个键值对 Args: key: 键名 value: 值 expiration_ttl: TTL过期时间(秒) expiration: 绝对过期时间(Unix时间戳) Returns: 操作结果 """ try: with httpx.Client() as client: params = {} response = client.put( f"{self.base_url}/values/{key}", headers=self.headers, params=params, content=value ) response.raise_for_status() return response.json() if response.text else {"success": True} except httpx.RequestError as e: logger.error(f"An error occurred while setting key '{key}' to cloudflare: {e}") raise e except httpx.HTTPStatusError as e: logger.error(f"HTTP error occurred while setting key '{key}' to cloudflare: {e}") raise e except Exception as e: logger.error(f"An unexpected error occurred while setting key '{key}': {e}") raise e def remove(self, key: str): """ 删除单个键 Args: key: 要删除的键名 Returns: 操作结果 """ try: with httpx.Client() as client: response = client.delete( f"{self.base_url}/values/{key}", headers=self.headers ) if response.status_code == 404: return {"success": True, "message": "Key not found"} response.raise_for_status() return response.json() if response.text else {"success": True} except httpx.RequestError as e: logger.error(f"An error occurred while removing key '{key}' from cloudflare: {e}") raise e except httpx.HTTPStatusError as e: if e.response.status_code == 404: return {"success": True, "message": "Key not found"} logger.error(f"HTTP error occurred while removing key '{key}' from cloudflare: {e}") raise e except Exception as e: logger.error(f"An unexpected error occurred while removing key '{key}': {e}") raise e def sets(self, caches: Dict[str, str]): """ 批量设置多个键值对 Args: caches: 包含键值对的字典 expiration_ttl: TTL过期时间(秒) expiration: 绝对过期时间(Unix时间戳) Returns: 操作结果 """ try: with httpx.Client() as client: bulk_data = [] for key, value in caches.items(): item = { "base64": False, "key": key, "value": value, } bulk_data.append(item) response = client.put( f"{self.base_url}/bulk", headers=self.headers, json=bulk_data ) response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"An error occurred while bulk setting keys to cloudflare: {e}") raise e except httpx.HTTPStatusError as e: logger.error(f"HTTP error occurred while bulk setting keys to cloudflare: {e}") raise e except Exception as e: logger.error(f"An unexpected error occurred while bulk setting keys: {e}") raise e def gets(self, keys: list[str]): """ 批量获取多个键的值 Args: keys: 要获取的键列表 Returns: 包含键值对的字典 """ try: # 由于Cloudflare KV的批量获取API有问题,我们使用单个获取的方式 result = {} for key in keys: value = self.get(key) if value is not None: result[key] = value return result except Exception as e: logger.error(f"An error occurred while bulk getting keys from cloudflare: {e}") raise e def removes(self, keys: list[str]): """ 批量删除多个键 Args: keys: 要删除的键列表 Returns: 操作结果 """ try: with httpx.Client() as client: response = client.post( f"{self.base_url}/bulk/delete", headers=self.headers, json=keys ) response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"An error occurred while bulk removing keys from cloudflare: {e}") raise e except httpx.HTTPStatusError as e: logger.error(f"HTTP error occurred while bulk removing keys from cloudflare: {e}") raise e except Exception as e: logger.error(f"An unexpected error occurred while bulk removing keys: {e}") raise e def exists(self, key: str) -> bool: """ 检查键是否存在 Args: key: 要检查的键名 Returns: 如果键存在返回True,否则返回False """ try: with httpx.Client() as client: response = client.get( f"{self.base_url}/values/{key}", headers=self.headers ) return response.status_code == 200 except httpx.HTTPStatusError as e: if e.response.status_code == 404: return False raise e except Exception: return False def list_keys(self, prefix: str = None, limit: int = 1000, cursor: str = None): """ 列出KV存储中的键 Args: prefix: 键名前缀过滤 limit: 返回的最大键数量(默认1000) cursor: 分页游标 Returns: 包含键列表和分页信息的字典 """ try: with httpx.Client() as client: params = {"limit": limit} if prefix: params["prefix"] = prefix if cursor: params["cursor"] = cursor response = client.get( f"{self.base_url}/keys", headers=self.headers, params=params ) response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"An error occurred while listing keys from cloudflare: {e}") raise e except httpx.HTTPStatusError as e: logger.error(f"HTTP error occurred while listing keys from cloudflare: {e}") raise e except Exception as e: logger.error(f"An unexpected error occurred while listing keys: {e}") raise e def get_metadata(self, key: str): """ 获取键的元数据 Args: key: 键名 Returns: 键的元数据信息 """ try: with httpx.Client() as client: response = client.get( f"{self.base_url}/metadata/{key}", headers=self.headers ) if response.status_code == 404: return None response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"An error occurred while getting metadata for key '{key}' from cloudflare: {e}") raise e except httpx.HTTPStatusError as e: if e.response.status_code == 404: return None logger.error(f"HTTP error occurred while getting metadata for key '{key}' from cloudflare: {e}") raise e except Exception as e: logger.error(f"An unexpected error occurred while getting metadata for key '{key}': {e}") raise e def clear_all(self, prefix: str = None): """ 清空所有键(危险操作!) Args: prefix: 如果提供,只删除指定前缀的键 Returns: 操作结果 """ try: # 首先获取所有键 all_keys = [] cursor = None while True: result = self.list_keys(prefix=prefix, limit=1000, cursor=cursor) keys_data = result.get("result", []) if not keys_data: break # 提取键名 for key_info in keys_data: if isinstance(key_info, dict): all_keys.append(key_info.get("name")) else: all_keys.append(str(key_info)) # 检查是否有更多数据 result_info = result.get("result_info", {}) cursor = result_info.get("cursor") if not cursor: break # 批量删除 if all_keys: return self.removes(all_keys) else: return {"success": True, "message": "No keys to delete"} except Exception as e: logger.error(f"An error occurred while clearing keys: {e}") raise e kv = Kv()