diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 98e6e53..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -// This configuration only applies to the package manager root. -/** @type {import("eslint").Linter.Config} */ -module.exports = { - ignorePatterns: ["apps/**", "packages/**"], - extends: ["@pm2.web/eslint-config/base.js"], -}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 780add8..47bd761 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,17 +14,21 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 11 - name: Setup Node.js environment uses: actions/setup-node@v4 with: - node-version: 20 - cache: 'npm' + node-version: 24 + cache: 'pnpm' - name: Install dependencies - run: npm install + run: pnpm install - name: Run lint - run: npm run lint + run: pnpm run lint - name: Run format - run: npm run format:check + run: pnpm run format:check test: runs-on: ubuntu-latest needs: analyze @@ -33,12 +37,26 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 11 - name: Setup Node.js environment uses: actions/setup-node@v4 with: - node-version: 20 - cache: 'npm' + node-version: 24 + cache: 'pnpm' - name: Install dependencies - run: npm install + run: pnpm install + - name: Install Playwright Browsers + working-directory: apps/dashboard + run: pnpm exec playwright install --with-deps - name: Run tests - run: npm test + run: pnpm test + - name: Upload Playwright Report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: apps/dashboard/playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index c1e96d3..e05134b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,10 @@ node_modules # Testing coverage +test-results/ +playwright-report/ +blob-report/ +playwright/.cache/ # Turbo .turbo diff --git a/README.md b/README.md index 33f59c9..3c6a35d 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,6 @@ Once pm2.web is installed and running, you can perform the following actions: ## Up Next -- E2E Tests for current functionality - Performance Improvements for Charts & Logs ## Contributing diff --git a/apps/backend/.eslintrc.js b/apps/backend/.eslintrc.js deleted file mode 100644 index da8914d..0000000 --- a/apps/backend/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ["@pm2.web/eslint-config/base.js"], -}; diff --git a/apps/backend/README.md b/apps/backend/README.md index 4c98b18..13afe8a 100644 --- a/apps/backend/README.md +++ b/apps/backend/README.md @@ -4,7 +4,7 @@ The Backend is a simple Node.js application that uses the pm2 BUS API to communi ## Prerequisites -- Node.js v18 +- Node.js v24 (LTS) - MongoDB Cluster - PM2 (installed globally) @@ -21,7 +21,7 @@ SERVER_NAME=used instead of the host name (optional) ```bash # run from the project root - npm install + pnpm install ``` 2. Create a `.env` file in the `apps/backend` directory and add the following variables @@ -36,7 +36,7 @@ You can start it using the following npm command: ```bash # run from the project root -npm run start:apps:backend +pnpm run start:apps:backend ``` To run the process in the background, you can use several tools such as PM2. @@ -47,5 +47,5 @@ This will start it using pm2. Furthermore, you can hide it from the process list ```bash # run from the project root -pm2 start npm --name "pm2.web-daemon" -- run "start:apps:backend" +pm2 start pnpm --name "pm2.web-daemon" -- run "start:apps:backend" ``` diff --git a/apps/backend/eslint.config.mjs b/apps/backend/eslint.config.mjs new file mode 100644 index 0000000..390e344 --- /dev/null +++ b/apps/backend/eslint.config.mjs @@ -0,0 +1,2 @@ +import baseConfig from "@pm2.web/eslint-config/eslint.config.mjs"; +export default baseConfig; diff --git a/apps/backend/package.json b/apps/backend/package.json index d96c14c..042e3c3 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -24,23 +24,26 @@ "format:check": "prettier --check \"**/*.{ts,json,css,scss,md}\"", "prestart": "tsc --build --clean && tsc", "watch": "tsc -w", - "dev": "npm run start" + "dev": "dotenv -v DB_URI=mongodb://127.0.0.1:27018/ -- pnpm run start", + "dev:pm2": "node scripts/start-pm2.mjs" }, "license": "LGPL-3.0-or-later", "dependencies": { - "@pm2.web/mongoose-models": "*", - "@pm2.web/typings": "*", - "bcrypt": "^5.1.1", + "@pm2.web/mongoose-models": "workspace:*", + "@pm2.web/typings": "workspace:*", + "bcrypt": "^6.0.0", "bytes-iec": "^3.1.1", - "dotenv": "^16.4.5", - "pm2": "^5.4.2", - "systeminformation": "^5.23.5" + "dotenv": "^17.4.2", + "mongoose": "^9.7.1", + "pm2": "^7.0.1", + "systeminformation": "^5.31.7" }, "devDependencies": { - "@pm2.web/eslint-config": "*", - "@pm2.web/typescript-config": "*", - "@types/bcrypt": "^5.0.2", - "eslint": "^8.57.1", - "typescript": "^5.6.2" + "@pm2.web/eslint-config": "workspace:*", + "@pm2.web/typescript-config": "workspace:*", + "@types/bcrypt": "^6.0.0", + "dotenv-cli": "^11.0.0", + "eslint": "^10.5.0", + "typescript": "^6.0.3" } } diff --git a/apps/backend/scripts/start-pm2.mjs b/apps/backend/scripts/start-pm2.mjs new file mode 100644 index 0000000..9a04d33 --- /dev/null +++ b/apps/backend/scripts/start-pm2.mjs @@ -0,0 +1,33 @@ +/* eslint-disable no-undef, unicorn/no-process-exit, no-empty */ +import { execSync } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const dummyScript = path.resolve( + __dirname, + "../../../scripts/dummy-process.js", +); + +console.log("Starting dummy PM2 process..."); +try { + execSync(`npx pm2 start ${dummyScript} --name dummy-app`, { + stdio: "inherit", + }); +} catch { + console.log("PM2 dummy-app might already be running or failed to start."); +} + +process.stdin.resume(); + +const cleanup = () => { + console.log(`\nShutting down dummy PM2 process...`); + try { + execSync(`npx pm2 delete dummy-app`, { stdio: "ignore" }); + execSync(`npx pm2 kill`, { stdio: "ignore" }); + } catch {} + process.exit(0); +}; + +process.on("SIGINT", cleanup); +process.on("SIGTERM", cleanup); diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index f8a1309..6687f03 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -2,6 +2,7 @@ "extends": "@pm2.web/typescript-config/base.json", "exclude": ["node_modules", "dist"], "compilerOptions": { + "rootDir": "./", "outDir": "./dist" } } diff --git a/apps/dashboard/.eslintrc.js b/apps/dashboard/.eslintrc.js deleted file mode 100644 index 19ecb99..0000000 --- a/apps/dashboard/.eslintrc.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @type {import('eslint').Linter.Config} - */ -module.exports = { - root: true, - extends: ["next", "plugin:@typescript-eslint/recommended", "plugin:unicorn/recommended", "prettier"], - plugins: ["simple-import-sort", "import"], - ignorePatterns: ["node_modules", "dist"], - rules: { - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "import/first": "error", - "import/newline-after-import": "error", - "import/no-duplicates": "error", - "unicorn/prevent-abbreviations": "off", - "unicorn/catch-error-name": "off", - "unicorn/no-null": "off", - "unicorn/prefer-module": "off", - "unicorn/filename-case": [ - "error", - { - cases: { - kebabCase: true, - pascalCase: true, - }, - }, - ], - }, - parserOptions: { - babelOptions: { - presets: [require.resolve("next/babel")], - }, - }, -}; diff --git a/apps/dashboard/README.md b/apps/dashboard/README.md index 4e49306..f50fc4a 100644 --- a/apps/dashboard/README.md +++ b/apps/dashboard/README.md @@ -6,7 +6,7 @@ The Dashboard is a Next.js application built on the t3 stack, utilizing trpc for ### Prerequisites -- Node v18 +- Node v24 (LTS) - MongoDB Cluster (required for Restart/Shutdown/Delete functionality) / MongoDB Atlas - Open Port 3000 or 80,443 (if you use a reverse proxy) @@ -24,7 +24,7 @@ NEXTAUTH_URL=http://localhost:3000 ```bash # from project root - npm install + pnpm install ``` 2. Create a `.env` file in the dashboard directory and add the following env variables @@ -38,7 +38,7 @@ NEXTAUTH_URL=http://localhost:3000 3. Build the frontend ```bash - npm run build:apps:dashboard + pnpm run build:apps:dashboard ``` ### Setup @@ -47,7 +47,7 @@ You can start it using the following npm command: ```bash # run from the project root -npm run start:apps:dashboard +pnpm run start:apps:dashboard ``` To run the process in the background, you can use several tools such as PM2. @@ -58,7 +58,7 @@ This will start it using pm2. Furthermore, you can hide it from the process list ```bash # run from the project root -pm2 start npm --name "pm2.web-dashboard" -- run "start:apps:dashboard" +pm2 start pnpm --name "pm2.web-dashboard" -- run "start:apps:dashboard" ``` ## Vercel & MongoDB Atlas diff --git a/apps/dashboard/components/misc/MultiSelect/CustomMultiSelect.tsx b/apps/dashboard/components/misc/MultiSelect/CustomMultiSelect.tsx index a27830f..a60f08d 100644 --- a/apps/dashboard/components/misc/MultiSelect/CustomMultiSelect.tsx +++ b/apps/dashboard/components/misc/MultiSelect/CustomMultiSelect.tsx @@ -1,6 +1,5 @@ /* eslint-disable unicorn/prefer-negative-index */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unused-vars */ + /* Source: https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/components/MultiSelect/MultiSelect.tsx */ import { @@ -30,8 +29,8 @@ export interface IItem { interface CustomMultiSelectProps extends MultiSelectProps { data: IItem[]; - pillComponent?: (item: IItem & any) => JSX.Element; - itemComponent?: (item: IItem & any) => JSX.Element; + pillComponent?: (item: IItem & any) => React.ReactNode; + itemComponent?: (item: IItem & any) => React.ReactNode; } const defaultProps: Partial = { @@ -47,8 +46,9 @@ type CustomMultiSelectFactory = { stylesNames: MultiSelectStylesNames; }; -export const CustomMultiSelect = factory((_props, ref) => { - const props = useProps("MultiSelect", defaultProps, _props); +export const CustomMultiSelect = factory((_props: any) => { + const ref = _props.ref; + const props = useProps("MultiSelect", defaultProps, _props) as CustomMultiSelectProps; const { classNames, className, @@ -280,7 +280,6 @@ export const CustomMultiSelect = factory((_props, ref) description={description} label={label} error={error} - multiline withErrorStyles={withErrorStyles} __stylesApiProps={{ ...props, @@ -298,13 +297,13 @@ export const CustomMultiSelect = factory((_props, ref) {values}