From 9f9de482ce163cf590f11f52b4f004cbe1e81928 Mon Sep 17 00:00:00 2001 From: Anso Date: Tue, 24 Mar 2026 22:32:44 -0400 Subject: [PATCH] =?UTF-8?q?chore:=20GitHub=20workflow=20revamp=20=E2=80=94?= =?UTF-8?q?=20GitHub=20Flow,=20community=20files,=20CI=20updates=20(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: add comprehensive .gitignore * ci: update CI workflow for GitHub Flow - Change triggers from develop to main (PRs to main + pushes to main) - Add concurrency controls to cancel stale runs - Update docker/build-push-action to v6 - Add descriptive job names for branch protection status checks - Update screenshot refresh and docs sync to trigger on main pushes * ci: update docker-publish for GitHub Flow - Remove develop branch trigger (no more dev tag) - Keep v* tag trigger for releases - Update docker/build-push-action to v6 * docs: add community and governance files - CONTRIBUTING.md with dev setup and PR guidelines - SECURITY.md with vulnerability reporting policy - CODE_OF_CONDUCT.md (Contributor Covenant v2.1 reference) - PR template with conventional commits checklist - Issue templates for bug reports and feature requests - CODEOWNERS defaulting to @AnsoCode - Dependabot config for npm (root, backend, frontend) and GitHub Actions * docs: add README with badges, quick start, and contributing section * chore: add LICENSE placeholder and open license decision issue (#100) * docs: update CLAUDE.md for GitHub Flow branching model - Replace develop-based Git Flow with GitHub Flow (main only) - All branches now created off main, PRs target main - Simplify release checklist (no develop-to-main merge step) - Update testing strategy to reference Vitest and Playwright - Fix docs.json reference (was mint.json) * chore: track CLAUDE.md in version control Remove CLAUDE.md from .gitignore so project workflow instructions are versioned alongside the code they govern. * docs: add MANUAL_STEPS.md for GitHub settings that require UI configuration --- .github/CODEOWNERS | 2 + .github/ISSUE_TEMPLATE/bug_report.yml | 56 +++++ .github/ISSUE_TEMPLATE/feature_request.yml | 40 +++ .github/PULL_REQUEST_TEMPLATE.md | 15 ++ .github/dependabot.yml | 32 +++ .github/workflows/ci.yml | 37 +-- .github/workflows/docker-publish.yml | 7 +- .gitignore | 28 ++- CLAUDE.md | 271 +++++++++++++++++++++ CODE_OF_CONDUCT.md | 19 ++ CONTRIBUTING.md | 47 ++++ LICENSE | 6 + MANUAL_STEPS.md | 63 +++++ README.md | 72 ++++++ SECURITY.md | 29 +++ package.json | 2 +- 16 files changed, 696 insertions(+), 30 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 CLAUDE.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 MANUAL_STEPS.md create mode 100644 README.md create mode 100644 SECURITY.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..edaa3bfa --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owner for everything +* @AnsoCode diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..f1434850 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,56 @@ +name: Bug Report +description: Report a bug in Sencho +labels: ["bug"] +body: + - type: textarea + id: description + attributes: + label: Describe the bug + placeholder: A clear description of the bug + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + placeholder: | + 1. Go to '...' + 2. Click on '...' + 3. See error + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true + - type: input + id: version + attributes: + label: Sencho version + placeholder: "0.2.2" + validations: + required: true + - type: dropdown + id: deployment + attributes: + label: Deployment method + options: + - Docker Compose + - Docker run + - Development (npm run dev) + - Other + validations: + required: true + - type: input + id: browser + attributes: + label: Browser (if UI issue) + placeholder: "Chrome 120, Firefox 121, etc." + - type: textarea + id: logs + attributes: + label: Relevant logs + description: Paste any relevant container or browser console logs + render: shell diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..5192b4ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,40 @@ +name: Feature Request +description: Suggest a new feature for Sencho +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problem or motivation + description: What problem does this feature solve? Why do you need it? + placeholder: I'm always frustrated when... + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: Describe the feature you'd like to see + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Any workarounds or alternative approaches you've considered + - type: dropdown + id: scope + attributes: + label: Feature scope + options: + - Dashboard / UI + - Stack management + - Multi-node / remote nodes + - App Store + - Observability / Logs + - Settings / Configuration + - Docker / Infrastructure + - API + - Other + validations: + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..9ddb3f4a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +## What does this PR do? + + + +## Related Issue + + + +## Checklist + +- [ ] Tests added/updated (if applicable) +- [ ] Documentation updated (if applicable) +- [ ] ESLint passes (`npm run lint` in backend/ and frontend/) +- [ ] Commit messages follow Conventional Commits +- [ ] CHANGELOG.md updated under `## [Unreleased]` (for user-facing changes) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..b97796c5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + labels: + - "dependencies" + open-pull-requests-limit: 10 + + - package-ecosystem: "npm" + directory: "/backend" + schedule: + interval: "weekly" + labels: + - "dependencies" + open-pull-requests-limit: 10 + + - package-ecosystem: "npm" + directory: "/frontend" + schedule: + interval: "weekly" + labels: + - "dependencies" + open-pull-requests-limit: 10 + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "ci" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42803702..bfece101 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,18 @@ name: Sencho CI on: pull_request: - branches: [ main, develop ] + branches: [main] push: - branches: [ develop ] + branches: [main] + +# Cancel previous runs on the same branch/PR +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true jobs: backend: + name: Backend (Build, Test, Lint) runs-on: ubuntu-latest steps: - name: Checkout Code @@ -41,6 +47,7 @@ jobs: run: npm audit --audit-level=high frontend: + name: Frontend (Build, Lint) runs-on: ubuntu-latest steps: - name: Checkout Code @@ -70,6 +77,7 @@ jobs: run: npm audit --audit-level=high docker-validate: + name: Docker Build & Scan runs-on: ubuntu-latest steps: - name: Checkout Code @@ -79,7 +87,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build Docker image (validation only) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: false @@ -98,8 +106,9 @@ jobs: continue-on-error: true e2e: + name: E2E Tests (Playwright) runs-on: ubuntu-latest - needs: [ backend, frontend ] + needs: [backend, frontend] steps: - name: Checkout Code uses: actions/checkout@v4 @@ -163,10 +172,11 @@ jobs: retention-days: 7 update-screenshots: + name: Refresh Doc Screenshots runs-on: ubuntu-latest - # Only on develop pushes; skip bot commits to avoid an infinite loop - if: "github.event_name == 'push' && github.ref == 'refs/heads/develop' && !contains(github.event.head_commit.message, 'docs: refresh screenshots')" - needs: [ backend, frontend ] + # Only on main pushes (merged PRs); skip bot commits to avoid infinite loop + if: "github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'docs: refresh screenshots')" + needs: [backend, frontend] permissions: contents: write pull-requests: write @@ -174,8 +184,6 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 with: - # PAT required — GITHUB_TOKEN cannot create PRs against a protected - # branch. DOCS_REPO_TOKEN is a classic PAT with repo scope. token: ${{ secrets.DOCS_REPO_TOKEN }} - name: Setup Node.js @@ -234,7 +242,7 @@ jobs: branch: chore/refresh-screenshots commit-message: "docs: refresh screenshots" title: "docs: refresh screenshots" - body: "Automated screenshot refresh — generated by the `update-screenshots` CI job on every push to `develop`." + body: "Automated screenshot refresh — generated by the `update-screenshots` CI job on push to `main`." add-paths: docs/images/ delete-branch: true @@ -248,8 +256,9 @@ jobs: --repo ${{ github.repository }} sync-docs: + name: Sync Docs to sencho-docs runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - name: Checkout Sencho repo uses: actions/checkout@v4 @@ -260,8 +269,6 @@ jobs: env: DOCS_TOKEN: ${{ secrets.DOCS_REPO_TOKEN }} run: | - # Always start clean so a previous partial/broken checkout can't cause - # "not in a git directory" on subsequent steps. rm -rf sencho-docs REMOTE="https://x-access-token:${DOCS_TOKEN}@github.com/AnsoCode/sencho-docs.git" @@ -278,8 +285,6 @@ jobs: fi - name: Copy /docs into sencho-docs root - # --exclude='.git' prevents rsync --delete from wiping the .git - # directory that was just cloned/initialized in the previous step. run: rsync -av --delete --exclude='.git' sencho/docs/ sencho-docs/ - name: Commit and push to sencho-docs @@ -288,5 +293,5 @@ jobs: git config user.email "docs-bot@sencho.io" git config user.name "Sencho Docs Bot" git add -A - git commit -m "docs: sync from develop@${{ github.sha }}" || echo "No changes to commit" + git commit -m "docs: sync from main@${{ github.sha }}" || echo "No changes to commit" git push origin HEAD:main diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 867f18ea..aa2584a8 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -2,8 +2,6 @@ name: Build and Publish Docker Image on: push: - branches: - - develop tags: - 'v*' workflow_dispatch: @@ -36,14 +34,11 @@ jobs: with: images: saelix/sencho tags: | - # If pushing to develop, tag as 'dev' - type=raw,value=dev,enable=${{ github.ref == 'refs/heads/develop' }} - # If pushing a v* tag, tag as 'latest' and the specific semver version type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: true diff --git a/.gitignore b/.gitignore index 2adad6c2..f8eda978 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ # Dependencies node_modules/ -frontend/node_modules/ -backend/node_modules/ # Environment Variables (CRITICAL: Never commit secrets) .env @@ -10,8 +8,6 @@ backend/node_modules/ # Build Outputs dist/ -frontend/dist/ -backend/dist/ # Sencho User Data & Mocks data/ @@ -22,13 +18,31 @@ sencho.json # OS Generated Files .DS_Store Thumbs.db +Desktop.ini +ehthumbs.db -#PRD -Product Requirements Document +# IDE / Editors +.vscode/ +.idea/ +*.swp +*.swo +*~ +*.sublime-project +*.sublime-workspace + +# Testing +test-results/ +e2e/report/ +html-report/ +.coverage/ +coverage/ + +# Logs +*.log +npm-debug.log* # Claude Code .claude/ -CLAUDE.md plans/ # Playwright MCP diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..0b8ff2da --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,271 @@ +# CLAUDE.md + +> **Every session:** Read this file fully before starting any task. +> **Every commit:** Before committing, run through the checklist below. + +## Project Overview + +Sencho is a self-hosted Docker Compose management dashboard - a React + Express full-stack app that provides a GUI for managing Docker Compose stacks. +**Architecture Model:** It uses a "Distributed API" model. It manages local stacks directly. To manage remote nodes, it acts as a transparent HTTP/WebSocket proxy, routing requests to other autonomous Sencho instances running on those remote servers via long-lived JWT Bearer tokens. + +## Development Commands + +### Backend +```bash +cd backend +npm install +npm run dev # nodemon + ts-node, watches src/ +npm run build # tsc → dist/ +npm start # node dist/index.js (production) +``` + +### Frontend +```bash +cd frontend +npm install +npm run dev # Vite dev server (proxies /api → localhost:3000) +npm run build # tsc -b && vite build +npm run lint # ESLint 9 flat config +npm run preview # preview production build +``` + +### Full Docker build +```bash +docker build -t sencho . # multi-stage: frontend build → backend build → runtime +``` + +--- + +## Versioning & Release Flow + +Sencho uses **Semantic Versioning** (`MAJOR.MINOR.PATCH`) driven by **Conventional Commits** and automated by the `release-please` GitHub Action. + +### How it works (GitHub Flow) + +1. Create a short-lived feature branch off `main` (e.g., `feat/my-feature`, `fix/my-bug`). +2. Commit using conventional commit prefixes (see below). +3. Open a PR into `main`. CI runs automatically. +4. Once CI is green, merge the PR (squash-merge recommended for clean history). +5. On every push to `main`, the `release-please` workflow automatically opens or updates a **Release PR** titled `chore(main): release vX.Y.Z`. This PR contains: + - An updated `CHANGELOG.md` entry generated from commit messages. + - A `package.json` version bump. +6. When you're ready to publish, **merge the Release PR**. This triggers: + - A `vX.Y.Z` git tag created by release-please. + - The `docker-publish.yml` workflow fires on the tag → builds and pushes `latest` + `X.Y.Z` to Docker Hub. + +> You never create tags manually. Merging the Release PR is the only release action required. +> There is no `develop` branch. `main` is the only long-lived branch. + +### Conventional Commit format + +``` +(optional scope): + +# Examples +fix(auth): prevent login loop on remote node 401 +feat(app-store): add category filter pill bar +feat!: replace SSH with Distributed API proxy ← BREAKING CHANGE +docs(quickstart): update Docker run command +chore(deps): bump express to 4.19.2 +ci: add arm64 platform to docker-publish +``` + +### Version bump rules + +| Commit type | Example | Version bump | +|---|---|---| +| `fix:` / `perf:` / `revert:` | Bug fixes, perf improvements | `PATCH` — 0.1.0 → 0.1.1 | +| `feat:` | New user-facing feature | `MINOR` — 0.1.x → 0.2.0 | +| `feat!:` / `BREAKING CHANGE:` footer | Breaking API or UX change | `MAJOR` — 0.x.y → 1.0.0 | +| `docs:` / `chore:` / `ci:` / `test:` / `refactor:` | Infrastructure, no user impact | No bump (hidden in CHANGELOG) | + +> While the version is `0.x`, `feat:` bumps the **minor** digit (0.1 → 0.2), not the patch. `fix:` still bumps patch (0.1.0 → 0.1.1). A `feat!:` will bump to `1.0.0`. + +### Enriching the auto-generated Release PR + +release-please generates CHANGELOG entries from commit subject lines. If a change deserves more context (e.g. a security fix or complex feature), **edit the Release PR description and CHANGELOG entry directly** before merging — release-please preserves manual edits on subsequent updates. + +--- + +## Architecture & Request Flow + +### The Distributed API Proxy Flow +1. **Frontend:** Makes API calls via `apiFetch` (`frontend/src/lib/api.ts`), which injects the `x-node-id` header (or `?nodeId=` query param for WebSockets) from `NodeContext`. +2. **Backend Gateway (`index.ts`):** The `nodeContextMiddleware` evaluates the node ID. + - **If Local:** The request passes through standard auth and hits the local Express route handlers. + - **If Remote:** The request hits `http-proxy-middleware`. The backend strips the `x-node-id` header, injects `Authorization: Bearer `, and seamlessly proxies the entire HTTP or WebSocket request to the remote Sencho instance's `api_url`. + +### Backend Services (`backend/src/services/`) +All core services execute **locally only**. (Remote execution is handled by proxying the request to the remote Sencho instance, which then executes locally on its own machine). +- **`DockerController`** - Dockerode wrapper for local containers, images, volumes, networks, stats, exec. +- **`ComposeService`** - Spawns `docker compose` CLI child processes locally. +- **`FileSystemService`** - Simple `fs.promises` wrapper. All reads/writes happen against the local `COMPOSE_DIR`. +- **`DatabaseService`** - SQLite via `better-sqlite3`. Stores alerts, settings, metrics, and node routing configs at `/app/data/sencho.db`. +- **`NodeRegistry`** - Manages node configurations. Returns the local Docker socket for local ops, and returns `{ api_url, api_token }` targets for remote proxies. + +### Frontend Structure +- **`App.tsx`** - Root router: setup flow → login → `EditorLayout`. +- **`context/AuthContext.tsx`** - JWT auth state; handles first-boot setup detection. +- **`context/NodeContext.tsx`** - Active node selection state. +- **`components/NodeManager.tsx`** - UI for adding nodes (requires Name, Sencho API URL, and API Token). +- **`lib/api.ts`** - `apiFetch` wrapper that handles headers and 401 redirects. + +### Authentication +- **Browser-to-Backend:** JWT stored in `httpOnly` cookies. +- **Node-to-Node (Proxy):** Long-lived JWT passed via `Authorization: Bearer ` header. +- **Middleware:** `authMiddleware` accepts *both* cookies and Bearer tokens seamlessly. + +--- + +## 🛑 AGENT DIRECTIVES (CRITICAL INSTRUCTIONS) + +To prevent hallucinations, regressions, and context loss, you MUST adhere to the following rules: + +**1. Targeted Reading of index.ts (Avoid Full-File Context Waste)** + - Before modifying `index.ts`, do NOT read the entire ~1500-line file. Read only the relevant section: Lines 50-250 (Auth/Setup), Lines 300-500 (Stacks/Compose), Lines 550-750 (Docker Entities), Lines 800-1000 (System/Metrics), Lines 1050-1250 (Nodes/Proxy), Lines 1300+ (WebSockets/Errors). + - For middleware ordering, check lines 100-300. + - Confirm exact placement by searching for the prefix. + +2. **Respect the Architecture:** + - We do NOT use SSH, SFTP, or remote Docker TCP sockets. For remote file reading, you must use the HTTP Proxy model. + - Ensure `http-proxy-middleware` catches remote requests before local route handlers. + +3. **Limit Repetition & Be Concise:** + - Do not output massive blocks of unchanged code. Use precise edits, diffs, or surgical replacements. + - Do not over-explain your steps unless asked. State your intent, perform the file modifications, and report the result. + +4. **Strict GitHub Flow & Conventional Commits:** + - All feature work and bug fixes must happen on a newly created branch off `main`. + - **Every commit MUST use a conventional commit prefix** (`fix:`, `feat:`, `docs:`, `chore:`, `ci:`, `refactor:`, `test:`, `perf:`). This is what drives automated versioning — no prefix means no version bump and no CHANGELOG entry. + - Update `CHANGELOG.md` under `## [Unreleased]` before committing (the auto-generated entry from release-please will be terse; add context here for significant changes). + - Stage, commit, push to `origin`, and **open a PR into `main`**. This is required to trigger automated CI/CD actions. + - **Do NOT manually create git tags or push version commits.** Versioning is fully automated by `release-please` — it fires on every push to `main` and creates the Release PR automatically. + +5. **Strict Security & API Standards:** + - **Default Deny:** EVERY new endpoint in `index.ts` under `/api/` MUST be protected by `authMiddleware` unless explicitly designated as public. + - **SQL Injection Prevention:** ALWAYS use `better-sqlite3` parameterized statements. Do not manually sanitize or string-escape inputs. + - **No Secrets in Code:** Never hardcode secrets. Rely on `DatabaseService.getInstance().getGlobalSettings()`. + +6. **UX & UI Engineering Constraints:** + - Use only Tailwind CSS and existing `shadcn/ui` components. + - Handle loading states. Every frontend API call must be wrapped in `try/catch`. + - When using `sonner` toast, use this exact defensive pattern: `toast.error(error?.message || error?.error || error?.data?.error || 'Something went wrong.');` + - Do not assume `error` is a standard Error instance. + +7. **Backend Error Handling:** + - **Do Not Swallow Errors:** Do not write empty `catch` blocks. + - **Standardized Responses:** If a backend operation fails, log the actual error to the console for debugging, but return a clean, standard JSON error to the frontend: `res.status(500).json({ error: "Clear message" });`. + - **No Server Crashes:** Ensure all asynchronous proxy streams (WebSockets/SSE) have robust error handling and `.on('error')` listeners to prevent the Node event loop from crashing. + +8. **WebSocket Nuances & Middleware Bypasses:** + - Express middlewares (`authMiddleware`, `nodeContextMiddleware`, `cookieParser`) **DO NOT RUN** on WebSocket (`server.on('upgrade')`) connections. + - You MUST manually parse cookies/headers and manually execute `jwt.verify()` inside the `upgrade` event handler. + - Extract `nodeId` from URL query parameters (`?nodeId=`) for WebSockets, NOT from the `x-node-id` header. + +9. **Filesystem Security & Path Traversal:** + - Perform strict directory traversal validation at the Express route level before any `FileSystemService` call. + - Use this exact pattern: `const safePath = path.resolve(COMPOSE_DIR, stackName, filename);` `if (!safePath.startsWith(path.resolve(COMPOSE_DIR))) { return res.status(400).json({ error: 'Invalid path' }); }` + - Reject inputs with `..` or absolute paths. + +10. **React Dependency Traps (Infinite Loops):** + - Be extremely cautious fixing `react-hooks/exhaustive-deps` warnings in global context files like `NodeContext.tsx`. + - We intentionally use `useRef` (e.g., `activeNodeRef`) to hold current state inside `useCallback` functions. **DO NOT blindly add state variables to dependency arrays** to satisfy ESLint, as it will trigger infinite API-fetching loops. + +11. **The Monolithic index.ts Constraint:** + - The backend `index.ts` file is currently a monolith (~1500 lines). + - Continue appending new routes into their logical groups (e.g., Auth, Stacks, System, Nodes). **DO NOT** attempt to proactively split or refactor this file into a multi-router architecture unless explicitly instructed by the user. + +12. **Environment & File System Context:** + - `FileSystemService` strictly operates against the `COMPOSE_DIR` environment variable. + - **DO NOT** hardcode `/app/compose` as an immutable path or implicitly assume it is the only path. It is merely the Docker default. Your code must dynamically read `process.env.COMPOSE_DIR` (or rely on `FileSystemService.getInstance(nodeId).getBaseDir()`) to respect each user's unique volume mounts or local OS paths. + +13. **Mandatory Test Validation Before Completing Any Task:** + - Validate behavior before considering a task done. + - **Always kill existing services first** — do not assume a running process has the latest code (it may have been started with `npm start` or `node dist/index.js`, not nodemon): + ```bash + kill $(lsof -ti:3000) 2>/dev/null; kill $(lsof -ti:5173) 2>/dev/null + ``` + - Then start fresh in the background using `npm run dev` (nodemon) only: + ```bash + cd backend && npm run dev & + cd frontend && npm run dev & + ``` + - Wait for readiness before proceeding: + ```bash + curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api/health # expect 200 + curl -s -o /dev/null -w "%{http_code}" http://localhost:5173 # expect 200 + ``` + - If Playwright MCP is available, use it to navigate and interact with the affected UI and confirm expected behavior. + - If Playwright is unavailable: explicitly state "Playwright validation unavailable; recommend manual testing: [describe exact steps]". + - **Always kill both services when done with validation** — never leave orphan processes: + ```bash + kill $(lsof -ti:3000) 2>/dev/null; kill $(lsof -ti:5173) 2>/dev/null + ``` + - Explicitly confirm in your response that services have been stopped after validation. + - Never open a PR without documented validation. + +14. **TypeScript Strictness Rules** + - `tsconfig.json` has `"strict": true`. + - Write code that compiles without `any` casts or `@ts-ignore`. Prefer proper types over `any`. + - If a library lacks types, import `@types/...` or use `unknown` + narrowing. + - Never add casts to silence errors. + +15. **Testing Strategy Reference** + - **Backend unit tests:** Vitest (`cd backend && npm test`). Add tests for new backend logic. + - **E2E tests:** Playwright (`npm run test:e2e`). Covers auth, stack management, and node management flows. + - **Manual validation:** Use the Playwright MCP server for UI changes, or `curl` for backend-only changes. + - For UI changes: use the Playwright MCP server to navigate to `http://localhost:5173`, interact with the affected components, and confirm expected behavior. + - For backend-only changes: validate via `curl` against `localhost:3000/api/...` with a valid `Authorization: Bearer ` or session cookie. + +16. **Environment Variables Reference** + - Read vars from `process.env` or `DatabaseService`. + - Never hardcode. Key vars: `COMPOSE_DIR` (base directory), `JWT_SECRET` (token signing), `PORT` (listen port), `NODE_ENV`. Check `.env.example` for the full list. + +17. **Code Review Standards** + - After completing any implementation, review the code for: + - Functions longer than 30 lines (likely doing too much) + - Logic duplicated more than twice (extract to utility) + - Any `any` type usage in TypeScript (replace with real types) + - Components with more than 3 props that could be grouped into an object + - Missing error handling on async operations + - Run /simplify before presenting code to the user. + +--- + +## Documentation + +Whenever you create, modify, or remove a user-facing feature, API endpoint, or configuration option, you MUST also update or create the relevant `.mdx` file inside the `/docs` folder. +- Follow the existing structure in `/docs/docs.json` navigation +- Write docs from the perspective of the end user, not internal implementation +- Use Mintlify MDX components where useful (e.g. ``, ``, ``) +- Add the new/updated doc page to the `navigation` array in `mint.json` if it's a new file +- Keep tone friendly and concise + +### Screenshots +When adding or updating a doc page for a user-facing feature: +1. Follow the service lifecycle in Directive 13: kill any running processes on ports 3000/5173, restart with `npm run dev`, wait for readiness, and kill again when done +2. Use the Playwright MCP server to navigate to the relevant page/feature in the app +3. Take a screenshot and save it to `/docs/images//.png` +4. Use descriptive filenames (e.g. `dashboard-overview.png`, `settings-dark-mode.png`) +5. For multi-step flows, highlight the relevant UI element by injecting a CSS border + before screenshotting: `element.style.border = '3px solid #0F172A'` +6. Reference screenshots in MDX files using: + `...` + or Mintlify's `` component for a polished look: + `...` +7. Always take screenshots at 1280x800 desktop resolution for consistency + +## Pre-Commit Checklist +- [ ] Am I on a feature branch off `main`? (never commit to main directly) +- [ ] Does my commit message start with a conventional prefix? (`fix:`, `feat:`, `docs:`, `chore:`, `ci:`, `refactor:`, `test:`, `perf:`) +- [ ] Did I update CHANGELOG.md under `## [Unreleased]`? (add context beyond what the commit subject captures) +- [ ] Did I update or create the relevant `/docs/*.mdx` file? +- [ ] Did I take/update screenshots via Playwright MCP if UI changed? +- [ ] Did I validate behavior via Playwright MCP (UI) or `curl` (backend), and kill the dev servers afterwards? (Directive 13) +- [ ] Is the PR going into `main`? + +## Release Checklist (when shipping to users) +- [ ] Is `main` stable and CI green? +- [ ] Wait for the `release-please` workflow to open the Release PR (titled `chore(main): release vX.Y.Z`). +- [ ] Review and optionally enrich the auto-generated CHANGELOG entry in the Release PR. +- [ ] Merge the Release PR → tag is created → Docker Hub `latest` + semver tags are published automatically. \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..4c0c5419 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,19 @@ +# Code of Conduct + +This project follows the **Contributor Covenant Code of Conduct v2.1**. + +The full text is available at: https://www.contributor-covenant.org/version/2/1/code_of_conduct/ + +## Summary + +We are committed to providing a friendly, safe, and welcoming environment for all contributors, regardless of experience level, identity, or background. + +## Enforcement + +Instances of unacceptable behavior may be reported to the project maintainers at **conduct@sencho.io**. + +All reports will be reviewed and investigated promptly and fairly. Project maintainers are obligated to respect the privacy and security of the reporter. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..c2aa2962 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# Contributing to Sencho + +Thank you for your interest in contributing to Sencho! + +## Getting Started + +1. Fork the repository +2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/Sencho.git` +3. Create a branch: `git checkout -b feature/your-feature` +4. Install dependencies: + ```bash + cd backend && npm install + cd ../frontend && npm install + ``` +5. Start the dev servers: + ```bash + cd backend && npm run dev # Express + nodemon on :3000 + cd frontend && npm run dev # Vite on :5173 + ``` + +## Development + +- **Backend:** Node.js + Express + TypeScript in `backend/` +- **Frontend:** React 19 + Vite + TypeScript in `frontend/` +- **Tests:** `cd backend && npm test` (Vitest) and `npm run test:e2e` (Playwright) +- **Lint:** `npm run lint` in both `backend/` and `frontend/` + +## Pull Request Process + +- All PRs target `main` +- Ensure CI passes before requesting review +- Use [Conventional Commits](https://www.conventionalcommits.org/) for commit messages +- Update documentation if your change affects user-facing behavior +- Add tests for new functionality +- Keep PRs focused — one feature or fix per PR +- Update `CHANGELOG.md` under `## [Unreleased]` for user-facing changes + +## Reporting Bugs + +Use the [bug report template](https://github.com/AnsoCode/Sencho/issues/new?template=bug_report.yml). Include: deployment method, Sencho version, browser (for UI issues), steps to reproduce, and expected vs actual behavior. + +## Code Style + +- TypeScript with `strict: true` — no `any` casts or `@ts-ignore` +- ESLint 9 flat config for both backend and frontend +- Tailwind CSS + shadcn/ui for frontend styling +- Follow existing patterns in the codebase diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..42ac8986 --- /dev/null +++ b/LICENSE @@ -0,0 +1,6 @@ +License pending — see https://github.com/AnsoCode/Sencho/issues/100 + +This project's license has not yet been finalized. The license decision is +being tracked in the issue linked above. + +Until a license is chosen, all rights are reserved by the project maintainers. diff --git a/MANUAL_STEPS.md b/MANUAL_STEPS.md new file mode 100644 index 00000000..fcef2400 --- /dev/null +++ b/MANUAL_STEPS.md @@ -0,0 +1,63 @@ +# Manual Steps Required + +These actions could not be performed automatically and need to be done manually in the GitHub UI. + +## 1. Branch Protection Rules for `main` + +Go to **Settings > Branches > Add branch ruleset** for `main`: + +- [x] Require a pull request before merging + - Required approvals: 0 (solo dev — you merge your own PRs after CI passes) + - Dismiss stale reviews when new commits are pushed: ON +- [x] Require status checks to pass before merging + - Add these job names: `Backend (Build, Test, Lint)`, `Frontend (Build, Lint)`, `Docker Build & Scan`, `E2E Tests (Playwright)` +- [x] Require conversation resolution before merging +- [x] Do not allow bypassing the above settings (even you must go through PRs) +- [x] Do not allow deletions +- [ ] Require signed commits (optional — future improvement) + +## 2. Repository Settings + +Go to **Settings > General > Pull Requests**: +- [x] Check **"Automatically delete head branches"** +- [x] Set **squash merge** as default merge strategy + +## 3. Security Settings + +Go to **Settings > Code security and analysis**: +- [x] Enable **Dependabot alerts** +- [x] Enable **Dependabot security updates** +- [x] Enable **Secret scanning** +- [x] Enable **Secret scanning push protection** +- [x] Enable **Private vulnerability reporting** + +## 4. Default Branch + +Verify that `main` is set as the default branch: +- Go to **Settings > Branches > Default branch** +- Should already be `main` + +## 5. Delete `develop` Branch (When Ready) + +The `develop` branch has 1 unmerged commit (`37f751c docs: refresh screenshots`). + +**Before deleting**, decide whether to: +- Cherry-pick that commit to main via a PR, OR +- Let it go (it's just a screenshot refresh) + +Then delete via: **Branches page > delete `develop`** + +Also clean up stale feature branches that have been merged: +- `chore/refresh-screenshots` +- `fix/editor-loading` +- `fix/release-please-config` +- `feat/automated-versioning` +- `fix/docker-publish-tag-trigger` +- `feat/arm64-docker-build` +- `chore/migrate-to-docs-json` +- `fix/sync-docs-rsync-excludes-git` +- `fix/ci-docs-jobs`, `fix/ci-docs-jobs-v2` + +## 6. Delete This File + +Once all manual steps are complete, delete `MANUAL_STEPS.md` from the repo. diff --git a/README.md b/README.md new file mode 100644 index 00000000..4cf4be34 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Sencho + +[![CI](https://github.com/AnsoCode/Sencho/actions/workflows/ci.yml/badge.svg)](https://github.com/AnsoCode/Sencho/actions/workflows/ci.yml) +[![Docker](https://img.shields.io/docker/v/saelix/sencho?sort=semver&label=Docker%20Hub)](https://hub.docker.com/r/saelix/sencho) +[![License](https://img.shields.io/badge/license-pending-lightgrey)](#license) + +A self-hosted Docker Compose management dashboard. Manage your stacks, containers, images, volumes, and networks through a modern web UI. + +![Sencho Dashboard](docs/images/dashboard.png) + +## Features + +- **Stack Management** — Create, edit, start, stop, and remove Docker Compose stacks with a built-in Monaco code editor +- **Multi-Node Support** — Manage remote Sencho instances through a transparent HTTP/WebSocket proxy (Distributed API model) +- **App Store** — One-click deployment from LinuxServer.io templates with editable ports, volumes, and environment variables +- **Resource Hub** — Browse and manage images, volumes, and networks with managed/external/unused classification +- **Live Logs** — Aggregated real-time log streaming across all containers with search and filtering +- **Dashboard** — Container stats, CPU/RAM metrics, health checks, and image update notifications +- **Alerts** — Configurable threshold alerts for CPU, RAM, and disk usage +- **Terminal** — In-browser host console and container exec via WebSocket + +## Quick Start + +```yaml +services: + sencho: + image: saelix/sencho:latest + container_name: sencho + restart: unless-stopped + ports: + - "3000:3000" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./data:/app/data + # 1:1 Compose Path Rule: host path MUST match container path + - /opt/docker:/opt/docker + environment: + - COMPOSE_DIR=/opt/docker + - DATA_DIR=/app/data +``` + +```bash +docker compose up -d +``` + +Then open `http://your-server:3000` and create your admin account. + +See the [full documentation](https://docs.sencho.io) for configuration details, multi-node setup, and more. + +## Development + +```bash +# Backend (Express + TypeScript) +cd backend && npm install && npm run dev + +# Frontend (React + Vite) +cd frontend && npm install && npm run dev +``` + +The frontend dev server proxies `/api` requests to the backend on port 3000. + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and PR guidelines. + +## Security + +See [SECURITY.md](SECURITY.md) for vulnerability reporting. **Do not open public issues for security vulnerabilities.** + +## License + +License pending. See [issue #100](https://github.com/AnsoCode/Sencho/issues/100) for the license decision discussion and [LICENSE](LICENSE) for current status. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..5bcbbb71 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.2.x | Yes | +| < 0.2 | No | + +## Reporting a Vulnerability + +**Please do not open a public issue for security vulnerabilities.** + +Instead, use GitHub's private vulnerability reporting: + +1. Go to the [Security tab](https://github.com/AnsoCode/Sencho/security) of this repository +2. Click **"Report a vulnerability"** +3. Provide details including: steps to reproduce, impact assessment, and any suggested fixes + +You can expect an initial response within 72 hours. We will work with you to understand and address the issue before any public disclosure. + +## Security Considerations + +Sencho manages Docker containers and has access to the Docker socket. When deploying: + +- Always run behind a reverse proxy with TLS in production +- Use strong passwords and rotate JWT secrets +- Restrict network access to the Sencho port +- Review the [security configuration docs](https://docs.sencho.io) for hardening guidance diff --git a/package.json b/package.json index 141b1a4e..6a4a05fe 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "keywords": [], "author": "", - "license": "ISC", + "license": "SEE LICENSE IN LICENSE", "type": "commonjs", "bugs": { "url": "https://github.com/AnsoCode/Sencho/issues"