feat(api): tiered cumulative quota + self-service registration
Anonymous callers get a free cumulative quota (1000 calls per IP); once exhausted they get 403 quota_exhausted and must register. Public users can self-register (email+password) to obtain a higher-quota API key, view usage, and regenerate the key. Quota counters live in Redis; the public API stays read-only except for the registration writes. - migration 0011: app_user table + api_key.quota_total + 'registered' tier - ratelimit: IncrTotal/TotalUsed/CopyTotal lifetime counters - middleware: enforce cumulative quota + X-Quota-* headers - store: RegisterUser/Authenticate/RegenerateKey (bcrypt) - handlers: POST /api/v1/register, /account, /account/regenerate - admin: quota_total column + registered tier - public: 'API 密钥' account page + API docs quota section Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -10,7 +10,8 @@
|
||||
"tags": [
|
||||
{ "name": "products" },
|
||||
{ "name": "catalog" },
|
||||
{ "name": "meta" }
|
||||
{ "name": "meta" },
|
||||
{ "name": "account" }
|
||||
],
|
||||
"security": [{ "ApiKeyHeader": [] }, { "BearerKey": [] }, {}],
|
||||
"paths": {
|
||||
@@ -114,6 +115,35 @@
|
||||
"parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
|
||||
"responses": { "200": { "description": "Source" }, "404": { "$ref": "#/components/responses/NotFound" } }
|
||||
}
|
||||
},
|
||||
"/register": {
|
||||
"post": {
|
||||
"tags": ["account"],
|
||||
"summary": "Register an account and issue an API key",
|
||||
"description": "Self-service registration; returns the plaintext API key exactly once.",
|
||||
"security": [],
|
||||
"requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string", "minLength": 8 } } } } } },
|
||||
"responses": { "201": { "description": "Account created; plaintext key returned once" }, "400": { "description": "Invalid email or weak password" }, "409": { "description": "Email already registered" } }
|
||||
}
|
||||
},
|
||||
"/account": {
|
||||
"post": {
|
||||
"tags": ["account"],
|
||||
"summary": "View account key metadata and cumulative quota usage",
|
||||
"security": [],
|
||||
"requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" } } } } } },
|
||||
"responses": { "200": { "description": "Account info with quota usage" }, "401": { "description": "Invalid credentials" } }
|
||||
}
|
||||
},
|
||||
"/account/regenerate": {
|
||||
"post": {
|
||||
"tags": ["account"],
|
||||
"summary": "Revoke the current key and issue a new one",
|
||||
"description": "Cumulative usage carries over; returns the plaintext key exactly once.",
|
||||
"security": [],
|
||||
"requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" } } } } } },
|
||||
"responses": { "200": { "description": "New plaintext key returned once" }, "401": { "description": "Invalid credentials" } }
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
Reference in New Issue
Block a user