诚实性修复: API限流Retry-After(wait=60s)+断言 / purge定时接线(04:00 cron+actor+测试) / Channel表单校验(ref+rules+validate) / README PG数字513

This commit is contained in:
seagull
2026-09-12 02:20:50 +08:00
parent b3f3095d53
commit 67540e8d30
40 changed files with 1805 additions and 31 deletions
+5
View File
@@ -42,6 +42,11 @@ class APIKeyAuthentication(authentication.BaseAuthentication):
if not key_obj.verify_key(raw_key):
raise exceptions.AuthenticationFailed("API Key 签名无效")
# P0-5:把纸面的 rate_limit 接进认证链(分钟窗口,超限 429)。
from apps.core.ratelimit import check_apikey_rate_limit
check_apikey_rate_limit(key_obj.prefix, key_obj.rate_limit)
# 记录调用时间并绑定 tenant
key_obj.last_used_at = timezone.now()
key_obj.save(update_fields=["last_used_at"])
+1
View File
@@ -18,6 +18,7 @@ class APIKeyCreateInputSerializer(serializers.Serializer):
name = serializers.CharField(max_length=128)
scopes = serializers.ListField(child=serializers.CharField(), required=False)
expires_at = serializers.DateTimeField(required=False, allow_null=True)
rate_limit = serializers.IntegerField(required=False, min_value=1)
class ExternalOrderLineInputSerializer(serializers.Serializer):
+4
View File
@@ -52,6 +52,10 @@ class APIKeyViewSet(BaseTenantViewSet):
expires_at=data.get("expires_at"),
created_by=user,
)
# P0-5:允许创建时指定 rate_limit(下界 1 由 serializer 保证)。
if data.get("rate_limit") is not None:
key_obj.rate_limit = data["rate_limit"]
await sync_to_async(key_obj.save)(update_fields=["rate_limit"])
res_data = APIKeySerializer(key_obj).data
res_data["raw_key"] = raw_key # 仅在创建时返回一次明文密钥!