-
Notifications
You must be signed in to change notification settings - Fork 0
Versao 1 #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Versao 1 #15
Changes from all commits
19bfcef
039aa40
fc23776
7a49e82
507c227
1ebba36
80f595e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,335 @@ | ||
| name: CI — Mães em Ação | ||
|
|
||
| on: | ||
| push: | ||
| branches: [versao_1, main] | ||
| pull_request: | ||
| branches: [versao_1, main] | ||
|
|
||
| env: | ||
| NODE_VERSION: "20" | ||
|
|
||
| jobs: | ||
| # ──────────────────────────────────────────────────────────────── | ||
| # JOB 1 — Lint (Backend + Frontend em paralelo) | ||
| # ──────────────────────────────────────────────────────────────── | ||
| lint-backend: | ||
| name: "Lint · Backend" | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: backend | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ env.NODE_VERSION }} | ||
| cache: "npm" | ||
| cache-dependency-path: backend/package-lock.json | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Run ESLint | ||
| run: npm run lint | ||
|
|
||
| lint-frontend: | ||
| name: "Lint · Frontend" | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: frontend | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ env.NODE_VERSION }} | ||
| cache: "npm" | ||
| cache-dependency-path: frontend/package-lock.json | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Run ESLint | ||
| run: npm run lint | ||
|
|
||
| # ──────────────────────────────────────────────────────────────── | ||
| # JOB 2 — Testes (Backend + Frontend em paralelo) | ||
| # ──────────────────────────────────────────────────────────────── | ||
| test-backend: | ||
| name: "Testes · Backend (Jest)" | ||
| runs-on: ubuntu-latest | ||
| needs: lint-backend | ||
| defaults: | ||
| run: | ||
| working-directory: backend | ||
|
|
||
| env: | ||
| NODE_ENV: test | ||
| JWT_SECRET: "ci_test_secret_key_at_least_32_chars_long!!" | ||
| API_KEY_SERVIDORES: "ci_test_api_key_64_chars_for_scanner_balcao_valid_1234567890abcdef" | ||
| GEMINI_API_KEY: "dummy_gemini_key_for_ci" | ||
| GROQ_API_KEY: "gsk_dummy_groq_key_for_ci_testing_purposes" | ||
| DATABASE_URL: "postgresql://ci:ci@localhost:5432/ci_test" | ||
| DIRECT_URL: "postgresql://ci:ci@localhost:5432/ci_test" | ||
| SUPABASE_URL: "https://dummy.supabase.co" | ||
| SUPABASE_SERVICE_KEY: "dummy_service_key_for_ci" | ||
| QSTASH_TOKEN: "dummy_qstash_token" | ||
| QSTASH_CURRENT_SIGNING_KEY: "dummy_signing_key" | ||
| QSTASH_NEXT_SIGNING_KEY: "dummy_next_signing_key" | ||
| SALARIO_MINIMO_ATUAL: "1621.00" | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ env.NODE_VERSION }} | ||
| cache: "npm" | ||
| cache-dependency-path: backend/package-lock.json | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Generate Prisma Client | ||
| run: npx prisma generate | ||
|
|
||
| - name: Run Jest tests with coverage | ||
| run: npm test -- --forceExit | ||
|
|
||
| - name: Upload coverage report | ||
| uses: actions/upload-artifact@v4 | ||
| if: always() | ||
| with: | ||
| name: backend-coverage | ||
| path: backend/logs/coverage/ | ||
| retention-days: 7 | ||
|
|
||
| - name: Coverage summary | ||
| if: always() | ||
| run: | | ||
| echo "## Backend Coverage" >> $GITHUB_STEP_SUMMARY | ||
| echo '```' >> $GITHUB_STEP_SUMMARY | ||
| cat logs/coverage/coverage-summary.json 2>/dev/null | node -e " | ||
| const data = require('fs').readFileSync('/dev/stdin','utf8'); | ||
| try { | ||
| const j = JSON.parse(data).total; | ||
| console.log('Statements:', j.statements.pct + '%'); | ||
| console.log('Branches: ', j.branches.pct + '%'); | ||
| console.log('Functions: ', j.functions.pct + '%'); | ||
| console.log('Lines: ', j.lines.pct + '%'); | ||
| } catch { console.log('Coverage data not available'); } | ||
| " 2>/dev/null || echo "Coverage data not available" | ||
| echo '```' >> $GITHUB_STEP_SUMMARY | ||
|
|
||
| test-frontend: | ||
| name: "Testes · Frontend (Vitest)" | ||
| runs-on: ubuntu-latest | ||
| needs: lint-frontend | ||
| defaults: | ||
| run: | ||
| working-directory: frontend | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ env.NODE_VERSION }} | ||
| cache: "npm" | ||
| cache-dependency-path: frontend/package-lock.json | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Run Vitest tests with coverage | ||
| run: npm run test:coverage | ||
| env: | ||
| VITE_API_URL: "http://localhost:8000" | ||
|
|
||
| - name: Upload coverage report | ||
| uses: actions/upload-artifact@v4 | ||
| if: always() | ||
| with: | ||
| name: frontend-coverage | ||
| path: frontend/coverage/ | ||
| retention-days: 7 | ||
|
|
||
| - name: Coverage summary | ||
| if: always() | ||
| run: | | ||
| echo "## Frontend Coverage" >> $GITHUB_STEP_SUMMARY | ||
| echo '```' >> $GITHUB_STEP_SUMMARY | ||
| cat coverage/coverage-summary.json 2>/dev/null | node -e " | ||
| const data = require('fs').readFileSync('/dev/stdin','utf8'); | ||
| try { | ||
| const j = JSON.parse(data).total; | ||
| console.log('Statements:', j.statements.pct + '%'); | ||
| console.log('Branches: ', j.branches.pct + '%'); | ||
| console.log('Functions: ', j.functions.pct + '%'); | ||
| console.log('Lines: ', j.lines.pct + '%'); | ||
| } catch { console.log('Coverage data not available'); } | ||
| " 2>/dev/null || echo "Coverage data not available" | ||
| echo '```' >> $GITHUB_STEP_SUMMARY | ||
|
|
||
| # ──────────────────────────────────────────────────────────────── | ||
| # JOB 3 — Security Audit (npm audit --audit-level=high) | ||
| # ──────────────────────────────────────────────────────────────── | ||
| security-audit: | ||
| name: "Segurança · npm audit" | ||
| runs-on: ubuntu-latest | ||
| # Roda em paralelo com testes — não bloqueia o pipeline por lint | ||
| needs: [lint-backend, lint-frontend] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ env.NODE_VERSION }} | ||
|
|
||
| - name: Audit Backend | ||
| working-directory: backend | ||
| run: | | ||
| npm ci | ||
| npm audit --audit-level=high --json > audit-backend.json || true | ||
| CRITICAL=$(node -e " | ||
| const a = require('./audit-backend.json'); | ||
| const v = a.metadata?.vulnerabilities || {}; | ||
| console.log(v.critical || 0); | ||
| ") | ||
| HIGH=$(node -e " | ||
| const a = require('./audit-backend.json'); | ||
| const v = a.metadata?.vulnerabilities || {}; | ||
| console.log(v.high || 0); | ||
| ") | ||
| echo "Backend — Critical: $CRITICAL | High: $HIGH" | ||
| echo "### Backend Audit" >> $GITHUB_STEP_SUMMARY | ||
| echo "- 🔴 Critical: $CRITICAL" >> $GITHUB_STEP_SUMMARY | ||
| echo "- 🟠 High: $HIGH" >> $GITHUB_STEP_SUMMARY | ||
| if [ "$CRITICAL" -gt "0" ]; then | ||
| echo "❌ Vulnerabilidades críticas encontradas no backend!" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Audit Frontend | ||
| working-directory: frontend | ||
| run: | | ||
| npm ci | ||
| npm audit --audit-level=high --json > audit-frontend.json || true | ||
| CRITICAL=$(node -e " | ||
| const a = require('./audit-frontend.json'); | ||
| const v = a.metadata?.vulnerabilities || {}; | ||
| console.log(v.critical || 0); | ||
| ") | ||
| HIGH=$(node -e " | ||
| const a = require('./audit-frontend.json'); | ||
| const v = a.metadata?.vulnerabilities || {}; | ||
| console.log(v.high || 0); | ||
| ") | ||
| echo "Frontend — Critical: $CRITICAL | High: $HIGH" | ||
| echo "### Frontend Audit" >> $GITHUB_STEP_SUMMARY | ||
| echo "- 🔴 Critical: $CRITICAL" >> $GITHUB_STEP_SUMMARY | ||
| echo "- 🟠 High: $HIGH" >> $GITHUB_STEP_SUMMARY | ||
| if [ "$CRITICAL" -gt "0" ]; then | ||
| echo "❌ Vulnerabilidades críticas encontradas no frontend!" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # ──────────────────────────────────────────────────────────────── | ||
| # JOB 4 — Build (valida que o código compila corretamente) | ||
| # ──────────────────────────────────────────────────────────────── | ||
| build-backend: | ||
| name: "Build · Backend (prisma generate)" | ||
| runs-on: ubuntu-latest | ||
| needs: test-backend | ||
| defaults: | ||
| run: | ||
| working-directory: backend | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ env.NODE_VERSION }} | ||
| cache: "npm" | ||
| cache-dependency-path: backend/package-lock.json | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Generate Prisma Client | ||
| run: npx prisma generate | ||
|
|
||
| - name: Verify server.js imports (syntax check) | ||
| run: node --check server.js | ||
|
|
||
| build-frontend: | ||
| name: "Build · Frontend (vite build)" | ||
| runs-on: ubuntu-latest | ||
| needs: test-frontend | ||
| defaults: | ||
| run: | ||
| working-directory: frontend | ||
|
|
||
| env: | ||
| VITE_API_URL: "https://api.mutirao.dpe.ba.gov.br" | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ env.NODE_VERSION }} | ||
| cache: "npm" | ||
| cache-dependency-path: frontend/package-lock.json | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Build | ||
| run: npm run build | ||
|
|
||
| - name: Upload build artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: frontend-dist | ||
| path: frontend/dist/ | ||
| retention-days: 3 | ||
|
|
||
| # ──────────────────────────────────────────────────────────────── | ||
| # JOB 5 — Status final (gate para merge) | ||
| # ──────────────────────────────────────────────────────────────── | ||
| ci-success: | ||
| name: "✅ CI Completo" | ||
| runs-on: ubuntu-latest | ||
| needs: [build-backend, build-frontend, security-audit] | ||
| if: always() | ||
|
|
||
| steps: | ||
| - name: Verificar se todos os jobs passaram | ||
| run: | | ||
| if [[ "${{ needs.build-backend.result }}" != "success" ]] || \ | ||
| [[ "${{ needs.build-frontend.result }}" != "success" ]] || \ | ||
| [[ "${{ needs.security-audit.result }}" != "success" ]]; then | ||
| echo "❌ Um ou mais jobs falharam" | ||
| echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY | ||
| echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY | ||
| echo "| build-backend | ${{ needs.build-backend.result }} |" >> $GITHUB_STEP_SUMMARY | ||
| echo "| build-frontend | ${{ needs.build-frontend.result }} |" >> $GITHUB_STEP_SUMMARY | ||
| echo "| security-audit | ${{ needs.security-audit.result }} |" >> $GITHUB_STEP_SUMMARY | ||
| exit 1 | ||
| fi | ||
| echo "✅ Todos os jobs passaram com sucesso!" >> $GITHUB_STEP_SUMMARY | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| # Arquitetura do Sistema — Mães em Ação · DPE-BA | ||
|
|
||
| > **Versão:** 3.2 · **Atualizado em:** 2026-04-24 (CodeRabbit Audit + Dashboard v3.0 + CPF Mandatory) | ||
| > **Versão:** 4.2 · **Atualizado em:** 2026-04-26 (Hardening de Segurança + Assistência Compartilhada) | ||
| > **Contexto:** Mutirão estadual da Defensoria Pública da Bahia | ||
|
|
||
| --- | ||
|
|
@@ -180,12 +180,12 @@ stateDiagram-v2 | |
|
|
||
| ### Locking — Sessões e Concorrência | ||
|
|
||
| - **Nível 1 (Servidor):** Bloqueia edição de dados jurídicos e relato | ||
| - **Nível 2 (Defensor):** Bloqueia a etapa de protocolo e finalização | ||
| - **HTTP 423 (Locked):** Retorno padrão quando outro usuário detém o lock | ||
| - **Admin Bypass:** Administradores podem forçar destravamento via painel | ||
| - **Nível 1 (Servidor/Estagiário/Defensor/Coordenador):** Atribuição de `servidor_id` — bloqueia edição de dados jurídicos e relato. Ativo em `pronto_para_analise` e `em_atendimento`. | ||
| - **Nível 2 (Defensor/Coordenador/Admin):** Atribuição de `defensor_id` — bloqueia etapa de protocolo e finalização. Ativo em `liberado_para_protocolo` e `em_protocolo`. **`servidor` e `estagiario` NUNCA adquirem Nível 2.** | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistência: A linha 184 declara que o Nível 2 é adquirido por "Defensor/Coordenador/Admin", omitindo 🤖 Prompt for AI Agents |
||
| - **Isolamento de Unidade:** Middleware `requireSameUnit` bloqueia IDOR. **Admin e Gestor** possuem bypass global. | ||
| - **HTTP 423 (Locked):** Retorno padrão quando outro usuário detém o lock. | ||
| - **Unlock Privilegiado:** Administradores, Gestores e Coordenadores podem forçar destravamento via painel. | ||
| - **Auto-release:** Lock liberado após 30min de inatividade. | ||
| - **Manual Unlock:** Administradores podem forçar destravamento via painel. | ||
|
|
||
| --- | ||
|
|
||
|
|
@@ -329,15 +329,17 @@ sequenceDiagram | |
|
|
||
| ### Permissões por Cargo (RBAC) | ||
|
|
||
| | Cargo | Leitura | Escrita | Admin | | ||
| |:------|:--------|:--------|:------| | ||
| | `admin` | ✅ | ✅ | ✅ | | ||
| | `defensor` | ✅ | ✅ | ❌ | | ||
| | `estagiario` | ✅ | ✅ | ❌ | | ||
| | `recepcao` | ✅ | ✅ | ❌ | | ||
| | `visualizador` | ✅ | ❌ | ❌ | | ||
| | Cargo | Leitura | Escrita | Protocolo/Finalizar | Admin/Global | Unlock | | ||
| |:------|:--------|:--------|:--------------------|:-------------|:-------| | ||
| | `admin` | ✅ | ✅ | ✅ | ✅ | ✅ | | ||
| | `gestor` | ✅ | ✅ | ✅ | ✅ | ✅ | | ||
| | `coordenador` | ✅ | ✅ | ✅ | ❌ | ✅ | | ||
| | `defensor` | ✅ | ✅ | ✅ | ❌ | ❌ | | ||
| | `servidor` | ✅ | ✅ | ❌ | ❌ | ❌ | | ||
| | `estagiario` | ✅ | ✅ | ❌ | ❌ | ❌ | | ||
|
|
||
| > **Middleware:** `requireWriteAccess` bloqueia `visualizador` de operações POST/PATCH/DELETE com HTTP 403. | ||
| > **Middleware:** `requireWriteAccess` usa whitelist positiva: apenas `admin`, `gestor`, `coordenador`, `defensor`, `servidor`, `estagiario` passam. | ||
| > **Isolamento de Unidade:** Middleware `requireSameUnit` bloqueia IDOR. Admins e Gestores possuem bypass global. | ||
|
|
||
| --- | ||
|
|
||
|
|
@@ -489,6 +491,7 @@ QSTASH_NEXT_SIGNING_KEY=... | |
| JWT_SECRET=64_chars_random_string | ||
| API_KEY_SERVIDORES=64_chars_random | ||
| SALARIO_MINIMO_ATUAL=1621.00 | ||
| ALLOWED_ORIGINS=https://maesemacao.defsulbahia.com.br,https://maes-acao.vercel.app | ||
|
|
||
| # Frontend | ||
| VITE_API_URL=https://api.mutirao.dpe.ba.gov.br | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Os resumos de cobertura nunca vão ler percentuais reais.
Hoje esses steps procuram
coverage-summary.json, mas nembackend/jest.config.jsnemfrontend/vitest.config.jsgeram esse arquivo — ambos só exportamtext,lcovehtml. Na prática, o summary sempre cai emCoverage data not available, mesmo com cobertura gerada. Alinhe o workflow com um artefato existente ou adicionejson-summarynos dois configs.💡 Ajuste sugerido nos configs de teste
Also applies to: 166-180
🤖 Prompt for AI Agents