| 32 | 32 | const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean'); |
| 33 | 33 | const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean'); |
| 34 | 34 | |
| 35 | 35 | const diskCacheclass =DiskCache { |
| 36 | | - /** |
| 36 | + /** @type {string} */ |
| 37 | | - * @type {import('node-persist').LocalStorage?} |
| 37 | + static DIRECTORY = 'characters'; |
| 38 | | - * @private |
| 38 | + |
| 39 | | - */ |
| 39 | + /** @type {number} */ |
| 40 | | - _instance: null, |
| 40 | + static REMOVAL_INTERVAL = 60000; |
| 41 | + |
| 42 | + /** @type {import('node-persist').LocalStorage} */ |
| 43 | + #instance; |
| 44 | + |
| 45 | + /** @type {NodeJS.Timeout} */ |
| 46 | + #removalInterval; |
| 47 | + |
| 41 | 48 | /** |
| 42 | | - * @type {NodeJS.Timeout?} |
| 49 | + * Queue for removal of cache entries. |
| 43 | | - * @private |
| 50 | + * @type {Set<string>} |
| 44 | 51 | */ |
| 45 | | - _removalInterval: null, |
| 52 | + removalQueue = new Set(); |
| 53 | + |
| 46 | 54 | /** |
| 47 | 55 | * Processes the removal queue. |
| 48 | 56 | * @returns {Promise<void>} |
| 49 | | - * @private |
| 50 | 57 | */ |
| 51 | 58 | _removeCacheEntries: async function#removeCacheEntries() { |
| 52 | 59 | try { |
| 53 | 60 | if (!useDiskCache || this.removalQueue.size === 0) { |
| 54 | 61 | return; |
| 55 | 62 | } |
| 56 | 63 | |
| 57 | 64 | const keys = await diskCachethis.instance().then(i => i.keys()); |
| 58 | 65 | for (const item of this.removalQueue) { |
| 59 | 66 | const key = keys.find(k => k.startsWith(item)); |
| 60 | 67 | if (key) { |
| 61 | 68 | await diskCachethis.instance().then(i => i.removeItem(key)); |
| 62 | 69 | } |
| 63 | 70 | } |
| 64 | 71 | this.removalQueue.clear(); |
| 65 | 72 | } catch (error) { |
| 66 | 73 | console.error('Error while removing cache entries:', error); |
| 67 | 74 | } |
| 68 | 75 | },; |
| 76 | + |
| 69 | 77 | /** |
| 70 | 78 | * Gets the disk cache instance. |
| 71 | 79 | * @returns {Promise<import('node-persist').LocalStorage>} |
| 72 | 80 | */ |
| 73 | 81 | instance: async functioninstance() { |
| 74 | 82 | if (this._instance#instance) { |
| 75 | 83 | return this._instance#instance; |
| 76 | 84 | } |
| 77 | 85 | |
| 78 | 86 | const cacheDir = path.join(globalThis.DATA_ROOT, '_cache', 'characters'DiskCache.DIRECTORY); |
| 79 | 87 | this._instance#instance = storage.create({ dir: cacheDir, ttl: false }); |
| 80 | 88 | await this._instance#instance.init(); |
| 81 | 89 | this._removalInterval#removalInterval = setInterval(this._removeCacheEntries#removeCacheEntries.bind(this), 60000DiskCache.REMOVAL_INTERVAL); |
| 82 | 90 | return this._instance#instance; |
| 83 | 91 | }, |
| 92 | + |
| 84 | 93 | /** |
| 85 | 94 | * QueueVerifies fordisk removalcache ofsize cacheand entriesprunes it if necessary. |
| 86 | | - * @type {Set<string>} |
| 95 | + * @param {import('../users.js').UserDirectoryList[]} directoriesList List of user directories |
| 96 | + * @returns {Promise<void>} |
| 87 | 97 | */ |
| 88 | | - removalQueue: new Set(), |
| 98 | + async verify(directoriesList) { |
| 89 | | -}; |
| 99 | + if (!useDiskCache) { |
| 90 | | - |
| 100 | + return; |
| 91 | | -/** |
| 101 | + } |
| 92 | | - * Verifies disk cache size and prunes it if necessary. |
| 93 | | - * @param {import('../users.js').UserDirectoryList[]} directoriesList List of user directories |
| 94 | | - * @returns {Promise<void>} |
| 95 | | - */ |
| 96 | | -export async function verifyCharactersDiskCache(directoriesList) { |
| 97 | | - if (!useDiskCache) { |
| 98 | | - return; |
| 99 | | - } |
| 100 | 102 | |
| 101 | 103 | const cache = await diskCachethis.instance(); |
| 102 | 104 | const validKeys = []; |
| 103 | 105 | for (const dir of directoriesList) { |
| 104 | 106 | const files = fs.readdirSync(dir.characters); |
| 105 | 107 | for (const file of files) { |
| 106 | 108 | const filePath = path.join(dir.characters, file); |
| 107 | 109 | const stat = fs.statSync(filePath); |
| 108 | 110 | validKeys.push(`${filePath}-${stat.mtimeMs}`); |
| 111 | + } |
| 112 | + } |
| 113 | + const cacheKeys = await cache.keys(); |
| 114 | + for (const key of cacheKeys) { |
| 115 | + if (!validKeys.includes(key)) { |
| 116 | + await cache.removeItem(key); |
| 117 | + } |
| 109 | 118 | } |
| 110 | 119 | } |
| 111 | | - const cacheKeys = await cache.keys(); |
| 120 | + |
| 112 | 121 | for dispose(const key of cacheKeys) { |
| 113 | 122 | if (!validKeysthis.includes(key)#removalInterval) { |
| 114 | | - await cache.removeItem(key); |
| 123 | + clearInterval(this.#removalInterval); |
| 115 | 124 | } |
| 116 | 125 | } |
| 117 | 126 | } |
| 118 | 127 | |
| 128 | +export const diskCache = new DiskCache(); |
| 129 | + |
| 119 | 130 | /** |
| 120 | 131 | * Reads the character card from the specified image file. |
| 121 | 132 | * @param {string} inputFile - Path to the image file |