@dewtech/dare-cli 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +764 -764
- package/dist/__tests__/confidence.test.js +13 -13
- package/dist/__tests__/dag-converter.test.js +56 -56
- package/dist/__tests__/mcp-server/server.test.js +3 -16
- package/dist/__tests__/mcp-server/server.test.js.map +1 -1
- package/dist/__tests__/project-generator.test.js +2 -2
- package/dist/__tests__/project-generator.test.js.map +1 -1
- package/dist/__tests__/refine.test.js +49 -49
- package/dist/__tests__/reverse-collection.test.js +6 -6
- package/dist/__tests__/review.test.js +38 -38
- package/dist/__tests__/security-hardening.test.d.ts +2 -0
- package/dist/__tests__/security-hardening.test.d.ts.map +1 -0
- package/dist/__tests__/security-hardening.test.js +101 -0
- package/dist/__tests__/security-hardening.test.js.map +1 -0
- package/dist/__tests__/validate.test.js +65 -65
- package/dist/bin/dare.js +0 -0
- package/dist/commands/__tests__/init-validation.test.d.ts +2 -0
- package/dist/commands/__tests__/init-validation.test.d.ts.map +1 -0
- package/dist/commands/__tests__/init-validation.test.js +81 -0
- package/dist/commands/__tests__/init-validation.test.js.map +1 -0
- package/dist/commands/__tests__/init.integration.spec.js +6 -4
- package/dist/commands/__tests__/init.integration.spec.js.map +1 -1
- package/dist/commands/__tests__/init.spec.d.ts +2 -0
- package/dist/commands/__tests__/init.spec.d.ts.map +1 -0
- package/dist/commands/__tests__/init.spec.js +88 -0
- package/dist/commands/__tests__/init.spec.js.map +1 -0
- package/dist/commands/blueprint.js +122 -122
- package/dist/commands/design.js +20 -20
- package/dist/commands/init-validation.d.ts +22 -0
- package/dist/commands/init-validation.d.ts.map +1 -0
- package/dist/commands/init-validation.js +54 -0
- package/dist/commands/init-validation.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +26 -10
- package/dist/commands/init.js.map +1 -1
- package/dist/graphrag/graph-rag.js +24 -24
- package/dist/mcp-server/__tests__/auth.test.d.ts +2 -0
- package/dist/mcp-server/__tests__/auth.test.d.ts.map +1 -0
- package/dist/mcp-server/__tests__/auth.test.js +72 -0
- package/dist/mcp-server/__tests__/auth.test.js.map +1 -0
- package/dist/mcp-server/__tests__/boot-config.test.d.ts +2 -0
- package/dist/mcp-server/__tests__/boot-config.test.d.ts.map +1 -0
- package/dist/mcp-server/__tests__/boot-config.test.js +29 -0
- package/dist/mcp-server/__tests__/boot-config.test.js.map +1 -0
- package/dist/mcp-server/__tests__/error-sanitize.test.d.ts +2 -0
- package/dist/mcp-server/__tests__/error-sanitize.test.d.ts.map +1 -0
- package/dist/mcp-server/__tests__/error-sanitize.test.js +66 -0
- package/dist/mcp-server/__tests__/error-sanitize.test.js.map +1 -0
- package/dist/mcp-server/__tests__/path-confinement.test.d.ts +2 -0
- package/dist/mcp-server/__tests__/path-confinement.test.d.ts.map +1 -0
- package/dist/mcp-server/__tests__/path-confinement.test.js +135 -0
- package/dist/mcp-server/__tests__/path-confinement.test.js.map +1 -0
- package/dist/mcp-server/bin/server.js +18 -6
- package/dist/mcp-server/bin/server.js.map +1 -1
- package/dist/mcp-server/boot-config.d.ts +6 -0
- package/dist/mcp-server/boot-config.d.ts.map +1 -0
- package/dist/mcp-server/boot-config.js +17 -0
- package/dist/mcp-server/boot-config.js.map +1 -0
- package/dist/mcp-server/middleware/auth.d.ts +10 -0
- package/dist/mcp-server/middleware/auth.d.ts.map +1 -0
- package/dist/mcp-server/middleware/auth.js +44 -0
- package/dist/mcp-server/middleware/auth.js.map +1 -0
- package/dist/mcp-server/middleware/cors.d.ts +6 -0
- package/dist/mcp-server/middleware/cors.d.ts.map +1 -0
- package/dist/mcp-server/middleware/cors.js +30 -0
- package/dist/mcp-server/middleware/cors.js.map +1 -0
- package/dist/mcp-server/middleware/error-handler.d.ts +11 -0
- package/dist/mcp-server/middleware/error-handler.d.ts.map +1 -0
- package/dist/mcp-server/middleware/error-handler.js +14 -0
- package/dist/mcp-server/middleware/error-handler.js.map +1 -0
- package/dist/mcp-server/server.d.ts +7 -2
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +185 -105
- package/dist/mcp-server/server.js.map +1 -1
- package/dist/skills/registry-mock.json +109 -109
- package/dist/skills/tests/manifest.spec.js +20 -20
- package/dist/stacks/__tests__/dna-emitter.spec.js +6 -6
- package/dist/stacks/dna-emitter.js +69 -69
- package/dist/stacks/ruby-rails-8/scaffold.js +15 -15
- package/dist/utils/project-generator.d.ts.map +1 -1
- package/dist/utils/project-generator.js +254 -252
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/stack-bootstrap.js +371 -371
- package/dist/utils/templates.js +394 -394
- package/dist/verification/__tests__/anti-tamper.test.js +13 -13
- package/package.json +96 -93
- package/templates/DARE-dag-example.yaml +280 -280
- package/templates/UPDATE-MANIFEST.json +68 -68
- package/templates/backend/node-nestjs/.env.example +9 -9
- package/templates/backend/node-nestjs/nest-cli.json +8 -8
- package/templates/backend/node-nestjs/package.json +50 -50
- package/templates/backend/node-nestjs/src/app.controller.ts +12 -12
- package/templates/backend/node-nestjs/src/app.module.ts +15 -15
- package/templates/backend/node-nestjs/src/app.service.ts +8 -8
- package/templates/backend/node-nestjs/src/main.ts +24 -24
- package/templates/backend/node-nestjs/tsconfig.json +21 -21
- package/templates/backend/php-laravel/.env.example +22 -22
- package/templates/backend/php-laravel/app/Http/Controllers/HealthController.php +15 -15
- package/templates/backend/php-laravel/composer.json +40 -40
- package/templates/backend/python-fastapi/.env.example +4 -4
- package/templates/backend/python-fastapi/app/api/router.py +8 -8
- package/templates/backend/python-fastapi/app/core/config.py +20 -20
- package/templates/backend/python-fastapi/main.py +35 -35
- package/templates/backend/python-fastapi/requirements.txt +13 -13
- package/templates/backend/rust-axum/.env.example +3 -3
- package/templates/backend/rust-axum/Cargo.toml +23 -23
- package/templates/backend/rust-axum/src/errors.rs +30 -30
- package/templates/backend/rust-axum/src/main.rs +32 -32
- package/templates/backend/rust-axum/src/routes.rs +6 -6
- package/templates/frontend/leptos-csr/.cargo/config.toml +2 -2
- package/templates/frontend/leptos-csr/Cargo.toml +16 -16
- package/templates/frontend/leptos-csr/Trunk.toml +10 -10
- package/templates/frontend/leptos-csr/index.html +11 -11
- package/templates/frontend/leptos-csr/src/lib.rs +20 -20
- package/templates/frontend/leptos-csr/style/main.scss +19 -19
- package/templates/frontend/leptos-fullstack/.cargo/config.toml +4 -4
- package/templates/frontend/leptos-fullstack/Cargo.toml +56 -56
- package/templates/frontend/leptos-fullstack/src/app.rs +49 -49
- package/templates/frontend/leptos-fullstack/src/lib.rs +9 -9
- package/templates/frontend/leptos-fullstack/src/main.rs +29 -29
- package/templates/frontend/leptos-fullstack/style/main.scss +19 -19
- package/templates/frontend/react/index.html +12 -12
- package/templates/frontend/react/package.json +35 -35
- package/templates/frontend/react/src/App.tsx +25 -25
- package/templates/frontend/react/src/main.tsx +9 -9
- package/templates/frontend/vue/package.json +32 -32
- package/templates/frontend/vue/src/App.vue +7 -7
- package/templates/frontend/vue/src/main.ts +10 -10
- package/templates/frontend/vue/src/router/index.ts +14 -14
- package/templates/frontend/vue/src/views/HomeView.vue +6 -6
- package/templates/hooks/pre-commit-dare-validate +24 -24
- package/templates/ide/antigravity/.agents/skills/dare-ax/SKILL.md +152 -152
- package/templates/ide/antigravity/.agents/skills/dare-bench/SKILL.md +21 -21
- package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +368 -368
- package/templates/ide/antigravity/.agents/skills/dare-bootstrap/SKILL.md +32 -32
- package/templates/ide/antigravity/.agents/skills/dare-bugfix-design/SKILL.md +76 -76
- package/templates/ide/antigravity/.agents/skills/dare-dag/SKILL.md +32 -32
- package/templates/ide/antigravity/.agents/skills/dare-dag-build/SKILL.md +154 -154
- package/templates/ide/antigravity/.agents/skills/dare-dag-run/SKILL.md +130 -130
- package/templates/ide/antigravity/.agents/skills/dare-dag-runner/SKILL.md +203 -203
- package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +180 -180
- package/templates/ide/antigravity/.agents/skills/dare-discover/SKILL.md +33 -33
- package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +63 -63
- package/templates/ide/antigravity/.agents/skills/dare-docker/SKILL.md +315 -315
- package/templates/ide/antigravity/.agents/skills/dare-execute/SKILL.md +264 -264
- package/templates/ide/antigravity/.agents/skills/dare-feature-design/SKILL.md +74 -74
- package/templates/ide/antigravity/.agents/skills/dare-frontend-design/SKILL.md +192 -192
- package/templates/ide/antigravity/.agents/skills/dare-graph/SKILL.md +35 -35
- package/templates/ide/antigravity/.agents/skills/dare-info/SKILL.md +31 -31
- package/templates/ide/antigravity/.agents/skills/dare-init/SKILL.md +35 -35
- package/templates/ide/antigravity/.agents/skills/dare-laravel-api/SKILL.md +337 -337
- package/templates/ide/antigravity/.agents/skills/dare-layered-design/SKILL.md +166 -166
- package/templates/ide/antigravity/.agents/skills/dare-llm-integration/SKILL.md +217 -217
- package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +61 -61
- package/templates/ide/antigravity/.agents/skills/dare-quality-telemetry/SKILL.md +187 -187
- package/templates/ide/antigravity/.agents/skills/dare-realtime/SKILL.md +217 -217
- package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +114 -114
- package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +108 -108
- package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +111 -111
- package/templates/ide/antigravity/.agents/skills/dare-rust-leptos/SKILL.md +263 -263
- package/templates/ide/antigravity/.agents/skills/dare-rust-workspace/SKILL.md +275 -275
- package/templates/ide/antigravity/.agents/skills/dare-security/SKILL.md +274 -274
- package/templates/ide/antigravity/.agents/skills/dare-skill/SKILL.md +35 -35
- package/templates/ide/antigravity/.agents/skills/dare-tasks/SKILL.md +265 -265
- package/templates/ide/antigravity/.agents/skills/dare-telemetry/SKILL.md +188 -188
- package/templates/ide/antigravity/.agents/skills/dare-update/SKILL.md +33 -33
- package/templates/ide/antigravity/.agents/skills/dare-validate/SKILL.md +33 -33
- package/templates/ide/antigravity/.agents/skills/dare-welcome/SKILL.md +30 -30
- package/templates/ide/antigravity/.agents/skills/skill-fastapi-api/SKILL.md +343 -343
- package/templates/ide/antigravity/.agents/skills/skill-go-gin-api/SKILL.md +377 -377
- package/templates/ide/antigravity/.agents/skills/skill-mcp-server/SKILL.md +382 -382
- package/templates/ide/antigravity/.agents/skills/skill-nestjs-api/SKILL.md +326 -326
- package/templates/ide/antigravity/.agents/skills/skill-rails-api/SKILL.md +393 -393
- package/templates/ide/antigravity/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/antigravity/templates/DESIGN-template.md +129 -129
- package/templates/ide/antigravity/templates/TASK-SPEC-template.md +141 -141
- package/templates/ide/antigravity/templates/TASKS-template.md +26 -26
- package/templates/ide/antigravity/templates/TELEMETRY-template.md +125 -125
- package/templates/ide/claude/.claude/commands/dare-ax.md +131 -131
- package/templates/ide/claude/.claude/commands/dare-bench.md +18 -18
- package/templates/ide/claude/.claude/commands/dare-blueprint.md +134 -134
- package/templates/ide/claude/.claude/commands/dare-bootstrap.md +27 -27
- package/templates/ide/claude/.claude/commands/dare-bugfix-design.md +119 -119
- package/templates/ide/claude/.claude/commands/dare-dag-build.md +151 -151
- package/templates/ide/claude/.claude/commands/dare-dag-run.md +109 -109
- package/templates/ide/claude/.claude/commands/dare-dag-runner.md +117 -117
- package/templates/ide/claude/.claude/commands/dare-dag-viz.md +197 -197
- package/templates/ide/claude/.claude/commands/dare-dag.md +27 -27
- package/templates/ide/claude/.claude/commands/dare-design.md +69 -69
- package/templates/ide/claude/.claude/commands/dare-discover.md +28 -28
- package/templates/ide/claude/.claude/commands/dare-dna.md +75 -75
- package/templates/ide/claude/.claude/commands/dare-docker.md +207 -207
- package/templates/ide/claude/.claude/commands/dare-execute.md +152 -152
- package/templates/ide/claude/.claude/commands/dare-feature-design.md +147 -147
- package/templates/ide/claude/.claude/commands/dare-frontend-design.md +149 -149
- package/templates/ide/claude/.claude/commands/dare-graph.md +30 -30
- package/templates/ide/claude/.claude/commands/dare-info.md +26 -26
- package/templates/ide/claude/.claude/commands/dare-init.md +30 -30
- package/templates/ide/claude/.claude/commands/dare-laravel-api.md +211 -211
- package/templates/ide/claude/.claude/commands/dare-layered-design.md +124 -124
- package/templates/ide/claude/.claude/commands/dare-llm-integration.md +148 -148
- package/templates/ide/claude/.claude/commands/dare-migrate.md +72 -72
- package/templates/ide/claude/.claude/commands/dare-quality-telemetry.md +166 -166
- package/templates/ide/claude/.claude/commands/dare-realtime.md +159 -159
- package/templates/ide/claude/.claude/commands/dare-refine.md +145 -145
- package/templates/ide/claude/.claude/commands/dare-reverse.md +139 -139
- package/templates/ide/claude/.claude/commands/dare-review.md +113 -113
- package/templates/ide/claude/.claude/commands/dare-rust-leptos.md +269 -269
- package/templates/ide/claude/.claude/commands/dare-rust-workspace.md +209 -209
- package/templates/ide/claude/.claude/commands/dare-security.md +232 -232
- package/templates/ide/claude/.claude/commands/dare-skill.md +30 -30
- package/templates/ide/claude/.claude/commands/dare-tasks.md +70 -70
- package/templates/ide/claude/.claude/commands/dare-telemetry.md +132 -132
- package/templates/ide/claude/.claude/commands/dare-update.md +28 -28
- package/templates/ide/claude/.claude/commands/dare-validate.md +28 -28
- package/templates/ide/claude/.claude/commands/dare-welcome.md +25 -25
- package/templates/ide/claude/.claude/commands/skill-fastapi-api.md +205 -205
- package/templates/ide/claude/.claude/commands/skill-go-gin-api.md +232 -232
- package/templates/ide/claude/.claude/commands/skill-mcp-server.md +228 -228
- package/templates/ide/claude/.claude/commands/skill-nestjs-api.md +210 -210
- package/templates/ide/claude/.claude/commands/skill-rails-api.md +236 -236
- package/templates/ide/claude/.claude/settings.example.json +35 -35
- package/templates/ide/claude/CLAUDE.md +146 -146
- package/templates/ide/claude/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/claude/templates/DESIGN-template.md +129 -129
- package/templates/ide/claude/templates/TASK-SPEC-template.md +141 -141
- package/templates/ide/claude/templates/TASKS-template.md +26 -26
- package/templates/ide/claude/templates/TELEMETRY-template.md +125 -125
- package/templates/ide/cursor/.cursor/commands/dare-bench.md +18 -18
- package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +86 -86
- package/templates/ide/cursor/.cursor/commands/dare-bootstrap.md +27 -27
- package/templates/ide/cursor/.cursor/commands/dare-bugfix-design.md +64 -64
- package/templates/ide/cursor/.cursor/commands/dare-dag-run.md +110 -110
- package/templates/ide/cursor/.cursor/commands/dare-dag-viz.md +139 -139
- package/templates/ide/cursor/.cursor/commands/dare-dag.md +27 -27
- package/templates/ide/cursor/.cursor/commands/dare-design.md +35 -35
- package/templates/ide/cursor/.cursor/commands/dare-discover.md +28 -28
- package/templates/ide/cursor/.cursor/commands/dare-dna.md +75 -75
- package/templates/ide/cursor/.cursor/commands/dare-docker-compose.md +18 -18
- package/templates/ide/cursor/.cursor/commands/dare-dockerfile.md +17 -17
- package/templates/ide/cursor/.cursor/commands/dare-execute.md +19 -19
- package/templates/ide/cursor/.cursor/commands/dare-feature-design.md +64 -64
- package/templates/ide/cursor/.cursor/commands/dare-graph.md +30 -30
- package/templates/ide/cursor/.cursor/commands/dare-info.md +26 -26
- package/templates/ide/cursor/.cursor/commands/dare-init.md +30 -30
- package/templates/ide/cursor/.cursor/commands/dare-migrate.md +72 -72
- package/templates/ide/cursor/.cursor/commands/dare-refine.md +107 -107
- package/templates/ide/cursor/.cursor/commands/dare-reverse.md +139 -139
- package/templates/ide/cursor/.cursor/commands/dare-review.md +91 -91
- package/templates/ide/cursor/.cursor/commands/dare-skill.md +30 -30
- package/templates/ide/cursor/.cursor/commands/dare-tasks.md +184 -184
- package/templates/ide/cursor/.cursor/commands/dare-telemetry.md +42 -42
- package/templates/ide/cursor/.cursor/commands/dare-update.md +28 -28
- package/templates/ide/cursor/.cursor/commands/dare-validate.md +28 -28
- package/templates/ide/cursor/.cursor/commands/dare-welcome.md +25 -25
- package/templates/ide/cursor/.cursor/rules/skill-ax.mdc +263 -263
- package/templates/ide/cursor/.cursor/rules/skill-bugfix-design.mdc +51 -51
- package/templates/ide/cursor/.cursor/rules/skill-dag-build.mdc +173 -173
- package/templates/ide/cursor/.cursor/rules/skill-dag-run.mdc +134 -134
- package/templates/ide/cursor/.cursor/rules/skill-dag-runner.mdc +221 -221
- package/templates/ide/cursor/.cursor/rules/skill-dna.mdc +63 -63
- package/templates/ide/cursor/.cursor/rules/skill-docker.mdc +33 -33
- package/templates/ide/cursor/.cursor/rules/skill-fastapi-api.mdc +352 -352
- package/templates/ide/cursor/.cursor/rules/skill-feature-design.mdc +43 -43
- package/templates/ide/cursor/.cursor/rules/skill-frontend-design.mdc +244 -244
- package/templates/ide/cursor/.cursor/rules/skill-go-gin-api.mdc +371 -371
- package/templates/ide/cursor/.cursor/rules/skill-laravel-api.mdc +44 -44
- package/templates/ide/cursor/.cursor/rules/skill-layered-design.mdc +266 -266
- package/templates/ide/cursor/.cursor/rules/skill-llm-integration.mdc +295 -295
- package/templates/ide/cursor/.cursor/rules/skill-mcp-server.mdc +367 -367
- package/templates/ide/cursor/.cursor/rules/skill-migrate.mdc +58 -58
- package/templates/ide/cursor/.cursor/rules/skill-nestjs-api.mdc +346 -346
- package/templates/ide/cursor/.cursor/rules/skill-quality-telemetry.mdc +248 -248
- package/templates/ide/cursor/.cursor/rules/skill-rails-api.mdc +400 -400
- package/templates/ide/cursor/.cursor/rules/skill-realtime.mdc +262 -262
- package/templates/ide/cursor/.cursor/rules/skill-reverse.mdc +107 -107
- package/templates/ide/cursor/.cursor/rules/skill-rust-leptos.mdc +281 -281
- package/templates/ide/cursor/.cursor/rules/skill-rust-workspace.mdc +312 -312
- package/templates/ide/cursor/.cursor/rules/skill-security.mdc +245 -245
- package/templates/ide/cursor/.cursor/rules/skill-telemetry.mdc +156 -156
- package/templates/ide/cursor/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/cursor/templates/DESIGN-template.md +129 -129
- package/templates/ide/cursor/templates/TASK-SPEC-template.md +141 -141
- package/templates/ide/cursor/templates/TASKS-template.md +26 -26
- package/templates/ide/cursor/templates/TELEMETRY-template.md +125 -125
- package/templates/shared/docker-compose.yml +41 -41
- package/templates/stacks/go-gin/.dare/skills.yml +11 -11
- package/templates/stacks/go-gin/.env.example +24 -24
- package/templates/stacks/go-gin/.github/workflows/dare-ci.yml +42 -42
- package/templates/stacks/go-gin/README.md.tpl +38 -38
- package/templates/stacks/go-gin/cmd/server/main.go.tpl +78 -78
- package/templates/stacks/go-gin/db/migrations/0001_create_users.down.sql +2 -2
- package/templates/stacks/go-gin/db/migrations/0001_create_users.up.sql +12 -12
- package/templates/stacks/go-gin/db/queries/users.sql +23 -23
- package/templates/stacks/go-gin/gitignore +7 -7
- package/templates/stacks/go-gin/go.mod.tpl +17 -17
- package/templates/stacks/go-gin/internal/config/config.go +41 -41
- package/templates/stacks/go-gin/internal/db/postgres.go.tpl +25 -25
- package/templates/stacks/go-gin/internal/handler/auth_handler.go.tpl +72 -72
- package/templates/stacks/go-gin/internal/handler/users_handler.go.tpl +72 -72
- package/templates/stacks/go-gin/internal/handler/ws_handler.go +37 -37
- package/templates/stacks/go-gin/internal/llm/dummy.go +14 -14
- package/templates/stacks/go-gin/internal/llm/provider.go +8 -8
- package/templates/stacks/go-gin/internal/middleware/jwt.go.tpl +58 -58
- package/templates/stacks/go-gin/internal/middleware/rate_limit.go +55 -55
- package/templates/stacks/go-gin/internal/model/user.go +17 -17
- package/templates/stacks/go-gin/internal/repository/users_repository.go.tpl +79 -79
- package/templates/stacks/go-gin/internal/service/auth_service.go.tpl +55 -55
- package/templates/stacks/go-gin/internal/service/users_service.go.tpl +53 -53
- package/templates/stacks/go-gin/llms.txt.tpl +54 -54
- package/templates/stacks/go-gin/openapi.json.tpl +46 -46
- package/templates/stacks/go-gin/sqlc.yaml +14 -14
- package/templates/stacks/go-gin/tests/smoke_test.go.tpl +22 -22
- package/templates/stacks/go-stdlib/.dare/skills.yml +11 -11
- package/templates/stacks/go-stdlib/.env.example +24 -24
- package/templates/stacks/go-stdlib/.github/workflows/dare-ci.yml +42 -42
- package/templates/stacks/go-stdlib/README.md.tpl +41 -41
- package/templates/stacks/go-stdlib/cmd/server/main.go.tpl +82 -82
- package/templates/stacks/go-stdlib/db/migrations/0001_create_users.down.sql +2 -2
- package/templates/stacks/go-stdlib/db/migrations/0001_create_users.up.sql +12 -12
- package/templates/stacks/go-stdlib/db/queries/users.sql +23 -23
- package/templates/stacks/go-stdlib/gitignore +6 -6
- package/templates/stacks/go-stdlib/go.mod.tpl +15 -15
- package/templates/stacks/go-stdlib/internal/config/config.go +41 -41
- package/templates/stacks/go-stdlib/internal/db/postgres.go.tpl +24 -24
- package/templates/stacks/go-stdlib/internal/handler/auth_handler.go.tpl +71 -71
- package/templates/stacks/go-stdlib/internal/handler/users_handler.go.tpl +84 -84
- package/templates/stacks/go-stdlib/internal/handler/ws_handler.go +36 -36
- package/templates/stacks/go-stdlib/internal/httpx/json.go +32 -32
- package/templates/stacks/go-stdlib/internal/llm/dummy.go +14 -14
- package/templates/stacks/go-stdlib/internal/llm/provider.go +8 -8
- package/templates/stacks/go-stdlib/internal/middleware/chain.go +21 -21
- package/templates/stacks/go-stdlib/internal/middleware/cors.go +27 -27
- package/templates/stacks/go-stdlib/internal/middleware/jwt.go.tpl +51 -51
- package/templates/stacks/go-stdlib/internal/middleware/rate_limit.go +81 -81
- package/templates/stacks/go-stdlib/internal/model/user.go +17 -17
- package/templates/stacks/go-stdlib/internal/repository/users_repository.go.tpl +75 -75
- package/templates/stacks/go-stdlib/internal/service/auth_service.go.tpl +55 -55
- package/templates/stacks/go-stdlib/internal/service/users_service.go.tpl +53 -53
- package/templates/stacks/go-stdlib/llms.txt.tpl +60 -60
- package/templates/stacks/go-stdlib/openapi.json.tpl +46 -46
- package/templates/stacks/go-stdlib/sqlc.yaml +14 -14
- package/templates/stacks/go-stdlib/tests/smoke_test.go.tpl +45 -45
- package/templates/stacks/mcp-go/.dare/skills.yml +8 -8
- package/templates/stacks/mcp-go/.env.example +14 -14
- package/templates/stacks/mcp-go/.github/workflows/dare-ci.yml +42 -42
- package/templates/stacks/mcp-go/README.md.tpl +50 -50
- package/templates/stacks/mcp-go/cmd/server/main.go.tpl +62 -62
- package/templates/stacks/mcp-go/gitignore +6 -6
- package/templates/stacks/mcp-go/go.mod.tpl +9 -9
- package/templates/stacks/mcp-go/internal/prompts/summarize.go +9 -9
- package/templates/stacks/mcp-go/internal/server/server.go.tpl +80 -80
- package/templates/stacks/mcp-go/internal/tools/echo.go +15 -15
- package/templates/stacks/mcp-go/internal/transports/http.go.tpl +21 -21
- package/templates/stacks/mcp-go/internal/transports/sse.go.tpl +17 -17
- package/templates/stacks/mcp-go/internal/transports/stdio.go.tpl +14 -14
- package/templates/stacks/mcp-go/llms.txt.tpl +60 -60
- package/templates/stacks/mcp-go/openapi.json.tpl +31 -31
- package/templates/stacks/mcp-go/tests/echo_test.go.tpl +37 -37
- package/templates/stacks/mcp-node-ts/.dare/skills.yml +8 -8
- package/templates/stacks/mcp-node-ts/.env.example +16 -16
- package/templates/stacks/mcp-node-ts/.github/workflows/dare-ci.yml +54 -54
- package/templates/stacks/mcp-node-ts/README.md.hbs +49 -49
- package/templates/stacks/mcp-node-ts/gitignore +7 -7
- package/templates/stacks/mcp-node-ts/llms.txt.hbs +61 -61
- package/templates/stacks/mcp-node-ts/openapi.json.hbs +39 -39
- package/templates/stacks/mcp-node-ts/package.json.hbs +35 -35
- package/templates/stacks/mcp-node-ts/src/cli.ts.hbs +71 -71
- package/templates/stacks/mcp-node-ts/src/prompts/index.ts +36 -36
- package/templates/stacks/mcp-node-ts/src/server.ts.hbs +45 -45
- package/templates/stacks/mcp-node-ts/src/tools/echo.ts +23 -23
- package/templates/stacks/mcp-node-ts/src/tools/index.ts +18 -18
- package/templates/stacks/mcp-node-ts/src/transports/http.ts +68 -68
- package/templates/stacks/mcp-node-ts/src/transports/sse.ts +58 -58
- package/templates/stacks/mcp-node-ts/src/transports/stdio.ts +5 -5
- package/templates/stacks/mcp-node-ts/tests/echo.test.ts +50 -50
- package/templates/stacks/mcp-node-ts/tsconfig.json +17 -17
- package/templates/stacks/mcp-python/.dare/skills.yml +8 -8
- package/templates/stacks/mcp-python/.env.example +14 -14
- package/templates/stacks/mcp-python/.github/workflows/dare-ci.yml +42 -42
- package/templates/stacks/mcp-python/README.md.j2 +49 -49
- package/templates/stacks/mcp-python/gitignore +12 -12
- package/templates/stacks/mcp-python/llms.txt.j2 +56 -56
- package/templates/stacks/mcp-python/openapi.json.j2 +33 -33
- package/templates/stacks/mcp-python/pyproject.toml.j2 +37 -37
- package/templates/stacks/mcp-python/src/cli.py.j2 +68 -68
- package/templates/stacks/mcp-python/src/prompts/summarize.py +10 -10
- package/templates/stacks/mcp-python/src/server.py.j2 +28 -28
- package/templates/stacks/mcp-python/src/tools/echo.py +12 -12
- package/templates/stacks/mcp-python/src/transports/http.py +12 -12
- package/templates/stacks/mcp-python/src/transports/sse.py +13 -13
- package/templates/stacks/mcp-python/src/transports/stdio.py +6 -6
- package/templates/stacks/mcp-python/tests/test_echo.py +28 -28
- package/templates/stacks/mcp-rust/.dare/skills.yml +8 -8
- package/templates/stacks/mcp-rust/.env.example +14 -14
- package/templates/stacks/mcp-rust/.github/workflows/dare-ci.yml +38 -38
- package/templates/stacks/mcp-rust/Cargo.toml.tera +35 -35
- package/templates/stacks/mcp-rust/README.md.tera +50 -50
- package/templates/stacks/mcp-rust/gitignore +5 -5
- package/templates/stacks/mcp-rust/llms.txt.tera +60 -60
- package/templates/stacks/mcp-rust/openapi.json.tera +31 -31
- package/templates/stacks/mcp-rust/src/cli.rs.tera +33 -33
- package/templates/stacks/mcp-rust/src/lib.rs +6 -6
- package/templates/stacks/mcp-rust/src/main.rs.tera +30 -30
- package/templates/stacks/mcp-rust/src/prompts/mod.rs +1 -1
- package/templates/stacks/mcp-rust/src/prompts/summarize.rs +5 -5
- package/templates/stacks/mcp-rust/src/server.rs.tera +38 -38
- package/templates/stacks/mcp-rust/src/tools/echo.rs +18 -18
- package/templates/stacks/mcp-rust/src/tools/mod.rs +22 -22
- package/templates/stacks/mcp-rust/src/transports/http.rs +27 -27
- package/templates/stacks/mcp-rust/src/transports/mod.rs +3 -3
- package/templates/stacks/mcp-rust/src/transports/sse.rs +33 -33
- package/templates/stacks/mcp-rust/src/transports/stdio.rs +14 -14
- package/templates/stacks/mcp-rust/tests/echo_test.rs.tera +27 -27
- package/templates/stacks/node-nestjs/.dare/skills.yml +11 -11
- package/templates/stacks/node-nestjs/.env.example +21 -21
- package/templates/stacks/node-nestjs/.github/workflows/dare-ci.yml +54 -54
- package/templates/stacks/node-nestjs/README.md.hbs +35 -35
- package/templates/stacks/node-nestjs/gitignore +7 -7
- package/templates/stacks/node-nestjs/llms.txt.hbs +47 -47
- package/templates/stacks/node-nestjs/nest-cli.json +16 -16
- package/templates/stacks/node-nestjs/openapi.json.hbs +75 -75
- package/templates/stacks/node-nestjs/package.json.hbs +57 -57
- package/templates/stacks/node-nestjs/prisma/schema.prisma +25 -25
- package/templates/stacks/node-nestjs/prisma/seed.ts.hbs +25 -25
- package/templates/stacks/node-nestjs/src/app.module.ts +39 -39
- package/templates/stacks/node-nestjs/src/auth/auth.controller.ts +29 -29
- package/templates/stacks/node-nestjs/src/auth/auth.module.ts +25 -25
- package/templates/stacks/node-nestjs/src/auth/auth.service.ts +36 -36
- package/templates/stacks/node-nestjs/src/auth/dto/login-response.dto.ts +9 -9
- package/templates/stacks/node-nestjs/src/auth/dto/login.dto.ts +17 -17
- package/templates/stacks/node-nestjs/src/auth/jwt.strategy.ts +25 -25
- package/templates/stacks/node-nestjs/src/common/filters/problem-details.filter.ts +38 -38
- package/templates/stacks/node-nestjs/src/common/interceptors/json-response.interceptor.ts +13 -13
- package/templates/stacks/node-nestjs/src/main.ts.hbs +44 -44
- package/templates/stacks/node-nestjs/src/prisma/prisma.module.ts +9 -9
- package/templates/stacks/node-nestjs/src/prisma/prisma.service.ts +9 -9
- package/templates/stacks/node-nestjs/src/users/dto/create-user.dto.ts +22 -22
- package/templates/stacks/node-nestjs/src/users/dto/user.dto.ts +15 -15
- package/templates/stacks/node-nestjs/src/users/users.controller.ts +41 -41
- package/templates/stacks/node-nestjs/src/users/users.module.ts +11 -11
- package/templates/stacks/node-nestjs/src/users/users.repository.ts +38 -38
- package/templates/stacks/node-nestjs/src/users/users.service.ts +38 -38
- package/templates/stacks/node-nestjs/tsconfig.build.json +4 -4
- package/templates/stacks/node-nestjs/tsconfig.json +28 -28
- package/templates/stacks/php-laravel/.dare/skills.yml +11 -11
- package/templates/stacks/php-laravel/.env.example +41 -41
- package/templates/stacks/php-laravel/.github/workflows/dare-ci.yml +43 -43
- package/templates/stacks/php-laravel/README.md.hbs +36 -36
- package/templates/stacks/php-laravel/app/Http/Controllers/Api/AuthController.php +36 -36
- package/templates/stacks/php-laravel/app/Http/Controllers/Api/UsersController.php +33 -33
- package/templates/stacks/php-laravel/app/Http/Requests/CreateUserRequest.php +26 -26
- package/templates/stacks/php-laravel/app/Http/Requests/LoginRequest.php +34 -34
- package/templates/stacks/php-laravel/app/Llm/Contracts/LlmProvider.php +12 -12
- package/templates/stacks/php-laravel/app/Llm/Providers/DummyProvider.php +13 -13
- package/templates/stacks/php-laravel/app/Llm/Providers/OpenAiProvider.php +33 -33
- package/templates/stacks/php-laravel/app/Models/User.php +44 -44
- package/templates/stacks/php-laravel/app/Repositories/UsersRepository.php +32 -32
- package/templates/stacks/php-laravel/app/Services/AuthService.php +37 -37
- package/templates/stacks/php-laravel/app/Services/UsersService.php +57 -57
- package/templates/stacks/php-laravel/artisan +12 -12
- package/templates/stacks/php-laravel/bootstrap/app.php +29 -29
- package/templates/stacks/php-laravel/bootstrap/providers.php +5 -5
- package/templates/stacks/php-laravel/composer.json.hbs +58 -58
- package/templates/stacks/php-laravel/config/l5-swagger.php +41 -41
- package/templates/stacks/php-laravel/config/reverb.php +34 -34
- package/templates/stacks/php-laravel/config/sanctum.php +15 -15
- package/templates/stacks/php-laravel/database/migrations/2026_06_01_000001_create_users_table.php +27 -27
- package/templates/stacks/php-laravel/database/seeders/DatabaseSeeder.php +21 -21
- package/templates/stacks/php-laravel/gitignore +23 -23
- package/templates/stacks/php-laravel/llms.txt.hbs +53 -53
- package/templates/stacks/php-laravel/openapi.json.hbs +43 -43
- package/templates/stacks/php-laravel/phpstan.neon +9 -9
- package/templates/stacks/php-laravel/routes/api.php +13 -13
- package/templates/stacks/php-laravel/routes/channels.php +7 -7
- package/templates/stacks/php-laravel/tests/Feature/AuthTest.php +35 -35
- package/templates/stacks/php-laravel/tests/Feature/UsersTest.php +30 -30
- package/templates/stacks/php-laravel/tests/Pest.php +5 -5
- package/templates/stacks/python-fastapi/.dare/skills.yml +11 -11
- package/templates/stacks/python-fastapi/.env.example +21 -21
- package/templates/stacks/python-fastapi/.github/workflows/dare-ci.yml +43 -43
- package/templates/stacks/python-fastapi/README.md.j2 +35 -35
- package/templates/stacks/python-fastapi/alembic/env.py +46 -46
- package/templates/stacks/python-fastapi/alembic/script.py.mako +26 -26
- package/templates/stacks/python-fastapi/alembic/versions/0001_create_users.py.j2 +37 -37
- package/templates/stacks/python-fastapi/alembic.ini.j2 +39 -39
- package/templates/stacks/python-fastapi/app/core/config.py +24 -24
- package/templates/stacks/python-fastapi/app/core/security.py +34 -34
- package/templates/stacks/python-fastapi/app/db/session.py +22 -22
- package/templates/stacks/python-fastapi/app/main.py.j2 +36 -36
- package/templates/stacks/python-fastapi/app/models/__init__.py +3 -3
- package/templates/stacks/python-fastapi/app/models/user.py +30 -30
- package/templates/stacks/python-fastapi/app/repositories/user_repository.py +34 -34
- package/templates/stacks/python-fastapi/app/routers/auth.py +37 -37
- package/templates/stacks/python-fastapi/app/routers/users.py +46 -46
- package/templates/stacks/python-fastapi/app/schemas/user.py +56 -56
- package/templates/stacks/python-fastapi/app/services/auth_service.py +22 -22
- package/templates/stacks/python-fastapi/app/services/user_service.py +31 -31
- package/templates/stacks/python-fastapi/gitignore +12 -12
- package/templates/stacks/python-fastapi/llms.txt.j2 +53 -53
- package/templates/stacks/python-fastapi/openapi.json.j2 +43 -43
- package/templates/stacks/python-fastapi/pyproject.toml.j2 +45 -45
- package/templates/stacks/python-fastapi/tests/test_auth.py +22 -22
- package/templates/stacks/ruby-rails-8/.dare/skills.yml +50 -50
- package/templates/stacks/ruby-rails-8/.env.example +20 -20
- package/templates/stacks/ruby-rails-8/.github/workflows/dare-ci.yml +112 -112
- package/templates/stacks/ruby-rails-8/Gemfile.erb +61 -61
- package/templates/stacks/ruby-rails-8/app/channels/application_cable/channel.rb +11 -11
- package/templates/stacks/ruby-rails-8/app/channels/application_cable/connection.rb +34 -34
- package/templates/stacks/ruby-rails-8/app/channels/dare_updates_channel.rb +18 -18
- package/templates/stacks/ruby-rails-8/app/channels/user_updates_channel.rb +23 -23
- package/templates/stacks/ruby-rails-8/app/controllers/application_controller.rb +44 -44
- package/templates/stacks/ruby-rails-8/app/controllers/concerns/problem_details.rb +93 -93
- package/templates/stacks/ruby-rails-8/app/handlers/summarize_handler.rb +33 -33
- package/templates/stacks/ruby-rails-8/app/handlers/users_handler.rb +68 -68
- package/templates/stacks/ruby-rails-8/app/llm/cache/llm_cache.rb +44 -44
- package/templates/stacks/ruby-rails-8/app/llm/prompts/prompt_loader.rb +54 -54
- package/templates/stacks/ruby-rails-8/app/llm/prompts/summarize_v1.jinja2 +12 -12
- package/templates/stacks/ruby-rails-8/app/llm/providers/dummy_provider.rb +35 -35
- package/templates/stacks/ruby-rails-8/app/llm/providers/llm_provider.rb +67 -67
- package/templates/stacks/ruby-rails-8/app/llm/providers/openai_provider.rb +62 -62
- package/templates/stacks/ruby-rails-8/app/llm/rate_limit/token_bucket.rb +82 -82
- package/templates/stacks/ruby-rails-8/app/llm/validators/summarize_output_schema.json +21 -21
- package/templates/stacks/ruby-rails-8/app/llm/validators/validator.rb +52 -52
- package/templates/stacks/ruby-rails-8/app/models/user.rb +36 -36
- package/templates/stacks/ruby-rails-8/app/presenters/user_presenter.rb +48 -48
- package/templates/stacks/ruby-rails-8/app/repositories/document_repository.rb +57 -57
- package/templates/stacks/ruby-rails-8/app/repositories/user_repository.rb +73 -73
- package/templates/stacks/ruby-rails-8/app/services/create_user_service.rb +67 -67
- package/templates/stacks/ruby-rails-8/app/services/realtime_service.rb +53 -53
- package/templates/stacks/ruby-rails-8/app/services/summarize_document_service.rb +57 -57
- package/templates/stacks/ruby-rails-8/config/dare.yml +42 -42
- package/templates/stacks/ruby-rails-8/config/initializers/dare.rb +31 -31
- package/templates/stacks/ruby-rails-8/config/initializers/rack_attack.rb +64 -64
- package/templates/stacks/ruby-rails-8/config/initializers/rswag_api.rb +12 -12
- package/templates/stacks/ruby-rails-8/lib/tasks/dare.rake +159 -159
- package/templates/stacks/ruby-rails-8/llms.txt.erb +69 -69
- package/templates/stacks/ruby-rails-8/spec/api/summarize_spec.rb +56 -56
- package/templates/stacks/ruby-rails-8/spec/api/users_spec.rb +72 -72
- package/templates/stacks/ruby-rails-8/spec/channels/dare_updates_channel_spec.rb +61 -61
- package/templates/stacks/ruby-rails-8/spec/channels/user_updates_channel_spec.rb +56 -56
- package/templates/stacks/ruby-rails-8/spec/factories/users.rb +27 -27
- package/templates/stacks/ruby-rails-8/spec/handlers/users_handler_spec.rb +88 -88
- package/templates/stacks/ruby-rails-8/spec/rails_helper.rb +31 -31
- package/templates/stacks/ruby-rails-8/spec/services/create_user_service_spec.rb +88 -88
- package/templates/stacks/ruby-rails-8/spec/services/summarize_document_service_spec.rb +142 -142
- package/templates/stacks/ruby-rails-8/spec/swagger_helper.rb +73 -73
- package/templates/stacks/rust-axum/.dare/skills.yml +11 -11
- package/templates/stacks/rust-axum/.env.example +26 -26
- package/templates/stacks/rust-axum/.github/workflows/dare-ci.yml +40 -40
- package/templates/stacks/rust-axum/Cargo.toml.tera +53 -53
- package/templates/stacks/rust-axum/README.md.tera +37 -37
- package/templates/stacks/rust-axum/gitignore +5 -5
- package/templates/stacks/rust-axum/llms.txt.tera +54 -54
- package/templates/stacks/rust-axum/migrations/0001_create_users.sql +13 -13
- package/templates/stacks/rust-axum/openapi.json.tera +46 -46
- package/templates/stacks/rust-axum/src/config.rs +45 -45
- package/templates/stacks/rust-axum/src/errors.rs +48 -48
- package/templates/stacks/rust-axum/src/handlers/auth.rs +48 -48
- package/templates/stacks/rust-axum/src/handlers/mod.rs +3 -3
- package/templates/stacks/rust-axum/src/handlers/users.rs +81 -81
- package/templates/stacks/rust-axum/src/handlers/ws.rs +24 -24
- package/templates/stacks/rust-axum/src/lib.rs +19 -19
- package/templates/stacks/rust-axum/src/llm/mod.rs +1 -1
- package/templates/stacks/rust-axum/src/llm/provider.rs +48 -48
- package/templates/stacks/rust-axum/src/main.rs.tera +64 -64
- package/templates/stacks/rust-axum/src/middleware/auth.rs +20 -20
- package/templates/stacks/rust-axum/src/middleware/mod.rs +2 -2
- package/templates/stacks/rust-axum/src/middleware/rate_limit.rs +27 -27
- package/templates/stacks/rust-axum/src/models/mod.rs +1 -1
- package/templates/stacks/rust-axum/src/models/user.rs +13 -13
- package/templates/stacks/rust-axum/src/repositories/mod.rs +1 -1
- package/templates/stacks/rust-axum/src/repositories/user_repository.rs +62 -62
- package/templates/stacks/rust-axum/src/services/auth_service.rs +50 -50
- package/templates/stacks/rust-axum/src/services/mod.rs +2 -2
- package/templates/stacks/rust-axum/src/services/user_service.rs +53 -53
- package/templates/stacks/rust-axum/tests/integration_test.rs.tera +13 -13
- package/dist/commands/new.d.ts +0 -16
- package/dist/commands/new.d.ts.map +0 -1
- package/dist/commands/new.js +0 -104
- package/dist/commands/new.js.map +0 -1
|
@@ -1,352 +1,352 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Padrões DARE para APIs REST em Python + FastAPI + Pydantic v2 + SQLAlchemy 2.0 async + alembic. Routers, dependency injection, schemas Pydantic, OAuth2+JWT, slowapi rate limit, pytest+httpx, OpenAPI auto-gerado.
|
|
3
|
-
globs: **/*.py,pyproject.toml,requirements.txt,alembic.ini,alembic/**/*.py
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Skill: FastAPI API DARE
|
|
8
|
-
|
|
9
|
-
Você é um desenvolvedor sênior Python especialista em APIs REST com FastAPI. Esta skill garante código **assíncrono, fortemente tipado (Pydantic v2), com OpenAPI auto-gerado, auth/autz robustos**, seguindo Layered Design DARE.
|
|
10
|
-
|
|
11
|
-
## Stack canônica
|
|
12
|
-
|
|
13
|
-
- **Python 3.11+** com type hints obrigatórios
|
|
14
|
-
- **FastAPI 0.115+** com async/await
|
|
15
|
-
- **Pydantic v2** para schemas
|
|
16
|
-
- **SQLAlchemy 2.0** async + **asyncpg** (PostgreSQL)
|
|
17
|
-
- **alembic** para migrations
|
|
18
|
-
- **passlib + argon2** para hash de senhas
|
|
19
|
-
- **python-jose** ou **PyJWT** para JWT
|
|
20
|
-
- **slowapi** para rate limiting
|
|
21
|
-
- **pytest + pytest-asyncio + httpx**
|
|
22
|
-
- **ruff** (lint + format) + **mypy**
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Estrutura de pastas
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
app/
|
|
30
|
-
├── main.py ← FastAPI app + middlewares
|
|
31
|
-
├── core/
|
|
32
|
-
│ ├── config.py ← pydantic-settings
|
|
33
|
-
│ └── security.py ← hash, JWT
|
|
34
|
-
├── api/
|
|
35
|
-
│ ├── deps.py ← Depends() comuns
|
|
36
|
-
│ └── v1/
|
|
37
|
-
│ ├── users.py ← Handler (router)
|
|
38
|
-
│ └── auth.py
|
|
39
|
-
├── services/ ← Service layer
|
|
40
|
-
├── repositories/ ← Repository layer
|
|
41
|
-
├── models/ ← SQLAlchemy ORM
|
|
42
|
-
├── schemas/ ← Pydantic DTOs
|
|
43
|
-
└── tests/
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Routers (Handler)
|
|
49
|
-
|
|
50
|
-
```python
|
|
51
|
-
from fastapi import APIRouter, Depends, HTTPException, status
|
|
52
|
-
from app.api.deps import get_current_user
|
|
53
|
-
from app.schemas.user import UserCreate, UserOut
|
|
54
|
-
from app.services.register_user import RegisterUser, UserAlreadyExistsError
|
|
55
|
-
|
|
56
|
-
router = APIRouter(prefix="/users", tags=["users"])
|
|
57
|
-
|
|
58
|
-
@router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
|
|
59
|
-
async def create_user(
|
|
60
|
-
payload: UserCreate,
|
|
61
|
-
service: RegisterUser = Depends(),
|
|
62
|
-
_current: User = Depends(get_current_user),
|
|
63
|
-
):
|
|
64
|
-
try:
|
|
65
|
-
return await service.execute(payload)
|
|
66
|
-
except UserAlreadyExistsError:
|
|
67
|
-
raise HTTPException(status_code=409, detail="User already exists")
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Regras:
|
|
71
|
-
- Apenas: valida via Pydantic → chama Service → retorna response_model
|
|
72
|
-
- NUNCA: SQLAlchemy direto, lógica de negócio, validação manual
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## Schemas (Pydantic v2)
|
|
77
|
-
|
|
78
|
-
```python
|
|
79
|
-
from pydantic import BaseModel, EmailStr, Field
|
|
80
|
-
from datetime import datetime
|
|
81
|
-
|
|
82
|
-
class UserCreate(BaseModel):
|
|
83
|
-
email: EmailStr
|
|
84
|
-
name: str = Field(min_length=1, max_length=255)
|
|
85
|
-
password: str = Field(min_length=12)
|
|
86
|
-
|
|
87
|
-
class UserOut(BaseModel):
|
|
88
|
-
id: int
|
|
89
|
-
email: EmailStr
|
|
90
|
-
name: str
|
|
91
|
-
created_at: datetime
|
|
92
|
-
|
|
93
|
-
model_config = {"from_attributes": True} # Pydantic v2 substitui orm_mode
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
## Services
|
|
99
|
-
|
|
100
|
-
```python
|
|
101
|
-
class UserAlreadyExistsError(Exception): ...
|
|
102
|
-
|
|
103
|
-
class RegisterUser:
|
|
104
|
-
def __init__(self, repo: UsersRepository = Depends()):
|
|
105
|
-
self.repo = repo
|
|
106
|
-
|
|
107
|
-
async def execute(self, payload: UserCreate) -> User:
|
|
108
|
-
if await self.repo.exists_by_email(payload.email):
|
|
109
|
-
raise UserAlreadyExistsError()
|
|
110
|
-
return await self.repo.create(
|
|
111
|
-
email=payload.email,
|
|
112
|
-
name=payload.name,
|
|
113
|
-
password_hash=hash_password(payload.password),
|
|
114
|
-
)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## Repositories (SQLAlchemy 2.0 async)
|
|
120
|
-
|
|
121
|
-
```python
|
|
122
|
-
from sqlalchemy import select
|
|
123
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
124
|
-
from fastapi import Depends
|
|
125
|
-
from app.api.deps import get_db
|
|
126
|
-
|
|
127
|
-
class UsersRepository:
|
|
128
|
-
def __init__(self, db: AsyncSession = Depends(get_db)):
|
|
129
|
-
self.db = db
|
|
130
|
-
|
|
131
|
-
async def exists_by_email(self, email: str) -> bool:
|
|
132
|
-
result = await self.db.execute(select(User.id).where(User.email == email))
|
|
133
|
-
return result.scalar_one_or_none() is not None
|
|
134
|
-
|
|
135
|
-
async def create(self, *, email: str, name: str, password_hash: str) -> User:
|
|
136
|
-
user = User(email=email, name=name, password_hash=password_hash)
|
|
137
|
-
self.db.add(user)
|
|
138
|
-
await self.db.commit()
|
|
139
|
-
await self.db.refresh(user)
|
|
140
|
-
return user
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Models (SQLAlchemy 2.0)
|
|
146
|
-
|
|
147
|
-
```python
|
|
148
|
-
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
149
|
-
from datetime import datetime
|
|
150
|
-
|
|
151
|
-
class Base(DeclarativeBase): ...
|
|
152
|
-
|
|
153
|
-
class User(Base):
|
|
154
|
-
__tablename__ = "users"
|
|
155
|
-
id: Mapped[int] = mapped_column(primary_key=True)
|
|
156
|
-
email: Mapped[str] = mapped_column(unique=True, index=True)
|
|
157
|
-
name: Mapped[str]
|
|
158
|
-
password_hash: Mapped[str]
|
|
159
|
-
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## Auth (OAuth2 + JWT)
|
|
165
|
-
|
|
166
|
-
```python
|
|
167
|
-
# app/core/security.py
|
|
168
|
-
from passlib.hash import argon2
|
|
169
|
-
from datetime import datetime, timedelta
|
|
170
|
-
from jose import jwt
|
|
171
|
-
from app.core.config import settings
|
|
172
|
-
|
|
173
|
-
def hash_password(p: str) -> str: return argon2.hash(p)
|
|
174
|
-
def verify_password(p: str, h: str) -> bool: return argon2.verify(p, h)
|
|
175
|
-
|
|
176
|
-
def create_access_token(sub: str) -> str:
|
|
177
|
-
expire = datetime.utcnow() + timedelta(minutes=15)
|
|
178
|
-
return jwt.encode({"sub": sub, "exp": expire}, settings.JWT_SECRET, algorithm="HS256")
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
```python
|
|
182
|
-
# app/api/deps.py
|
|
183
|
-
from fastapi.security import OAuth2PasswordBearer
|
|
184
|
-
|
|
185
|
-
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v1/auth/login")
|
|
186
|
-
|
|
187
|
-
async def get_current_user(token: str = Depends(oauth2_scheme)) -> User:
|
|
188
|
-
try:
|
|
189
|
-
payload = jwt.decode(token, settings.JWT_SECRET, algorithms=["HS256"])
|
|
190
|
-
except JWTError:
|
|
191
|
-
raise HTTPException(status_code=401, detail="Invalid token")
|
|
192
|
-
...
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Rate limiting (slowapi)
|
|
198
|
-
|
|
199
|
-
```python
|
|
200
|
-
from slowapi import Limiter
|
|
201
|
-
from slowapi.util import get_remote_address
|
|
202
|
-
|
|
203
|
-
limiter = Limiter(key_func=get_remote_address)
|
|
204
|
-
app.state.limiter = limiter
|
|
205
|
-
app.add_middleware(SlowAPIMiddleware)
|
|
206
|
-
|
|
207
|
-
@router.post("/login")
|
|
208
|
-
@limiter.limit("5/15minute")
|
|
209
|
-
async def login(request: Request, ...):
|
|
210
|
-
...
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## OpenAPI
|
|
216
|
-
|
|
217
|
-
FastAPI auto-gera em `/openapi.json`. Customize:
|
|
218
|
-
|
|
219
|
-
```python
|
|
220
|
-
app = FastAPI(
|
|
221
|
-
title="Projeto API",
|
|
222
|
-
version="1.0",
|
|
223
|
-
openapi_url="/openapi.json",
|
|
224
|
-
docs_url="/docs",
|
|
225
|
-
redoc_url="/redoc",
|
|
226
|
-
)
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## Settings (pydantic-settings)
|
|
232
|
-
|
|
233
|
-
```python
|
|
234
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
235
|
-
|
|
236
|
-
class Settings(BaseSettings):
|
|
237
|
-
DATABASE_URL: str
|
|
238
|
-
JWT_SECRET: str
|
|
239
|
-
JWT_EXPIRE_MINUTES: int = 15
|
|
240
|
-
model_config = SettingsConfigDict(env_file=".env")
|
|
241
|
-
|
|
242
|
-
settings = Settings()
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## Testes (pytest + httpx)
|
|
248
|
-
|
|
249
|
-
```python
|
|
250
|
-
import pytest
|
|
251
|
-
from httpx import AsyncClient
|
|
252
|
-
|
|
253
|
-
@pytest.mark.asyncio
|
|
254
|
-
async def test_create_user(client: AsyncClient, admin_token: str):
|
|
255
|
-
response = await client.post(
|
|
256
|
-
"/v1/users",
|
|
257
|
-
json={"email": "jane@example.com", "name": "Jane", "password": "longsecret123"},
|
|
258
|
-
headers={"Authorization": f"Bearer {admin_token}"},
|
|
259
|
-
)
|
|
260
|
-
assert response.status_code == 201
|
|
261
|
-
assert response.json()["email"] == "jane@example.com"
|
|
262
|
-
|
|
263
|
-
@pytest.mark.asyncio
|
|
264
|
-
async def test_duplicate_email(client, admin_token):
|
|
265
|
-
await client.post("/v1/users", json={...}, headers={...})
|
|
266
|
-
response = await client.post("/v1/users", json={...}, headers={...})
|
|
267
|
-
assert response.status_code == 409
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Antipatterns
|
|
273
|
-
|
|
274
|
-
| AP | Antipattern | Correção |
|
|
275
|
-
|---|---|---|
|
|
276
|
-
| AP-01 | SQLAlchemy direto no router | Repository injetado via Depends |
|
|
277
|
-
| AP-02 | Validação manual no router | Pydantic schema |
|
|
278
|
-
| AP-03 | Lógica no router | Service |
|
|
279
|
-
| AP-04 | `return user` (ORM) sem response_model | `response_model=UserOut` |
|
|
280
|
-
| AP-05 | Secret hardcoded | pydantic-settings + .env |
|
|
281
|
-
| AP-06 | Login sem rate limit | `@limiter.limit("5/15minute")` |
|
|
282
|
-
| AP-07 | Senha em response | response_model sem password |
|
|
283
|
-
| AP-08 | Sem `from_attributes=True` | adicionar no model_config |
|
|
284
|
-
| AP-09 | `db.execute` sem await | sempre `await db.execute(...)` |
|
|
285
|
-
| AP-10 | Mistura sync/async no router | tudo `async def` |
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
## Segurança
|
|
290
|
-
|
|
291
|
-
- Senhas: `argon2.hash()` (passlib)
|
|
292
|
-
- JWT: HS256 com `JWT_SECRET ≥ 32 bytes`, ou RS256 com par de chaves
|
|
293
|
-
- Headers: middleware com HSTS, X-Frame-Options, CSP
|
|
294
|
-
- CORS específico, nunca `*`
|
|
295
|
-
- Rate limit em login + APIs públicas
|
|
296
|
-
- Refresh token com rotação no DB
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
## CI
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
ruff check .
|
|
304
|
-
ruff format --check .
|
|
305
|
-
mypy app
|
|
306
|
-
pytest --cov=app --cov-fail-under=80
|
|
307
|
-
pip-audit
|
|
308
|
-
alembic upgrade head --sql > /dev/null
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
## Aplicação por fase DARE
|
|
314
|
-
|
|
315
|
-
### Design
|
|
316
|
-
- Endpoints REST com tags
|
|
317
|
-
- Schemas Pydantic de input/output
|
|
318
|
-
- Auth strategy
|
|
319
|
-
|
|
320
|
-
### Blueprint
|
|
321
|
-
- Estrutura de pastas
|
|
322
|
-
- SQLAlchemy models preliminares
|
|
323
|
-
- Roteamento `app.include_router(...)`
|
|
324
|
-
|
|
325
|
-
### Tasks
|
|
326
|
-
- Por feature: 4 tasks (Model, Schema, Repository, Service, Router)
|
|
327
|
-
- Task de configuração slowapi + auth + OpenAPI custom
|
|
328
|
-
|
|
329
|
-
### Execute
|
|
330
|
-
- Ralph Loop:
|
|
331
|
-
```bash
|
|
332
|
-
ruff check . && mypy app && pytest && pip-audit
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
---
|
|
336
|
-
|
|
337
|
-
## Checklist final
|
|
338
|
-
|
|
339
|
-
- [ ] Python 3.11+, ruff configurado
|
|
340
|
-
- [ ] Pydantic v2 com `model_config = {"from_attributes": True}` quando ORM
|
|
341
|
-
- [ ] SQLAlchemy 2.0 async com `Mapped[...]`
|
|
342
|
-
- [ ] Todos routers usam `response_model=`
|
|
343
|
-
- [ ] Services injetáveis via `Depends()`
|
|
344
|
-
- [ ] Repositories encapsulam SQLAlchemy
|
|
345
|
-
- [ ] `slowapi` em login + APIs públicas
|
|
346
|
-
- [ ] Settings via pydantic-settings + .env
|
|
347
|
-
- [ ] `mypy --strict` passa
|
|
348
|
-
- [ ] Cobertura ≥ 80%
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
Skill licenciada MIT — parte do DARE Method v3.
|
|
1
|
+
---
|
|
2
|
+
description: Padrões DARE para APIs REST em Python + FastAPI + Pydantic v2 + SQLAlchemy 2.0 async + alembic. Routers, dependency injection, schemas Pydantic, OAuth2+JWT, slowapi rate limit, pytest+httpx, OpenAPI auto-gerado.
|
|
3
|
+
globs: **/*.py,pyproject.toml,requirements.txt,alembic.ini,alembic/**/*.py
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: FastAPI API DARE
|
|
8
|
+
|
|
9
|
+
Você é um desenvolvedor sênior Python especialista em APIs REST com FastAPI. Esta skill garante código **assíncrono, fortemente tipado (Pydantic v2), com OpenAPI auto-gerado, auth/autz robustos**, seguindo Layered Design DARE.
|
|
10
|
+
|
|
11
|
+
## Stack canônica
|
|
12
|
+
|
|
13
|
+
- **Python 3.11+** com type hints obrigatórios
|
|
14
|
+
- **FastAPI 0.115+** com async/await
|
|
15
|
+
- **Pydantic v2** para schemas
|
|
16
|
+
- **SQLAlchemy 2.0** async + **asyncpg** (PostgreSQL)
|
|
17
|
+
- **alembic** para migrations
|
|
18
|
+
- **passlib + argon2** para hash de senhas
|
|
19
|
+
- **python-jose** ou **PyJWT** para JWT
|
|
20
|
+
- **slowapi** para rate limiting
|
|
21
|
+
- **pytest + pytest-asyncio + httpx**
|
|
22
|
+
- **ruff** (lint + format) + **mypy**
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Estrutura de pastas
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
app/
|
|
30
|
+
├── main.py ← FastAPI app + middlewares
|
|
31
|
+
├── core/
|
|
32
|
+
│ ├── config.py ← pydantic-settings
|
|
33
|
+
│ └── security.py ← hash, JWT
|
|
34
|
+
├── api/
|
|
35
|
+
│ ├── deps.py ← Depends() comuns
|
|
36
|
+
│ └── v1/
|
|
37
|
+
│ ├── users.py ← Handler (router)
|
|
38
|
+
│ └── auth.py
|
|
39
|
+
├── services/ ← Service layer
|
|
40
|
+
├── repositories/ ← Repository layer
|
|
41
|
+
├── models/ ← SQLAlchemy ORM
|
|
42
|
+
├── schemas/ ← Pydantic DTOs
|
|
43
|
+
└── tests/
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Routers (Handler)
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from fastapi import APIRouter, Depends, HTTPException, status
|
|
52
|
+
from app.api.deps import get_current_user
|
|
53
|
+
from app.schemas.user import UserCreate, UserOut
|
|
54
|
+
from app.services.register_user import RegisterUser, UserAlreadyExistsError
|
|
55
|
+
|
|
56
|
+
router = APIRouter(prefix="/users", tags=["users"])
|
|
57
|
+
|
|
58
|
+
@router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
|
|
59
|
+
async def create_user(
|
|
60
|
+
payload: UserCreate,
|
|
61
|
+
service: RegisterUser = Depends(),
|
|
62
|
+
_current: User = Depends(get_current_user),
|
|
63
|
+
):
|
|
64
|
+
try:
|
|
65
|
+
return await service.execute(payload)
|
|
66
|
+
except UserAlreadyExistsError:
|
|
67
|
+
raise HTTPException(status_code=409, detail="User already exists")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Regras:
|
|
71
|
+
- Apenas: valida via Pydantic → chama Service → retorna response_model
|
|
72
|
+
- NUNCA: SQLAlchemy direto, lógica de negócio, validação manual
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Schemas (Pydantic v2)
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from pydantic import BaseModel, EmailStr, Field
|
|
80
|
+
from datetime import datetime
|
|
81
|
+
|
|
82
|
+
class UserCreate(BaseModel):
|
|
83
|
+
email: EmailStr
|
|
84
|
+
name: str = Field(min_length=1, max_length=255)
|
|
85
|
+
password: str = Field(min_length=12)
|
|
86
|
+
|
|
87
|
+
class UserOut(BaseModel):
|
|
88
|
+
id: int
|
|
89
|
+
email: EmailStr
|
|
90
|
+
name: str
|
|
91
|
+
created_at: datetime
|
|
92
|
+
|
|
93
|
+
model_config = {"from_attributes": True} # Pydantic v2 substitui orm_mode
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Services
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
class UserAlreadyExistsError(Exception): ...
|
|
102
|
+
|
|
103
|
+
class RegisterUser:
|
|
104
|
+
def __init__(self, repo: UsersRepository = Depends()):
|
|
105
|
+
self.repo = repo
|
|
106
|
+
|
|
107
|
+
async def execute(self, payload: UserCreate) -> User:
|
|
108
|
+
if await self.repo.exists_by_email(payload.email):
|
|
109
|
+
raise UserAlreadyExistsError()
|
|
110
|
+
return await self.repo.create(
|
|
111
|
+
email=payload.email,
|
|
112
|
+
name=payload.name,
|
|
113
|
+
password_hash=hash_password(payload.password),
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Repositories (SQLAlchemy 2.0 async)
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from sqlalchemy import select
|
|
123
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
124
|
+
from fastapi import Depends
|
|
125
|
+
from app.api.deps import get_db
|
|
126
|
+
|
|
127
|
+
class UsersRepository:
|
|
128
|
+
def __init__(self, db: AsyncSession = Depends(get_db)):
|
|
129
|
+
self.db = db
|
|
130
|
+
|
|
131
|
+
async def exists_by_email(self, email: str) -> bool:
|
|
132
|
+
result = await self.db.execute(select(User.id).where(User.email == email))
|
|
133
|
+
return result.scalar_one_or_none() is not None
|
|
134
|
+
|
|
135
|
+
async def create(self, *, email: str, name: str, password_hash: str) -> User:
|
|
136
|
+
user = User(email=email, name=name, password_hash=password_hash)
|
|
137
|
+
self.db.add(user)
|
|
138
|
+
await self.db.commit()
|
|
139
|
+
await self.db.refresh(user)
|
|
140
|
+
return user
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Models (SQLAlchemy 2.0)
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
149
|
+
from datetime import datetime
|
|
150
|
+
|
|
151
|
+
class Base(DeclarativeBase): ...
|
|
152
|
+
|
|
153
|
+
class User(Base):
|
|
154
|
+
__tablename__ = "users"
|
|
155
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
|
156
|
+
email: Mapped[str] = mapped_column(unique=True, index=True)
|
|
157
|
+
name: Mapped[str]
|
|
158
|
+
password_hash: Mapped[str]
|
|
159
|
+
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Auth (OAuth2 + JWT)
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
# app/core/security.py
|
|
168
|
+
from passlib.hash import argon2
|
|
169
|
+
from datetime import datetime, timedelta
|
|
170
|
+
from jose import jwt
|
|
171
|
+
from app.core.config import settings
|
|
172
|
+
|
|
173
|
+
def hash_password(p: str) -> str: return argon2.hash(p)
|
|
174
|
+
def verify_password(p: str, h: str) -> bool: return argon2.verify(p, h)
|
|
175
|
+
|
|
176
|
+
def create_access_token(sub: str) -> str:
|
|
177
|
+
expire = datetime.utcnow() + timedelta(minutes=15)
|
|
178
|
+
return jwt.encode({"sub": sub, "exp": expire}, settings.JWT_SECRET, algorithm="HS256")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
# app/api/deps.py
|
|
183
|
+
from fastapi.security import OAuth2PasswordBearer
|
|
184
|
+
|
|
185
|
+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v1/auth/login")
|
|
186
|
+
|
|
187
|
+
async def get_current_user(token: str = Depends(oauth2_scheme)) -> User:
|
|
188
|
+
try:
|
|
189
|
+
payload = jwt.decode(token, settings.JWT_SECRET, algorithms=["HS256"])
|
|
190
|
+
except JWTError:
|
|
191
|
+
raise HTTPException(status_code=401, detail="Invalid token")
|
|
192
|
+
...
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Rate limiting (slowapi)
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from slowapi import Limiter
|
|
201
|
+
from slowapi.util import get_remote_address
|
|
202
|
+
|
|
203
|
+
limiter = Limiter(key_func=get_remote_address)
|
|
204
|
+
app.state.limiter = limiter
|
|
205
|
+
app.add_middleware(SlowAPIMiddleware)
|
|
206
|
+
|
|
207
|
+
@router.post("/login")
|
|
208
|
+
@limiter.limit("5/15minute")
|
|
209
|
+
async def login(request: Request, ...):
|
|
210
|
+
...
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## OpenAPI
|
|
216
|
+
|
|
217
|
+
FastAPI auto-gera em `/openapi.json`. Customize:
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
app = FastAPI(
|
|
221
|
+
title="Projeto API",
|
|
222
|
+
version="1.0",
|
|
223
|
+
openapi_url="/openapi.json",
|
|
224
|
+
docs_url="/docs",
|
|
225
|
+
redoc_url="/redoc",
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Settings (pydantic-settings)
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
235
|
+
|
|
236
|
+
class Settings(BaseSettings):
|
|
237
|
+
DATABASE_URL: str
|
|
238
|
+
JWT_SECRET: str
|
|
239
|
+
JWT_EXPIRE_MINUTES: int = 15
|
|
240
|
+
model_config = SettingsConfigDict(env_file=".env")
|
|
241
|
+
|
|
242
|
+
settings = Settings()
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Testes (pytest + httpx)
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
import pytest
|
|
251
|
+
from httpx import AsyncClient
|
|
252
|
+
|
|
253
|
+
@pytest.mark.asyncio
|
|
254
|
+
async def test_create_user(client: AsyncClient, admin_token: str):
|
|
255
|
+
response = await client.post(
|
|
256
|
+
"/v1/users",
|
|
257
|
+
json={"email": "jane@example.com", "name": "Jane", "password": "longsecret123"},
|
|
258
|
+
headers={"Authorization": f"Bearer {admin_token}"},
|
|
259
|
+
)
|
|
260
|
+
assert response.status_code == 201
|
|
261
|
+
assert response.json()["email"] == "jane@example.com"
|
|
262
|
+
|
|
263
|
+
@pytest.mark.asyncio
|
|
264
|
+
async def test_duplicate_email(client, admin_token):
|
|
265
|
+
await client.post("/v1/users", json={...}, headers={...})
|
|
266
|
+
response = await client.post("/v1/users", json={...}, headers={...})
|
|
267
|
+
assert response.status_code == 409
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Antipatterns
|
|
273
|
+
|
|
274
|
+
| AP | Antipattern | Correção |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| AP-01 | SQLAlchemy direto no router | Repository injetado via Depends |
|
|
277
|
+
| AP-02 | Validação manual no router | Pydantic schema |
|
|
278
|
+
| AP-03 | Lógica no router | Service |
|
|
279
|
+
| AP-04 | `return user` (ORM) sem response_model | `response_model=UserOut` |
|
|
280
|
+
| AP-05 | Secret hardcoded | pydantic-settings + .env |
|
|
281
|
+
| AP-06 | Login sem rate limit | `@limiter.limit("5/15minute")` |
|
|
282
|
+
| AP-07 | Senha em response | response_model sem password |
|
|
283
|
+
| AP-08 | Sem `from_attributes=True` | adicionar no model_config |
|
|
284
|
+
| AP-09 | `db.execute` sem await | sempre `await db.execute(...)` |
|
|
285
|
+
| AP-10 | Mistura sync/async no router | tudo `async def` |
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Segurança
|
|
290
|
+
|
|
291
|
+
- Senhas: `argon2.hash()` (passlib)
|
|
292
|
+
- JWT: HS256 com `JWT_SECRET ≥ 32 bytes`, ou RS256 com par de chaves
|
|
293
|
+
- Headers: middleware com HSTS, X-Frame-Options, CSP
|
|
294
|
+
- CORS específico, nunca `*`
|
|
295
|
+
- Rate limit em login + APIs públicas
|
|
296
|
+
- Refresh token com rotação no DB
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## CI
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
ruff check .
|
|
304
|
+
ruff format --check .
|
|
305
|
+
mypy app
|
|
306
|
+
pytest --cov=app --cov-fail-under=80
|
|
307
|
+
pip-audit
|
|
308
|
+
alembic upgrade head --sql > /dev/null
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Aplicação por fase DARE
|
|
314
|
+
|
|
315
|
+
### Design
|
|
316
|
+
- Endpoints REST com tags
|
|
317
|
+
- Schemas Pydantic de input/output
|
|
318
|
+
- Auth strategy
|
|
319
|
+
|
|
320
|
+
### Blueprint
|
|
321
|
+
- Estrutura de pastas
|
|
322
|
+
- SQLAlchemy models preliminares
|
|
323
|
+
- Roteamento `app.include_router(...)`
|
|
324
|
+
|
|
325
|
+
### Tasks
|
|
326
|
+
- Por feature: 4 tasks (Model, Schema, Repository, Service, Router)
|
|
327
|
+
- Task de configuração slowapi + auth + OpenAPI custom
|
|
328
|
+
|
|
329
|
+
### Execute
|
|
330
|
+
- Ralph Loop:
|
|
331
|
+
```bash
|
|
332
|
+
ruff check . && mypy app && pytest && pip-audit
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Checklist final
|
|
338
|
+
|
|
339
|
+
- [ ] Python 3.11+, ruff configurado
|
|
340
|
+
- [ ] Pydantic v2 com `model_config = {"from_attributes": True}` quando ORM
|
|
341
|
+
- [ ] SQLAlchemy 2.0 async com `Mapped[...]`
|
|
342
|
+
- [ ] Todos routers usam `response_model=`
|
|
343
|
+
- [ ] Services injetáveis via `Depends()`
|
|
344
|
+
- [ ] Repositories encapsulam SQLAlchemy
|
|
345
|
+
- [ ] `slowapi` em login + APIs públicas
|
|
346
|
+
- [ ] Settings via pydantic-settings + .env
|
|
347
|
+
- [ ] `mypy --strict` passa
|
|
348
|
+
- [ ] Cobertura ≥ 80%
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
Skill licenciada MIT — parte do DARE Method v3.
|