docs(release): add devops features design document

This commit is contained in:
Tuan-Dat Tran
2026-02-20 16:46:34 +01:00
parent 950d812159
commit 252afb7e9f

View File

@@ -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