diff --git a/apps/core/src/modules/config/manager.test.ts b/apps/core/src/modules/config/manager.test.ts index f214fe7..77d2c80 100644 --- a/apps/core/src/modules/config/manager.test.ts +++ b/apps/core/src/modules/config/manager.test.ts @@ -236,7 +236,13 @@ describe('ConfigManager', () => { it('auto-detects Linux fxserver inside a valid directory', async () => { setPlatform('linux'); - const dirPath = path.join(process.cwd(), 'fxserver-folder'); + const dirPath = path.join( + process.cwd(), + 'fxserver', + 'alpine', + 'opt', + 'cfx-server', + ); const exePath = path.join(dirPath, 'fxserver'); fileSystem.set(dirPath, 'dir'); @@ -248,6 +254,22 @@ describe('ConfigManager', () => { expect(res.path).toBe(exePath); }); + it('auto-detects Linux fxserver from alpine path', async () => { + setPlatform('linux'); + const rootPath = path.join(process.cwd(), 'fxserver'); + const dirPath = path.join(rootPath, 'alpine', 'opt', 'cfx-server'); + const exePath = path.join(dirPath, 'fxserver'); + + fileSystem.set(rootPath, 'dir'); + fileSystem.set(dirPath, 'dir'); + fileSystem.set(exePath, 'file'); + + const config = ConfigManager.getInstance(); + const res = await config.validateExecutablePath(rootPath); + expect(res.valid).toBe(true); + expect(res.path).toBe(exePath); + }); + it('validates a direct file path (Case-Insensitive on Windows)', async () => { setPlatform('win32'); const exePath = path.join(process.cwd(), 'fXseRver.eXe'); diff --git a/apps/core/src/modules/config/manager.ts b/apps/core/src/modules/config/manager.ts index 0018853..4e977c3 100644 --- a/apps/core/src/modules/config/manager.ts +++ b/apps/core/src/modules/config/manager.ts @@ -142,10 +142,16 @@ export class ConfigManager { if (execStats.isDirectory()) { const validNames = process.platform === 'win32' - // cover both as a sanity check - ? ['fxserver.exe', 'FXServer.exe'] - // should only be FXServer, but just in case we do both - : ['fxserver', 'FXServer']; + ? // cover both as a sanity check + ['fxserver.exe', 'FXServer.exe'] + : // should only be FXServer, but just in case we do both + // we also cover the alpine path if omitted + [ + 'fxserver', + 'FXServer', + path.join('alpine', 'opt', 'cfx-server', 'fxserver'), + path.join('alpine', 'opt', 'cfx-server', 'FXServer'), + ]; for (const name of validNames) { const testPath = path.join(initialPath, name); @@ -165,9 +171,9 @@ export class ConfigManager { ? /^fxserver\.exe$/i : /^(fxserver|FXServer)$/; - if (validNames.test(fileName)) { - found = true; - } + if (validNames.test(fileName)) { + found = true; + } } } catch { found = false; diff --git a/apps/core/src/routes/api/setup.ts b/apps/core/src/routes/api/setup.ts index a65a7cb..f7fce71 100644 --- a/apps/core/src/routes/api/setup.ts +++ b/apps/core/src/routes/api/setup.ts @@ -53,19 +53,22 @@ const SetupEndpoint: FastifyPluginAsync = async (fastify) => { ? cfg.serverConfigFile : path.join(cfg.serverDataPath, cfg.serverConfigFile); - const [executable, dataPath, cfgFound] = await Promise.all([ - fileExists(cfg.executablePath), - fileExists(cfg.serverDataPath), - fileExists(cfgPath), - ]); + const result = await ConfigManager.getInstance().checkFXServerPaths( + cfg.executablePath, + cfg.serverDataPath, + ); return { success: true, data: { - executable: cfg.executablePath, - dataPath: cfg.serverDataPath, - cfgPath, - found: { executable, dataPath, cfg: cfgFound }, + executable: result.files.executable, + dataPath: result.files.serverdata, + cfgPath: result.files.cfg, + found: { + executable: result.exists.executable, + dataPath: result.exists.serverdata, + cfg: result.exists.cfg, + }, }, } satisfies ApiResponse; });