2024-03-14 19:25:27 +00:00
|
|
|
import ResumptionToken from './ResumptionToken.js';
|
2023-10-03 19:11:02 +00:00
|
|
|
import { createClient, RedisClientType } from 'redis';
|
2024-04-29 09:25:50 +00:00
|
|
|
import InternalServerErrorException from '#app/exceptions/InternalServerException';
|
2023-10-03 19:11:02 +00:00
|
|
|
import { sprintf } from 'sprintf-js';
|
|
|
|
import dayjs from 'dayjs';
|
2024-03-14 19:25:27 +00:00
|
|
|
import TokenWorkerContract from './TokenWorkerContract.js';
|
2023-10-03 19:11:02 +00:00
|
|
|
|
|
|
|
export default class TokenWorkerService implements TokenWorkerContract {
|
|
|
|
protected filePrefix = 'rs_';
|
|
|
|
protected fileExtension = 'txt';
|
|
|
|
|
|
|
|
private cache: RedisClientType;
|
|
|
|
public ttl: number;
|
|
|
|
private url: string;
|
|
|
|
private connected = false;
|
|
|
|
|
|
|
|
constructor(ttl: number) {
|
|
|
|
this.ttl = ttl; // time to live
|
|
|
|
this.url = process.env.REDIS_URL || 'redis://127.0.0.1:6379';
|
|
|
|
}
|
|
|
|
|
|
|
|
public async connect() {
|
|
|
|
this.cache = createClient({ url: this.url });
|
|
|
|
this.cache.on('error', (err) => {
|
|
|
|
this.connected = false;
|
|
|
|
console.log('[Redis] Redis Client Error: ', err);
|
|
|
|
});
|
|
|
|
this.cache.on('connect', () => {
|
|
|
|
this.connected = true;
|
|
|
|
});
|
|
|
|
await this.cache.connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
public get isConnected(): boolean {
|
|
|
|
return this.connected;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async has(key: string): Promise<boolean> {
|
|
|
|
const result = await this.cache.get(key);
|
|
|
|
return result !== undefined && result !== null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async set(token: ResumptionToken): Promise<string> {
|
|
|
|
const uniqueName = await this.generateUniqueName();
|
|
|
|
|
|
|
|
const serialToken = JSON.stringify(token);
|
|
|
|
await this.cache.setEx(uniqueName, this.ttl, serialToken);
|
|
|
|
return uniqueName;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async generateUniqueName(): Promise<string> {
|
|
|
|
let fc = 0;
|
|
|
|
const uniqueId = dayjs().unix().toString();
|
|
|
|
let uniqueName: string;
|
|
|
|
let cacheKeyExists: boolean;
|
|
|
|
do {
|
|
|
|
// format values
|
|
|
|
// %s - String
|
|
|
|
// %d - Signed decimal number (negative, zero or positive)
|
|
|
|
// [0-9] (Specifies the minimum width held of to the variable value)
|
|
|
|
uniqueName = sprintf('%s%05d', uniqueId, fc++);
|
|
|
|
cacheKeyExists = await this.has(uniqueName);
|
|
|
|
} while (cacheKeyExists);
|
|
|
|
return uniqueName;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async get(key: string): Promise<ResumptionToken | null> {
|
|
|
|
if (!this.cache) {
|
|
|
|
throw new InternalServerErrorException('Dataset is not available for OAI export!');
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = await this.cache.get(key);
|
|
|
|
return result ? this.parseToken(result) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private parseToken(result: string): ResumptionToken {
|
|
|
|
const rToken: ResumptionToken = new ResumptionToken();
|
|
|
|
const parsed = JSON.parse(result);
|
|
|
|
Object.assign(rToken, parsed);
|
|
|
|
return rToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
public del(key: string) {
|
|
|
|
this.cache.del(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
public flush() {
|
|
|
|
this.cache.flushAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async close() {
|
|
|
|
await this.cache.disconnect();
|
|
|
|
this.connected = false;
|
|
|
|
}
|
|
|
|
}
|