openapi: 3.1.0 info: title: Proxy Pool HTTP API version: 1.0.0 description: | Distribution API performs one-time exclusive extraction. A successful Redis operation atomically removes or marks every returned proxy as extracted in the TTL activity pool. Proxy details and per-extraction records are not persisted in PostgreSQL. There is no release, renew, or lease API. servers: - url: http://127.0.0.1:8081 description: Distribution API tags: - name: Distribution - name: Health paths: /api/v1/proxies/extract: post: tags: [Distribution] operationId: extractProxies summary: 一次性独占提取代理 description: | 服务端通过单个 Redis 原子操作完成筛选、TTL/所有权复核、Gateway 预留、 从可分配池移除所选条目和短期幂等结果写入,再返回代理。相同代理不会 返回给两个成功请求,提取路径不访问 PostgreSQL。 `partial` 允许实际返回数量小于请求数量;`allOrNothing` 数量不足时不 提取任何代理并返回 409。未传 `fulfillment` 时使用服务端配置,默认 为 `partial`。`Idempotency-Key` 在 Redis 的有界 TTL 窗口内避免客户端因 响应丢失重试而再次消耗库存。 security: - ApiKeyAuth: [] - BasicAuth: [] - BearerAuth: [] - {} parameters: - $ref: '#/components/parameters/RequestID' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ExtractRequest' examples: partial: value: count: 5 fulfillment: partial filters: protocols: [http] regions: [shanghai] allOrNothing: value: count: 10 fulfillment: allOrNothing filters: allowedUpstreams: [provider-a, provider-b] responses: '200': description: Redis 原子提取已提交;返回的代理已退出当前可分配池 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/json: schema: $ref: '#/components/schemas/ExtractResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': description: allOrNothing 模式下符合条件的库存不足,未提取任何代理 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' example: type: https://proxy-pool.local/problems/insufficient-proxies title: Insufficient proxies status: 409 code: INSUFFICIENT_PROXIES detail: requested 10 proxies but only 6 are currently eligible requestId: req_01J4EXAMPLE '422': $ref: '#/components/responses/UnprocessableEntity' '413': $ref: '#/components/responses/RequestEntityTooLarge' '415': $ref: '#/components/responses/UnsupportedMediaType' '429': $ref: '#/components/responses/TooManyRequests' '503': $ref: '#/components/responses/ServiceUnavailable' '500': $ref: '#/components/responses/InternalServerError' /health/live: get: tags: [Health] operationId: getLiveness summary: 进程存活探针 security: [] responses: '200': description: 进程存活 content: application/json: schema: $ref: '#/components/schemas/Health' /health/ready: get: tags: [Health] operationId: getReadiness summary: Distribution 就绪探针 description: Redis TTL 活动池不可用或无法保证原子提取时返回 503。 security: [] responses: '200': description: 可接受提取请求 content: application/json: schema: $ref: '#/components/schemas/Health' '503': $ref: '#/components/responses/ServiceUnavailable' components: securitySchemes: ApiKeyAuth: type: apiKey in: header name: X-API-Key BasicAuth: type: http scheme: basic BearerAuth: type: http scheme: bearer parameters: RequestID: name: X-Request-ID in: header required: false description: 调用方请求标识;缺省时由服务端生成。 schema: type: string maxLength: 128 IdempotencyKey: name: Idempotency-Key in: header required: false description: | 同一客户端在幂等记录保留期内重用该键会得到首次提交结果,不会再次 提取。未认证客户端默认以可信代理链解析后的规范化来源 IP 标识。 建议所有会自动重试的客户端提供。 schema: type: string minLength: 8 maxLength: 128 headers: RequestID: description: 服务端最终使用的请求标识。 schema: type: string schemas: ExtractRequest: type: object additionalProperties: false required: [count] properties: count: type: integer minimum: 1 maximum: 1000 description: 仍受服务端 maxCountPerRequest 限制。 fulfillment: type: string enum: [partial, allOrNothing] description: 缺省时使用服务端配置;默认 partial。 filters: $ref: '#/components/schemas/ExtractFilters' ExtractFilters: type: object additionalProperties: false properties: protocols: type: array maxItems: 64 uniqueItems: true items: type: string enum: [http, https, socks5] regions: type: array maxItems: 64 uniqueItems: true items: type: string carriers: type: array maxItems: 64 uniqueItems: true items: type: string allowedUpstreams: type: array maxItems: 64 uniqueItems: true items: type: string ExtractResponse: type: object additionalProperties: false required: [requestId, requested, returned, proxies] properties: requestId: type: string requested: type: integer minimum: 1 returned: type: integer minimum: 0 proxies: type: array items: $ref: '#/components/schemas/ExtractedProxy' ExtractedProxy: type: object additionalProperties: false required: - id - protocol - host - port - url - upstream - expiresAt - remainingTtlSeconds - extractedAt properties: id: type: string example: px_01J4EXAMPLE protocol: type: string enum: [http, https, socks5] host: type: string example: 192.0.2.10 port: type: integer minimum: 1 maximum: 65535 username: type: string password: type: string format: password description: 真实代理凭据,只出现在提取成功响应中。 url: type: string format: uri description: 含真实代理凭据的连接 URL,必须按敏感数据处理。 example: http://USER:PASSWORD@192.0.2.10:8080 region: type: string carrier: type: string upstream: type: string expiresAt: type: string format: date-time remainingTtlSeconds: type: integer minimum: 0 extractedAt: type: string format: date-time Health: type: object additionalProperties: false required: [status] properties: status: type: string enum: [ok, degraded] version: type: string configVersion: type: string Problem: type: object additionalProperties: true required: [type, title, status, code] properties: type: type: string format: uri title: type: string status: type: integer code: type: string detail: type: string requestId: type: string invalidParams: type: array items: type: object required: [name, reason] properties: name: type: string reason: type: string responses: BadRequest: description: 请求体、Header 或 JSON 格式无效 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' Unauthorized: description: 所配置的认证方法未通过 headers: X-Request-ID: $ref: '#/components/headers/RequestID' WWW-Authenticate: schema: type: string content: application/problem+json: schema: $ref: '#/components/schemas/Problem' Forbidden: description: 来源访问控制或客户端权限拒绝 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' UnprocessableEntity: description: 参数语法有效但违反业务约束 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' TooManyRequests: description: 超过全局或客户端速率限制 headers: X-Request-ID: $ref: '#/components/headers/RequestID' Retry-After: schema: type: integer content: application/problem+json: schema: $ref: '#/components/schemas/Problem' ServiceUnavailable: description: Redis TTL 活动池不可用、原子提取不可执行或服务正在排空 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' RequestEntityTooLarge: description: 请求体超过服务端限制 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' UnsupportedMediaType: description: Content-Type 不是 application/json headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem' InternalServerError: description: 未分类的服务端错误 headers: X-Request-ID: $ref: '#/components/headers/RequestID' content: application/problem+json: schema: $ref: '#/components/schemas/Problem'