import ResumptionToken from './ResumptionToken.js'; import { createClient, RedisClientType } from 'redis'; import InternalServerErrorException from '#app/exceptions/InternalServerException'; import { sprintf } from 'sprintf-js'; import dayjs from 'dayjs'; import TokenWorkerContract from './TokenWorkerContract.js'; 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 { const result = await this.cache.get(key); return result !== undefined && result !== null; } public async set(token: ResumptionToken): Promise { const uniqueName = await this.generateUniqueName(); const serialToken = JSON.stringify(token); await this.cache.setEx(uniqueName, this.ttl, serialToken); return uniqueName; } private async generateUniqueName(): Promise { 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 { 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; } }