diff --git a/.erb/configs/webpack.config.backend.ts b/.erb/configs/webpack.config.backend.ts new file mode 100644 index 0000000..28d97b4 --- /dev/null +++ b/.erb/configs/webpack.config.backend.ts @@ -0,0 +1,75 @@ +/** + * Webpack config for production electron main process + */ + +import path from 'path'; +import webpack from 'webpack'; +import { merge } from 'webpack-merge'; +import TerserPlugin from 'terser-webpack-plugin'; +import baseConfig from './webpack.config.base'; +import webpackPaths from './webpack.paths'; +// import checkNodeEnv from '../scripts/check-node-env'; + +// checkNodeEnv('production'); + +const configuration: webpack.Configuration = { + target: 'node', + + mode: 'production', + + entry: { + main: path.join(webpackPaths.srcBackendPath, 'run.ts'), + }, + + output: { + path: webpackPaths.distBackendPath, + filename: '[name].js', + library: { + type: 'umd', + }, + }, + + optimization: { + minimizer: [ + new TerserPlugin({ + parallel: true, + }), + ], + }, + + plugins: [ + // new BundleAnalyzerPlugin({ + // analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', + // analyzerPort: 8888, + // }), + // /** + // * Create global constants which can be configured at compile time. + // * + // * Useful for allowing different behaviour between development builds and + // * release builds + // * + // * NODE_ENV should be production so that modules do not perform certain + // * development checks + // */ + // new webpack.EnvironmentPlugin({ + // NODE_ENV: 'production', + // DEBUG_PROD: false, + // START_MINIMIZED: false, + // }), + // new webpack.DefinePlugin({ + // 'process.type': '"browser"', + // }), + ], + + /** + * Disables webpack processing of __dirname and __filename. + * If you run the bundle in node.js it falls back to these values of node.js. + * https://github.com/webpack/webpack/issues/2010 + */ + node: { + __dirname: false, + __filename: false, + }, +}; + +export default merge(baseConfig, configuration); diff --git a/.erb/configs/webpack.paths.ts b/.erb/configs/webpack.paths.ts index e5ba573..0f4e42e 100644 --- a/.erb/configs/webpack.paths.ts +++ b/.erb/configs/webpack.paths.ts @@ -7,6 +7,7 @@ const dllPath = path.join(__dirname, '../dll'); const srcPath = path.join(rootPath, 'src'); const srcMainPath = path.join(srcPath, 'main'); const srcRendererPath = path.join(srcPath, 'renderer'); +const srcBackendPath = path.join(srcPath, 'backend'); const releasePath = path.join(rootPath, 'release'); const appPath = path.join(releasePath, 'app'); @@ -16,6 +17,7 @@ const srcNodeModulesPath = path.join(srcPath, 'node_modules'); const distPath = path.join(appPath, 'dist'); const distMainPath = path.join(distPath, 'main'); +const distBackendPath = path.join(rootPath, 'docker'); const distRendererPath = path.join(distPath, 'renderer'); const buildPath = path.join(releasePath, 'build'); @@ -35,4 +37,6 @@ export default { distMainPath, distRendererPath, buildPath, + srcBackendPath, + distBackendPath, }; diff --git a/.gitignore b/.gitignore index 34868ed..b4c4921 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ npm-debug.log.* *.scss.d.ts mock-data-test +docker/* +!.package.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..599f22a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM timbru31/node-alpine-git + +COPY docker/* . + +EXPOSE 1512 3000-3050 + +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/README.md b/README.md index 0d7cbe3..a926e7e 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,14 @@ To apply a preset: 1. Select the desired preset from the list. 2. Apply the preset to update all active responses. +## Using Docker + +Folder contains scripts to run the project as a standalone server. + +npm run build:backend to build teh JS files required for the standalone server. + +Then use docker build . -t mockingbird to create the image. + ## Licensing Mockingbird is dual-licensed under the GNU Affero General Public License v3.0 (AGPLv3) for open-source use and a commercial license for proprietary use. For more details, see [LICENSE](./LICENSE) and [COMMERCIAL_LICENSE](./COMMERCIAL_LICENSE). diff --git a/docker/package.json b/docker/package.json new file mode 100644 index 0000000..28fe8cb --- /dev/null +++ b/docker/package.json @@ -0,0 +1,12 @@ +{ + "name": "dist", + "version": "1.0.0", + "description": "", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node main.js" + }, + "author": "", + "license": "ISC" +} diff --git a/package-lock.json b/package-lock.json index d3663c4..59fcbff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -107,6 +107,7 @@ "electron-builder": "^24.6.4", "electron-devtools-installer": "^3.2.0", "electronmon": "^2.0.2", + "esbuild": "^0.23.0", "eslint": "^8.49.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-erb": "^4.1.0-0", @@ -132,6 +133,7 @@ "rimraf": "^5.0.1", "sass": "^1.67.0", "sass-loader": "^13.3.2", + "shx": "^0.3.4", "style-loader": "^3.3.3", "terser-webpack-plugin": "^5.3.9", "ts-jest": "^29.1.1", @@ -2629,6 +2631,390 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -10386,6 +10772,45 @@ "dev": true, "optional": true }, + "node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -19014,6 +19439,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs/node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/shx": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", + "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", + "dev": true, + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", diff --git a/package.json b/package.json index 26ba448..a72f567 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "build:dll": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts", "build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts", "build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts", + "build:backend": "npm run generateSwagger && cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.backend.ts", "postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && npm run build:dll", "lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx", "package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never && npm run build:dll", @@ -33,6 +34,7 @@ "start:main": "cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only .", "start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts", "start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts", + "start:backend": "ts-node ./src/backend/run.ts", "generateSwagger": "ts-node src/backend/swagger.ts", "test": "jest" }, @@ -183,6 +185,8 @@ "electron-builder": "^24.6.4", "electron-devtools-installer": "^3.2.0", "electronmon": "^2.0.2", + "esbuild": "^0.23.0", + "esbuild-plugin-copy": "^2.1.1", "eslint": "^8.49.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-erb": "^4.1.0-0", @@ -208,6 +212,7 @@ "rimraf": "^5.0.1", "sass": "^1.67.0", "sass-loader": "^13.3.2", + "shx": "^0.3.4", "style-loader": "^3.3.3", "terser-webpack-plugin": "^5.3.9", "ts-jest": "^29.1.1", diff --git a/src/backend/actions/general.ts b/src/backend/actions/general.ts index e69de29..3d1fe04 100644 --- a/src/backend/actions/general.ts +++ b/src/backend/actions/general.ts @@ -0,0 +1,84 @@ +import { v4 as uuid } from 'uuid'; +import { execSync } from 'child_process'; +import path from 'path'; +import { Socket, Server as SocketIOServer } from 'socket.io'; +import { + addCredentialsToGitURI, + createExampleProject, + createProjectPath, + getProjectsNameList, + isDirectoryEmpty, + readAppSettings, + updateAppSettings, + verifyProjectFoldersExist, +} from '../utils'; +import { EVENT_KEYS } from '../../types/events'; +import { updateClientProjectData } from '../utils/events'; +import { emitSocketMessage } from '../socket'; + +type CreateProjectType = { + sshUrl?: string; + httpsUrl?: string; + cloneType: 'OPEN' | 'SSH' | 'HTTPS' | 'LOCAL'; + username?: string; + password?: string; + projectName: string; + directoryPath?: string; +}; + +export const createProject = async ( + socket: Socket | SocketIOServer, + { + sshUrl, + httpsUrl, + cloneType, + username, + password, + projectName, + directoryPath, + }: CreateProjectType, +) => { + await verifyProjectFoldersExist(); + + const projectPath = + cloneType === 'OPEN' ? directoryPath || '' : createProjectPath(projectName); + + const appSettings = await readAppSettings(); + const newProject = { + id: uuid(), + name: projectName, + directoryPath: projectPath, + }; + await updateAppSettings({ + ...appSettings, + projects: [...(appSettings.projects || []), newProject], + }); + + if (cloneType === 'SSH') { + await execSync(`git clone ${sshUrl} ${projectPath}`, { + stdio: [0, 1, 2], // we need this so node will print the command output + cwd: path.resolve('', ''), // path to where you want to save the file + }); + } else if (cloneType === 'HTTPS' && httpsUrl && username && password) { + const updatedURI = addCredentialsToGitURI(httpsUrl, username, password); + + await execSync(`git clone ${updatedURI} ${projectPath}`, { + stdio: [0, 1, 2], // we need this so node will print the command output + cwd: path.resolve('', ''), // path to where you want to save the file + }); + } + + if (await isDirectoryEmpty(projectName)) { + await createExampleProject(projectName); + } + + const newProjectsNameList = await getProjectsNameList(); + + await updateClientProjectData(socket, projectName); + + emitSocketMessage(socket, EVENT_KEYS.CREATE_PROJECT, { + success: true, + newProjectsNameList, + newProjectName: projectName, + }); +}; diff --git a/src/backend/events/general.ts b/src/backend/events/general.ts index 298eab1..be13a3b 100644 --- a/src/backend/events/general.ts +++ b/src/backend/events/general.ts @@ -1,17 +1,11 @@ -import { v4 as uuid } from 'uuid'; import { execSync } from 'child_process'; -import path from 'path'; import { Socket } from 'socket.io'; import { checkIServerUp, closeProjectServers } from '../server'; import { activateProgram, - addCredentialsToGitURI, - createExampleProject, - createProjectPath, getActiveProjectName, getProjectPath, getProjectsNameList, - isDirectoryEmpty, readAppSettings, updateAppSettings, verifyProjectFoldersExist, @@ -20,6 +14,7 @@ import { generateUniqueIdentifier } from '../utils/utils'; import { EVENT_KEYS } from '../../types/events'; import { updateClientProjectData } from '../utils/events'; import { emitSocketMessage } from '../socket'; +import { createProject } from '../actions/general'; export const generalEvents = (socket: Socket) => { socket.on(EVENT_KEYS.INIT, async () => { @@ -68,48 +63,15 @@ export const generalEvents = (socket: Socket) => { projectName, directoryPath, } = arg; - await verifyProjectFoldersExist(); - - const projectPath = - cloneType === 'OPEN' ? directoryPath : createProjectPath(projectName); - - const appSettings = await readAppSettings(); - const newProject = { - id: uuid(), - name: projectName, - directoryPath: projectPath, - }; - await updateAppSettings({ - ...appSettings, - projects: [...(appSettings.projects || []), newProject], - }); - - if (cloneType === 'SSH') { - await execSync(`git clone ${sshUrl} ${projectPath}`, { - stdio: [0, 1, 2], // we need this so node will print the command output - cwd: path.resolve('', ''), // path to where you want to save the file - }); - } else if (cloneType === 'HTTPS') { - const updatedURI = addCredentialsToGitURI(httpsUrl, username, password); - - await execSync(`git clone ${updatedURI} ${projectPath}`, { - stdio: [0, 1, 2], // we need this so node will print the command output - cwd: path.resolve('', ''), // path to where you want to save the file - }); - } - - if (await isDirectoryEmpty(projectName)) { - await createExampleProject(projectName); - } - - const newProjectsNameList = await getProjectsNameList(); - - await updateClientProjectData(socket, projectName); - emitSocketMessage(socket, EVENT_KEYS.CREATE_PROJECT, { - success: true, - newProjectsNameList, - newProjectName: projectName, + createProject(socket, { + sshUrl, + httpsUrl, + cloneType, + username, + password, + projectName, + directoryPath, }); } catch (error) { console.log('Error create project ', error); diff --git a/src/backend/internalServer.ts b/src/backend/internalServer.ts index fadf545..c0b5a15 100644 --- a/src/backend/internalServer.ts +++ b/src/backend/internalServer.ts @@ -1,14 +1,15 @@ import { getCurrentIPAddresses } from './utils'; import { server } from './app'; -export const startInternalServer = () => { +export const startInternalServer = (port?: number) => { const iPAddresses = getCurrentIPAddresses(); const host = iPAddresses[0]; + const _port = port ?? 1511; try { - server.listen(1511, () => { + server.listen(_port, () => { console.log( - `*****internal server is up listening on http://${host}:1511/`, + `*****internal server is up listening on http://${host}:${_port}/`, ); }); } catch (error) { diff --git a/src/backend/managers/projectsManager.ts b/src/backend/managers/projectsManager.ts index 62c3330..3f0bee5 100644 --- a/src/backend/managers/projectsManager.ts +++ b/src/backend/managers/projectsManager.ts @@ -1,4 +1,4 @@ -import chokidar from 'chokidar'; +// import chokidar from 'chokidar'; import path from 'path'; import { SUPPORTED_PROJECT_DATA_VERSION } from '../../consts'; import { @@ -56,38 +56,35 @@ class ProjectsManager { // socketIo.emit('reload', {projectName}); } + // eslint-disable-next-line class-methods-use-this private async watchProjectFolder(projectName: string) { const projectPath = await getProjectPath(projectName); - const watcher = chokidar.watch(projectPath, { - // eslint-disable-next-line no-useless-escape - ignored: /(^|[\/\\])\.git/, - persistent: true, - ignoreInitial: true, - depth: 99, // Adjust depth as needed - }); - - watcher - .on('add', (filePath) => - this.handleProjectDataChanged(projectName, 'File added', filePath), - ) - .on('change', (filePath) => - this.handleProjectDataChanged(projectName, 'File changed', filePath), - ) - .on('unlink', (filePath) => - this.handleProjectDataChanged(projectName, 'File removed', filePath), - ) - .on('addDir', (dirPath) => - this.handleProjectDataChanged(projectName, 'Directory added', dirPath), - ) - .on('unlinkDir', (dirPath) => - this.handleProjectDataChanged( - projectName, - 'Directory removed', - dirPath, - ), - ) - .on('error', (error) => console.error('Error watching files:', error)); + // const watcher = chokidar.watch(projectPath, { + // eslint-disable-next-line no-useless-escape + // ignored: /(^|[\/\\])\.git/, + // persistent: true, + // ignoreInitial: true, + // depth: 99, // Adjust depth as needed + // }); + + // watcher + // .on('add', (filePath) => + // this.handleProjectDataChanged(projectName, 'File added', filePath), + // .on('change', (filePath) => + // this.handleProjectDataChanged(projectName, 'File changed', filePath), + // .on('unlink', (filePath) => + // this.handleProjectDataChanged(projectName, 'File removed', filePath), + // .on('addDir', (dirPath) => + // this.handleProjectDataChanged(projectName, 'Directory added', dirPath), + // .on('unlinkDir', (dirPath) => + // this.handleProjectDataChanged( + // projectName, + // 'Directory removed', + // dirPath, + // ), + // ) + // .on('error', (error) => console.error('Error watching files:', error)); console.log(`---Watching changes for ${projectName} in: ${projectPath}`); } diff --git a/src/backend/routers/actions.ts b/src/backend/routers/actions.ts deleted file mode 100644 index d99d899..0000000 --- a/src/backend/routers/actions.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Router, Request, Response } from 'express'; -import { - handleCloseServer, - handleRestartServer, - handleStartServer, -} from '../actions'; -import { logger } from '../utils/logger'; - -const actionsRouter = Router(); - -actionsRouter.post('/server/start', async (req: Request, res: Response) => { - try { - const { projectName } = req.body; - - logger('start server', { projectName }); - - const { host } = await handleStartServer(projectName); - - res.status(200).send({ success: true, host }); - } catch (error: any) { - console.log(error); - logger('Error start server', error?.message); - - res.status(500).send({ success: false, message: 'fail to start server' }); - } -}); - -actionsRouter.post('/server/close', async (req: Request, res: Response) => { - try { - logger('close server'); - - handleCloseServer(); - - res.status(200).send({ success: true }); - } catch (error: any) { - console.log(error); - logger('Error close server', error?.message); - - res.status(500).send({ success: false, message: 'fail to close server' }); - } -}); - -actionsRouter.post('/server/restart', async (req: Request, res: Response) => { - try { - const { projectName } = req.body; - - logger('restart server', { projectName }); - - handleRestartServer(projectName); - - res.status(200).send({ success: true }); - } catch (error: any) { - console.log(error); - logger('Error restart server', error?.message); - - res.status(500).send({ success: false, message: 'fail to restart server' }); - } -}); - -export { actionsRouter }; diff --git a/src/backend/routers/index.ts b/src/backend/routers/index.ts index c91b8dd..496c51d 100644 --- a/src/backend/routers/index.ts +++ b/src/backend/routers/index.ts @@ -4,15 +4,15 @@ import { routesRouter } from './routes'; import { serverRouter } from './server'; import { parentsRouter } from './parents'; import { appDataRouter } from './appData'; -import { actionsRouter } from './actions'; +import { projectRouter } from './project'; import { serverLogsRouter } from './serverLogs'; export const routes = express.Router(); -routes.use('/actions', actionsRouter); routes.use('/presets', presetsRouter); routes.use('/routes', routesRouter); routes.use('/servers', serverRouter); routes.use('/parents', parentsRouter); +routes.use('/project', projectRouter); routes.use('/app-data', appDataRouter); routes.use('/server-logs', serverLogsRouter); diff --git a/src/backend/routers/project.ts b/src/backend/routers/project.ts new file mode 100644 index 0000000..7877998 --- /dev/null +++ b/src/backend/routers/project.ts @@ -0,0 +1,95 @@ +import { Router, Request, Response } from 'express'; +import { logger } from '../utils/logger'; +import { getGlobalSocketIo } from '../socket'; +import { createProject } from '../actions/general'; + +const projectRouter = Router(); + +projectRouter.post('/clone/ssh', async (req: Request, res: Response) => { + try { + const { projectName, sshUrl } = req.body; + + const socket = getGlobalSocketIo(); + + await createProject(socket, { + projectName, + sshUrl, + cloneType: 'SSH', + }); + + res.status(200).send({ success: true }); + } catch (error: any) { + console.log(error); + logger('Error restart server', error?.message); + + res + .status(500) + .send({ success: false, message: 'fail to ssh clone the project' }); + } +}); + +projectRouter.post('/clone/https', async (req: Request, res: Response) => { + try { + const { projectName, httpsUrl, username, password } = req.body; + + const socket = getGlobalSocketIo(); + + await createProject(socket, { + projectName, + httpsUrl, + username, + password, + cloneType: 'HTTPS', + }); + + res.status(200).send({ success: true }); + } catch (error: any) { + console.log(error); + logger('Error restart server', error?.message); + + res.status(500).send({ success: false, message: 'fail to restart server' }); + } +}); + +projectRouter.post('/open', async (req: Request, res: Response) => { + try { + const { projectName, directoryPath } = req.body; + + const socket = getGlobalSocketIo(); + + await createProject(socket, { + projectName, + directoryPath, + cloneType: 'OPEN', + }); + + res.status(200).send({ success: true }); + } catch (error: any) { + console.log(error); + logger('Error restart server', error?.message); + + res.status(500).send({ success: false, message: 'fail to restart server' }); + } +}); + +projectRouter.post('/create', async (req: Request, res: Response) => { + try { + const { projectName } = req.body; + + const socket = getGlobalSocketIo(); + + await createProject(socket, { + projectName, + cloneType: 'LOCAL', + }); + + res.status(200).send({ success: true }); + } catch (error: any) { + console.log(error); + logger('Error restart server', error?.message); + + res.status(500).send({ success: false, message: 'fail to restart server' }); + } +}); + +export { projectRouter }; diff --git a/src/backend/routers/server.ts b/src/backend/routers/server.ts index bc59466..70b1cfc 100644 --- a/src/backend/routers/server.ts +++ b/src/backend/routers/server.ts @@ -2,6 +2,11 @@ import { Router, Request, Response } from 'express'; import { ProjectServer, RouteParentHash } from '../../types'; import { projectsManager } from '../managers'; import { logger } from '../utils/logger'; +import { + handleCloseServer, + handleRestartServer, + handleStartServer, +} from '../actions'; const serverRouter = Router(); @@ -46,4 +51,53 @@ serverRouter.get( }, ); +serverRouter.post('/start', async (req: Request, res: Response) => { + try { + const { projectName } = req.body; + + logger('start server', { projectName }); + + const { host } = await handleStartServer(projectName); + + res.status(200).send({ success: true, host }); + } catch (error: any) { + console.log(error); + logger('Error start server', error?.message); + + res.status(500).send({ success: false, message: 'fail to start server' }); + } +}); + +serverRouter.post('/close', async (req: Request, res: Response) => { + try { + logger('close server'); + + handleCloseServer(); + + res.status(200).send({ success: true }); + } catch (error: any) { + console.log(error); + logger('Error close server', error?.message); + + res.status(500).send({ success: false, message: 'fail to close server' }); + } +}); + +serverRouter.post('/restart', async (req: Request, res: Response) => { + try { + const { projectName } = req.body; + + logger('restart server', { projectName }); + + handleRestartServer(projectName); + + res.status(200).send({ success: true }); + } catch (error: any) { + console.log(error); + logger('Error restart server', error?.message); + + res.status(500).send({ success: false, message: 'fail to restart server' }); + } +}); + export { serverRouter }; diff --git a/src/backend/run.ts b/src/backend/run.ts new file mode 100644 index 0000000..c58de0a --- /dev/null +++ b/src/backend/run.ts @@ -0,0 +1,3 @@ +import { startInternalServer } from './index'; + +startInternalServer(1512); diff --git a/src/backend/socket.ts b/src/backend/socket.ts index 0122a0a..aa85824 100644 --- a/src/backend/socket.ts +++ b/src/backend/socket.ts @@ -18,15 +18,15 @@ export const initSocketIO = (server: HttpServer) => { return socketIo; }; -// export const getSocketIo = () => { -// if (!socketIo) { -// throw new Error('Socket.io not initialized!'); -// } -// return socketIo; -// }; +export const getGlobalSocketIo = () => { + if (!socketIo) { + throw new Error('Socket.io not initialized!'); + } + return socketIo; +}; export const emitSocketMessage = ( - socket: Socket, + socket: Socket | SocketIOServer, key: EVENT_KEYS, args: any, ) => { diff --git a/src/backend/swagger.ts b/src/backend/swagger.ts index d87ed33..5d46786 100644 --- a/src/backend/swagger.ts +++ b/src/backend/swagger.ts @@ -2,14 +2,14 @@ import swaggerAutogen from 'swagger-autogen'; const doc = { info: { - version: 'v1.0.0', + version: 'v0.0.6', title: 'Mockingbird API documentation', description: 'A list of an api endpoints to interact with Mockingbird using api calls', }, servers: [ { - url: 'http://localhost:1511', + url: 'http://localhost:1512', description: '', }, ], diff --git a/src/backend/swagger_output.json b/src/backend/swagger_output.json index b6651fe..1f5fa40 100644 --- a/src/backend/swagger_output.json +++ b/src/backend/swagger_output.json @@ -1,84 +1,17 @@ { "openapi": "3.0.0", "info": { - "version": "v1.0.0", + "version": "v0.0.6", "title": "Mockingbird API documentation", "description": "A list of an api endpoints to interact with Mockingbird using api calls" }, "servers": [ { - "url": "http://localhost:1511", + "url": "http://localhost:1512", "description": "" } ], "paths": { - "/actions/server/start": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "500": { - "description": "Internal Server Error" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "projectName": { - "example": "any" - } - } - } - } - } - } - } - }, - "/actions/server/close": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/actions/server/restart": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "500": { - "description": "Internal Server Error" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "projectName": { - "example": "any" - } - } - } - } - } - } - } - }, "/presets/{projectName}/{presetFolderName}/{presetName}/apply": { "get": { "description": "", @@ -202,6 +135,73 @@ } } }, + "/servers/start": { + "post": { + "description": "", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "projectName": { + "example": "any" + } + } + } + } + } + } + } + }, + "/servers/close": { + "post": { + "description": "", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/servers/restart": { + "post": { + "description": "", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "projectName": { + "example": "any" + } + } + } + } + } + } + } + }, "/parents/{projectName}/{serverName}/{parentId}": { "get": { "description": "", @@ -244,6 +244,129 @@ } } }, + "/project/clone/ssh": { + "post": { + "description": "", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "projectName": { + "example": "any" + }, + "sshUrl": { + "example": "any" + } + } + } + } + } + } + } + }, + "/project/clone/https": { + "post": { + "description": "", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "projectName": { + "example": "any" + }, + "httpsUrl": { + "example": "any" + }, + "username": { + "example": "any" + }, + "password": { + "example": "any" + } + } + } + } + } + } + } + }, + "/project/open": { + "post": { + "description": "", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "projectName": { + "example": "any" + }, + "directoryPath": { + "example": "any" + } + } + } + } + } + } + } + }, + "/project/create": { + "post": { + "description": "", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "projectName": { + "example": "any" + } + } + } + } + } + } + } + }, "/app-data/{projectName}": { "get": { "description": "", diff --git a/src/backend/utils/events.ts b/src/backend/utils/events.ts index f8e5eb9..c429701 100644 --- a/src/backend/utils/events.ts +++ b/src/backend/utils/events.ts @@ -1,4 +1,4 @@ -import { Socket } from 'socket.io'; +import { Socket, Server as SocketIOServer } from 'socket.io'; import { SUPPORTED_PROJECT_DATA_VERSION } from '../../consts'; import { checkIsGitInit, @@ -15,7 +15,7 @@ import { getProjectServers, isFirstVersionGreater } from './general'; import { getProjectPresets } from './files'; export const updateClientProjectData = async ( - socket: Socket, + socket: Socket | SocketIOServer, projectName: string, ) => { emitSocketMessage(socket, EVENT_KEYS.IS_LOADING_DATA, {}); diff --git a/src/backend/utils/files.ts b/src/backend/utils/files.ts index b26227b..8a4e101 100644 --- a/src/backend/utils/files.ts +++ b/src/backend/utils/files.ts @@ -19,6 +19,7 @@ import { projectsPath, DEFAULT_PROJECT_SETTINGS, SUPPORTED_PROJECT_DATA_VERSION, + mainFolderPath, } from '../../consts'; import { listToHashmap, replaceUndefined } from './utils'; @@ -76,9 +77,23 @@ function formatFolderName(input: string): string { return formattedName; } +export const verifyProjectFoldersExist = () => { + if (!fs.existsSync(mainFolderPath)) { + fs.mkdirSync(mainFolderPath); + } + if (!fs.existsSync(projectsPath)) { + fs.mkdirSync(projectsPath); + } + if (!fs.existsSync(appSettingsFolder)) { + fs.mkdirSync(appSettingsFolder); + } +}; + export const createProjectPath = (projectName: string) => { const projectPath = `${projectsPath + formatFolderName(projectName + new Date().getTime())}/`; + verifyProjectFoldersExist(); + if (!fs.existsSync(projectsPath)) { fs.mkdirSync(projectsPath); } diff --git a/src/backend/utils/general.ts b/src/backend/utils/general.ts index 29f5f2e..0cf5f09 100644 --- a/src/backend/utils/general.ts +++ b/src/backend/utils/general.ts @@ -1,13 +1,6 @@ -import fs from 'fs'; import os from 'os'; import jwt from 'jsonwebtoken'; -import { - mainFolderPath, - projectsPath, - appSettingsFolder, - SECRET_KEY, - DEFAULT_SERVER_SETTINGS, -} from '../../consts'; +import { SECRET_KEY, DEFAULT_SERVER_SETTINGS } from '../../consts'; import { getProjectPath, getProjectServersNameList, @@ -24,6 +17,7 @@ import { updatePresetFile, updateRouteParentFile, updateServerSettings, + verifyProjectFoldersExist, } from './files'; import pjson from '../../../package.json'; import { @@ -60,24 +54,12 @@ export const addCredentialsToGitURI = ( return updatedURI; }; -export const verifyProjectFoldersExist = () => { - if (!fs.existsSync(mainFolderPath)) { - fs.mkdirSync(mainFolderPath); - } - if (!fs.existsSync(projectsPath)) { - fs.mkdirSync(projectsPath); - } - if (!fs.existsSync(appSettingsFolder)) { - fs.mkdirSync(appSettingsFolder); - } -}; - export const verifyProjectFolderExist = async (repoFolderName: string) => { + verifyProjectFoldersExist(); + await getProjectPath(repoFolderName); await getServersFolderPath(repoFolderName); await getProjectSettingsPath(repoFolderName); - - verifyProjectFoldersExist(); }; export const verifyServerFoldersExist = ( diff --git a/src/renderer/components/logRow/restLogRow/restLogRow.tsx b/src/renderer/components/logRow/restLogRow/restLogRow.tsx index 7ad55d9..6759b1f 100644 --- a/src/renderer/components/logRow/restLogRow/restLogRow.tsx +++ b/src/renderer/components/logRow/restLogRow/restLogRow.tsx @@ -139,7 +139,7 @@ export function RestLogRow({ const matchedParents = useMemo( () => - Object.values(routesParents)?.filter((parent) => + Object.values(routesParents || {}).filter((parent) => requestUrl.includes(parent.path), ), [routesParents, requestUrl],