@@ -75,7 +75,164 @@ async function openLocation(key) {
7575 }
7676}
7777
78+ /**
79+ * Check for the existence of the clients_jar_ttl.json file in the microbot directory.
80+ * Creating with an empty object if it does not exist.
81+ * @return {Object } The parsed JSON object from the file.
82+ */
83+ async function getClientsJarTTL ( ) {
84+ const fs = require ( 'fs' ) . promises ;
85+ const filePath = path . join ( microbotDir , 'clients_jar_ttl.json' ) ;
86+ try {
87+ await fs . mkdir ( microbotDir , { recursive : true } ) ;
88+ try {
89+ const data = await fs . readFile ( filePath , 'utf8' ) ;
90+ return { success : true , data : JSON . parse ( data ) } ;
91+ } catch ( readErr ) {
92+ if ( readErr . code === 'ENOENT' ) {
93+ await fs . writeFile ( filePath , JSON . stringify ( { } ) ) ;
94+ return { success : true , data : { } } ;
95+ }
96+ return { success : false , error : readErr . message } ;
97+ }
98+ } catch ( err ) {
99+ return { success : false , error : err . message } ;
100+ }
101+ }
102+
103+ /**
104+ * Cleanup routines for clients jar using the TTL data.
105+ * Delete any jar files that haven't been used in the past 3 days, with the exception
106+ * of the latest version.
107+ * @param {string } latestVersion The latest version string to exclude from deletion.
108+ * @return {Promise<{success: boolean, error?: string}> } Result object indicating success or failure.
109+ */
110+ async function cleanupUnusedClientsJar ( latestVersion ) {
111+ const fs = require ( 'fs' ) . promises ;
112+ const result = await getClientsJarTTL ( ) ;
113+ if ( ! result . success ) {
114+ return { success : false , error : result . error } ;
115+ }
116+
117+ if ( ! latestVersion ) {
118+ return { success : false , error : 'Latest version not provided' } ;
119+ }
120+
121+ let ttlData = result . data || { } ;
122+ let updated = false ;
123+ try {
124+ const files = await fs . readdir ( microbotDir ) ;
125+ const jarFiles = files . filter (
126+ ( f ) =>
127+ f . endsWith ( '.jar' ) &&
128+ f . startsWith ( 'microbot-' ) &&
129+ ! f . includes ( 'launcher' )
130+ ) ;
131+ const now = Date . now ( ) ;
132+ for ( const jarFile of jarFiles ) {
133+ let version = jarFile . replace ( '.jar' , '' ) ;
134+ version = version . replace ( 'microbot-' , '' ) ;
135+ if ( ! ( version in ttlData ) ) {
136+ ttlData [ version ] = now ;
137+ updated = true ;
138+ }
139+ }
140+ } catch ( err ) {
141+ return { success : false , error : err . message } ;
142+ }
143+
144+ const now = Date . now ( ) ;
145+ const threeDays = 3 * 24 * 60 * 60 * 1000 ;
146+ let deletedAny = false ;
147+
148+ for ( const [ version , lastUsed ] of Object . entries ( ttlData ) ) {
149+ if ( version === latestVersion ) {
150+ continue ;
151+ }
152+ if ( now - lastUsed > threeDays ) {
153+ const jarPath = path . join ( microbotDir , `microbot-${ version } .jar` ) ;
154+ try {
155+ await fs . unlink ( jarPath ) ;
156+ delete ttlData [ version ] ;
157+ deletedAny = true ;
158+ updated = true ;
159+ } catch ( err ) {
160+ if ( err . code === 'ENOENT' ) {
161+ // File already gone: drop TTL entry and continue.
162+ delete ttlData [ version ] ;
163+ updated = true ;
164+ continue ;
165+ }
166+ return { success : false , error : err . message } ;
167+ }
168+ }
169+ }
170+
171+ // we return if nothing changed
172+ if ( ! updated ) {
173+ return { success : true } ;
174+ }
175+
176+ const filePath = path . join ( microbotDir , 'clients_jar_ttl.json' ) ;
177+ try {
178+ await fs . writeFile ( filePath , JSON . stringify ( ttlData , null , 2 ) , 'utf8' ) ;
179+ } catch ( err ) {
180+ return { success : false , error : err . message } ;
181+ }
182+
183+ return { success : true } ;
184+ }
185+
186+ /**
187+ * Update the last used timestamp for a specific client version.
188+ * @param {string } version The client version to update.
189+ * @return {Promise<{success: boolean, error?: string}> } Result object indicating success or failure.
190+ */
191+ async function updateClientJarTTL ( version ) {
192+ const fs = require ( 'fs' ) . promises ;
193+ if ( ! version ) {
194+ return { success : false , error : 'Version not provided' } ;
195+ }
196+
197+ if ( ! / ^ [ a - z A - Z 0 - 9 . _ - ] + $ / . test ( version ) ) {
198+ return { success : false , error : 'Invalid version format' } ;
199+ }
200+
201+ const jarPath = path . join ( microbotDir , `microbot-${ version } .jar` ) ;
202+ try {
203+ await fs . stat ( jarPath ) ;
204+ } catch ( e ) {
205+ if ( e . code === 'ENOENT' ) {
206+ return {
207+ success : false ,
208+ error : `Jar not found for version: ${ version } `
209+ } ;
210+ }
211+ return { success : false , error : e . message } ;
212+ }
213+
214+ const result = await getClientsJarTTL ( ) ;
215+ if ( ! result . success ) {
216+ return { success : false , error : result . error } ;
217+ }
218+
219+ const ttlData = result . data ;
220+ ttlData [ version ] = Date . now ( ) ;
221+
222+ const filePath = path . join ( microbotDir , 'clients_jar_ttl.json' ) ;
223+ try {
224+ await fs . writeFile ( filePath , JSON . stringify ( ttlData , null , 2 ) , 'utf8' ) ;
225+ } catch ( err ) {
226+ return { success : false , error : err . message } ;
227+ }
228+
229+ return { success : true } ;
230+ }
231+
78232module . exports = {
79233 microbotDir,
80- openLocation
234+ openLocation,
235+ getClientsJarTTL,
236+ cleanupUnusedClientsJar,
237+ updateClientJarTTL
81238} ;
0 commit comments