fix: 原子化NapCat WebUI票据兑换
This commit is contained in:
parent
6f23db64be
commit
8ee7866afe
@ -35,14 +35,13 @@ export class NapcatWebuiGatewayTicketService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redeems a ticket once by deleting the Redis key before returning the session id.
|
* Redeems a ticket once with Redis GETDEL so deletion happens before returning.
|
||||||
* @param ticket - Opaque ticket from the bootstrap iframe URL.
|
* @param ticket - Opaque ticket from the bootstrap iframe URL.
|
||||||
* @returns Session id when the ticket existed, otherwise undefined.
|
* @returns Session id when the ticket existed, otherwise undefined.
|
||||||
*/
|
*/
|
||||||
async redeem(ticket: string) {
|
async redeem(ticket: string) {
|
||||||
const key = this.ticketKey(ticket);
|
const key = this.ticketKey(ticket);
|
||||||
const sessionId = await this.redis.get(key);
|
const sessionId = await this.redis.getdel(key);
|
||||||
await this.redis.del(key);
|
|
||||||
return sessionId || undefined;
|
return sessionId || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -116,6 +116,19 @@ class FakeRedis {
|
|||||||
return this.values.get(key) ?? null;
|
return this.values.get(key) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically reads and deletes one key from the fake Redis store.
|
||||||
|
* @param key - Redis key to consume.
|
||||||
|
* @returns Stored value before deletion or null.
|
||||||
|
*/
|
||||||
|
async getdel(key: string) {
|
||||||
|
this.calls.push(`getdel:${key}`);
|
||||||
|
const value = this.values.get(key) ?? null;
|
||||||
|
this.values.delete(key);
|
||||||
|
this.ttl.delete(key);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes one or more keys from the fake Redis store.
|
* Deletes one or more keys from the fake Redis store.
|
||||||
* @param keys - Redis keys to delete.
|
* @param keys - Redis keys to delete.
|
||||||
@ -318,13 +331,16 @@ describe('NapcatWebuiGatewayTicketService', () => {
|
|||||||
|
|
||||||
expect(sessionId).toBe('session-1');
|
expect(sessionId).toBe('session-1');
|
||||||
expect(secondRedeem).toBeUndefined();
|
expect(secondRedeem).toBeUndefined();
|
||||||
expect(redis.values.get(`napcat:webui:ticket:${ticket}`)).toBeUndefined();
|
const ticketKey = `napcat:webui:ticket:${ticket}`;
|
||||||
expect(redis.calls).toEqual(
|
const ticketCalls = redis.calls.filter((call) => call.includes(ticketKey));
|
||||||
expect.arrayContaining([
|
expect(redis.values.get(ticketKey)).toBeUndefined();
|
||||||
`get:napcat:webui:ticket:${ticket}`,
|
expect(ticketCalls).toEqual([
|
||||||
`del:napcat:webui:ticket:${ticket}`,
|
`set:${ticketKey}:PX:60000`,
|
||||||
]),
|
`getdel:${ticketKey}`,
|
||||||
);
|
`getdel:${ticketKey}`,
|
||||||
|
]);
|
||||||
|
expect(ticketCalls).not.toContain(`get:${ticketKey}`);
|
||||||
|
expect(ticketCalls).not.toContain(`del:${ticketKey}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user