| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- """
- 更新 n8n 中的 Cohere API Key 凭证
- 用于修复 "Forbidden" 错误,重新配置凭证
- """
- import os
- import sys
- import json
- from typing import Optional, Dict, Any
- import requests
- from loguru import logger
- # 添加项目根目录到路径
- sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
- from app.config.config import BaseConfig
- # 配置日志(避免 emoji 编码问题)
- logger.remove()
- logger.add(
- sys.stdout,
- level="INFO",
- format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}",
- )
- def update_cohere_credential(
- credential_id: str,
- api_url: Optional[str] = None,
- api_key: Optional[str] = None,
- cohere_api_key: str = "4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C",
- credential_name: Optional[str] = None,
- ) -> Dict[str, Any]:
- """
- 更新 n8n 中的 Cohere 凭证
- Args:
- credential_id: 凭证 ID
- api_url: n8n API 地址
- api_key: n8n API Key
- cohere_api_key: Cohere API Key 值
- credential_name: 凭证名称(可选)
- Returns:
- 更新结果
- """
- # 获取配置
- if api_url is None or api_key is None:
- config = BaseConfig()
- api_url = api_url or config.N8N_API_URL
- api_key = api_key or config.N8N_API_KEY
- base_url = api_url.rstrip("/")
- headers = {
- "X-N8N-API-KEY": api_key,
- "Content-Type": "application/json",
- "Accept": "application/json",
- }
- # 先获取现有凭证信息
- logger.info(f"获取凭证信息: {credential_id}")
- try:
- get_response = requests.get(
- f"{base_url}/api/v1/credentials/{credential_id}",
- headers=headers,
- timeout=30,
- )
- if get_response.status_code == 200:
- existing_credential = get_response.json()
- logger.info(f"现有凭证名称: {existing_credential.get('name')}")
- credential_name = credential_name or existing_credential.get("name", "Cohere API Key")
- else:
- logger.warning(f"无法获取现有凭证: {get_response.status_code}")
- credential_name = credential_name or "Cohere API Key"
- except Exception as e:
- logger.warning(f"获取凭证信息失败: {str(e)}")
- credential_name = credential_name or "Cohere API Key"
- # 准备更新数据
- # 注意: n8n 凭证更新可能需要特定的数据格式
- update_data = {
- "name": credential_name,
- "type": "cohereApi",
- "data": {
- "apiKey": cohere_api_key,
- },
- }
- logger.info(f"更新凭证: {credential_id}")
- logger.info(f"API Key (前8位): {cohere_api_key[:8]}...")
- try:
- # 尝试更新凭证
- response = requests.put(
- f"{base_url}/api/v1/credentials/{credential_id}",
- headers=headers,
- json=update_data,
- timeout=30,
- )
- logger.debug(f"响应状态码: {response.status_code}")
- logger.debug(f"响应内容: {response.text[:500]}")
- if response.status_code == 200:
- result = response.json()
- logger.success(f"凭证更新成功: {result.get('name')}")
- return {
- "success": True,
- "message": "凭证更新成功",
- "data": result,
- }
- elif response.status_code == 401:
- logger.error("API 认证失败,请检查 n8n API Key")
- return {
- "success": False,
- "message": "API 认证失败,请检查 n8n API Key",
- "error": "Unauthorized",
- }
- elif response.status_code == 403:
- logger.error("API 权限不足,凭证更新可能需要 Owner 权限")
- return {
- "success": False,
- "message": "API 权限不足,请使用 Web UI 手动更新",
- "error": "Forbidden",
- }
- elif response.status_code == 404:
- logger.error(f"凭证不存在: {credential_id}")
- return {
- "success": False,
- "message": f"凭证不存在: {credential_id}",
- "error": "Not Found",
- }
- else:
- logger.warning(f"更新失败: {response.status_code} - {response.text[:200]}")
- return {
- "success": False,
- "message": f"更新失败: {response.status_code}",
- "error": response.text[:200],
- "status_code": response.status_code,
- }
- except requests.exceptions.RequestException as e:
- logger.error(f"请求异常: {str(e)}")
- return {
- "success": False,
- "message": f"请求失败: {str(e)}",
- "error": str(e),
- }
- def delete_and_recreate_credential(
- credential_id: Optional[str] = None,
- api_url: Optional[str] = None,
- api_key: Optional[str] = None,
- cohere_api_key: str = "4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C",
- ) -> Dict[str, Any]:
- """
- 删除旧凭证并重新创建
- Args:
- credential_id: 要删除的凭证 ID(如果为 None,则只创建新凭证)
- api_url: n8n API 地址
- api_key: n8n API Key
- cohere_api_key: Cohere API Key 值
- Returns:
- 操作结果
- """
- # 获取配置
- if api_url is None or api_key is None:
- config = BaseConfig()
- api_url = api_url or config.N8N_API_URL
- api_key = api_key or config.N8N_API_KEY
- base_url = api_url.rstrip("/")
- headers = {
- "X-N8N-API-KEY": api_key,
- "Content-Type": "application/json",
- "Accept": "application/json",
- }
- # 删除旧凭证(如果提供)
- if credential_id:
- logger.info(f"删除旧凭证: {credential_id}")
- try:
- delete_response = requests.delete(
- f"{base_url}/api/v1/credentials/{credential_id}",
- headers=headers,
- timeout=30,
- )
- if delete_response.status_code in [200, 204]:
- logger.success("旧凭证已删除")
- else:
- logger.warning(f"删除凭证失败: {delete_response.status_code}")
- except Exception as e:
- logger.warning(f"删除凭证异常: {str(e)}")
- # 创建新凭证
- logger.info("创建新凭证...")
- credential_data = {
- "name": "Cohere API Key",
- "type": "cohereApi",
- "data": {
- "apiKey": cohere_api_key,
- },
- }
- try:
- response = requests.post(
- f"{base_url}/api/v1/credentials",
- headers=headers,
- json=credential_data,
- timeout=30,
- )
- if response.status_code in [200, 201]:
- result = response.json()
- logger.success(f"新凭证创建成功: {result.get('id')}")
- return {
- "success": True,
- "message": "凭证重新创建成功",
- "data": result,
- "credential_id": result.get("id"),
- }
- else:
- logger.error(f"创建凭证失败: {response.status_code} - {response.text[:200]}")
- return {
- "success": False,
- "message": f"创建凭证失败: {response.status_code}",
- "error": response.text[:200],
- }
- except requests.exceptions.RequestException as e:
- logger.error(f"请求异常: {str(e)}")
- return {
- "success": False,
- "message": f"请求失败: {str(e)}",
- "error": str(e),
- }
- def main():
- """主函数"""
- import argparse
- parser = argparse.ArgumentParser(description="更新 n8n Cohere API Key 凭证")
- parser.add_argument(
- "--credential-id",
- type=str,
- help="要更新的凭证 ID(如果不提供,将创建新凭证)",
- )
- parser.add_argument(
- "--recreate",
- action="store_true",
- help="删除旧凭证并重新创建",
- )
- parser.add_argument(
- "--api-key",
- type=str,
- help="Cohere API Key(默认使用配置中的值)",
- default="4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C",
- )
- args = parser.parse_args()
- logger.info("开始更新 n8n Cohere API Key 凭证...")
- if args.recreate:
- result = delete_and_recreate_credential(
- credential_id=args.credential_id,
- cohere_api_key=args.api_key,
- )
- elif args.credential_id:
- result = update_cohere_credential(
- credential_id=args.credential_id,
- cohere_api_key=args.api_key,
- )
- else:
- logger.error("请提供 --credential-id 或使用 --recreate 选项")
- return 1
- print("\n" + "=" * 60)
- print("执行结果:")
- print("=" * 60)
- print(json.dumps(result, indent=2, ensure_ascii=False))
- if result.get("success"):
- if "credential_id" in result:
- print(f"\n新凭证 ID: {result.get('credential_id')}")
- print("\n请在工作流中使用更新后的凭证。")
- else:
- print("\n" + "=" * 60)
- print("建议:")
- print("=" * 60)
- print("如果 API 更新失败,请使用 Web UI 手动更新:")
- print("1. 访问: https://n8n.citupro.com/home/credentials")
- print("2. 找到 Cohere API Key 凭证")
- print("3. 点击编辑,重新输入 API Key")
- print("4. 确保 API Key 没有多余的空格或换行")
- return 0 if result.get("success") else 1
- if __name__ == "__main__":
- sys.exit(main())
|