Convert to class

3355c682ca1b0e88ede899ee0e4c4e975c9867fd

Cohee <18619528+Cohee1207@users.noreply.github.com>

2 files changed, +62 -51Ignore whitespace
server.js+2 -2
@@ -66,7 +66,7 @@ import { init as statsInit, onExit as statsOnExit } from './src/endpoints/stats.
66import { checkForNewContent } from './src/endpoints/content-manager.js';66import { checkForNewContent } from './src/endpoints/content-manager.js';
67import { init as settingsInit } from './src/endpoints/settings.js';67import { init as settingsInit } from './src/endpoints/settings.js';
68import { redirectDeprecatedEndpoints, ServerStartup, setupPrivateEndpoints } from './src/server-startup.js';68import { redirectDeprecatedEndpoints, ServerStartup, setupPrivateEndpoints } from './src/server-startup.js';
69import { verifyCharactersDiskCache } from './src/endpoints/characters.js';69import { diskCache } from './src/endpoints/characters.js';
7070
71// Unrestrict console logs display limit71// Unrestrict console logs display limit
72util.inspect.defaultOptions.maxArrayLength = null;72util.inspect.defaultOptions.maxArrayLength = null;
@@ -269,7 +269,7 @@ async function preSetupTasks() {
269 const directories = await getUserDirectoriesList();269 const directories = await getUserDirectoriesList();
270 await checkForNewContent(directories);270 await checkForNewContent(directories);
271 await ensureThumbnailCache();271 await ensureThumbnailCache();
272 await verifyCharactersDiskCache(directories);272 await diskCache.verify(directories);
273 cleanUploads();273 cleanUploads();
274 migrateAccessLog();274 migrateAccessLog();
275275
src/endpoints/characters.js+60 -49
@@ -32,90 +32,101 @@ const isAndroid = process.platform === 'android';
32const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean');32const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean');
33const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean');33const useDiskCache = !!getConfigValue('performance.useDiskCache', true, 'boolean');
3434
35const diskCache = {35class DiskCache {
36 /**36 /** @type {string} */
37 * @type {import('node-persist').LocalStorage?}37 static DIRECTORY = 'characters';
38 * @private38
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 * @private50 * @type {Set<string>}
44 */51 */
45 _removalInterval: null,52 removalQueue = new Set();
53
46 /**54 /**
47 * Processes the removal queue.55 * Processes the removal queue.
48 * @returns {Promise<void>}56 * @returns {Promise<void>}
49 * @private
50 */57 */
51 _removeCacheEntries: async function() {58 async #removeCacheEntries() {
52 try {59 try {
53 if (!useDiskCache || this.removalQueue.size === 0) {60 if (!useDiskCache || this.removalQueue.size === 0) {
54 return;61 return;
55 }62 }
5663
57 const keys = await diskCache.instance().then(i => i.keys());64 const keys = await this.instance().then(i => i.keys());
58 for (const item of this.removalQueue) {65 for (const item of this.removalQueue) {
59 const key = keys.find(k => k.startsWith(item));66 const key = keys.find(k => k.startsWith(item));
60 if (key) {67 if (key) {
61 await diskCache.instance().then(i => i.removeItem(key));68 await this.instance().then(i => i.removeItem(key));
62 }69 }
63 }70 }
64 this.removalQueue.clear();71 this.removalQueue.clear();
65 } catch (error) {72 } catch (error) {
66 console.error('Error while removing cache entries:', error);73 console.error('Error while removing cache entries:', error);
67 }74 }
68 },75 };
76
69 /**77 /**
70 * Gets the disk cache instance.78 * Gets the disk cache instance.
71 * @returns {Promise<import('node-persist').LocalStorage>}79 * @returns {Promise<import('node-persist').LocalStorage>}
72 */80 */
73 instance: async function() {81 async instance() {
74 if (this._instance) {82 if (this.#instance) {
75 return this._instance;83 return this.#instance;
76 }84 }
7785
78 const cacheDir = path.join(globalThis.DATA_ROOT, '_cache', 'characters');86 const cacheDir = path.join(globalThis.DATA_ROOT, '_cache', DiskCache.DIRECTORY);
79 this._instance = storage.create({ dir: cacheDir, ttl: false });87 this.#instance = storage.create({ dir: cacheDir, ttl: false });
80 await this._instance.init();88 await this.#instance.init();
81 this._removalInterval = setInterval(this._removeCacheEntries.bind(this), 60000);89 this.#removalInterval = setInterval(this.#removeCacheEntries.bind(this), DiskCache.REMOVAL_INTERVAL);
82 return this._instance;90 return this.#instance;
83 },91 }
92
84 /**93 /**
85 * Queue for removal of cache entries.94 * Verifies disk cache size and prunes 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) {
90100 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 */
96export async function verifyCharactersDiskCache(directoriesList) {
97 if (!useDiskCache) {
98 return;
99 }
100102
101 const cache = await diskCache.instance();103 const cache = await this.instance();
102 const validKeys = [];104 const validKeys = [];
103 for (const dir of directoriesList) {105 for (const dir of directoriesList) {
104 const files = fs.readdirSync(dir.characters);106 const files = fs.readdirSync(dir.characters);
105 for (const file of files) {107 for (const file of files) {
106 const filePath = path.join(dir.characters, file);108 const filePath = path.join(dir.characters, file);
107 const stat = fs.statSync(filePath);109 const stat = fs.statSync(filePath);
108 validKeys.push(`${filePath}-${stat.mtimeMs}`);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 for (const key of cacheKeys) {121 dispose() {
113 if (!validKeys.includes(key)) {122 if (this.#removalInterval) {
114 await cache.removeItem(key);123 clearInterval(this.#removalInterval);
115 }124 }
116 }125 }
117}126}
118127
128export const diskCache = new DiskCache();
129
119/**130/**
120 * Reads the character card from the specified image file.131 * Reads the character card from the specified image file.
121 * @param {string} inputFile - Path to the image file132 * @param {string} inputFile - Path to the image file