diff --git a/docs/plans/2026-02-20-devops-features-design.md b/docs/plans/2026-02-20-devops-features-design.md new file mode 100644 index 0000000..b9a351d --- /dev/null +++ b/docs/plans/2026-02-20-devops-features-design.md @@ -0,0 +1,549 @@ +# DevOps Features Design + +**Date:** 2026-02-20 +**Status:** Approved +**Author:** Claude (AI Assistant) + +## Overview + +Implement four DevOps features for the CV application: +1. **Swagger/OpenAPI Documentation** - Interactive API docs at `/api/docs` +2. **Database Migrations** - Knex.js-based migration system with auto-migration +3. **Quality Gates** - Code coverage, bundle size, npm audit, Lighthouse CI +4. **Secret Scanning** - Gitleaks pre-commit hook + +## 1. Swagger/OpenAPI Documentation + +### Architecture + +``` +Route Files (JSDoc comments) + │ + ▼ + swagger-jsdoc + (generates spec at runtime) + │ + ▼ + swagger-ui-express + │ + ▼ + GET /api/docs → Interactive Swagger UI + GET /api/docs.json → Raw OpenAPI spec +``` + +### Endpoints + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/docs` | Swagger UI interface | +| GET | `/api/docs.json` | Raw OpenAPI JSON spec | + +### Implementation + +**Dependencies:** +- `swagger-jsdoc` - Generate OpenAPI spec from JSDoc +- `swagger-ui-express` - Serve Swagger UI + +**JSDoc format in route files:** +```javascript +/** + * @openapi + * /api/cv: + * get: + * summary: Get CV data + * tags: [CV] + * responses: + * 200: + * description: CV data + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CVData' + * 404: + * description: CV data not found + */ +router.get('/', (req, res) => { ... }); + +/** + * @openapi + * /api/cv: + * put: + * summary: Update CV data + * tags: [CV] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CVData' + * responses: + * 200: + * description: CV data updated + * 401: + * description: Unauthorized + */ +router.put('/', authMiddleware, (req, res) => { ... }); +``` + +**Swagger setup:** +```javascript +// backend/routes/docs.js +import swaggerJsdoc from 'swagger-jsdoc'; +import swaggerUi from 'swagger-ui-express'; + +const options = { + definition: { + openapi: '3.0.0', + info: { + title: 'CV API', + version: '1.0.0', + description: 'API for CV/Resume management', + }, + servers: [ + { url: '/api', description: 'API server' } + ], + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + }, + }, + schemas: { + CVData: { /* schema definition */ }, + }, + }, + }, + apis: ['./routes/*.js'], +}; + +const specs = swaggerJsdoc(options); + +router.use('/docs', swaggerUi.serve, swaggerUi.setup(specs)); +router.get('/docs.json', (req, res) => res.json(specs)); +``` + +--- + +## 2. Database Migrations (Knex.js) + +### Architecture + +``` +backend/ +├── knexfile.js # Knex configuration +├── migrations/ +│ ├── 20260220000001_initial_schema.js +│ └── 20260220000002_add_auth_tables.js +├── seeds/ +│ └── initial_cv_data.js +└── db/ + ├── connection.js # Knex instance + └── init.js # Auto-migration on startup +``` + +### Knex Configuration + +```javascript +// knexfile.js +export default { + client: 'better-sqlite3', + connection: { + filename: process.env.DB_PATH || './data/cv.db' + }, + migrations: { + directory: './migrations', + tableName: 'knex_migrations' + }, + seeds: { + directory: './seeds' + }, + useNullAsDefault: true +}; +``` + +### Migration File Format + +```javascript +// migrations/20260220000001_initial_schema.js +export async function up(knex) { + await knex.schema.createTable('cv_data', (table) => { + table.integer('id').primary().checkTo('=', 1); + table.text('data').notNullable(); + table.datetime('updated_at').defaultTo(knex.fn.now()); + }); +} + +export async function down(knex) { + await knex.schema.dropTableIfExists('cv_data'); +} +``` + +### Auto-Migration on Startup + +```javascript +// db/init.js +import knex from 'knex'; +import config from '../knexfile.js'; + +let db = null; + +export async function getDB() { + if (!db) { + db = knex(config); + await db.migrate.latest(); + } + return db; +} + +export async function closeDB() { + if (db) { + await db.destroy(); + db = null; + } +} +``` + +### CLI Commands + +```bash +# Create new migration +npx knex migrate:make migration_name --knexfile knexfile.js + +# Run migrations manually +npx knex migrate:latest --knexfile knexfile.js + +# Rollback last migration +npx knex migrate:rollback --knexfile knexfile.js + +# Run seeds +npx knex seed:run --knexfile knexfile.js +``` + +### Dependencies + +- `knex` - SQL query builder and migrations + +--- + +## 3. Quality Gates + +### Overview + +| Gate | Tool | Threshold | Action | +|------|------|-----------|--------| +| Code Coverage | vitest + Codecov | 80% minimum | Fail CI | +| Bundle Size | bundlesize | 500KB max | Fail CI | +| npm audit | npm audit | Moderate+ | Fail CI | +| Lighthouse | @lhci/cli | Performance 90+ | Fail CI | + +### CI Workflow Integration + +```yaml +# .github/workflows/ci.yml additions + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + # Code Coverage + - name: Run tests with coverage + run: npm run test:coverage + + - name: Upload to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + + # Bundle Size + - name: Build + run: npm run build + + - name: Check bundle size + run: npx bundlesize + + # npm audit + - name: Security audit + run: npm audit --audit-level=moderate + + lighthouse: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run Lighthouse CI + run: npm run test:lighthouse +``` + +### Configuration Files + +**package.json:** +```json +{ + "scripts": { + "test:coverage": "vitest run --coverage" + }, + "bundlesize": [ + { + "path": "./dist/assets/*.js", + "maxSize": "500kb" + }, + { + "path": "./dist/assets/*.css", + "maxSize": "100kb" + } + ] +} +``` + +**vitest.config.js:** +```javascript +export default defineConfig({ + test: { + coverage: { + provider: 'v8', + reporter: ['text', 'lcov', 'html'], + exclude: ['node_modules/', 'tests/'], + lines: 80, + functions: 80, + branches: 80, + statements: 80 + } + } +}); +``` + +**.lighthouserc.json:** +```json +{ + "ci": { + "collect": { + "url": ["http://localhost:4173/"], + "numberOfRuns": 3 + }, + "assert": { + "assertions": { + "categories:performance": ["error", { "minScore": 0.9 }], + "categories:accessibility": ["warn", { "minScore": 0.9 }], + "categories:best-practices": ["warn", { "minScore": 0.9 }] + } + }, + "upload": { + "target": "temporary-public-storage" + } + } +} +``` + +### Dependencies + +- `@vitest/coverage-v8` - Coverage reporter +- `bundlesize` - Bundle size checking + +### Secrets Required + +| Secret | Description | +|--------|-------------| +| `CODECOV_TOKEN` | Codecov upload token | + +--- + +## 4. Secret Scanning (Gitleaks) + +### Architecture + +``` +Developer stages files + │ + ▼ + git commit + │ + ▼ + .husky/pre-commit + │ + ├─► npm run lint + │ + └─► gitleaks protect --staged + │ + ▼ + Secrets found? + │ │ + Yes No + │ │ + ▼ ▼ + Block commit Allow commit +``` + +### Pre-commit Hook + +```bash +# .husky/pre-commit +npm run lint + +# Gitleaks secret scanning +if command -v gitleaks &> /dev/null; then + gitleaks protect --verbose --staged + if [ $? -eq 1 ]; then + echo "" + echo "❌ Secrets detected in staged files!" + echo "Please remove sensitive data before committing." + echo "" + exit 1 + fi +fi +``` + +### Gitleaks Configuration + +```toml +# .gitleaks.toml +title = "Gitleaks Configuration" + +[extend] +useDefault = true + +[[allowlists]] +description = "Allowlisted files" +paths = [ + 'package-lock.json', + 'backend/package-lock.json', + '.*\\.md$' +] + +[[allowlists]] +description = "Allowlisted patterns" +regexes = [ + 'VITE_API_URL.*localhost', +] +``` + +### Developer Setup + +Gitleaks must be installed on developer machines: + +```bash +# macOS +brew install gitleaks + +# Linux +# Download from https://github.com/gitleaks/gitleaks/releases +# Or use go install +go install github.com/gitleaks/gitleaks/v8@latest + +# Windows +scoop install gitleaks +# Or +choco install gitleaks +``` + +### Documentation + +Add to README or CONTRIBUTING.md: + +```markdown +### Pre-commit Setup + +This project uses Gitleaks for secret scanning. Install it before committing: + +```bash +# macOS +brew install gitleaks + +# Linux +curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.18.1/gitleaks_8.18.1_linux_x64.tar.gz | tar -xz +sudo mv gitleaks /usr/local/bin/ +``` + +The pre-commit hook will automatically scan for secrets before each commit. +``` + +--- + +## Implementation Summary + +### New Dependencies + +**Backend:** +- `knex` - Database migrations and query builder +- `swagger-jsdoc` - OpenAPI spec generation +- `swagger-ui-express` - Swagger UI middleware + +**Frontend:** +- `@vitest/coverage-v8` - Coverage reporting +- `bundlesize` - Bundle size checking + +**Dev:** +- `gitleaks` (binary, not npm package) + +### New Files + +``` +backend/ +├── knexfile.js +├── migrations/ +│ └── 20260220000001_initial_schema.js +├── seeds/ +│ └── initial_cv_data.js +├── routes/ +│ └── docs.js +└── db/ + └── connection.js + +.gitleaks.toml +.lighthouserc.json +``` + +### Modified Files + +``` +package.json # Add scripts, bundlesize config +vitest.config.js # Add coverage config +.github/workflows/ci.yml # Add quality gates +.husky/pre-commit # Add gitleaks +backend/db/init.js # Use Knex instead of better-sqlite3 directly +backend/routes/cv.js # Add JSDoc comments +backend/routes/auth.js # Add JSDoc comments +README.md # Add gitleaks setup instructions +``` + +--- + +## Post-Implementation Checklist + +- [ ] Install dependencies +- [ ] Configure Knex and create initial migration +- [ ] Add Swagger UI route +- [ ] Add JSDoc comments to all API routes +- [ ] Configure Codecov (add CODECOV_TOKEN secret) +- [ ] Add bundlesize configuration +- [ ] Update CI workflow with quality gates +- [ ] Add gitleaks to pre-commit hook +- [ ] Update documentation