fix(dashboard): Phase 5 P0 — correct CI workspace path + real ESLint
- ci.yml: actions/checkout into the runner workspace instead of cd-ing into a
hard-coded host path and `git reset --hard origin/main` on the live checkout;
install via `pnpm install:gitea` (self-contained, no sibling common-plat
checkout); E2E step left as a TODO pointer (ci-e2e-hardening, Phase 5 P2).
- Fix the same stale /opt/bytelyst/bytelyst-devops-tools path in deploy.sh,
scripts/deploy-hotcopy.sh, DEPLOYMENT.md, DEPLOYMENT_GUIDE.md.
- Replace the no-op `lint` echoes with real ESLint 9 flat configs (js +
typescript-eslint recommended) for backend and web; add a root `pnpm lint`.
- Fix the 10 errors lint surfaced, incl. require('os') in an ESM backend
(system/repository.ts -> import * as os), prefer-const x4, and a ternary
expression-statement in web vm/page.tsx.
Verified locally: secret-scan, lint (0 errors; correctly fails on bad code),
typecheck, unit tests (backend 9 / web 11), and build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
51e4c5f271
commit
3ee4e7104e
@ -11,74 +11,72 @@ on:
|
|||||||
- 'pnpm-lock.yaml'
|
- 'pnpm-lock.yaml'
|
||||||
- 'pnpm-workspace.yaml'
|
- 'pnpm-workspace.yaml'
|
||||||
- '.pnpmfile.cjs'
|
- '.pnpmfile.cjs'
|
||||||
|
- '.gitea/workflows/ci.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'backend/**'
|
||||||
|
- 'web/**'
|
||||||
|
- 'shared/**'
|
||||||
|
- 'package.json'
|
||||||
|
- 'pnpm-lock.yaml'
|
||||||
|
- 'pnpm-workspace.yaml'
|
||||||
|
- '.pnpmfile.cjs'
|
||||||
|
- '.gitea/workflows/ci.yml'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ci-devops-dashboard-${{ github.ref }}
|
group: ci-devops-dashboard-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Self-contained CI: resolve @bytelyst/* deps from the local Gitea registry
|
||||||
|
# rather than a sibling learning_ai_common_plat checkout on the runner.
|
||||||
|
BYTELYST_PACKAGE_SOURCE: gitea
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-test:
|
build-and-test:
|
||||||
name: Build, Test & Typecheck
|
name: Build, Test & Typecheck
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- name: Pull latest
|
# Check out into the runner workspace (${{ gitea.workspace }}) instead of
|
||||||
|
# cd-ing into a hard-coded host path and `git reset --hard` on the live
|
||||||
|
# checkout. CI must never mutate an operator's working tree.
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
|
- name: Enable pnpm
|
||||||
run: |
|
run: |
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
corepack enable
|
||||||
git fetch origin main
|
corepack prepare pnpm@10.6.5 --activate
|
||||||
git checkout main
|
|
||||||
git reset --hard origin/main
|
|
||||||
|
|
||||||
- name: Secret scan
|
- name: Secret scan
|
||||||
run: |
|
run: pnpm secret-scan
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm secret-scan
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: pnpm install:gitea
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm install:common-plat
|
|
||||||
|
|
||||||
- name: Build backend
|
|
||||||
run: |
|
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-backend build
|
|
||||||
|
|
||||||
- name: Build web
|
|
||||||
run: |
|
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-web build
|
|
||||||
|
|
||||||
- name: Typecheck backend
|
|
||||||
run: |
|
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-backend typecheck
|
|
||||||
|
|
||||||
- name: Typecheck web
|
|
||||||
run: |
|
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-web typecheck
|
|
||||||
|
|
||||||
- name: Test backend
|
|
||||||
run: |
|
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-backend test:run
|
|
||||||
|
|
||||||
- name: Test web
|
|
||||||
run: |
|
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-web test:run
|
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: |
|
run: pnpm lint
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-backend lint
|
|
||||||
pnpm --filter @bytelyst/devops-web lint
|
|
||||||
|
|
||||||
- name: E2E tests
|
- name: Typecheck
|
||||||
run: |
|
run: pnpm typecheck
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
pnpm --filter @bytelyst/devops-web test:e2e
|
- name: Build
|
||||||
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Unit tests
|
||||||
|
run: pnpm test:run
|
||||||
|
|
||||||
|
# TODO(ci-e2e-hardening): Playwright E2E needs a started stack + ops-API
|
||||||
|
# interception before it can run deterministically in CI. Tracked in
|
||||||
|
# docs/prompts/ci-e2e-hardening.md (Phase 5 P2). Re-enable once wired.
|
||||||
|
# - name: E2E tests
|
||||||
|
# run: pnpm --filter @bytelyst/devops-web test:e2e
|
||||||
|
|
||||||
docker-build:
|
docker-build:
|
||||||
name: Build Docker Images
|
name: Build Docker Images
|
||||||
@ -86,26 +84,17 @@ jobs:
|
|||||||
needs: [build-and-test]
|
needs: [build-and-test]
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
- name: Pull latest
|
- name: Checkout
|
||||||
run: |
|
uses: actions/checkout@v4
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
git fetch origin main
|
|
||||||
git checkout main
|
|
||||||
git reset --hard origin/main
|
|
||||||
|
|
||||||
- name: Build backend Docker image
|
- name: Build backend Docker image
|
||||||
run: |
|
run: docker build -f backend/Dockerfile -t devops-backend:latest .
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
docker build -f backend/Dockerfile -t devops-backend:latest .
|
|
||||||
|
|
||||||
- name: Build web Docker image
|
- name: Build web Docker image
|
||||||
run: |
|
run: docker build -f web/Dockerfile -t devops-web:latest .
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
docker build -f web/Dockerfile -t devops-web:latest .
|
|
||||||
|
|
||||||
- name: Test Docker Compose
|
- name: Test Docker Compose
|
||||||
run: |
|
run: |
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
sleep 10
|
sleep 10
|
||||||
docker compose down
|
docker compose down
|
||||||
|
|||||||
@ -25,7 +25,7 @@ The dashboard currently depends on workspace packages from `learning_ai_common_p
|
|||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
```bash
|
```bash
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard
|
||||||
|
|
||||||
# Install dependencies with common platform
|
# Install dependencies with common platform
|
||||||
pnpm install:common-plat
|
pnpm install:common-plat
|
||||||
@ -57,13 +57,13 @@ CSRF_SECRET=your-csrf-secret-change-in-production
|
|||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
```bash
|
```bash
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard/backend
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard/backend
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
npm start
|
npm start
|
||||||
|
|
||||||
# In another terminal:
|
# In another terminal:
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard/web
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard/web
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
npm start
|
npm start
|
||||||
|
|||||||
@ -50,7 +50,7 @@ docker-compose up -d
|
|||||||
### 2. Deploy Dashboards
|
### 2. Deploy Dashboards
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard
|
||||||
./deploy.sh
|
./deploy.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ This will:
|
|||||||
### Deploy DevOps Dashboard
|
### Deploy DevOps Dashboard
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard
|
||||||
docker-compose up -d --build
|
docker-compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ Monitor these through:
|
|||||||
|
|
||||||
### Stop Services
|
### Stop Services
|
||||||
```bash
|
```bash
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard
|
||||||
docker-compose down
|
docker-compose down
|
||||||
|
|
||||||
cd /opt/bytelyst/learning_ai_common_plat
|
cd /opt/bytelyst/learning_ai_common_plat
|
||||||
@ -305,7 +305,7 @@ docker-compose stop admin-web
|
|||||||
|
|
||||||
### Restart Services
|
### Restart Services
|
||||||
```bash
|
```bash
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard
|
||||||
docker-compose restart
|
docker-compose restart
|
||||||
|
|
||||||
cd /opt/bytelyst/learning_ai_common_plat
|
cd /opt/bytelyst/learning_ai_common_plat
|
||||||
|
|||||||
96
dashboard/REVIEW_ACTIONS.md
Normal file
96
dashboard/REVIEW_ACTIONS.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Dashboard Repo Review — Top Actions
|
||||||
|
|
||||||
|
Reviewed: 2026-05-27. Scope: `/opt/bytelyst/learning_ai_devops_tools/dashboard` (the ByteLyst DevOps Dashboard pnpm workspace: `backend/` Fastify 5 + `web/` Next.js 16).
|
||||||
|
|
||||||
|
Baseline state (verified during review):
|
||||||
|
- `pnpm typecheck` — passes for both backend and web.
|
||||||
|
- `pnpm test:run` — passes (backend 9 tests / 1 file, web 11 tests / 2 files).
|
||||||
|
- `pnpm secret-scan` — clean.
|
||||||
|
- `.env` is gitignored; only `.env.example` files are tracked.
|
||||||
|
|
||||||
|
The dashboard is functional and well-structured, but several issues block CI, hide regressions, and create operational risk. Actions are ordered by priority.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0 — Broken / Urgent
|
||||||
|
|
||||||
|
### 1. CI workflow points at a non-existent path
|
||||||
|
`.gitea/workflows/ci.yml` runs everything from `/opt/bytelyst/bytelyst-devops-tools/dashboard`, but the actual checkout lives at `/opt/bytelyst/learning_ai_devops_tools/dashboard`. The same wrong path is hard-coded in `DEPLOYMENT.md` and `scripts/deploy-hotcopy.sh`.
|
||||||
|
|
||||||
|
- Action: replace the hard-coded path with `${{ gitea.workspace }}` (or a single `WORKDIR` env var) in <ref_file file="/opt/bytelyst/learning_ai_devops_tools/dashboard/.gitea/workflows/ci.yml" />, then fix the two other references in <ref_file file="/opt/bytelyst/learning_ai_devops_tools/dashboard/DEPLOYMENT.md" /> and <ref_file file="/opt/bytelyst/learning_ai_devops_tools/dashboard/scripts/deploy-hotcopy.sh" />.
|
||||||
|
- Verify: trigger a CI run on a throwaway branch and confirm green.
|
||||||
|
|
||||||
|
### 2. "Lint" steps are no-ops
|
||||||
|
Both `backend/package.json` and `web/package.json` define `lint` as `echo 'No linting configured...'`. The CI step "Lint" therefore always passes regardless of code quality. There is no ESLint, Biome, or equivalent configured anywhere in the workspace.
|
||||||
|
|
||||||
|
- Action: pick one tool (recommend ESLint + `@typescript-eslint` for backend, Next.js's built-in ESLint config for web, since `next` already ships it). Wire `next lint` into `web/package.json` and add a minimal `.eslintrc` to backend.
|
||||||
|
- Verify: `pnpm lint` returns a non-zero exit on a deliberately bad change.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P1 — Important Gaps
|
||||||
|
|
||||||
|
### 3. Test coverage is extremely thin
|
||||||
|
Backend has 12 modules (`services`, `deployments`, `health`, `audit`, `backup`, `system`, `env`, `azure-config`, `code-quality`, `cosmos-config`, `hermes-ops`, `vm`) but only `services` has a test file. The deployment orchestrator (`backend/src/modules/deployments/orchestrator.ts`), CSRF (`backend/src/lib/csrf.ts`), and auth (`backend/src/lib/auth.ts`) — the highest-risk surfaces — have no tests at all.
|
||||||
|
|
||||||
|
- Action: add `*.test.ts` for at least `auth`, `csrf`, `deployments/orchestrator`, and `health` repository before adding more features. Mirror the style of <ref_file file="/opt/bytelyst/learning_ai_devops_tools/dashboard/backend/src/modules/services/services.test.ts" />.
|
||||||
|
- Add `pnpm test:coverage` to CI and fail under a threshold (start at 50 %, raise over time).
|
||||||
|
|
||||||
|
### 4. SSE deployment-log streaming is disabled
|
||||||
|
`backend/src/server.ts` and `backend/src/modules/deployments/routes.ts` contain `TODO: fastify-sse-v2 has compatibility issues with Fastify 5`, with the SSE plugin commented out. The README still advertises real-time log streaming and the frontend code in <ref_file file="/opt/bytelyst/learning_ai_devops_tools/dashboard/web/src/app/page.tsx" /> imports `LogViewer`, so the user-facing feature is silently broken.
|
||||||
|
|
||||||
|
- Action: pin a Fastify-5-compatible SSE library (`@fastify/eventsource`, `fastify-sse-v2 >= 5`, or a small handcrafted handler using `reply.raw`) and re-enable the route, OR remove the SSE claims from `README.md` / `ENDPOINTS.md` until it ships. Choose one — do not leave the gap.
|
||||||
|
|
||||||
|
### 5. Documentation drift
|
||||||
|
- `README.md` says "Web port: 3000" but `docker-compose.yml` exposes web as `3049:3000`.
|
||||||
|
- `README.md` lists API endpoints inline; `ENDPOINTS.md` is the canonical source and contradicts in places (e.g. note about `https://api.bytelyst.com/api/devops` vs `https://api.bytelyst.com/devops`).
|
||||||
|
- `DEPLOYMENT.md` and `DEPLOYMENT_GUIDE.md` overlap; unclear which is canonical.
|
||||||
|
|
||||||
|
- Action: pick `ENDPOINTS.md` as the single source for URLs and reduce `README.md` to a pointer. Merge the two deployment docs into one (`DEPLOYMENT.md`) and delete the loser. Fix the 3000 vs 3049 mismatch.
|
||||||
|
|
||||||
|
### 6. Docker socket + host log mounts are very privileged
|
||||||
|
`docker-compose.yml` mounts `/var/run/docker.sock`, the host `scripts` directory, and three host log paths into `devops-backend`. This is the same risk profile as Portainer but with custom code reading/writing those mounts. There is no documentation of which backend module talks to the docker socket or what commands it issues.
|
||||||
|
|
||||||
|
- Action: document the privilege surface (which routes shell out, which call `docker`), and consider a thin allow-list wrapper instead of mounting the raw socket. At minimum, add a section to `DEPLOYMENT.md` enumerating these mounts and their purpose so reviewers know the blast radius.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P2 — Hygiene
|
||||||
|
|
||||||
|
### 7. Backend module structure isn't enforced
|
||||||
|
Most modules follow the `routes.ts / repository.ts / types.ts` triple, but a few have extras (`deployments/orchestrator.ts`). There is no architectural test, README, or generator. New contributors will diverge.
|
||||||
|
|
||||||
|
- Action: add a short `backend/src/modules/README.md` describing the convention, and (optionally) an architectural test using `dependency-cruiser` or a custom vitest.
|
||||||
|
|
||||||
|
### 8. README is unfocused
|
||||||
|
`README.md` mixes "Recent Improvements" (a changelog), feature list, setup, env vars, and full API docs into one 219-line file. The first cat of the file even shows it begins with two blank lines after the title — easy to miss content.
|
||||||
|
|
||||||
|
- Action: trim README to: what / quickstart / pointers. Move "Recent Improvements" into `CHANGELOG.md` and keep API docs only in `ENDPOINTS.md` / Swagger.
|
||||||
|
|
||||||
|
### 9. `.pnpmfile.cjs` dual-mode install is undocumented in CI
|
||||||
|
`pnpm install:common-plat` vs `pnpm install:gitea` is only mentioned in the README. The CI workflow uses `install:common-plat`, which only works if the runner has the sibling `learning_ai_common_plat` checkout available. That assumption isn't asserted anywhere.
|
||||||
|
|
||||||
|
- Action: add a pre-install check that fails fast with a clear message if the expected workspace path is missing, and document the runner prerequisites in the CI file.
|
||||||
|
|
||||||
|
### 10. No production logging / metrics story
|
||||||
|
`backend/src/server.ts` uses Fastify's default logger only. There is a `web/src/lib/telemetry.ts` file but nothing wires it to a backend. The dashboard advertises "monitoring" but doesn't emit its own structured telemetry.
|
||||||
|
|
||||||
|
- Action: decide on a target (pino transports → stdout for container logs is enough for now) and write down the choice. If Prometheus / OpenTelemetry is in scope, file a tracked issue rather than leaving it implied.
|
||||||
|
|
||||||
|
### 11. E2E tests aren't wired into local workflow
|
||||||
|
`web/e2e/dashboard.spec.ts` and `web/e2e/hermes.spec.ts` exist and `pnpm test:e2e` is defined, but nothing documents how to start the backend+web before running them, and CI's E2E step (visible in `.gitea/workflows/ci.yml`) is cut off in the file — need to confirm it actually launches the stack.
|
||||||
|
|
||||||
|
- Action: read the bottom half of `ci.yml` and confirm the E2E job sets up backend+web; if not, fix it. Add a `pnpm test:e2e` recipe to README that explicitly says "run `pnpm dev` first" or use Playwright's `webServer` config.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Suggested execution order
|
||||||
|
|
||||||
|
1. Fix the CI path (#1) — unblocks everything else.
|
||||||
|
2. Reconcile the SSE TODO (#4) — either remove the claim or ship the feature.
|
||||||
|
3. Add real linting (#2) and tighten test coverage on auth/csrf/orchestrator (#3).
|
||||||
|
4. Documentation pass: ports, deployment docs, README trim (#5, #8).
|
||||||
|
5. Privilege/operational hardening (#6, #10).
|
||||||
|
6. Convention + DX polish (#7, #9, #11).
|
||||||
|
|
||||||
|
Each item above is small enough to land as a single PR.
|
||||||
27
dashboard/backend/eslint.config.js
Normal file
27
dashboard/backend/eslint.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
// Flat config (ESLint 9). Real linting — replaces the previous no-op `echo`.
|
||||||
|
// Correctness rules from the recommended sets stay errors and fail CI;
|
||||||
|
// stylistic/known-pattern rules are relaxed so the current tree is clean.
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ['dist/**', 'coverage/**', 'node_modules/**'] },
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,mts,cts}'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.node },
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Fastify request/reply are cast to `any` at framework boundaries.
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
// Surface dead code without failing the build on work-in-progress.
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'none' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:run": "vitest run",
|
"test:run": "vitest run",
|
||||||
"test:coverage": "vitest run --coverage",
|
"test:coverage": "vitest run --coverage",
|
||||||
"lint": "echo 'No linting configured for backend'",
|
"lint": "eslint src",
|
||||||
"migrate": "tsx src/scripts/run-migrations.ts up",
|
"migrate": "tsx src/scripts/run-migrations.ts up",
|
||||||
"migrate:rollback": "tsx src/scripts/run-migrations.ts down"
|
"migrate:rollback": "tsx src/scripts/run-migrations.ts down"
|
||||||
},
|
},
|
||||||
@ -31,10 +31,14 @@
|
|||||||
"zod": "^3.24.1"
|
"zod": "^3.24.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.18.0",
|
||||||
"@types/node": "^25.0.3",
|
"@types/node": "^25.0.3",
|
||||||
"@vitest/coverage-v8": "3.2.4",
|
"@vitest/coverage-v8": "3.2.4",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
|
"typescript-eslint": "^8.20.0",
|
||||||
"vitest": "^3.1.2"
|
"vitest": "^3.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
import * as os from 'os';
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
@ -8,16 +9,16 @@ export async function getSystemMetrics() {
|
|||||||
const uptime = process.uptime();
|
const uptime = process.uptime();
|
||||||
|
|
||||||
// Get CPU usage and load average
|
// Get CPU usage and load average
|
||||||
const cpus = require('os').cpus();
|
const cpus = os.cpus();
|
||||||
const loadAvg = require('os').loadavg();
|
const loadAvg = os.loadavg();
|
||||||
const cpuUsage = process.cpuUsage();
|
const cpuUsage = process.cpuUsage();
|
||||||
const totalCpuTime = cpus.reduce((acc: number, cpu: any) => {
|
const totalCpuTime = cpus.reduce((acc: number, cpu: any) => {
|
||||||
return acc + (cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq + cpu.times.steal);
|
return acc + (cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq + cpu.times.steal);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
// Get memory info
|
// Get memory info
|
||||||
const totalMem = require('os').totalmem();
|
const totalMem = os.totalmem();
|
||||||
const freeMem = require('os').freemem();
|
const freeMem = os.freemem();
|
||||||
const usedMem = totalMem - freeMem;
|
const usedMem = totalMem - freeMem;
|
||||||
|
|
||||||
// Get disk info
|
// Get disk info
|
||||||
@ -71,7 +72,7 @@ export async function getSystemMetrics() {
|
|||||||
nodeVersion: process.version,
|
nodeVersion: process.version,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
arch: process.arch,
|
arch: process.arch,
|
||||||
hostname: require('os').hostname(),
|
hostname: os.hostname(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -86,9 +87,9 @@ function parseSize(sizeStr: string): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getDockerStats() {
|
export async function getDockerStats() {
|
||||||
let images = { total: 0, dangling: 0, size: 0 };
|
const images = { total: 0, dangling: 0, size: 0 };
|
||||||
let containers = { total: 0, running: 0, stopped: 0, size: 0 };
|
const containers = { total: 0, running: 0, stopped: 0, size: 0 };
|
||||||
let volumes = { total: 0, unused: 0, size: 0 };
|
const volumes = { total: 0, unused: 0, size: 0 };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get image stats
|
// Get image stats
|
||||||
|
|||||||
@ -15,7 +15,7 @@ YELLOW='\033[1;33m'
|
|||||||
NC='\033[0m' # No Color
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
DEVOPS_DIR="/opt/bytelyst/bytelyst-devops-tools/dashboard"
|
DEVOPS_DIR="/opt/bytelyst/learning_ai_devops_tools/dashboard"
|
||||||
PLATFORM_DIR="/opt/bytelyst/learning_ai_common_plat"
|
PLATFORM_DIR="/opt/bytelyst/learning_ai_common_plat"
|
||||||
|
|
||||||
# Function to print colored output
|
# Function to print colored output
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
"dev": "pnpm --filter @bytelyst/devops-backend dev & pnpm --filter @bytelyst/devops-web dev",
|
"dev": "pnpm --filter @bytelyst/devops-backend dev & pnpm --filter @bytelyst/devops-web dev",
|
||||||
"build": "pnpm --filter @bytelyst/devops-backend build && pnpm --filter @bytelyst/devops-web build",
|
"build": "pnpm --filter @bytelyst/devops-backend build && pnpm --filter @bytelyst/devops-web build",
|
||||||
"typecheck": "pnpm --filter @bytelyst/devops-backend typecheck && pnpm --filter @bytelyst/devops-web typecheck",
|
"typecheck": "pnpm --filter @bytelyst/devops-backend typecheck && pnpm --filter @bytelyst/devops-web typecheck",
|
||||||
|
"lint": "pnpm --filter @bytelyst/devops-backend lint && pnpm --filter @bytelyst/devops-web lint",
|
||||||
"test": "pnpm --filter @bytelyst/devops-backend test && pnpm --filter @bytelyst/devops-web test",
|
"test": "pnpm --filter @bytelyst/devops-backend test && pnpm --filter @bytelyst/devops-web test",
|
||||||
"test:run": "pnpm --filter @bytelyst/devops-backend test:run && pnpm --filter @bytelyst/devops-web test:run",
|
"test:run": "pnpm --filter @bytelyst/devops-backend test:run && pnpm --filter @bytelyst/devops-web test:run",
|
||||||
"test:coverage": "pnpm --filter @bytelyst/devops-backend test:coverage && pnpm --filter @bytelyst/devops-web test:coverage",
|
"test:coverage": "pnpm --filter @bytelyst/devops-backend test:coverage && pnpm --filter @bytelyst/devops-web test:coverage",
|
||||||
|
|||||||
803
dashboard/pnpm-lock.yaml
generated
803
dashboard/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,8 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
echo "Building DevOps web and backend artifacts..."
|
echo "Building DevOps web and backend artifacts..."
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard/web && pnpm build
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard/web && pnpm build
|
||||||
cd /opt/bytelyst/bytelyst-devops-tools/dashboard/backend && pnpm build
|
cd /opt/bytelyst/learning_ai_devops_tools/dashboard/backend && pnpm build
|
||||||
|
|
||||||
echo "Copying frontend assets into devops-web..."
|
echo "Copying frontend assets into devops-web..."
|
||||||
docker cp web/.next devops-web:/app/web/.next
|
docker cp web/.next devops-web:/app/web/.next
|
||||||
|
|||||||
26
dashboard/web/eslint.config.mjs
Normal file
26
dashboard/web/eslint.config.mjs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
// Flat config (ESLint 9). Real linting — replaces the previous no-op `echo`.
|
||||||
|
// Next.js-specific rules are intentionally omitted to keep the install
|
||||||
|
// self-contained; correctness rules from the recommended sets fail CI.
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ['.next/**', 'coverage/**', 'node_modules/**', 'next-env.d.ts'] },
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx,mts,cts}'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.browser, ...globals.node },
|
||||||
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'none' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -7,7 +7,7 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "BROWSERSLIST_IGNORE_OLD_DATA=true BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA=true next build",
|
"build": "BROWSERSLIST_IGNORE_OLD_DATA=true BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA=true next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "echo 'No dedicated frontend lint config; rely on typecheck, tests, and next build'",
|
"lint": "eslint src",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:run": "vitest run",
|
"test:run": "vitest run",
|
||||||
@ -31,14 +31,18 @@
|
|||||||
"@types/node": "^25.0.3",
|
"@types/node": "^25.0.3",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-dom": "^19.0.0",
|
"@types/react-dom": "^19.0.0",
|
||||||
|
"@eslint/js": "^9.18.0",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"@vitest/coverage-v8": "3.2.4",
|
"@vitest/coverage-v8": "3.2.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
"jsdom": "^26.0.3",
|
"jsdom": "^26.0.3",
|
||||||
"playwright": "^1.58.2",
|
"playwright": "^1.58.2",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
|
"typescript-eslint": "^8.20.0",
|
||||||
"vitest": "^3.1.2"
|
"vitest": "^3.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -208,7 +208,8 @@ function UnhealthyContainersPanel({
|
|||||||
const toggle = (name: string) =>
|
const toggle = (name: string) =>
|
||||||
setExpanded(prev => {
|
setExpanded(prev => {
|
||||||
const next = new Set(prev);
|
const next = new Set(prev);
|
||||||
next.has(name) ? next.delete(name) : next.add(name);
|
if (next.has(name)) next.delete(name);
|
||||||
|
else next.add(name);
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -179,7 +179,7 @@ export async function apiRequest<T>(
|
|||||||
endpoint: string,
|
endpoint: string,
|
||||||
options: RequestInit = {}
|
options: RequestInit = {}
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
let token = await getAccessToken();
|
const token = await getAccessToken();
|
||||||
const headers: HeadersInit = {
|
const headers: HeadersInit = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...options.headers,
|
...options.headers,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user