|
@@ -1,45 +1,37 @@
|
|
import { Request, Response } from 'express';
|
|
import { Request, Response } from 'express';
|
|
-import fs from 'fs';
|
|
|
|
import process from 'child_process';
|
|
import process from 'child_process';
|
|
-import config from '../config';
|
|
|
|
-import { AppConfig } from '../config/types';
|
|
|
|
|
|
+import config from '../../config';
|
|
|
|
+import { AppConfig } from '../../config/types';
|
|
|
|
+import { createFolder, fileExists, readJsonFile, writeFile, copyFile, runScript, deleteFolder } from '../fs/fs.helpers';
|
|
|
|
|
|
-const appScript = `${config.ROOT_FOLDER}/scripts/app.sh`;
|
|
|
|
|
|
+type AppsState = { installed: string };
|
|
|
|
|
|
-const getAppFolder = (appName: string) => `${config.ROOT_FOLDER}/apps/${appName}`;
|
|
|
|
-const getDataFolder = (appName: string) => `${config.ROOT_FOLDER}/app-data/${appName}`;
|
|
|
|
-
|
|
|
|
-const getStateFile = () => {
|
|
|
|
- // Add app to apps.json
|
|
|
|
- const rawFile = fs.readFileSync(`${config.ROOT_FOLDER}/state/apps.json`).toString();
|
|
|
|
- let apps = JSON.parse(rawFile);
|
|
|
|
-
|
|
|
|
- return apps;
|
|
|
|
|
|
+const getStateFile = (): AppsState => {
|
|
|
|
+ return readJsonFile('/state/apps.json');
|
|
};
|
|
};
|
|
|
|
|
|
const generateEnvFile = (appName: string, form: Record<string, string>) => {
|
|
const generateEnvFile = (appName: string, form: Record<string, string>) => {
|
|
- const appExists = fs.existsSync(getDataFolder(appName));
|
|
|
|
|
|
+ const appExists = fileExists(`/app-data/${appName}`);
|
|
|
|
|
|
if (!appExists) {
|
|
if (!appExists) {
|
|
throw new Error(`App ${appName} not installed`);
|
|
throw new Error(`App ${appName} not installed`);
|
|
}
|
|
}
|
|
|
|
|
|
- const rawFile = fs.readFileSync(`${getAppFolder(appName)}/config.json`).toString();
|
|
|
|
- let configFile: AppConfig = JSON.parse(rawFile);
|
|
|
|
|
|
+ const configFile: AppConfig = readJsonFile(`/apps/${appName}/config.json`);
|
|
let envFile = '';
|
|
let envFile = '';
|
|
|
|
|
|
- Object.keys(configFile.form_fields).forEach(key => {
|
|
|
|
|
|
+ Object.keys(configFile.form_fields).forEach((key) => {
|
|
const value = form[key];
|
|
const value = form[key];
|
|
|
|
|
|
if (value) {
|
|
if (value) {
|
|
const envVar = configFile.form_fields[key].env_variable;
|
|
const envVar = configFile.form_fields[key].env_variable;
|
|
envFile += `${envVar}=${value}\n`;
|
|
envFile += `${envVar}=${value}\n`;
|
|
- } else if (configFile[key].required) {
|
|
|
|
|
|
+ } else if (configFile.form_fields[key].required) {
|
|
throw new Error(`Variable ${key} is required`);
|
|
throw new Error(`Variable ${key} is required`);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
- fs.writeFileSync(`${getDataFolder(appName)}/.env`, envFile);
|
|
|
|
|
|
+ writeFile(`/app-data/${appName}/.env`, envFile);
|
|
};
|
|
};
|
|
|
|
|
|
const installApp = (req: Request, res: Response) => {
|
|
const installApp = (req: Request, res: Response) => {
|
|
@@ -50,30 +42,25 @@ const installApp = (req: Request, res: Response) => {
|
|
throw new Error('App name is required');
|
|
throw new Error('App name is required');
|
|
}
|
|
}
|
|
|
|
|
|
- const appDataFolder = `${config.ROOT_FOLDER}/app-data/${appName}`;
|
|
|
|
- const appFolder = `${config.ROOT_FOLDER}/apps/${appName}`;
|
|
|
|
-
|
|
|
|
- const appExists = fs.existsSync(appDataFolder);
|
|
|
|
|
|
+ const appExists = fileExists(`/app-data/${appName}`);
|
|
|
|
|
|
if (appExists) {
|
|
if (appExists) {
|
|
throw new Error(`App ${appName} already installed`);
|
|
throw new Error(`App ${appName} already installed`);
|
|
}
|
|
}
|
|
|
|
|
|
// Create app folder
|
|
// Create app folder
|
|
- fs.mkdirSync(appFolder);
|
|
|
|
-
|
|
|
|
|
|
+ createFolder(`/app-data/${appName}`);
|
|
// Copy default app files from app-data folder
|
|
// Copy default app files from app-data folder
|
|
- fs.copyFileSync(`${appFolder}/data`, `${appDataFolder}/data`);
|
|
|
|
|
|
+ copyFile(`/apps/${appName}/data`, `/app-data/${appName}/data`);
|
|
|
|
|
|
|
|
+ // Create env file
|
|
generateEnvFile(appName, form);
|
|
generateEnvFile(appName, form);
|
|
-
|
|
|
|
const state = getStateFile();
|
|
const state = getStateFile();
|
|
state.installed += ` ${appName}`;
|
|
state.installed += ` ${appName}`;
|
|
-
|
|
|
|
- fs.writeFileSync(`${config.ROOT_FOLDER}/state/apps.json`, JSON.stringify(state));
|
|
|
|
|
|
+ writeFile('/state/apps.json', JSON.stringify(state));
|
|
|
|
|
|
// Run script
|
|
// Run script
|
|
- process.spawnSync(appScript, ['install', appName], {});
|
|
|
|
|
|
+ runScript('/scripts/app.sh', ['install', appName]);
|
|
|
|
|
|
res.status(200).json({ message: 'App installed successfully' });
|
|
res.status(200).json({ message: 'App installed successfully' });
|
|
} catch (e) {
|
|
} catch (e) {
|
|
@@ -88,22 +75,23 @@ const uninstallApp = (req: Request, res: Response) => {
|
|
if (!appName) {
|
|
if (!appName) {
|
|
throw new Error('App name is required');
|
|
throw new Error('App name is required');
|
|
}
|
|
}
|
|
- const appExists = fs.existsSync(getDataFolder(appName));
|
|
|
|
|
|
+
|
|
|
|
+ const appExists = fileExists(`/app-data/${appName}`);
|
|
|
|
|
|
if (!appExists) {
|
|
if (!appExists) {
|
|
throw new Error(`App ${appName} not installed`);
|
|
throw new Error(`App ${appName} not installed`);
|
|
}
|
|
}
|
|
|
|
|
|
// Delete app folder
|
|
// Delete app folder
|
|
- fs.rmdirSync(getAppFolder(appName), { recursive: true });
|
|
|
|
|
|
+ deleteFolder(`/app-data/${appName}`);
|
|
|
|
|
|
// Remove app from apps.json
|
|
// Remove app from apps.json
|
|
const state = getStateFile();
|
|
const state = getStateFile();
|
|
state.installed = state.installed.replace(` ${appName}`, '');
|
|
state.installed = state.installed.replace(` ${appName}`, '');
|
|
- fs.writeFileSync(`${config.ROOT_FOLDER}/state/apps.json`, JSON.stringify(state));
|
|
|
|
|
|
+ writeFile('/state/apps.json', JSON.stringify(state));
|
|
|
|
|
|
// Run script
|
|
// Run script
|
|
- process.spawnSync(appScript, ['uninstall', appName], {});
|
|
|
|
|
|
+ runScript('/scripts/app.sh', ['uninstall', appName]);
|
|
|
|
|
|
res.status(200).json({ message: 'App uninstalled successfully' });
|
|
res.status(200).json({ message: 'App uninstalled successfully' });
|
|
} catch (e) {
|
|
} catch (e) {
|
|
@@ -119,18 +107,18 @@ const stopApp = (req: Request, res: Response) => {
|
|
throw new Error('App name is required');
|
|
throw new Error('App name is required');
|
|
}
|
|
}
|
|
|
|
|
|
- const appExists = fs.existsSync(getDataFolder(appName));
|
|
|
|
|
|
+ const appExists = fileExists(`/app-data/${appName}`);
|
|
|
|
|
|
if (!appExists) {
|
|
if (!appExists) {
|
|
throw new Error(`App ${appName} not installed`);
|
|
throw new Error(`App ${appName} not installed`);
|
|
}
|
|
}
|
|
|
|
|
|
// Run script
|
|
// Run script
|
|
- process.spawnSync(appScript, ['stop', appName], {});
|
|
|
|
|
|
+ runScript('/scripts/app.sh', ['stop', appName]);
|
|
|
|
|
|
res.status(200).json({ message: 'App stopped successfully' });
|
|
res.status(200).json({ message: 'App stopped successfully' });
|
|
} catch (e) {
|
|
} catch (e) {
|
|
- res.status(500).send(e);
|
|
|
|
|
|
+ res.status(500).end(e);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -142,7 +130,7 @@ const updateAppConfig = (req: Request, res: Response) => {
|
|
throw new Error('App name is required');
|
|
throw new Error('App name is required');
|
|
}
|
|
}
|
|
|
|
|
|
- const appExists = fs.existsSync(getDataFolder(appName));
|
|
|
|
|
|
+ const appExists = fileExists(`/app-data/${appName}`);
|
|
|
|
|
|
if (!appExists) {
|
|
if (!appExists) {
|
|
throw new Error(`App ${appName} not installed`);
|
|
throw new Error(`App ${appName} not installed`);
|
|
@@ -151,26 +139,49 @@ const updateAppConfig = (req: Request, res: Response) => {
|
|
generateEnvFile(appName, form);
|
|
generateEnvFile(appName, form);
|
|
|
|
|
|
// Run script
|
|
// Run script
|
|
- process.spawnSync(appScript, ['stop', appName], {});
|
|
|
|
- process.spawnSync(appScript, ['start', appName], {});
|
|
|
|
|
|
+ runScript('/scripts/app.sh', ['stop', appName]);
|
|
|
|
+ runScript('/scripts/app.sh', ['start', appName]);
|
|
|
|
|
|
res.status(200).json({ message: 'App updated successfully' });
|
|
res.status(200).json({ message: 'App updated successfully' });
|
|
} catch (e) {
|
|
} catch (e) {
|
|
- res.status(500).send(e);
|
|
|
|
|
|
+ res.status(500).end(e);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
const installedApps = (req: Request, res: Response) => {
|
|
const installedApps = (req: Request, res: Response) => {
|
|
try {
|
|
try {
|
|
- const rawFile = fs.readFileSync(`${config.ROOT_FOLDER}/state/apps.json`).toString();
|
|
|
|
- const apps = JSON.parse(rawFile);
|
|
|
|
-
|
|
|
|
|
|
+ const apps = readJsonFile('/state/apps.json');
|
|
const appNames = apps.installed.split(' ');
|
|
const appNames = apps.installed.split(' ');
|
|
|
|
|
|
res.status(200).json(appNames);
|
|
res.status(200).json(appNames);
|
|
} catch (e) {
|
|
} catch (e) {
|
|
- res.status(500).send(e);
|
|
|
|
|
|
+ res.status(500).end(e);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
-export default { uninstallApp, installApp, stopApp, updateAppConfig, installedApps };
|
|
|
|
|
|
+const getAppInfo = (req: Request, res: Response<AppConfig>) => {
|
|
|
|
+ try {
|
|
|
|
+ const { appName } = req.body;
|
|
|
|
+
|
|
|
|
+ if (!appName) {
|
|
|
|
+ throw new Error('App name is required');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const configFile: AppConfig = readJsonFile(`/apps/${appName}/config.json`);
|
|
|
|
+
|
|
|
|
+ res.status(200).json(configFile);
|
|
|
|
+ } catch (e) {
|
|
|
|
+ res.status(500).end(e);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const AppController = {
|
|
|
|
+ uninstallApp,
|
|
|
|
+ installApp,
|
|
|
|
+ stopApp,
|
|
|
|
+ updateAppConfig,
|
|
|
|
+ installedApps,
|
|
|
|
+ getAppInfo,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export default AppController;
|