add fs helpers
This commit is contained in:
parent
1283457268
commit
4e5bf34ece
21 changed files with 19568 additions and 234 deletions
3
system-api/.eslintignore
Normal file
3
system-api/.eslintignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
*.cjs
|
|
@ -1,136 +1,16 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
env: { node: true },
|
||||||
env: {
|
extends: ['airbnb-base', 'eslint:recommended', 'plugin:import/typescript'],
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: ['./tsconfig.json'],
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
extends: ['plugin:prettier/recommended', 'airbnb-typescript', 'plugin:sonarjs/recommended', 'plugin:@typescript-eslint/recommended', 'plugin:unicorn/recommended', 'hardcore'],
|
plugins: ['@typescript-eslint', 'import'],
|
||||||
plugins: ['prettier', '@typescript-eslint', 'no-loops', 'sonarjs', 'deprecate', 'no-secrets', 'jest', 'react'],
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['**/*.test.ts', '**/*.test.tsx', 'jest.setup.ts', 'jest.config.js'],
|
|
||||||
rules: {
|
|
||||||
'import/unambiguous': 0,
|
|
||||||
'unicorn/consistent-function-scoping': 0,
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.d.ts'],
|
|
||||||
rules: {
|
|
||||||
'import/unambiguous': 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
'max-statements': 0,
|
|
||||||
camelcase: 0,
|
|
||||||
'unicorn/prefer-node-protocol': 0,
|
|
||||||
'newline-per-chained-call': 0,
|
|
||||||
'new-cap': 0,
|
|
||||||
'security/detect-non-literal-regexp': 0,
|
|
||||||
'promise/avoid-new': 0,
|
|
||||||
'import/no-commonjs': 0,
|
|
||||||
'unicorn/prefer-module': 0,
|
|
||||||
'@typescript-eslint/no-var-requires': 0,
|
|
||||||
'security/detect-unsafe-regex': 0,
|
|
||||||
'unicorn/no-unsafe-regex': 0,
|
|
||||||
'no-param-reassign': ['error', { props: true, ignorePropertyModificationsFor: ['^draft'] }],
|
|
||||||
'unicorn/no-array-callback-reference': 0,
|
|
||||||
'import/no-namespace': 0,
|
|
||||||
'unicorn/no-null': 0,
|
|
||||||
'unicorn/no-useless-undefined': 0,
|
|
||||||
'import/max-dependencies': 0,
|
|
||||||
'no-use-before-define': 'off',
|
|
||||||
'@typescript-eslint/no-use-before-define': ['error'],
|
|
||||||
'unicorn/no-array-for-each': 0,
|
|
||||||
'unicorn/prevent-abbreviations': 0,
|
|
||||||
'import/order': 0,
|
|
||||||
'import/extensions': 0,
|
|
||||||
'ext/lines-between-object-properties': 0,
|
|
||||||
'putout/putout': 0,
|
|
||||||
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
|
||||||
'deprecate/function': 1,
|
|
||||||
'deprecate/member-expression': 1,
|
|
||||||
'deprecate/import': 1,
|
|
||||||
'no-secrets/no-secrets': 'error',
|
|
||||||
'arrow-body-style': 0,
|
'arrow-body-style': 0,
|
||||||
semi: 0,
|
'no-restricted-exports': 0,
|
||||||
'@typescript-eslint/semi': 0,
|
'max-len': [{ code: 200 }],
|
||||||
'@typescript-eslint/indent': 0,
|
'import/extensions': ['error', 'ignorePackages', { js: 'never', jsx: 'never', ts: 'never', tsx: 'never' }],
|
||||||
'implicit-arrow-linebreak': 0,
|
|
||||||
'function-paren-newline': 0,
|
|
||||||
'operator-linebreak': 0,
|
|
||||||
'import/no-unused-modules': [1, { unusedExports: true }],
|
|
||||||
'import/no-extraneous-dependencies': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
devDependencies: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
quotes: ['warn', 'single'],
|
|
||||||
'no-restricted-syntax': 2,
|
|
||||||
'no-await-in-loop': 2,
|
|
||||||
'object-curly-newline': 0,
|
|
||||||
'no-constant-condition': 2,
|
|
||||||
'no-mixed-operators': 1,
|
|
||||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
|
||||||
'no-underscore-dangle': 0,
|
|
||||||
'no-global-assign': 2,
|
|
||||||
'prefer-const': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
destructuring: 'any',
|
|
||||||
ignoreReadBeforeAssign: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/prefer-default-export': 0,
|
|
||||||
'import/no-named-as-default': 0,
|
|
||||||
'max-lines': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
max: 200,
|
|
||||||
skipBlankLines: true,
|
|
||||||
skipComments: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'max-len': [
|
|
||||||
2,
|
|
||||||
200,
|
|
||||||
{
|
|
||||||
ignoreComments: true,
|
|
||||||
ignoreTemplateLiterals: true,
|
|
||||||
ignoreStrings: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
curly: 0,
|
|
||||||
'arrow-parens': 0,
|
|
||||||
'no-return-assign': 2,
|
|
||||||
'comma-dangle': 0,
|
|
||||||
'no-multi-str': 0,
|
|
||||||
'newline-before-return': 2,
|
|
||||||
'newline-after-var': 2,
|
|
||||||
'newline-per-chained-call': 2,
|
|
||||||
'import/newline-after-import': 2,
|
|
||||||
'no-loops/no-loops': 2,
|
|
||||||
'jest/no-disabled-tests': 'warn',
|
|
||||||
'jest/no-focused-tests': 'error',
|
|
||||||
'jest/no-identical-title': 'error',
|
|
||||||
'jest/prefer-to-have-length': 'warn',
|
|
||||||
'jest/valid-expect': 'error',
|
|
||||||
'id-length': 0,
|
|
||||||
'no-magic-numbers': 0,
|
|
||||||
'unicorn/prefer-type-error': 0,
|
|
||||||
'unicorn/no-array-method-this-argument': 0,
|
|
||||||
'no-shadow': 'off',
|
|
||||||
'@typescript-eslint/no-shadow': ['error'],
|
|
||||||
},
|
|
||||||
globals: {
|
|
||||||
JSX: true,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
semi: true,
|
semi: true,
|
||||||
trailingComma: "all",
|
trailingComma: 'all',
|
||||||
arrowParens: "avoid",
|
|
||||||
printWidth: 200,
|
printWidth: 200,
|
||||||
};
|
};
|
||||||
|
|
19426
system-api/package-lock.json
generated
19426
system-api/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,7 @@
|
||||||
"main": "src/server.ts",
|
"main": "src/server.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"lint": "eslint . --ext .ts",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build": "esbuild --bundle src/server.ts --outdir=dist --allow-overwrite --sourcemap --platform=node --minify --analyze=verbose --external:./node_modules/* --format=esm",
|
"build": "esbuild --bundle src/server.ts --outdir=dist --allow-overwrite --sourcemap --platform=node --minify --analyze=verbose --external:./node_modules/* --format=esm",
|
||||||
"build:watch": "esbuild --bundle src/server.ts --outdir=dist --allow-overwrite --sourcemap --platform=node --external:./node_modules/* --format=esm --watch",
|
"build:watch": "esbuild --bundle src/server.ts --outdir=dist --allow-overwrite --sourcemap --platform=node --external:./node_modules/* --format=esm --watch",
|
||||||
|
@ -26,8 +27,17 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/compression": "^1.7.2",
|
"@types/compression": "^1.7.2",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/validator": "^13.7.2",
|
||||||
"concurrently": "^7.1.0",
|
"concurrently": "^7.1.0",
|
||||||
"esbuild": "^0.14.32",
|
"esbuild": "^0.14.32",
|
||||||
"nodemon": "^2.0.15"
|
"eslint": "^8.13.0",
|
||||||
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||||
|
"eslint-config-hardcore": "^24.5.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"eslint-plugin-unicorn": "^42.0.0",
|
||||||
|
"nodemon": "^2.0.15",
|
||||||
|
"prettier": "2.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,3 @@
|
||||||
// "form_fields": {
|
|
||||||
// "username": {
|
|
||||||
// "type": "text",
|
|
||||||
// "label": "Username",
|
|
||||||
// "max": 50,
|
|
||||||
// "min": 3,
|
|
||||||
// "required": true,
|
|
||||||
// "env_variable": "NEXTCLOUD_USERNAME"
|
|
||||||
// },
|
|
||||||
// "password": {
|
|
||||||
// "type": "password",
|
|
||||||
// "label": "Password",
|
|
||||||
// "max": 50,
|
|
||||||
// "min": 3,
|
|
||||||
// "required": true,
|
|
||||||
// "env_variable": "NEXTCLOUD_PASSWORD"
|
|
||||||
// }
|
|
||||||
|
|
||||||
interface FormField {
|
interface FormField {
|
||||||
type: string;
|
type: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -29,5 +11,5 @@ export interface AppConfig {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
version: string;
|
version: string;
|
||||||
form_fields: Record<string, FormField[]>;
|
form_fields: Record<string, FormField>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,4 @@ import config from '../config';
|
||||||
|
|
||||||
export const APP_DATA_FOLDER = 'app-data';
|
export const APP_DATA_FOLDER = 'app-data';
|
||||||
export const APPS_FOLDER = 'apps';
|
export const APPS_FOLDER = 'apps';
|
||||||
export const __prod__ = config.NODE_ENV === 'production';
|
export const isProd = config.NODE_ENV === 'production';
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
export { default as SystemController } from './system.controller';
|
|
||||||
export { default as AppController } from './app.controller';
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { Request, Response } from "express";
|
|
||||||
import publicIp from "public-ip";
|
|
||||||
import portScanner from "node-port-scanner";
|
|
||||||
|
|
||||||
const isPortOpen = async (req: Request, res: Response<boolean>) => {
|
|
||||||
const port = req.params.port;
|
|
||||||
|
|
||||||
const host = await publicIp.v4();
|
|
||||||
|
|
||||||
const isOpen = await portScanner(host, [port]);
|
|
||||||
|
|
||||||
console.log(port);
|
|
||||||
|
|
||||||
res.status(200).send(isOpen);
|
|
||||||
};
|
|
3
system-api/src/helpers/helpers.ts
Normal file
3
system-api/src/helpers/helpers.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const objectKeys = <T>(obj: T): (keyof T)[] => Object.keys(obj) as (keyof T)[];
|
||||||
|
|
||||||
|
export default { objectKeys };
|
|
@ -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 config from '../../config';
|
||||||
import { AppConfig } from '../config/types';
|
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 getStateFile = (): AppsState => {
|
||||||
const getDataFolder = (appName: string) => `${config.ROOT_FOLDER}/app-data/${appName}`;
|
return readJsonFile('/state/apps.json');
|
||||||
|
|
||||||
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 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();
|
const configFile: AppConfig = readJsonFile(`/apps/${appName}/config.json`);
|
||||||
let configFile: AppConfig = JSON.parse(rawFile);
|
|
||||||
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 appExists = fileExists(`/app-data/${appName}`);
|
||||||
const appFolder = `${config.ROOT_FOLDER}/apps/${appName}`;
|
|
||||||
|
|
||||||
const appExists = fs.existsSync(appDataFolder);
|
|
||||||
|
|
||||||
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}`;
|
||||||
|
writeFile('/state/apps.json', JSON.stringify(state));
|
||||||
fs.writeFileSync(`${config.ROOT_FOLDER}/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], {});
|
runScript('/scripts/app.sh', ['stop', appName]);
|
||||||
process.spawnSync(appScript, ['start', 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 = readJsonFile('/state/apps.json');
|
||||||
const apps = JSON.parse(rawFile);
|
|
||||||
|
|
||||||
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;
|
|
@ -1,5 +1,5 @@
|
||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import { AppController } from '../controllers';
|
import AppController from './apps.controller';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
21
system-api/src/modules/fs/fs.helpers.ts
Normal file
21
system-api/src/modules/fs/fs.helpers.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import childProcess from 'child_process';
|
||||||
|
import config from '../../config';
|
||||||
|
|
||||||
|
export const getAbsolutePath = (path: string) => `${config.ROOT_FOLDER}${path}`;
|
||||||
|
|
||||||
|
export const readJsonFile = (path: string): any => {
|
||||||
|
const rawFile = fs.readFileSync(getAbsolutePath(path)).toString();
|
||||||
|
return JSON.parse(rawFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fileExists = (path: string): boolean => fs.existsSync(getAbsolutePath(path));
|
||||||
|
|
||||||
|
export const writeFile = (path: string, data: any) => fs.writeFileSync(getAbsolutePath(path), data);
|
||||||
|
|
||||||
|
export const createFolder = (path: string) => fs.mkdirSync(getAbsolutePath(path));
|
||||||
|
export const deleteFolder = (path: string) => fs.rmSync(getAbsolutePath(path), { recursive: true });
|
||||||
|
|
||||||
|
export const copyFile = (source: string, destination: string) => fs.copyFileSync(getAbsolutePath(source), getAbsolutePath(destination));
|
||||||
|
|
||||||
|
export const runScript = (path: string, args: string[]) => childProcess.spawnSync(getAbsolutePath(path), args, {});
|
19
system-api/src/modules/network/network.controller.ts
Normal file
19
system-api/src/modules/network/network.controller.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import publicIp from 'public-ip';
|
||||||
|
import portScanner from 'node-port-scanner';
|
||||||
|
|
||||||
|
const isPortOpen = async (req: Request, res: Response<boolean>) => {
|
||||||
|
const { port } = req.params;
|
||||||
|
|
||||||
|
const host = await publicIp.v4();
|
||||||
|
|
||||||
|
const isOpen = await portScanner(host, [port]);
|
||||||
|
|
||||||
|
res.status(200).send(isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const NetworkController = {
|
||||||
|
isPortOpen,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NetworkController;
|
|
@ -37,7 +37,7 @@ const getCpuInfo = async (req: Request, res: Response<CpuData>) => {
|
||||||
const getDiskInfo = async (req: Request, res: Response<DiskData>) => {
|
const getDiskInfo = async (req: Request, res: Response<DiskData>) => {
|
||||||
const disk = await si.fsSize();
|
const disk = await si.fsSize();
|
||||||
|
|
||||||
const rootDisk = disk.find(item => item.mount === '/');
|
const rootDisk = disk.find((item) => item.mount === '/');
|
||||||
|
|
||||||
if (!rootDisk) {
|
if (!rootDisk) {
|
||||||
throw new Error('Could not find root disk');
|
throw new Error('Could not find root disk');
|
10
system-api/src/modules/system/system.routes.ts
Normal file
10
system-api/src/modules/system/system.routes.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Router } from 'express';
|
||||||
|
import SystemController from './system.controller';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.route('/cpu').get(SystemController.getCpuInfo);
|
||||||
|
router.route('/disk').get(SystemController.getDiskInfo);
|
||||||
|
router.route('/memory').get(SystemController.getMemoryInfo);
|
||||||
|
|
||||||
|
export default router;
|
|
@ -1,2 +0,0 @@
|
||||||
export { default as systemRoutes } from './system.routes';
|
|
||||||
export { default as appRoutes } from './app.routes';
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { Router } from "express";
|
|
||||||
import { SystemController } from "../controllers";
|
|
||||||
|
|
||||||
const router = Router();
|
|
||||||
|
|
||||||
router.route("/cpu").get(SystemController.getCpuInfo);
|
|
||||||
router.route("/disk").get(SystemController.getDiskInfo);
|
|
||||||
router.route("/memory").get(SystemController.getMemoryInfo);
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -1,19 +1,20 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import compression from 'compression';
|
import compression from 'compression';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
import { __prod__ } from './constants/constants';
|
import { isProd } from './constants/constants';
|
||||||
import { appRoutes, systemRoutes } from './routes';
|
import appsRoutes from './modules/apps/apps.routes';
|
||||||
|
import systemRoutes from './modules/system/system.routes';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 3001;
|
const port = 3001;
|
||||||
|
|
||||||
if (__prod__) {
|
if (isProd) {
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use('/system', systemRoutes);
|
app.use('/system', systemRoutes);
|
||||||
app.use('/app', appRoutes);
|
app.use('/app', appsRoutes);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`System API listening on port ${port}`);
|
console.log(`System API listening on port ${port}`);
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"incremental": true
|
"incremental": true
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue