diff --git a/package-lock.json b/package-lock.json index f1c05bf..8abad2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "jose": "^5.1.0", "langdetect": "^0.2.1", "linkedom": "^0.18.4", + "lru-cache": "^11.0.2", "maxmind": "^4.3.18", "minio": "^7.1.3", "node-libcurl": "^4.1.0", @@ -1946,6 +1947,12 @@ "node": ">= 14" } }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/@npmcli/fs": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", @@ -3834,6 +3841,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/cacache/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -8561,9 +8574,13 @@ "optional": true }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/lru-memoizer": { "version": "2.2.0", @@ -9970,6 +9987,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", diff --git a/package.json b/package.json index 7f9181f..47f0ec6 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "jose": "^5.1.0", "langdetect": "^0.2.1", "linkedom": "^0.18.4", + "lru-cache": "^11.0.2", "maxmind": "^4.3.18", "minio": "^7.1.3", "node-libcurl": "^4.1.0", diff --git a/src/dto/jina-embeddings-auth.ts b/src/dto/jina-embeddings-auth.ts index 5a55834..5d7a27d 100644 --- a/src/dto/jina-embeddings-auth.ts +++ b/src/dto/jina-embeddings-auth.ts @@ -17,9 +17,18 @@ import envConfig from '../shared/services/secrets'; import { JinaEmbeddingsDashboardHTTP } from '../shared/3rd-party/jina-embeddings'; import { JinaEmbeddingsTokenAccount } from '../shared/db/jina-embeddings-token-account'; +import { LRUCache } from 'lru-cache'; const authDtoLogger = logger.child({ service: 'JinaAuthDTO' }); +const invalidTokenLRU = new LRUCache({ + max: 256, + ttl: 60 * 60 * 1000, + updateAgeOnGet: false, + updateAgeOnHas: false, +}); + + const THE_VERY_SAME_JINA_EMBEDDINGS_CLIENT = new JinaEmbeddingsDashboardHTTP(envConfig.JINA_EMBEDDINGS_DASHBOARD_API_KEY); @Also({ @@ -81,6 +90,12 @@ export class JinaEmbeddingsAuthDTO extends AutoCastable { }); } + if (invalidTokenLRU.get(this.bearerToken)) { + throw new AuthenticationFailedError({ + message: 'Invalid API key, please get a new one from https://jina.ai' + }); + } + let account; try { account = await JinaEmbeddingsTokenAccount.fromFirestore(this.bearerToken); @@ -118,6 +133,7 @@ export class JinaEmbeddingsAuthDTO extends AutoCastable { authDtoLogger.warn(`Failed to get user brief: ${err}`, { err: marshalErrorLike(err) }); if (err?.status === 401) { + invalidTokenLRU.set(this.bearerToken, true); throw new AuthenticationFailedError({ message: 'Invalid API key, please get a new one from https://jina.ai' });