diff --git a/skills/in-progress/README.md b/skills/in-progress/README.md index b5bacad..85a4a54 100644 --- a/skills/in-progress/README.md +++ b/skills/in-progress/README.md @@ -4,4 +4,11 @@ Skills in this folder are **work in progress**. They are not stable, not reviewe ## What this folder is for -Use `skills/in-progress/` as a staging area while a skill is being drafted, tested, or refined before it moves into the main skills set. Content here may be incomplete, change frequently, or follow conventions that have not been finalized yet. \ No newline at end of file +Use `skills/in-progress/` as a staging area while a skill is being drafted, tested, or refined before it moves into the main skills set. Content here may be incomplete, change frequently, or follow conventions that have not been finalized yet. + +## Skills in this folder + +| Skill | Purpose | +| --- | --- | +| [strapi-rest-api-testing](strapi-rest-api-testing/SKILL.md) | REST API integration tests — docs for harness/DB; skill adds smoke test, safety checks, helpers | +| [strapi-repair](strapi-repair/SKILL.md) | Diagnose and fix common app issues — build, admin, pnpm, 403s, security audit | \ No newline at end of file diff --git a/skills/in-progress/strapi-repair/REFERENCE.md b/skills/in-progress/strapi-repair/REFERENCE.md new file mode 100644 index 0000000..3393efa --- /dev/null +++ b/skills/in-progress/strapi-repair/REFERENCE.md @@ -0,0 +1,104 @@ +# Strapi repair reference + +## Table of contents + +- [Security hardening (PR #26737)](#security-hardening-pr-26737) +- [pnpm & monorepo](#pnpm--monorepo) +- [Deployment & URLs](#deployment--urls) +- [Environment & database](#environment--database) +- [API issues (usually config, not bugs)](#api-issues-usually-config-not-bugs) +- [Clean reinstall ladder](#clean-reinstall-ladder) +- [Likely product bugs (stop and link)](#likely-product-bugs-stop-and-link) + +## Security hardening (PR #26737) + +**Source:** [PR #26737 — security defaults for create-strapi-app](https://github.com/strapi/strapi/pull/26737) (contributor doc: `docs/docs/docs/01-core/configuration/01-security-defaults.md` in that PR). + +Complete the checklist in [SKILL.md — Security audit](../strapi-repair/SKILL.md#security-audit-checklist) by reading project files. Apply values from the PR / future docs — do not duplicate full config blocks in the skill. + +| Area | Config file | What to check | +| --- | --- | --- | +| Strict API params | `config/api.*` | `rest.strictParams`, `documents.strictParams` | +| Refresh auth | `config/plugins.*` | `users-permissions`: `jwtManagement: 'refresh'`, `sessions.httpOnly` | +| Upload MIME | `config/plugins.*` | `upload.config.security.allowedTypes` / `deniedTypes` | +| Webhooks | `config/server.*` | `webhooks.populateRelations: false` | +| Secrets | `.env` | `JWT_SECRET` present | + +**Also for production** (PR doc): [CORS](https://docs.strapi.io/cms/configurations/middlewares#cors), database SSL, `server.proxy` behind reverse proxy, HTTPS for [data transfer](https://docs.strapi.io/cms/data-management/transfer). + +**Client docs for auth changes:** [JWT management modes](https://docs.strapi.io/cms/features/users-permissions#jwt-management-modes). + +## pnpm & monorepo + +| Topic | Link | +| --- | --- | +| pnpm v11 config migration | [pnpm.io/migration](https://pnpm.io/migration) — `pnpm-workspace.yaml` required | +| `catalog:` build failures | [strapi/strapi#22849](https://github.com/strapi/strapi/issues/22849) — set `USE_EXPERIMENTAL_DEPENDENCIES=true` | +| Hoisting for admin Vite | [strapi/strapi#15992](https://github.com/strapi/strapi/issues/15992), [discussion #25404](https://github.com/strapi/strapi/discussions/25404) | +| Strapi build checks peer deps | [Build command (contributor)](https://docs.strapi.io/cms/cli#strapi-build) | + +**pnpm 11 checklist:** + +1. Root `pnpm-workspace.yaml` with `packages`, and any `catalogs` / `overrides` / hoist settings (moved out of `package.json#pnpm`). +2. `.npmrc` in v11: only auth/registry — other settings belong in `pnpm-workspace.yaml`. +3. Strapi app keeps `react`, `react-dom`, `styled-components`, `react-router-dom` in its own `package.json`. +4. Monorepo + catalogs: `USE_EXPERIMENTAL_DEPENDENCIES=true` when running `strapi build` / `strapi develop`. +5. Reinstall after config changes: remove `node_modules` + lockfile → `pnpm install`. + +## Deployment & URLs + +| Symptom | Reference | +| --- | --- | +| Admin calls `localhost:1337` in browser | [strapi/strapi#12129](https://github.com/strapi/strapi/issues/12129) — `server.url`, `admin.url`, proxy | +| Subpath / reverse proxy deploy | [Server configuration](https://docs.strapi.io/cms/configurations/server), [middlewares](https://docs.strapi.io/cms/configurations/middlewares) | +| Admin not loading on public internet | Rebuild admin, URL config, CORS, HTTPS | + +## Environment & database + +| Symptom | Likely cause | Reference | +| --- | --- | --- | +| `Unknown dialect undefined` | `DATABASE_CLIENT` missing in `.env` | [Database configuration](https://docs.strapi.io/cms/configurations/database) | +| Knex pool timeout | DB down or pool exhausted | [strapi/strapi#11860](https://github.com/strapi/strapi/issues/11860) | +| Wrong config after TS build | `dist/` shadowing source | [Testing guide — dist pitfall](https://docs.strapi.io/cms/testing#set-up-a-testing-environment) | +| sqlite3 errors after Node upgrade | Native module mismatch | Reinstall `sqlite3` / `better-sqlite3` | +| Missing secrets | Incomplete `.env` | [Environment variables](https://docs.strapi.io/cms/configurations/environment) — `APP_KEYS`, `JWT_SECRET`, etc. | + +## API issues (usually config, not bugs) + +Explain before "fixing" code — these are frequently **working as intended**. + +| User report | What's going on | Reference | +| --- | --- | --- | +| 403 on `/api/*` | Role permissions not enabled | [Users & Permissions](https://docs.strapi.io/cms/features/users-permissions) — Public / Authenticated roles | +| 403 after `strictParams` | Unknown query keys rejected | [Custom Content API parameters](https://docs.strapi.io/cms/backend-customization/routes#custom-content-api-parameters) | +| Populate empty / wrong | Draft vs published, relation direction, syntax | [REST populate](https://docs.strapi.io/cms/api/rest/populate-select), [strapi/strapi#21355](https://github.com/strapi/strapi/issues/21355) | +| Can't populate UP users | Plugin limitation | [strapi/strapi#11957](https://github.com/strapi/strapi/issues/11957) | +| Schema change didn't drop DB columns | No auto-drop by design | [strapi/strapi#1114](https://github.com/strapi/strapi/issues/1114) | +| Webhooks missing relations | `populateRelations: false` (security default) | PR #26737 | +| Using `id` instead of `documentId` | Strapi 5 Document Service | [REST API docs](https://docs.strapi.io/cms/api/rest) | + +For 403 in **tests**, use [strapi-rest-api-testing](../strapi-rest-api-testing/SKILL.md) permission helpers. + +## Clean reinstall ladder + +Only after softer fixes fail. **Confirm** before deleting databases. + +1. Stop Strapi. +2. Delete `.cache`, `.strapi/client` (admin build cache). +3. `strapi build` +4. Delete `node_modules` + lockfile → reinstall with the same package manager. +5. Rebuild native modules if using SQLite (`sqlite3` / `better-sqlite3`). +6. `strapi develop` or `strapi start`. + +Do not delete `.tmp/*.db` if it is the **development** database unless the user confirms. + +## Likely product bugs (stop and link) + +After rebuild + cache clear, if still broken, check open issues before more local hacks: + +| Symptom | Issue | +| --- | --- | +| Admin "Failed to fetch dynamically imported module" | [strapi/strapi#20870](https://github.com/strapi/strapi/issues/20870) | +| pnpm catalog semver errors | [strapi/strapi#22849](https://github.com/strapi/strapi/issues/22849) (workaround documented above) | + +Suggest upgrading Strapi patch version or commenting on the issue with reproduction steps. diff --git a/skills/in-progress/strapi-repair/SKILL.md b/skills/in-progress/strapi-repair/SKILL.md new file mode 100644 index 0000000..0f21f45 --- /dev/null +++ b/skills/in-progress/strapi-repair/SKILL.md @@ -0,0 +1,150 @@ +--- +name: strapi-repair +description: Diagnoses and repairs common Strapi 5 application project issues — won't start, admin/build failures, 403 API errors, pnpm/monorepo setup, and missing security defaults. Use when the user asks to fix, repair, debug, or harden their Strapi app (not the Strapi monorepo). +--- + +# Strapi repair + +Fix **Strapi 5 application projects** (`package.json`, `config/`, `src/api/`). Not the [Strapi monorepo](https://github.com/strapi/strapi). Not v4→v5 migration (separate skill, out of scope). + +## Hard rules + +1. **Diagnose before destructive fixes** — don't delete `node_modules` first. +2. **Verify after each fix** — `strapi build`, `strapi develop`, or `GET /_health` (204). +3. **Don't mark done** until the reported symptom is resolved, or you've identified an upstream bug with a linked issue. +4. **Security hardening** — recommend by default on repair/audit; apply **breaking** changes only with explicit user approval (see [Security audit](#security-audit)). +5. **Scripts for facts only** — run `diagnose.js` for environment inventory; **read config files yourself** for security audit (no regex scripts on source). + +## Triage + +| Symptom | Start here | +| --- | --- | +| Won't start / crash on boot | [Environment & database](REFERENCE.md#environment--database) | +| Admin blank, chunk load error | [Admin & build](#admin--build-repair) | +| `strapi build` fails | [Package manager](#package-manager-repair) → admin repair | +| 403 on `/api/*` | [API "broken" (usually config)](REFERENCE.md#api-issues-usually-config-not-bugs) | +| Works locally, broken when deployed | [Deployment & URLs](REFERENCE.md#deployment--urls) | +| pnpm / monorepo | [Package manager](#package-manager-repair) | +| "Secure" / "harden" / general repair | [Security audit](#security-audit) at end | + +Run read-only diagnostics first (copy into project or run from skill path): + +```bash +node scripts/diagnose.js +``` + +For security defaults, **read** `config/` and `.env` — see [Security audit](#security-audit). + +## Workflow + +### Phase 0 — Gather facts + +- Strapi version (`package.json`) +- Package manager + version (`pnpm -v`, lockfile type) +- Error message / logs (full stack trace) +- Dev vs production, reverse proxy, monorepo layout +- Recent changes (upgrade, schema edit, env edit) + +### Phase 1 — Least-invasive fix + +Pick **one** matching playbook below. Apply the smallest change that could fix the symptom. + +### Phase 2 — Verify + +```bash +# Pick what fits the symptom +strapi build +strapi develop +curl -s -o /dev/null -w "%{http_code}" http://localhost:1337/_health # expect 204 +``` + +### Phase 3 — Escalate if still broken + +Follow the [clean reinstall ladder](REFERENCE.md#clean-reinstall-ladder) in order. + +### Phase 4 — Security audit (default on repair) + +Unless the user opted out of config changes, complete the [security audit checklist](#security-audit-checklist) by **reading** project files. Apply safe items; propose breaking items for approval. + +Source: [PR #26737](https://github.com/strapi/strapi/pull/26737), [REFERENCE.md — Security](REFERENCE.md#security-hardening-pr-26737). + +## Admin & build repair + +Official references: + +- [Build command](https://docs.strapi.io/cms/cli#strapi-build) +- [Develop command](https://docs.strapi.io/cms/cli#strapi-develop) + +Try in order: + +1. `strapi build` — required after upgrades or admin customizations. +2. Clear stale admin artifacts: `.strapi/client`, `.cache` (project layout may vary). +3. `strapi develop --watch-admin` for local admin dev. +4. Hard browser refresh / disable cache — common for "Failed to fetch dynamically imported module" ([issue #20870](https://github.com/strapi/strapi/issues/20870)). +5. Confirm `react`, `react-dom`, `styled-components`, `react-router-dom` are in the **app** `package.json` (Strapi checks these on build). + +If still failing after rebuild → package manager repair, then reinstall ladder. + +## Package manager repair + +### pnpm (especially v11+) + +Official pnpm migration: [Migrating to v11](https://pnpm.io/migration) — config moves from `package.json#pnpm` and most `.npmrc` settings to **`pnpm-workspace.yaml`**. + +Strapi-specific (community workarounds, not Strapi bugs): + +| Problem | Reference | +| --- | --- | +| `catalog:` version errors on build | [issue #22849](https://github.com/strapi/strapi/issues/22849) — `USE_EXPERIMENTAL_DEPENDENCIES=true` | +| Admin can't resolve React deps | [issue #15992](https://github.com/strapi/strapi/issues/15992), [discussion #25404](https://github.com/strapi/strapi/discussions/25404) — hoisting in `pnpm-workspace.yaml` | +| Monorepo layout | Keep Strapi peer deps in each app's `package.json` | + +Full checklist: [REFERENCE.md — pnpm](REFERENCE.md#pnpm--monorepo). + +### yarn / npm + +- Lockfile + `node_modules` corruption → [clean reinstall ladder](REFERENCE.md#clean-reinstall-ladder). +- Duplicate React → `yarn why react` / `npm ls react`. + +## Security audit + +**Source of truth:** [PR #26737](https://github.com/strapi/strapi/pull/26737). Read config files — do not regex-scan source. Resolve `env()` / factory exports mentally (e.g. `env.bool('X', true)` counts as enabled). + +### Security audit checklist + +Read these files (`.ts` or `.js`): + +- `config/api.*` +- `config/plugins.*` +- `config/server.*` +- `.env` (secrets only — never log values) + +Report to the user as a table: + +| Check | Status | Effective value | Breaking if applied? | Action | +| --- | --- | --- | --- | --- | +| `rest.strictParams` | present / missing / via env | … | **Yes** | … | +| `documents.strictParams` | … | … | **Yes** | … | +| `users-permissions.jwtManagement: 'refresh'` | … | … | **Yes** | … | +| `users-permissions.sessions.httpOnly` | … | … | **Yes** | … | +| `upload.security.allowedTypes` | … | … | **Yes** | … | +| `upload.security.deniedTypes` | … | … | **Yes** | … | +| `webhooks.populateRelations: false` | … | … | Usually no | … | +| `.env` `JWT_SECRET` | … | … | No | … | + +**Status** = `present` | `missing` | `unclear` (split config, imports, or dynamic logic — explain). + +**Apply without asking:** missing `JWT_SECRET`; unset `populateRelations` when user wants PR defaults. + +**Ask first:** `strictParams`, refresh JWT / httpOnly sessions, upload MIME lists, production CORS / SSL / `server.proxy`. + +Also recommend for production (from PR doc): restrict CORS, `DATABASE_SSL`, `server.proxy` behind reverse proxy. Details: [REFERENCE.md — Security](REFERENCE.md#security-hardening-pr-26737). + +## Related skills + +- REST API test setup / 403 in tests → [strapi-rest-api-testing](../strapi-rest-api-testing/SKILL.md) +- Test type routing → [strapi-testing](../strapi-testing/SKILL.md) (if present) + +## Advanced + +- WAIBU issue index, deployment, env/DB, reinstall ladder: [REFERENCE.md](REFERENCE.md) diff --git a/skills/in-progress/strapi-repair/scripts/diagnose.js b/skills/in-progress/strapi-repair/scripts/diagnose.js new file mode 100644 index 0000000..652ecb4 --- /dev/null +++ b/skills/in-progress/strapi-repair/scripts/diagnose.js @@ -0,0 +1,107 @@ +'use strict'; + +/** + * Read-only Strapi project diagnostics (environment facts only). + * Does not parse or audit config source — use the security checklist in SKILL.md for that. + * Run from the Strapi application root: node scripts/diagnose.js + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +const root = process.cwd(); + +function exists(rel) { + return fs.existsSync(path.join(root, rel)); +} + +function readJson(rel) { + try { + return JSON.parse(fs.readFileSync(path.join(root, rel), 'utf8')); + } catch { + return null; + } +} + +function run(cmd) { + try { + return execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim(); + } catch { + return null; + } +} + +function detectPackageManager() { + if (exists('pnpm-lock.yaml')) return 'pnpm'; + if (exists('yarn.lock')) return 'yarn'; + if (exists('package-lock.json')) return 'npm'; + if (exists('bun.lockb') || exists('bun.lock')) return 'bun'; + return 'unknown'; +} + +function envKeysPresent() { + const envPath = path.join(root, '.env'); + if (!fs.existsSync(envPath)) return { file: false, keys: [] }; + const content = fs.readFileSync(envPath, 'utf8'); + const keys = [ + 'APP_KEYS', + 'API_TOKEN_SALT', + 'ADMIN_JWT_SECRET', + 'JWT_SECRET', + 'DATABASE_CLIENT', + ]; + return { + file: true, + keys: keys.map((k) => ({ name: k, present: new RegExp(`^${k}=`, 'm').test(content) })), + }; +} + +const pkg = readJson('package.json') || {}; +const pm = detectPackageManager(); +const strapiVersion = + pkg.dependencies?.['@strapi/strapi'] || pkg.devDependencies?.['@strapi/strapi'] || 'not found'; + +const report = { + cwd: root, + node: process.version, + packageManager: pm, + packageManagerVersion: pm === 'pnpm' ? run('pnpm -v') : pm === 'yarn' ? run('yarn -v') : pm === 'npm' ? run('npm -v') : null, + strapiVersion, + paths: { + dist: exists('dist'), + dotStrapi: exists('.strapi'), + dotStrapiClient: exists('.strapi/client'), + dotCache: exists('.cache'), + dotTmp: exists('.tmp'), + configEnvTest: exists('config/env/test'), + pnpmWorkspaceYaml: exists('pnpm-workspace.yaml'), + }, + adminPeerDeps: ['react', 'react-dom', 'styled-components', 'react-router-dom'].map((dep) => ({ + name: dep, + declared: Boolean(pkg.dependencies?.[dep] || pkg.devDependencies?.[dep]), + })), + env: envKeysPresent(), +}; + +const warnings = []; + +if (pm === 'pnpm' && !report.paths.pnpmWorkspaceYaml) { + const major = parseInt(report.packageManagerVersion || '0', 10); + if (major >= 11) { + warnings.push('pnpm 11+ expects pnpm-workspace.yaml — see https://pnpm.io/migration'); + } +} + +if (report.paths.dist) { + warnings.push('dist/ exists — can shadow config in some setups; see docs.strapi.io/cms/testing'); +} + +const missingPeers = report.adminPeerDeps.filter((d) => !d.declared); +if (missingPeers.length) { + warnings.push( + `Admin peer deps not in package.json: ${missingPeers.map((d) => d.name).join(', ')} — strapi build may fail` + ); +} + +console.log(JSON.stringify({ ...report, warnings }, null, 2));