@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,343 +1,343 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: skill-fastapi-api
|
|
3
|
-
description: Padrões DARE para APIs REST em Python + FastAPI + Pydantic + uvicorn. Routers, dependency injection, Pydantic v2 schemas, async SQLAlchemy 2.0, autenticação OAuth2 + JWT, rate limit com slowapi, pytest + httpx, OpenAPI auto-gerado.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# DARE FastAPI Skill
|
|
7
|
-
|
|
8
|
-
Você é um desenvolvedor sênior Python especialista em APIs REST com FastAPI. Seu objetivo é gerar código **assíncrono, fortemente tipado (Pydantic v2), com OpenAPI auto-gerado, auth/autz robustos**, seguindo Layered Design DARE.
|
|
9
|
-
|
|
10
|
-
## Quando usar
|
|
11
|
-
|
|
12
|
-
- Projeto FastAPI novo via DARE
|
|
13
|
-
- Adicionar feature em API FastAPI existente
|
|
14
|
-
- Migrar de Flask/Django para FastAPI
|
|
15
|
-
- Auditar projeto FastAPI para conformidade DARE
|
|
16
|
-
|
|
17
|
-
## Stack canônica
|
|
18
|
-
|
|
19
|
-
- **Python 3.11+** com type hints obrigatórios
|
|
20
|
-
- **FastAPI 0.115+** com async/await
|
|
21
|
-
- **Pydantic v2** para schemas
|
|
22
|
-
- **SQLAlchemy 2.0** async + **asyncpg** (PostgreSQL)
|
|
23
|
-
- **alembic** para migrations
|
|
24
|
-
- **passlib + argon2** para hash de senhas
|
|
25
|
-
- **python-jose** ou **PyJWT** para JWT
|
|
26
|
-
- **slowapi** para rate limiting
|
|
27
|
-
- **pytest + pytest-asyncio + httpx** para testes
|
|
28
|
-
- **ruff** para lint + format
|
|
29
|
-
- **mypy** para type checking
|
|
30
|
-
|
|
31
|
-
## Layered Design em FastAPI
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
app/
|
|
35
|
-
├── main.py ← FastAPI app + middlewares
|
|
36
|
-
├── core/
|
|
37
|
-
│ ├── config.py ← Settings via pydantic-settings
|
|
38
|
-
│ └── security.py ← hash, JWT
|
|
39
|
-
├── api/
|
|
40
|
-
│ ├── deps.py ← Depends() comuns
|
|
41
|
-
│ └── v1/
|
|
42
|
-
│ ├── users.py ← Handler (router)
|
|
43
|
-
│ └── auth.py
|
|
44
|
-
├── services/
|
|
45
|
-
│ └── register_user.py ← Service
|
|
46
|
-
├── repositories/
|
|
47
|
-
│ └── users.py ← Repository
|
|
48
|
-
├── models/ ← SQLAlchemy ORM
|
|
49
|
-
│ └── user.py
|
|
50
|
-
├── schemas/ ← Pydantic DTOs
|
|
51
|
-
│ ├── user.py
|
|
52
|
-
│ └── auth.py
|
|
53
|
-
└── tests/
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Routers (Handler)
|
|
57
|
-
|
|
58
|
-
```python
|
|
59
|
-
from fastapi import APIRouter, Depends, HTTPException, status
|
|
60
|
-
from app.api.deps import get_current_user
|
|
61
|
-
from app.schemas.user import UserCreate, UserOut
|
|
62
|
-
from app.services.register_user import RegisterUser, UserAlreadyExistsError
|
|
63
|
-
|
|
64
|
-
router = APIRouter(prefix="/users", tags=["users"])
|
|
65
|
-
|
|
66
|
-
@router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
|
|
67
|
-
async def create_user(
|
|
68
|
-
payload: UserCreate,
|
|
69
|
-
service: RegisterUser = Depends(),
|
|
70
|
-
_current: User = Depends(get_current_user),
|
|
71
|
-
):
|
|
72
|
-
try:
|
|
73
|
-
return await service.execute(payload)
|
|
74
|
-
except UserAlreadyExistsError:
|
|
75
|
-
raise HTTPException(status_code=409, detail="User already exists")
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Regras:
|
|
79
|
-
- Apenas: valida via Pydantic → chama Service → retorna response_model
|
|
80
|
-
- NUNCA: SQLAlchemy direto, lógica de negócio
|
|
81
|
-
|
|
82
|
-
## Schemas (Pydantic v2)
|
|
83
|
-
|
|
84
|
-
```python
|
|
85
|
-
from pydantic import BaseModel, EmailStr, Field
|
|
86
|
-
from datetime import datetime
|
|
87
|
-
|
|
88
|
-
class UserCreate(BaseModel):
|
|
89
|
-
email: EmailStr
|
|
90
|
-
name: str = Field(min_length=1, max_length=255)
|
|
91
|
-
password: str = Field(min_length=12)
|
|
92
|
-
|
|
93
|
-
class UserOut(BaseModel):
|
|
94
|
-
id: int
|
|
95
|
-
email: EmailStr
|
|
96
|
-
name: str
|
|
97
|
-
created_at: datetime
|
|
98
|
-
|
|
99
|
-
model_config = {"from_attributes": True} # Pydantic v2 / ex-orm_mode
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Services
|
|
103
|
-
|
|
104
|
-
```python
|
|
105
|
-
from app.repositories.users import UsersRepository
|
|
106
|
-
from app.core.security import hash_password
|
|
107
|
-
from app.schemas.user import UserCreate
|
|
108
|
-
from app.models.user import User
|
|
109
|
-
|
|
110
|
-
class UserAlreadyExistsError(Exception): ...
|
|
111
|
-
|
|
112
|
-
class RegisterUser:
|
|
113
|
-
def __init__(self, repo: UsersRepository = Depends()):
|
|
114
|
-
self.repo = repo
|
|
115
|
-
|
|
116
|
-
async def execute(self, payload: UserCreate) -> User:
|
|
117
|
-
if await self.repo.exists_by_email(payload.email):
|
|
118
|
-
raise UserAlreadyExistsError()
|
|
119
|
-
return await self.repo.create(
|
|
120
|
-
email=payload.email,
|
|
121
|
-
name=payload.name,
|
|
122
|
-
password_hash=hash_password(payload.password),
|
|
123
|
-
)
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Repositories (SQLAlchemy async 2.0)
|
|
127
|
-
|
|
128
|
-
```python
|
|
129
|
-
from sqlalchemy import select
|
|
130
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
131
|
-
from fastapi import Depends
|
|
132
|
-
from app.api.deps import get_db
|
|
133
|
-
from app.models.user import User
|
|
134
|
-
|
|
135
|
-
class UsersRepository:
|
|
136
|
-
def __init__(self, db: AsyncSession = Depends(get_db)):
|
|
137
|
-
self.db = db
|
|
138
|
-
|
|
139
|
-
async def exists_by_email(self, email: str) -> bool:
|
|
140
|
-
result = await self.db.execute(select(User.id).where(User.email == email))
|
|
141
|
-
return result.scalar_one_or_none() is not None
|
|
142
|
-
|
|
143
|
-
async def create(self, *, email: str, name: str, password_hash: str) -> User:
|
|
144
|
-
user = User(email=email, name=name, password_hash=password_hash)
|
|
145
|
-
self.db.add(user)
|
|
146
|
-
await self.db.commit()
|
|
147
|
-
await self.db.refresh(user)
|
|
148
|
-
return user
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## Models (SQLAlchemy)
|
|
152
|
-
|
|
153
|
-
```python
|
|
154
|
-
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
155
|
-
from datetime import datetime
|
|
156
|
-
|
|
157
|
-
class Base(DeclarativeBase): ...
|
|
158
|
-
|
|
159
|
-
class User(Base):
|
|
160
|
-
__tablename__ = "users"
|
|
161
|
-
id: Mapped[int] = mapped_column(primary_key=True)
|
|
162
|
-
email: Mapped[str] = mapped_column(unique=True, index=True)
|
|
163
|
-
name: Mapped[str]
|
|
164
|
-
password_hash: Mapped[str]
|
|
165
|
-
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
## Auth (OAuth2 + JWT)
|
|
169
|
-
|
|
170
|
-
```python
|
|
171
|
-
# app/core/security.py
|
|
172
|
-
from passlib.hash import argon2
|
|
173
|
-
from datetime import datetime, timedelta
|
|
174
|
-
from jose import jwt
|
|
175
|
-
from app.core.config import settings
|
|
176
|
-
|
|
177
|
-
def hash_password(p: str) -> str: return argon2.hash(p)
|
|
178
|
-
def verify_password(p: str, h: str) -> bool: return argon2.verify(p, h)
|
|
179
|
-
|
|
180
|
-
def create_access_token(sub: str) -> str:
|
|
181
|
-
expire = datetime.utcnow() + timedelta(minutes=15)
|
|
182
|
-
payload = {"sub": sub, "exp": expire}
|
|
183
|
-
return jwt.encode(payload, settings.JWT_SECRET, algorithm="HS256")
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
```python
|
|
187
|
-
# app/api/deps.py
|
|
188
|
-
from fastapi import Depends, HTTPException, status
|
|
189
|
-
from fastapi.security import OAuth2PasswordBearer
|
|
190
|
-
from jose import jwt, JWTError
|
|
191
|
-
from app.core.config import settings
|
|
192
|
-
|
|
193
|
-
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v1/auth/login")
|
|
194
|
-
|
|
195
|
-
async def get_current_user(token: str = Depends(oauth2_scheme)) -> User:
|
|
196
|
-
try:
|
|
197
|
-
payload = jwt.decode(token, settings.JWT_SECRET, algorithms=["HS256"])
|
|
198
|
-
sub: str = payload.get("sub")
|
|
199
|
-
except JWTError:
|
|
200
|
-
raise HTTPException(status_code=401, detail="Invalid token")
|
|
201
|
-
# buscar user no DB ...
|
|
202
|
-
return user
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Rate limiting (slowapi)
|
|
206
|
-
|
|
207
|
-
```python
|
|
208
|
-
from slowapi import Limiter
|
|
209
|
-
from slowapi.util import get_remote_address
|
|
210
|
-
|
|
211
|
-
limiter = Limiter(key_func=get_remote_address)
|
|
212
|
-
app.state.limiter = limiter
|
|
213
|
-
app.add_middleware(SlowAPIMiddleware)
|
|
214
|
-
|
|
215
|
-
@router.post("/login")
|
|
216
|
-
@limiter.limit("5/15minute")
|
|
217
|
-
async def login(request: Request, ...):
|
|
218
|
-
...
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## OpenAPI
|
|
222
|
-
|
|
223
|
-
FastAPI auto-gera em `/openapi.json`. Customize title/version/auth:
|
|
224
|
-
|
|
225
|
-
```python
|
|
226
|
-
app = FastAPI(
|
|
227
|
-
title="Projeto API",
|
|
228
|
-
version="1.0",
|
|
229
|
-
openapi_url="/openapi.json",
|
|
230
|
-
docs_url="/docs",
|
|
231
|
-
redoc_url="/redoc",
|
|
232
|
-
)
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Settings (pydantic-settings)
|
|
236
|
-
|
|
237
|
-
```python
|
|
238
|
-
# app/core/config.py
|
|
239
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
240
|
-
|
|
241
|
-
class Settings(BaseSettings):
|
|
242
|
-
DATABASE_URL: str
|
|
243
|
-
JWT_SECRET: str
|
|
244
|
-
JWT_EXPIRE_MINUTES: int = 15
|
|
245
|
-
model_config = SettingsConfigDict(env_file=".env")
|
|
246
|
-
|
|
247
|
-
settings = Settings()
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## Testes
|
|
251
|
-
|
|
252
|
-
```python
|
|
253
|
-
# tests/test_users.py
|
|
254
|
-
import pytest
|
|
255
|
-
from httpx import AsyncClient
|
|
256
|
-
|
|
257
|
-
@pytest.mark.asyncio
|
|
258
|
-
async def test_create_user(client: AsyncClient, admin_token: str):
|
|
259
|
-
response = await client.post(
|
|
260
|
-
"/v1/users",
|
|
261
|
-
json={"email": "jane@example.com", "name": "Jane", "password": "longsecret123"},
|
|
262
|
-
headers={"Authorization": f"Bearer {admin_token}"},
|
|
263
|
-
)
|
|
264
|
-
assert response.status_code == 201
|
|
265
|
-
assert response.json()["email"] == "jane@example.com"
|
|
266
|
-
|
|
267
|
-
@pytest.mark.asyncio
|
|
268
|
-
async def test_duplicate_email(client: AsyncClient, admin_token: str):
|
|
269
|
-
# cria primeiro
|
|
270
|
-
await client.post("/v1/users", json={...}, headers={...})
|
|
271
|
-
# tenta de novo
|
|
272
|
-
response = await client.post("/v1/users", json={...}, headers={...})
|
|
273
|
-
assert response.status_code == 409
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## Antipatterns
|
|
277
|
-
|
|
278
|
-
| AP | Antipattern | Correção |
|
|
279
|
-
|---|---|---|
|
|
280
|
-
| AP-01 | SQLAlchemy direto no router | Repository injetado via Depends |
|
|
281
|
-
| AP-02 | Validação manual no router | Pydantic schema |
|
|
282
|
-
| AP-03 | Lógica no router | Service |
|
|
283
|
-
| AP-04 | `return user` (ORM) sem response_model | `response_model=UserOut` |
|
|
284
|
-
| AP-05 | Secret hardcoded | `pydantic-settings` + `.env` |
|
|
285
|
-
| AP-06 | Login sem rate limit | `@limiter.limit("5/15minute")` |
|
|
286
|
-
| AP-07 | `password=user.password` em response | `response_model=UserOut` sem password |
|
|
287
|
-
| AP-08 | Sem `from_attributes=True` | response_model não converte ORM |
|
|
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
|
|
297
|
-
|
|
298
|
-
## CI
|
|
299
|
-
|
|
300
|
-
```bash
|
|
301
|
-
ruff check .
|
|
302
|
-
ruff format --check .
|
|
303
|
-
mypy app
|
|
304
|
-
pytest --cov=app --cov-fail-under=80
|
|
305
|
-
pip-audit
|
|
306
|
-
alembic upgrade head --sql > /dev/null # valida migrations
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
## Como aplicar
|
|
310
|
-
|
|
311
|
-
### Passo 1: Audit
|
|
312
|
-
|
|
313
|
-
```bash
|
|
314
|
-
grep -rn "db\.execute\|db\.query" app/api/ # AP-01
|
|
315
|
-
grep -rn "request\.json()" app/api/ # AP-02
|
|
316
|
-
grep -rn "JWT_SECRET\s*=\s*['\"]" app/ # AP-05
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Passo 2: Migrar para Pydantic schemas
|
|
320
|
-
|
|
321
|
-
Para cada `request.json()` ou validação inline → schema Pydantic.
|
|
322
|
-
|
|
323
|
-
### Passo 3: Extrair Services
|
|
324
|
-
|
|
325
|
-
Lógica > 5 linhas no router → Service injetável.
|
|
326
|
-
|
|
327
|
-
### Passo 4: Adicionar Repositories
|
|
328
|
-
|
|
329
|
-
Encapsular SQLAlchemy. Router chama Service via Depends, Service chama Repository.
|
|
330
|
-
|
|
331
|
-
### Passo 5: Configurar slowapi + auth
|
|
332
|
-
|
|
333
|
-
`app.add_middleware(SlowAPIMiddleware)` global + `@limiter.limit` em login.
|
|
334
|
-
|
|
335
|
-
## Dicas
|
|
336
|
-
|
|
337
|
-
- **Combine** com `dare-docker` (Python 3.11-slim, não-root, multi-stage)
|
|
338
|
-
- **Use** `dare-llm-integration` se houver Gemini/Claude (FastAPI + httpx async)
|
|
339
|
-
- **Para realtime**, FastAPI tem `WebSocket` nativo + `sse-starlette`
|
|
340
|
-
|
|
341
|
-
---
|
|
342
|
-
|
|
343
|
-
Esta skill é parte do DARE Method e está sob licença MIT.
|
|
1
|
+
---
|
|
2
|
+
name: skill-fastapi-api
|
|
3
|
+
description: Padrões DARE para APIs REST em Python + FastAPI + Pydantic + uvicorn. Routers, dependency injection, Pydantic v2 schemas, async SQLAlchemy 2.0, autenticação OAuth2 + JWT, rate limit com slowapi, pytest + httpx, OpenAPI auto-gerado.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DARE FastAPI Skill
|
|
7
|
+
|
|
8
|
+
Você é um desenvolvedor sênior Python especialista em APIs REST com FastAPI. Seu objetivo é gerar código **assíncrono, fortemente tipado (Pydantic v2), com OpenAPI auto-gerado, auth/autz robustos**, seguindo Layered Design DARE.
|
|
9
|
+
|
|
10
|
+
## Quando usar
|
|
11
|
+
|
|
12
|
+
- Projeto FastAPI novo via DARE
|
|
13
|
+
- Adicionar feature em API FastAPI existente
|
|
14
|
+
- Migrar de Flask/Django para FastAPI
|
|
15
|
+
- Auditar projeto FastAPI para conformidade DARE
|
|
16
|
+
|
|
17
|
+
## Stack canônica
|
|
18
|
+
|
|
19
|
+
- **Python 3.11+** com type hints obrigatórios
|
|
20
|
+
- **FastAPI 0.115+** com async/await
|
|
21
|
+
- **Pydantic v2** para schemas
|
|
22
|
+
- **SQLAlchemy 2.0** async + **asyncpg** (PostgreSQL)
|
|
23
|
+
- **alembic** para migrations
|
|
24
|
+
- **passlib + argon2** para hash de senhas
|
|
25
|
+
- **python-jose** ou **PyJWT** para JWT
|
|
26
|
+
- **slowapi** para rate limiting
|
|
27
|
+
- **pytest + pytest-asyncio + httpx** para testes
|
|
28
|
+
- **ruff** para lint + format
|
|
29
|
+
- **mypy** para type checking
|
|
30
|
+
|
|
31
|
+
## Layered Design em FastAPI
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
app/
|
|
35
|
+
├── main.py ← FastAPI app + middlewares
|
|
36
|
+
├── core/
|
|
37
|
+
│ ├── config.py ← Settings via pydantic-settings
|
|
38
|
+
│ └── security.py ← hash, JWT
|
|
39
|
+
├── api/
|
|
40
|
+
│ ├── deps.py ← Depends() comuns
|
|
41
|
+
│ └── v1/
|
|
42
|
+
│ ├── users.py ← Handler (router)
|
|
43
|
+
│ └── auth.py
|
|
44
|
+
├── services/
|
|
45
|
+
│ └── register_user.py ← Service
|
|
46
|
+
├── repositories/
|
|
47
|
+
│ └── users.py ← Repository
|
|
48
|
+
├── models/ ← SQLAlchemy ORM
|
|
49
|
+
│ └── user.py
|
|
50
|
+
├── schemas/ ← Pydantic DTOs
|
|
51
|
+
│ ├── user.py
|
|
52
|
+
│ └── auth.py
|
|
53
|
+
└── tests/
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Routers (Handler)
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from fastapi import APIRouter, Depends, HTTPException, status
|
|
60
|
+
from app.api.deps import get_current_user
|
|
61
|
+
from app.schemas.user import UserCreate, UserOut
|
|
62
|
+
from app.services.register_user import RegisterUser, UserAlreadyExistsError
|
|
63
|
+
|
|
64
|
+
router = APIRouter(prefix="/users", tags=["users"])
|
|
65
|
+
|
|
66
|
+
@router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
|
|
67
|
+
async def create_user(
|
|
68
|
+
payload: UserCreate,
|
|
69
|
+
service: RegisterUser = Depends(),
|
|
70
|
+
_current: User = Depends(get_current_user),
|
|
71
|
+
):
|
|
72
|
+
try:
|
|
73
|
+
return await service.execute(payload)
|
|
74
|
+
except UserAlreadyExistsError:
|
|
75
|
+
raise HTTPException(status_code=409, detail="User already exists")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Regras:
|
|
79
|
+
- Apenas: valida via Pydantic → chama Service → retorna response_model
|
|
80
|
+
- NUNCA: SQLAlchemy direto, lógica de negócio
|
|
81
|
+
|
|
82
|
+
## Schemas (Pydantic v2)
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from pydantic import BaseModel, EmailStr, Field
|
|
86
|
+
from datetime import datetime
|
|
87
|
+
|
|
88
|
+
class UserCreate(BaseModel):
|
|
89
|
+
email: EmailStr
|
|
90
|
+
name: str = Field(min_length=1, max_length=255)
|
|
91
|
+
password: str = Field(min_length=12)
|
|
92
|
+
|
|
93
|
+
class UserOut(BaseModel):
|
|
94
|
+
id: int
|
|
95
|
+
email: EmailStr
|
|
96
|
+
name: str
|
|
97
|
+
created_at: datetime
|
|
98
|
+
|
|
99
|
+
model_config = {"from_attributes": True} # Pydantic v2 / ex-orm_mode
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Services
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from app.repositories.users import UsersRepository
|
|
106
|
+
from app.core.security import hash_password
|
|
107
|
+
from app.schemas.user import UserCreate
|
|
108
|
+
from app.models.user import User
|
|
109
|
+
|
|
110
|
+
class UserAlreadyExistsError(Exception): ...
|
|
111
|
+
|
|
112
|
+
class RegisterUser:
|
|
113
|
+
def __init__(self, repo: UsersRepository = Depends()):
|
|
114
|
+
self.repo = repo
|
|
115
|
+
|
|
116
|
+
async def execute(self, payload: UserCreate) -> User:
|
|
117
|
+
if await self.repo.exists_by_email(payload.email):
|
|
118
|
+
raise UserAlreadyExistsError()
|
|
119
|
+
return await self.repo.create(
|
|
120
|
+
email=payload.email,
|
|
121
|
+
name=payload.name,
|
|
122
|
+
password_hash=hash_password(payload.password),
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Repositories (SQLAlchemy async 2.0)
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from sqlalchemy import select
|
|
130
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
131
|
+
from fastapi import Depends
|
|
132
|
+
from app.api.deps import get_db
|
|
133
|
+
from app.models.user import User
|
|
134
|
+
|
|
135
|
+
class UsersRepository:
|
|
136
|
+
def __init__(self, db: AsyncSession = Depends(get_db)):
|
|
137
|
+
self.db = db
|
|
138
|
+
|
|
139
|
+
async def exists_by_email(self, email: str) -> bool:
|
|
140
|
+
result = await self.db.execute(select(User.id).where(User.email == email))
|
|
141
|
+
return result.scalar_one_or_none() is not None
|
|
142
|
+
|
|
143
|
+
async def create(self, *, email: str, name: str, password_hash: str) -> User:
|
|
144
|
+
user = User(email=email, name=name, password_hash=password_hash)
|
|
145
|
+
self.db.add(user)
|
|
146
|
+
await self.db.commit()
|
|
147
|
+
await self.db.refresh(user)
|
|
148
|
+
return user
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Models (SQLAlchemy)
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
155
|
+
from datetime import datetime
|
|
156
|
+
|
|
157
|
+
class Base(DeclarativeBase): ...
|
|
158
|
+
|
|
159
|
+
class User(Base):
|
|
160
|
+
__tablename__ = "users"
|
|
161
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
|
162
|
+
email: Mapped[str] = mapped_column(unique=True, index=True)
|
|
163
|
+
name: Mapped[str]
|
|
164
|
+
password_hash: Mapped[str]
|
|
165
|
+
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Auth (OAuth2 + JWT)
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
# app/core/security.py
|
|
172
|
+
from passlib.hash import argon2
|
|
173
|
+
from datetime import datetime, timedelta
|
|
174
|
+
from jose import jwt
|
|
175
|
+
from app.core.config import settings
|
|
176
|
+
|
|
177
|
+
def hash_password(p: str) -> str: return argon2.hash(p)
|
|
178
|
+
def verify_password(p: str, h: str) -> bool: return argon2.verify(p, h)
|
|
179
|
+
|
|
180
|
+
def create_access_token(sub: str) -> str:
|
|
181
|
+
expire = datetime.utcnow() + timedelta(minutes=15)
|
|
182
|
+
payload = {"sub": sub, "exp": expire}
|
|
183
|
+
return jwt.encode(payload, settings.JWT_SECRET, algorithm="HS256")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
# app/api/deps.py
|
|
188
|
+
from fastapi import Depends, HTTPException, status
|
|
189
|
+
from fastapi.security import OAuth2PasswordBearer
|
|
190
|
+
from jose import jwt, JWTError
|
|
191
|
+
from app.core.config import settings
|
|
192
|
+
|
|
193
|
+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v1/auth/login")
|
|
194
|
+
|
|
195
|
+
async def get_current_user(token: str = Depends(oauth2_scheme)) -> User:
|
|
196
|
+
try:
|
|
197
|
+
payload = jwt.decode(token, settings.JWT_SECRET, algorithms=["HS256"])
|
|
198
|
+
sub: str = payload.get("sub")
|
|
199
|
+
except JWTError:
|
|
200
|
+
raise HTTPException(status_code=401, detail="Invalid token")
|
|
201
|
+
# buscar user no DB ...
|
|
202
|
+
return user
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Rate limiting (slowapi)
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from slowapi import Limiter
|
|
209
|
+
from slowapi.util import get_remote_address
|
|
210
|
+
|
|
211
|
+
limiter = Limiter(key_func=get_remote_address)
|
|
212
|
+
app.state.limiter = limiter
|
|
213
|
+
app.add_middleware(SlowAPIMiddleware)
|
|
214
|
+
|
|
215
|
+
@router.post("/login")
|
|
216
|
+
@limiter.limit("5/15minute")
|
|
217
|
+
async def login(request: Request, ...):
|
|
218
|
+
...
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## OpenAPI
|
|
222
|
+
|
|
223
|
+
FastAPI auto-gera em `/openapi.json`. Customize title/version/auth:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
app = FastAPI(
|
|
227
|
+
title="Projeto API",
|
|
228
|
+
version="1.0",
|
|
229
|
+
openapi_url="/openapi.json",
|
|
230
|
+
docs_url="/docs",
|
|
231
|
+
redoc_url="/redoc",
|
|
232
|
+
)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Settings (pydantic-settings)
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
# app/core/config.py
|
|
239
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
240
|
+
|
|
241
|
+
class Settings(BaseSettings):
|
|
242
|
+
DATABASE_URL: str
|
|
243
|
+
JWT_SECRET: str
|
|
244
|
+
JWT_EXPIRE_MINUTES: int = 15
|
|
245
|
+
model_config = SettingsConfigDict(env_file=".env")
|
|
246
|
+
|
|
247
|
+
settings = Settings()
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Testes
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
# tests/test_users.py
|
|
254
|
+
import pytest
|
|
255
|
+
from httpx import AsyncClient
|
|
256
|
+
|
|
257
|
+
@pytest.mark.asyncio
|
|
258
|
+
async def test_create_user(client: AsyncClient, admin_token: str):
|
|
259
|
+
response = await client.post(
|
|
260
|
+
"/v1/users",
|
|
261
|
+
json={"email": "jane@example.com", "name": "Jane", "password": "longsecret123"},
|
|
262
|
+
headers={"Authorization": f"Bearer {admin_token}"},
|
|
263
|
+
)
|
|
264
|
+
assert response.status_code == 201
|
|
265
|
+
assert response.json()["email"] == "jane@example.com"
|
|
266
|
+
|
|
267
|
+
@pytest.mark.asyncio
|
|
268
|
+
async def test_duplicate_email(client: AsyncClient, admin_token: str):
|
|
269
|
+
# cria primeiro
|
|
270
|
+
await client.post("/v1/users", json={...}, headers={...})
|
|
271
|
+
# tenta de novo
|
|
272
|
+
response = await client.post("/v1/users", json={...}, headers={...})
|
|
273
|
+
assert response.status_code == 409
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Antipatterns
|
|
277
|
+
|
|
278
|
+
| AP | Antipattern | Correção |
|
|
279
|
+
|---|---|---|
|
|
280
|
+
| AP-01 | SQLAlchemy direto no router | Repository injetado via Depends |
|
|
281
|
+
| AP-02 | Validação manual no router | Pydantic schema |
|
|
282
|
+
| AP-03 | Lógica no router | Service |
|
|
283
|
+
| AP-04 | `return user` (ORM) sem response_model | `response_model=UserOut` |
|
|
284
|
+
| AP-05 | Secret hardcoded | `pydantic-settings` + `.env` |
|
|
285
|
+
| AP-06 | Login sem rate limit | `@limiter.limit("5/15minute")` |
|
|
286
|
+
| AP-07 | `password=user.password` em response | `response_model=UserOut` sem password |
|
|
287
|
+
| AP-08 | Sem `from_attributes=True` | response_model não converte ORM |
|
|
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
|
|
297
|
+
|
|
298
|
+
## CI
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
ruff check .
|
|
302
|
+
ruff format --check .
|
|
303
|
+
mypy app
|
|
304
|
+
pytest --cov=app --cov-fail-under=80
|
|
305
|
+
pip-audit
|
|
306
|
+
alembic upgrade head --sql > /dev/null # valida migrations
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Como aplicar
|
|
310
|
+
|
|
311
|
+
### Passo 1: Audit
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
grep -rn "db\.execute\|db\.query" app/api/ # AP-01
|
|
315
|
+
grep -rn "request\.json()" app/api/ # AP-02
|
|
316
|
+
grep -rn "JWT_SECRET\s*=\s*['\"]" app/ # AP-05
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Passo 2: Migrar para Pydantic schemas
|
|
320
|
+
|
|
321
|
+
Para cada `request.json()` ou validação inline → schema Pydantic.
|
|
322
|
+
|
|
323
|
+
### Passo 3: Extrair Services
|
|
324
|
+
|
|
325
|
+
Lógica > 5 linhas no router → Service injetável.
|
|
326
|
+
|
|
327
|
+
### Passo 4: Adicionar Repositories
|
|
328
|
+
|
|
329
|
+
Encapsular SQLAlchemy. Router chama Service via Depends, Service chama Repository.
|
|
330
|
+
|
|
331
|
+
### Passo 5: Configurar slowapi + auth
|
|
332
|
+
|
|
333
|
+
`app.add_middleware(SlowAPIMiddleware)` global + `@limiter.limit` em login.
|
|
334
|
+
|
|
335
|
+
## Dicas
|
|
336
|
+
|
|
337
|
+
- **Combine** com `dare-docker` (Python 3.11-slim, não-root, multi-stage)
|
|
338
|
+
- **Use** `dare-llm-integration` se houver Gemini/Claude (FastAPI + httpx async)
|
|
339
|
+
- **Para realtime**, FastAPI tem `WebSocket` nativo + `sse-starlette`
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
Esta skill é parte do DARE Method e está sob licença MIT.
|