apps.helpers.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import portUsed from 'tcp-port-used';
  2. import { fileExists, readdirSync, readFile, readJsonFile, runScript, writeFile } from '../fs/fs.helpers';
  3. import InternalIp from 'internal-ip';
  4. import crypto from 'crypto';
  5. import config from '../../config';
  6. import { AppInfo } from './apps.types';
  7. export const checkAppRequirements = async (appName: string) => {
  8. let valid = true;
  9. const configFile: AppInfo = readJsonFile(`/apps/${appName}/config.json`);
  10. if (configFile?.requirements?.ports) {
  11. for (const port of configFile.requirements.ports) {
  12. const ip = await InternalIp.v4();
  13. const used = await portUsed.check(port, ip);
  14. if (used) valid = false;
  15. }
  16. }
  17. return valid;
  18. };
  19. export const getEnvMap = (appName: string): Map<string, string> => {
  20. const envFile = readFile(`/app-data/${appName}/app.env`).toString();
  21. const envVars = envFile.split('\n');
  22. const envVarsMap = new Map<string, string>();
  23. envVars.forEach((envVar) => {
  24. const [key, value] = envVar.split('=');
  25. envVarsMap.set(key, value);
  26. });
  27. return envVarsMap;
  28. };
  29. export const checkEnvFile = (appName: string) => {
  30. const configFile: AppInfo = readJsonFile(`/apps/${appName}/config.json`);
  31. const envMap = getEnvMap(appName);
  32. configFile.form_fields?.forEach((field) => {
  33. const envVar = field.env_variable;
  34. const envVarValue = envMap.get(envVar);
  35. if (!envVarValue && field.required) {
  36. throw new Error('New info needed. App config needs to be updated');
  37. }
  38. });
  39. };
  40. export const checkAppExists = (appName: string) => {
  41. const appExists = fileExists(`/app-data/${appName}`);
  42. if (!appExists) {
  43. throw new Error(`App ${appName} not installed`);
  44. }
  45. };
  46. export const runAppScript = (params: string[]): Promise<void> => {
  47. return new Promise((resolve, reject) => {
  48. runScript('/scripts/app.sh', [...params, config.ROOT_FOLDER_HOST], (err: string) => {
  49. if (err) {
  50. reject(err);
  51. }
  52. resolve();
  53. });
  54. });
  55. };
  56. export const ensureAppState = (appName: string, installed: boolean) => {
  57. const state = readJsonFile('/state/apps.json');
  58. if (installed) {
  59. if (state.installed.indexOf(appName) === -1) {
  60. state.installed += ` ${appName}`;
  61. }
  62. } else {
  63. if (state.installed.indexOf(appName) !== -1) {
  64. state.installed = state.installed.replace(`${appName}`, '');
  65. }
  66. }
  67. state.installed = state.installed.replace(/\s+/g, ' ').trim();
  68. writeFile('/state/apps.json', JSON.stringify(state));
  69. };
  70. const getEntropy = (name: string, length: number) => {
  71. const hash = crypto.createHash('sha256');
  72. hash.update(name);
  73. return hash.digest('hex').substring(0, length);
  74. };
  75. export const generateEnvFile = (appName: string, form: Record<string, string>) => {
  76. const configFile: AppInfo = readJsonFile(`/apps/${appName}/config.json`);
  77. const baseEnvFile = readFile('/.env').toString();
  78. let envFile = `${baseEnvFile}\nAPP_PORT=${configFile.port}\n`;
  79. const envMap = getEnvMap(appName);
  80. configFile.form_fields?.forEach((field) => {
  81. const formValue = form[field.env_variable];
  82. const envVar = field.env_variable;
  83. if (formValue) {
  84. envFile += `${envVar}=${formValue}\n`;
  85. } else if (field.type === 'random') {
  86. if (envMap.has(envVar)) {
  87. envFile += `${envVar}=${envMap.get(envVar)}\n`;
  88. } else {
  89. const length = field.min || 32;
  90. const randomString = getEntropy(field.env_variable, length);
  91. envFile += `${envVar}=${randomString}\n`;
  92. }
  93. } else if (field.required) {
  94. throw new Error(`Variable ${field.env_variable} is required`);
  95. }
  96. });
  97. writeFile(`/app-data/${appName}/app.env`, envFile);
  98. };
  99. export const getAvailableApps = (): string[] => {
  100. const apps: string[] = [];
  101. const appsDir = readdirSync('/apps');
  102. appsDir.forEach((app) => {
  103. if (fileExists(`/apps/${app}/config.json`)) {
  104. const configFile: AppInfo = readJsonFile(`/apps/${app}/config.json`);
  105. if (configFile.available) {
  106. apps.push(app);
  107. }
  108. }
  109. });
  110. return apps;
  111. };
  112. export const getAppInfo = (id: string): AppInfo => {
  113. try {
  114. const configFile: AppInfo = readJsonFile(`/apps/${id}/config.json`);
  115. configFile.description = readFile(`/apps/${id}/metadata/description.md`);
  116. return configFile;
  117. } catch (e) {
  118. throw new Error(`App ${id} not found`);
  119. }
  120. };