From c1a33333d191d919d6f3303e9a97dad3e35fd634 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 12 Jul 2025 19:51:13 +0800 Subject: [PATCH] fix --- python_core/config.py | 2 +- python_core/kv.py | 328 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 286 insertions(+), 44 deletions(-) diff --git a/python_core/config.py b/python_core/config.py index 3c006e7..23b65eb 100644 --- a/python_core/config.py +++ b/python_core/config.py @@ -36,7 +36,7 @@ class Settings(BaseSettings): default_video_codec: str = "libx264" default_audio_codec: str = "aac" # cloud flare - cloudflare_api_key: str = "dlGquMNiAX-S7SV9pXne7YGfdH_fEgq3TfIGgNcQ" + cloudflare_api_key: str = "hAiXy3jB-Lt5q-XiIV588lyvyH1c8FJaG3oieqeT" cloudflare_url: str = "https://api.cloudflare.com/client/v4/" cloudflare_account_id: str = "67720b647ff2b55cf37ba3ef9e677083" cloudflare_kv_id: str = "995d7eff088547d7a7d8fea53e56115b" diff --git a/python_core/kv.py b/python_core/kv.py index 78bf0a7..efe616e 100644 --- a/python_core/kv.py +++ b/python_core/kv.py @@ -12,73 +12,315 @@ class Kv: 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 - - def gets(self, keys: list[str]): + 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.post( - f"https://api.cloudflare.com/client/v4/accounts/{self.cf_account_id}/storage/kv/namespaces/{self.cf_kv_id}/bulk/get", - headers={"Authorization": f"Bearer {self.cf_kv_api_token}"}, - json={ - "keys": keys, - "type": "json" - } + 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 put kv to cloudflare") + logger.error(f"An error occurred while getting key '{key}' from cloudflare: {e}") raise e except httpx.HTTPStatusError as e: - logger.error(f"HTTP error occurred while get kv from cloudflare {str(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: {str(e)}") + logger.error(f"An unexpected error occurred while getting key '{key}': {e}") raise e - - def sets(self, caches: Dict[str, str]): + + 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"https://api.cloudflare.com/client/v4/accounts/{self.cf_account_id}/storage/kv/namespaces/{self.cf_kv_id}/bulk", - headers={"Authorization": f"Bearer {self.cf_kv_api_token}"}, - json=[ - { - "based64": False, - "key": key, - "value": value, - } - for (key, value) in caches.items() - ] + 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 put kv to cloudflare") + 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 get kv from cloudflare {str(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: {str(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 remove(self, keys: list[str]): - with httpx.Client() as client: - try: + def removes(self, keys: list[str]): + """ + 批量删除多个键 + + Args: + keys: 要删除的键列表 + + Returns: + 操作结果 + """ + try: + with httpx.Client() as client: response = client.post( - f"https://api.cloudflare.com/client/v4/accounts/{self.cf_account_id}/storage/kv/namespaces/{self.cf_kv_id}/bulk/delete", - headers={"Authorization": f"Bearer {self.cf_kv_api_token}"}, + f"{self.base_url}/bulk/delete", + headers=self.headers, json=keys ) response.raise_for_status() - except httpx.RequestError as e: - logger.error(f"An error occurred while put kv to cloudflare") - raise e - except httpx.HTTPStatusError as e: - logger.error(f"HTTP error occurred while get kv from cloudflare {str(e)}") - raise e - except Exception as e: - logger.error(f"An unexpected error occurred: {str(e)}") - raise e - - + 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() \ No newline at end of file