fix: stop replaying non-idempotent requests and expose the rotation grace
A retried POST /v1/auth/rotate loses the replacement token because the presented token is already in grace, and a retried webhook registration creates duplicates. Only idempotent methods now retry a 429 or 5xx. Add previous_token_grace_until to RotateApiTokenResponse. Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
This commit is contained in:
1 parent
89bdd6e3b8
commit
a63c360539
2 files changed
+9
-1
No files matched your search
+8
-1
@@ -344,7 +344,7 @@ export class ElydoraClient {
|
|||||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (attempt < this.maxRetries && (res.status === 429 || res.status >= 500)) {
|
if (attempt < this.maxRetries && isIdempotent(method) && (res.status === 429 || res.status >= 500)) {
|
||||||
const retryAfter = res.headers.get('Retry-After');
|
const retryAfter = res.headers.get('Retry-After');
|
||||||
const delayMs = retryAfter
|
const delayMs = retryAfter
|
||||||
? parseInt(retryAfter, 10) * 1000
|
? parseInt(retryAfter, 10) * 1000
|
||||||
@@ -372,6 +372,13 @@ export class ElydoraClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const IDEMPOTENT_METHODS = new Set(['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE']);
|
||||||
|
|
||||||
|
/** A lost response to a non-idempotent request must not be replayed. */
|
||||||
|
function isIdempotent(method: string): boolean {
|
||||||
|
return IDEMPOTENT_METHODS.has(method.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
async function handleResponse<T>(res: Response): Promise<T> {
|
async function handleResponse<T>(res: Response): Promise<T> {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
if (res.status === 204) {
|
if (res.status === 204) {
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ export interface RotateApiTokenResponse {
|
|||||||
readonly token: string;
|
readonly token: string;
|
||||||
readonly expires_at: number | null;
|
readonly expires_at: number | null;
|
||||||
readonly token_id: string;
|
readonly token_id: string;
|
||||||
|
readonly previous_token_grace_until: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HealthResponse {
|
export interface HealthResponse {
|
||||||
|
|||||||
Reference in new issue
Block a user