service.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import hashlib
  2. import hmac
  3. import secrets
  4. import time
  5. from datetime import datetime, timedelta
  6. from typing import Optional
  7. from app.api.v1.module_system.auth.schema import AuthSchema
  8. from app.plugin.module_payment.apikey.crud import TenantApiKeyCRUD, TenantApiLogCRUD
  9. from app.plugin.module_payment.apikey.model import TenantApiKeyModel
  10. from app.plugin.module_payment.apikey.schema import TenantApiKeyListResponse
  11. class TenantApiKeyService:
  12. """租户API Key服务"""
  13. @staticmethod
  14. def generate_api_key(tenant_id: int) -> tuple[str, str]:
  15. random_part = secrets.token_hex(32)
  16. timestamp = str(int(time.time()))
  17. api_key = f"TENANT_{tenant_id}_{timestamp}_{random_part[:16]}"
  18. api_secret = secrets.token_hex(64)
  19. return api_key, api_secret
  20. @staticmethod
  21. def generate_signature(api_secret: str, request_data: dict) -> str:
  22. sorted_data = sorted(request_data.items(), key=lambda x: x[0])
  23. sign_str = "&".join([f"{k}={v}" for k, v in sorted_data])
  24. signature = hmac.new(
  25. api_secret.encode('utf-8'),
  26. sign_str.encode('utf-8'),
  27. hashlib.sha256
  28. ).hexdigest()
  29. return signature
  30. @staticmethod
  31. def verify_signature(api_secret: str, request_data: dict, signature: str) -> bool:
  32. expected_signature = TenantApiKeyService.generate_signature(api_secret, request_data)
  33. return expected_signature == signature
  34. @staticmethod
  35. async def create_api_key(
  36. auth: AuthSchema,
  37. tenant_id: int,
  38. expired_days: Optional[int] = 365,
  39. description: Optional[str] = None,
  40. ) -> TenantApiKeyModel:
  41. api_key, api_secret = TenantApiKeyService.generate_api_key(tenant_id)
  42. return await TenantApiKeyCRUD(auth).create_crud(
  43. api_key=api_key,
  44. api_secret=api_secret,
  45. tenant_id=tenant_id,
  46. expired_at=datetime.now() + timedelta(days=expired_days or 365),
  47. description=description,
  48. )
  49. @staticmethod
  50. async def validate_api_key(auth: AuthSchema, api_key: str) -> Optional[TenantApiKeyModel]:
  51. return await TenantApiKeyCRUD(auth).get_by_api_key(api_key)
  52. @staticmethod
  53. async def get_api_key_page_service(
  54. auth: AuthSchema,
  55. page_no: int,
  56. page_size: int,
  57. tenant_id: Optional[int] = None,
  58. status: Optional[str] = None,
  59. ) -> dict:
  60. offset = (page_no - 1) * page_size
  61. search = {}
  62. if tenant_id:
  63. search["tenant_id"] = tenant_id
  64. if status:
  65. search["status"] = status
  66. return await TenantApiKeyCRUD(auth).page(
  67. offset=offset,
  68. limit=page_size,
  69. order_by=[{"created_time": "desc"}],
  70. search=search if search else {},
  71. out_schema=TenantApiKeyListResponse,
  72. )
  73. @staticmethod
  74. async def update_api_key_status(
  75. auth: AuthSchema,
  76. api_key_id: int,
  77. status: str,
  78. ) -> Optional[TenantApiKeyModel]:
  79. return await TenantApiKeyCRUD(auth).update_status_crud(api_key_id=api_key_id, status=status)
  80. @staticmethod
  81. async def delete_api_key(auth: AuthSchema, api_key_id: int) -> bool:
  82. return await TenantApiKeyCRUD(auth).delete_crud(api_key_id=api_key_id)
  83. @staticmethod
  84. async def log_api_call(
  85. auth: AuthSchema,
  86. api_key_id: Optional[int],
  87. tenant_id: int,
  88. endpoint: str,
  89. method: str,
  90. request_ip: str,
  91. request_data: Optional[dict],
  92. response_code: int,
  93. start_time: float,
  94. ) -> None:
  95. response_time = (time.time() - start_time) * 1000
  96. await TenantApiLogCRUD(auth).create_crud(
  97. api_key_id=api_key_id,
  98. tenant_id=tenant_id,
  99. endpoint=endpoint,
  100. method=method,
  101. request_ip=request_ip,
  102. request_data=str(request_data) if request_data else None,
  103. response_code=response_code,
  104. response_time=response_time,
  105. )
  106. @staticmethod
  107. async def update_last_used(auth: AuthSchema, api_key_id: int) -> None:
  108. await TenantApiKeyCRUD(auth).update_last_used_crud(api_key_id)