22type ErrorTrackingModule = typeof import ( '../error-tracking' ) ;
33type IndexTypeModule = typeof import ( '../index' ) ;
44
5+ import * as path from 'path' ;
6+ import { promises as fs } from 'fs'
7+ import * as semver from 'semver' ;
8+ import * as rimraf from 'rimraf' ;
9+
510import { IS_PROD_BUILD } from '../constants' ;
611
712function maybeBundleImport < T > ( moduleName : string ) : T {
@@ -40,6 +45,8 @@ class HttpToolkitServer extends Command {
4045 async run ( ) {
4146 const { flags } = this . parse ( HttpToolkitServer ) ;
4247
48+ this . cleanupOldServers ( ) ; // Async cleanup old server versions
49+
4350 await runHTK ( {
4451 configPath : flags . config ,
4552 authToken : flags . token
@@ -48,6 +55,66 @@ class HttpToolkitServer extends Command {
4855 throw error ;
4956 } ) ;
5057 }
58+
59+ // On startup, we want to kill any downloaded servers that are not longer necessary
60+ async cleanupOldServers ( ) {
61+ const { dataDir, version : currentVersion } = this . config ;
62+
63+ const serverUpdatesPath = process . env . OCLIF_CLIENT_HOME ||
64+ path . join ( dataDir , 'client' ) ;
65+
66+ // Be careful - if the server path isn't clearly ours somehow, ignore it.
67+ if ( ! serverUpdatesPath . split ( path . sep ) . includes ( 'httptoolkit-server' ) ) {
68+ reportError ( `Unexpected server path (${ serverUpdatesPath } ), ignoring` ) ;
69+ return ;
70+ }
71+
72+ const serverPaths = await fs . readdir ( serverUpdatesPath )
73+ . catch ( ( e ) => {
74+ if ( e . code === 'ENOENT' ) return null ;
75+ else throw e ;
76+ } ) ;
77+ if ( ! serverPaths ) return ; // No server update path means we're all good
78+
79+ // Similarly, if the folder contains anything unexpected, be careful and do nothing.
80+ if ( serverPaths . some ( ( filename ) =>
81+ ! semver . valid ( filename . replace ( / \. p a r t i a l \. \d + $ / , '' ) ) &&
82+ filename !== 'bin' &&
83+ filename !== 'current'
84+ ) ) {
85+ console . log ( serverPaths ) ;
86+ reportError (
87+ `Server path (${ serverUpdatesPath } ) contains unexpected content, ignoring`
88+ ) ;
89+ return ;
90+ }
91+
92+ if ( serverPaths . every ( ( filename ) => {
93+ const version = semver . valid ( filename . replace ( / \. p a r t i a l \. \d + $ / , '' ) ) ;
94+ return ! version || semver . lt ( version , currentVersion ) ;
95+ } ) ) {
96+ // If everything is outdated, just drop the whole folder. Useful if you start
97+ // a new server standalone (not just from an update), because otherwise the
98+ // update dir can end up in a broken state. Better to clear it completely.
99+ console . log ( "Downloaded server directory is entirely outdated, deleting it" ) ;
100+ rimraf ( serverUpdatesPath , ( error ) => {
101+ if ( error ) reportError ( error ) ;
102+ } ) ;
103+ } else {
104+ // Some of the servers are outdated, but not all (maybe it includes us).
105+ // Async delete all server versions older than this currently running version.
106+ serverPaths . forEach ( ( filename ) => {
107+ const version = semver . valid ( filename . replace ( / \. p a r t i a l \. \d + $ / , '' ) ) ;
108+
109+ if ( version && semver . lt ( version , currentVersion ) ) {
110+ console . log ( `Deleting old server ${ filename } ` ) ;
111+ rimraf ( path . join ( serverUpdatesPath , filename ) , ( error ) => {
112+ if ( error ) reportError ( error ) ;
113+ } ) ;
114+ }
115+ } ) ;
116+ }
117+ }
51118}
52119
53120export = HttpToolkitServer ;
0 commit comments