oh-my-agent-commandcode 0.0.0-stage → 20.38.3
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/README.md +35 -2
- package/filter-test-output.sh +52 -0
- package/mods/oma.ts +347 -0
- package/package.json +30 -4
- package/skills/_shared/conditional/experiment-ledger.md +28 -0
- package/skills/_shared/conditional/exploration-loop.md +22 -0
- package/skills/_shared/conditional/quality-score.md +33 -0
- package/skills/_shared/core/anti-ai-prose.md +191 -0
- package/skills/_shared/core/api-contracts/README.md +28 -0
- package/skills/_shared/core/api-contracts/template.md +90 -0
- package/skills/_shared/core/clarification-protocol.md +23 -0
- package/skills/_shared/core/code-intelligence.md +46 -0
- package/skills/_shared/core/collaboration-policy.md +28 -0
- package/skills/_shared/core/common-checklist.md +28 -0
- package/skills/_shared/core/context-budget.md +22 -0
- package/skills/_shared/core/context-loading.md +34 -0
- package/skills/_shared/core/difficulty-guide.md +26 -0
- package/skills/_shared/core/execution-policy.md +31 -0
- package/skills/_shared/core/lessons-learned.md +33 -0
- package/skills/_shared/core/prompt-structure.md +18 -0
- package/skills/_shared/core/quality-principles.md +11 -0
- package/skills/_shared/core/session-metrics.md +35 -0
- package/skills/_shared/core/skill-routing.md +39 -0
- package/skills/_shared/core/test-approach.md +54 -0
- package/skills/_shared/core/vendor-detection.md +52 -0
- package/skills/_shared/runtime/memory-protocol.md +118 -0
- package/skills/_shared/runtime/result-contract.md +64 -0
- package/skills/_shared/ui-interaction-quality.md +133 -0
- package/skills/oma-academic-writing/SKILL.md +181 -0
- package/skills/oma-academic-writing/resources/abstract-guide.md +51 -0
- package/skills/oma-academic-writing/resources/academic-verb-tiers.md +42 -0
- package/skills/oma-academic-writing/resources/anti-ai-checklist.md +52 -0
- package/skills/oma-academic-writing/resources/hedging-guide.md +132 -0
- package/skills/oma-academic-writing/resources/sentence-structure-reference.md +151 -0
- package/skills/oma-architecture/SKILL.md +203 -0
- package/skills/oma-architecture/resources/api-evolution.md +41 -0
- package/skills/oma-architecture/resources/checklist.md +42 -0
- package/skills/oma-architecture/resources/execution-protocol.md +115 -0
- package/skills/oma-architecture/resources/methodology-selection.md +92 -0
- package/skills/oma-architecture/resources/migration-patterns.md +28 -0
- package/skills/oma-architecture/resources/output-templates.md +111 -0
- package/skills/oma-architecture/resources/stakeholder-synthesis.md +80 -0
- package/skills/oma-backend/SKILL.md +202 -0
- package/skills/oma-backend/resources/checklist.md +45 -0
- package/skills/oma-backend/resources/error-playbook.md +97 -0
- package/skills/oma-backend/resources/execution-protocol.md +39 -0
- package/skills/oma-backend/resources/orm-reference.md +61 -0
- package/skills/oma-backend/variants/node/api-template.ts +315 -0
- package/skills/oma-backend/variants/node/snippets.md +475 -0
- package/skills/oma-backend/variants/node/stack.yaml +21 -0
- package/skills/oma-backend/variants/node/tech-stack.md +33 -0
- package/skills/oma-backend/variants/python/api-template.py +378 -0
- package/skills/oma-backend/variants/python/snippets.md +199 -0
- package/skills/oma-backend/variants/python/stack.yaml +18 -0
- package/skills/oma-backend/variants/python/tech-stack.md +45 -0
- package/skills/oma-backend/variants/rust/api-template.rs +635 -0
- package/skills/oma-backend/variants/rust/snippets.md +471 -0
- package/skills/oma-backend/variants/rust/stack.yaml +20 -0
- package/skills/oma-backend/variants/rust/tech-stack.md +36 -0
- package/skills/oma-backend/variants/stack.schema.json +73 -0
- package/skills/oma-brainstorm/SKILL.md +166 -0
- package/skills/oma-brainstorm/resources/triz-lite.md +117 -0
- package/skills/oma-coordination/SKILL.md +171 -0
- package/skills/oma-db/SKILL.md +201 -0
- package/skills/oma-db/resources/anti-patterns.md +177 -0
- package/skills/oma-db/resources/checklist.md +67 -0
- package/skills/oma-db/resources/document-templates.md +107 -0
- package/skills/oma-db/resources/error-playbook.md +50 -0
- package/skills/oma-db/resources/examples.md +170 -0
- package/skills/oma-db/resources/execution-protocol.md +127 -0
- package/skills/oma-db/resources/iso-controls.md +98 -0
- package/skills/oma-db/resources/migration-playbook.md +93 -0
- package/skills/oma-db/resources/query-tuning.md +57 -0
- package/skills/oma-db/resources/vector-db.md +159 -0
- package/skills/oma-debug/SKILL.md +146 -0
- package/skills/oma-debug/resources/bug-report-template.md +327 -0
- package/skills/oma-debug/resources/checklist.md +31 -0
- package/skills/oma-debug/resources/common-patterns.md +734 -0
- package/skills/oma-debug/resources/debugging-checklist.md +364 -0
- package/skills/oma-debug/resources/error-playbook.md +94 -0
- package/skills/oma-debug/resources/execution-protocol.md +40 -0
- package/skills/oma-design/SKILL.md +135 -0
- package/skills/oma-design/resources/asset-strategy.md +34 -0
- package/skills/oma-design/resources/checklist.md +63 -0
- package/skills/oma-design/resources/data-grid.md +48 -0
- package/skills/oma-design/resources/design-md-spec.md +107 -0
- package/skills/oma-design/resources/design-tokens.md +53 -0
- package/skills/oma-design/resources/error-playbook.md +18 -0
- package/skills/oma-design/resources/execution-protocol.md +95 -0
- package/skills/oma-design/resources/external-design-skills.md +130 -0
- package/skills/oma-design/resources/getdesign-fetcher.md +104 -0
- package/skills/oma-design/resources/product-interface-review.md +59 -0
- package/skills/oma-design/resources/redesign-protocol.md +52 -0
- package/skills/oma-design/resources/stitch-integration.md +244 -0
- package/skills/oma-dev-workflow/SKILL.md +375 -0
- package/skills/oma-dev-workflow/resources/affected-checks.py +58 -0
- package/skills/oma-dev-workflow/resources/api-workflows.md +58 -0
- package/skills/oma-dev-workflow/resources/database-patterns.md +107 -0
- package/skills/oma-dev-workflow/resources/i18n-patterns.md +63 -0
- package/skills/oma-dev-workflow/resources/release-coordination.md +240 -0
- package/skills/oma-dev-workflow/resources/tooling-profiles.md +217 -0
- package/skills/oma-dev-workflow/resources/troubleshooting.md +78 -0
- package/skills/oma-dev-workflow/resources/validation-pipeline.md +246 -0
- package/skills/oma-docs/SKILL.md +77 -0
- package/skills/oma-docs/resources/commands.md +76 -0
- package/skills/oma-explanation/SKILL.md +212 -0
- package/skills/oma-explanation/resources/document-structure.md +115 -0
- package/skills/oma-explanation/resources/draft-format.md +199 -0
- package/skills/oma-explanation/resources/html-contract.md +102 -0
- package/skills/oma-frontend/SKILL.md +227 -0
- package/skills/oma-frontend/resources/angular-rules.md +110 -0
- package/skills/oma-frontend/resources/checklist.md +76 -0
- package/skills/oma-frontend/resources/error-playbook.md +125 -0
- package/skills/oma-frontend/resources/execution-protocol.md +46 -0
- package/skills/oma-frontend/resources/snippets.md +354 -0
- package/skills/oma-frontend/resources/specialized-visual-implementation.md +193 -0
- package/skills/oma-frontend/resources/tailwind-rules.md +83 -0
- package/skills/oma-frontend/resources/tech-stack.md +156 -0
- package/skills/oma-hwp/SKILL.md +166 -0
- package/skills/oma-hwp/config/hwp-config.yaml +35 -0
- package/skills/oma-hwp/resources/balanced-tables.ts +39 -0
- package/skills/oma-hwp/resources/execution-protocol.md +145 -0
- package/skills/oma-hwp/resources/flatten-tables.ts +101 -0
- package/skills/oma-hwp/resources/package.json +13 -0
- package/skills/oma-hwp/resources/troubleshooting.md +70 -0
- package/skills/oma-hwp/resources/tsconfig.json +10 -0
- package/skills/oma-hwp/resources/turndown-plugin-gfm.d.ts +9 -0
- package/skills/oma-image/SKILL.md +142 -0
- package/skills/oma-image/config/image-config.yaml +70 -0
- package/skills/oma-image/resources/checklist.md +18 -0
- package/skills/oma-image/resources/execution-protocol.md +117 -0
- package/skills/oma-image/resources/invocation.md +105 -0
- package/skills/oma-image/resources/prompt-tips.md +71 -0
- package/skills/oma-image/resources/vendor-matrix.md +90 -0
- package/skills/oma-market/SKILL.md +142 -0
- package/skills/oma-market/resources/checklist.md +12 -0
- package/skills/oma-market/resources/error-playbook.md +12 -0
- package/skills/oma-market/resources/execution-protocol.md +60 -0
- package/skills/oma-market/resources/frameworks/pestel.md +101 -0
- package/skills/oma-market/resources/frameworks/porters-5f.md +90 -0
- package/skills/oma-market/resources/frameworks/swot.md +61 -0
- package/skills/oma-market/resources/intent-rules.md +35 -0
- package/skills/oma-market/resources/output-laws.md +53 -0
- package/skills/oma-mobile/SKILL.md +168 -0
- package/skills/oma-mobile/resources/checklist.md +77 -0
- package/skills/oma-mobile/resources/error-playbook.md +198 -0
- package/skills/oma-mobile/resources/execution-protocol.md +47 -0
- package/skills/oma-mobile/resources/screen-template.dart +331 -0
- package/skills/oma-mobile/resources/screen-template.swift +239 -0
- package/skills/oma-mobile/resources/screen-template.tsx +179 -0
- package/skills/oma-mobile/resources/tech-stack.md +69 -0
- package/skills/oma-mobile/variants/README.md +45 -0
- package/skills/oma-mobile/variants/flutter/api-template.dart +461 -0
- package/skills/oma-mobile/variants/flutter/snippets.md +1353 -0
- package/skills/oma-mobile/variants/flutter/stack.yaml +21 -0
- package/skills/oma-mobile/variants/flutter/tech-stack.md +201 -0
- package/skills/oma-mobile/variants/kotlin-android/api-template.kt +182 -0
- package/skills/oma-mobile/variants/kotlin-android/snippets.md +871 -0
- package/skills/oma-mobile/variants/kotlin-android/stack.yaml +32 -0
- package/skills/oma-mobile/variants/kotlin-android/tech-stack.md +159 -0
- package/skills/oma-mobile/variants/react-native/api-template.ts +531 -0
- package/skills/oma-mobile/variants/react-native/snippets.md +1014 -0
- package/skills/oma-mobile/variants/react-native/stack.yaml +22 -0
- package/skills/oma-mobile/variants/react-native/tech-stack.md +192 -0
- package/skills/oma-mobile/variants/stack.schema.json +68 -0
- package/skills/oma-mobile/variants/swift-ios/api-template.swift +165 -0
- package/skills/oma-mobile/variants/swift-ios/snippets.md +1028 -0
- package/skills/oma-mobile/variants/swift-ios/stack.yaml +25 -0
- package/skills/oma-mobile/variants/swift-ios/tech-stack.md +174 -0
- package/skills/oma-observability/SKILL.md +225 -0
- package/skills/oma-observability/resources/anti-patterns.md +555 -0
- package/skills/oma-observability/resources/boundaries/cross-application.md +348 -0
- package/skills/oma-observability/resources/boundaries/multi-tenant.md +290 -0
- package/skills/oma-observability/resources/boundaries/release.md +372 -0
- package/skills/oma-observability/resources/boundaries/slo.md +225 -0
- package/skills/oma-observability/resources/checklist.md +123 -0
- package/skills/oma-observability/resources/examples.md +448 -0
- package/skills/oma-observability/resources/execution-protocol.md +166 -0
- package/skills/oma-observability/resources/incident-forensics.md +348 -0
- package/skills/oma-observability/resources/intent-rules.md +307 -0
- package/skills/oma-observability/resources/layers/L3-network.md +281 -0
- package/skills/oma-observability/resources/layers/L4-transport.md +299 -0
- package/skills/oma-observability/resources/layers/L7-application/crash-analytics.md +359 -0
- package/skills/oma-observability/resources/layers/L7-application/mobile-rum.md +278 -0
- package/skills/oma-observability/resources/layers/L7-application/waf.md +246 -0
- package/skills/oma-observability/resources/layers/L7-application/web-rum.md +350 -0
- package/skills/oma-observability/resources/layers/mesh.md +368 -0
- package/skills/oma-observability/resources/matrix.md +177 -0
- package/skills/oma-observability/resources/meta-observability.md +605 -0
- package/skills/oma-observability/resources/observability-as-code.md +666 -0
- package/skills/oma-observability/resources/signals/audit.md +310 -0
- package/skills/oma-observability/resources/signals/cost.md +303 -0
- package/skills/oma-observability/resources/signals/logs.md +263 -0
- package/skills/oma-observability/resources/signals/metrics.md +478 -0
- package/skills/oma-observability/resources/signals/privacy.md +351 -0
- package/skills/oma-observability/resources/signals/profiles.md +245 -0
- package/skills/oma-observability/resources/signals/traces.md +360 -0
- package/skills/oma-observability/resources/standards.md +214 -0
- package/skills/oma-observability/resources/transport/collector-topology.md +308 -0
- package/skills/oma-observability/resources/transport/otlp-grpc-vs-http.md +256 -0
- package/skills/oma-observability/resources/transport/sampling-recipes.md +297 -0
- package/skills/oma-observability/resources/transport/udp-statsd-mtu.md +167 -0
- package/skills/oma-observability/resources/vendor-categories.md +352 -0
- package/skills/oma-orchestration/SKILL.md +302 -0
- package/skills/oma-orchestration/config/cli-config.yaml +92 -0
- package/skills/oma-orchestration/resources/memory-schema.md +232 -0
- package/skills/oma-orchestration/resources/subagent-prompt-template.md +30 -0
- package/skills/oma-orchestration/scripts/parallel-run.sh +6 -0
- package/skills/oma-orchestration/scripts/spawn-agent.sh +5 -0
- package/skills/oma-orchestration/scripts/verify.sh +20 -0
- package/skills/oma-orchestration/templates/backend-task.md +19 -0
- package/skills/oma-orchestration/templates/debug-task.md +16 -0
- package/skills/oma-orchestration/templates/frontend-task.md +18 -0
- package/skills/oma-orchestration/templates/mobile-task.md +18 -0
- package/skills/oma-orchestration/templates/qa-task.md +16 -0
- package/skills/oma-orchestration/templates/tasks-example.yaml +15 -0
- package/skills/oma-pdf/SKILL.md +162 -0
- package/skills/oma-pdf/config/pdf-config.yaml +27 -0
- package/skills/oma-pdf/resources/execution-protocol.md +127 -0
- package/skills/oma-pm/SKILL.md +157 -0
- package/skills/oma-pm/resources/error-playbook.md +75 -0
- package/skills/oma-pm/resources/examples.md +439 -0
- package/skills/oma-pm/resources/execution-protocol.md +62 -0
- package/skills/oma-pm/resources/iso-planning.md +110 -0
- package/skills/oma-pm/resources/plan-phase-protocol.md +66 -0
- package/skills/oma-pm/resources/task-template.json +86 -0
- package/skills/oma-qa/SKILL.md +141 -0
- package/skills/oma-qa/resources/checklist.md +339 -0
- package/skills/oma-qa/resources/error-playbook.md +98 -0
- package/skills/oma-qa/resources/execution-protocol.md +169 -0
- package/skills/oma-qa/resources/iso-quality.md +82 -0
- package/skills/oma-qa/resources/verify-ship-protocol.md +116 -0
- package/skills/oma-recap/SKILL.md +245 -0
- package/skills/oma-recap/resources/output-formats.md +83 -0
- package/skills/oma-refactor/SKILL.md +184 -0
- package/skills/oma-refactor/resources/definition.md +108 -0
- package/skills/oma-refactor/resources/governance.md +33 -0
- package/skills/oma-refactor/resources/hotspots.py +44 -0
- package/skills/oma-refactor/resources/measurement.md +93 -0
- package/skills/oma-scholar/SKILL.md +276 -0
- package/skills/oma-scholar/config/scholar-config.yaml +79 -0
- package/skills/oma-scholar/resources/api-endpoints.md +174 -0
- package/skills/oma-scholar/resources/checklist.md +92 -0
- package/skills/oma-scholar/resources/execution-protocol.md +224 -0
- package/skills/oma-scholar/resources/fallback-providers.md +143 -0
- package/skills/oma-scholar/resources/setup-openalex.md +85 -0
- package/skills/oma-scholar/resources/sidecar-spec.md +341 -0
- package/skills/oma-scholar/resources/upstream-spec-cache.md +972 -0
- package/skills/oma-scm/SKILL.md +103 -0
- package/skills/oma-scm/resources/cm-operations.md +56 -0
- package/skills/oma-scm/resources/codeowners-playbook.md +45 -0
- package/skills/oma-scm/resources/conventional-commits.md +108 -0
- package/skills/oma-scm/resources/merge-risk.md +76 -0
- package/skills/oma-scm/resources/onboarding-risk-signals.md +71 -0
- package/skills/oma-search/SKILL.md +125 -0
- package/skills/oma-search/resources/checklist.md +36 -0
- package/skills/oma-search/resources/error-playbook.md +66 -0
- package/skills/oma-search/resources/examples.md +80 -0
- package/skills/oma-search/resources/execution-protocol.md +86 -0
- package/skills/oma-search/resources/intent-rules.md +47 -0
- package/skills/oma-search/resources/trust-registry.md +53 -0
- package/skills/oma-security/SKILL.md +172 -0
- package/skills/oma-security/resources/artex.md +108 -0
- package/skills/oma-security/resources/ci.md +134 -0
- package/skills/oma-security/resources/cisco-source.md +108 -0
- package/skills/oma-security/resources/deepsec-config.md +45 -0
- package/skills/oma-security/resources/deepsec-matchers.md +35 -0
- package/skills/oma-security/resources/deepsec-scanning.md +45 -0
- package/skills/oma-security/resources/deepsec-setup.md +41 -0
- package/skills/oma-security/resources/findings-contract.md +408 -0
- package/skills/oma-security/resources/mcp-scanning.md +122 -0
- package/skills/oma-security/resources/skill-scanning.md +106 -0
- package/skills/oma-security/resources/validation.md +71 -0
- package/skills/oma-skill-creation/SKILL.md +84 -0
- package/skills/oma-skill-creation/resources/prompt-evaluation.md +36 -0
- package/skills/oma-skill-creation/resources/ssl-lite-template.md +108 -0
- package/skills/oma-skill-creation/resources/validation-checklist.md +101 -0
- package/skills/oma-slide/SKILL.md +277 -0
- package/skills/oma-slide/resources/animation-patterns.md +245 -0
- package/skills/oma-slide/resources/assets/deck-stage.js +514 -0
- package/skills/oma-slide/resources/assets/viewport-base.css +257 -0
- package/skills/oma-slide/resources/canva-integration.md +203 -0
- package/skills/oma-slide/resources/checklist.md +46 -0
- package/skills/oma-slide/resources/design-doctrine.md +260 -0
- package/skills/oma-slide/resources/fixed-stage.md +232 -0
- package/skills/oma-slide/resources/generation-protocol.md +393 -0
- package/skills/oma-slide/resources/native-pptx.md +84 -0
- package/skills/oma-slide/resources/selection-index.json +461 -0
- package/skills/oma-slide/resources/style-presets.LICENSE +10 -0
- package/skills/oma-slide/resources/style-presets.md +520 -0
- package/skills/oma-tf-infra/SKILL.md +201 -0
- package/skills/oma-tf-infra/resources/checklist.md +53 -0
- package/skills/oma-tf-infra/resources/cost-optimization.md +574 -0
- package/skills/oma-tf-infra/resources/error-playbook.md +68 -0
- package/skills/oma-tf-infra/resources/examples.md +38 -0
- package/skills/oma-tf-infra/resources/execution-protocol.md +129 -0
- package/skills/oma-tf-infra/resources/iso-42001-infra.md +229 -0
- package/skills/oma-tf-infra/resources/multi-cloud-examples.md +241 -0
- package/skills/oma-tf-infra/resources/policy-testing-examples.md +332 -0
- package/skills/oma-translation/SKILL.md +229 -0
- package/skills/oma-translation/resources/anti-ai-patterns.md +104 -0
- package/skills/oma-translation/resources/lang/_template.md +73 -0
- package/skills/oma-translation/resources/lang/en.md +123 -0
- package/skills/oma-translation/resources/lang/ja.md +274 -0
- package/skills/oma-translation/resources/lang/ko.md +228 -0
- package/skills/oma-translation/resources/lang/zh.md +285 -0
- package/skills/oma-translation/resources/style-skills.md +104 -0
- package/skills/oma-translation/resources/translation-rubric.md +126 -0
- package/skills/oma-video/SKILL.md +229 -0
- package/skills/oma-video/config/video-config.yaml +60 -0
- package/skills/oma-video/resources/checklist.md +26 -0
- package/skills/oma-video/resources/execution-protocol.md +122 -0
- package/skills/oma-video/resources/existing-media.md +185 -0
- package/skills/oma-video/resources/hyperframes-authoring/README.md +44 -0
- package/skills/oma-video/resources/hyperframes-authoring/demo.md +31 -0
- package/skills/oma-video/resources/hyperframes-authoring/explainer.md +10 -0
- package/skills/oma-video/resources/hyperframes-authoring/shorts.md +10 -0
- package/skills/oma-video/resources/motion-principles.md +81 -0
- package/skills/oma-video/resources/mpt/driver.py +301 -0
- package/skills/oma-video/resources/prompt-tips.md +75 -0
- package/skills/oma-video/resources/script-schema.md +81 -0
- package/skills/oma-video/resources/sound-design.md +127 -0
- package/skills/oma-video/resources/strudel/package.json +11 -0
- package/skills/oma-video/resources/strudel/page.html +87 -0
- package/skills/oma-video/resources/strudel/render.mjs +295 -0
- package/skills/oma-video/resources/vendor-matrix.md +124 -0
- package/skills/oma-voice/SKILL.md +249 -0
- package/skills/oma-voice/config/voice-config.yaml +30 -0
- package/skills/oma-voice/resources/audio-processing.md +119 -0
- package/skills/oma-voice/resources/checklist.md +47 -0
- package/skills/oma-voice/resources/execution-protocol.md +215 -0
- package/skills/oma-voice/resources/prompt-tips.md +43 -0
- package/skills/oma-voice/resources/voice-matrix.md +51 -0
- package/skills/oma-voice/resources/voicestudio.md +87 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
# Backend Agent - Code Snippets (Rust / Axum)
|
|
2
|
+
|
|
3
|
+
Copy-paste ready patterns. Use these as starting points, adapt to the specific task.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Axum Handler with Auth Middleware
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
use axum::{
|
|
11
|
+
extract::{Path, State},
|
|
12
|
+
http::StatusCode,
|
|
13
|
+
Json,
|
|
14
|
+
};
|
|
15
|
+
use uuid::Uuid;
|
|
16
|
+
|
|
17
|
+
use crate::{
|
|
18
|
+
error::AppError,
|
|
19
|
+
extractors::AuthUser,
|
|
20
|
+
models::resource::{ResourceCreate, ResourceResponse},
|
|
21
|
+
services::ResourceService,
|
|
22
|
+
AppState,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
pub async fn create_resource(
|
|
26
|
+
State(state): State<AppState>,
|
|
27
|
+
user: AuthUser,
|
|
28
|
+
Json(payload): Json<ResourceCreate>,
|
|
29
|
+
) -> Result<(StatusCode, Json<ResourceResponse>), AppError> {
|
|
30
|
+
let resource = state
|
|
31
|
+
.resource_service
|
|
32
|
+
.create(&state.db, user.id, payload)
|
|
33
|
+
.await?;
|
|
34
|
+
|
|
35
|
+
Ok((StatusCode::CREATED, Json(resource)))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub async fn get_resource(
|
|
39
|
+
State(state): State<AppState>,
|
|
40
|
+
user: AuthUser,
|
|
41
|
+
Path(resource_id): Path<Uuid>,
|
|
42
|
+
) -> Result<Json<ResourceResponse>, AppError> {
|
|
43
|
+
let resource = state
|
|
44
|
+
.resource_service
|
|
45
|
+
.get_by_id(&state.db, resource_id)
|
|
46
|
+
.await?;
|
|
47
|
+
|
|
48
|
+
if resource.user_id != user.id {
|
|
49
|
+
return Err(AppError::Forbidden);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Ok(Json(resource))
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 2. Serde + Validator Struct
|
|
59
|
+
|
|
60
|
+
```rust
|
|
61
|
+
use serde::{Deserialize, Serialize};
|
|
62
|
+
use uuid::Uuid;
|
|
63
|
+
use chrono::{DateTime, Utc};
|
|
64
|
+
use validator::Validate;
|
|
65
|
+
|
|
66
|
+
#[derive(Debug, Deserialize, Validate)]
|
|
67
|
+
pub struct ResourceCreate {
|
|
68
|
+
#[validate(length(min = 1, max = 200))]
|
|
69
|
+
pub title: String,
|
|
70
|
+
|
|
71
|
+
#[validate(length(max = 2000))]
|
|
72
|
+
pub description: Option<String>,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[derive(Debug, Deserialize, Validate)]
|
|
76
|
+
pub struct ResourceUpdate {
|
|
77
|
+
#[validate(length(min = 1, max = 200))]
|
|
78
|
+
pub title: Option<String>,
|
|
79
|
+
|
|
80
|
+
#[serde(default, with = "serde_with::rust::double_option")]
|
|
81
|
+
pub description: Option<Option<String>>,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[derive(Debug, Serialize, sqlx::FromRow)]
|
|
85
|
+
pub struct ResourceResponse {
|
|
86
|
+
pub id: Uuid,
|
|
87
|
+
pub title: String,
|
|
88
|
+
pub description: Option<String>,
|
|
89
|
+
pub user_id: Uuid,
|
|
90
|
+
pub created_at: DateTime<Utc>,
|
|
91
|
+
pub updated_at: DateTime<Utc>,
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 3. SQLx Query Example (Compile-Time Checked)
|
|
98
|
+
|
|
99
|
+
```rust
|
|
100
|
+
use sqlx::PgPool;
|
|
101
|
+
use uuid::Uuid;
|
|
102
|
+
|
|
103
|
+
use crate::{error::AppError, models::resource::ResourceResponse};
|
|
104
|
+
|
|
105
|
+
// sqlx::query_as! performs compile-time SQL verification against DATABASE_URL
|
|
106
|
+
pub async fn find_by_id(
|
|
107
|
+
pool: &PgPool,
|
|
108
|
+
id: Uuid,
|
|
109
|
+
) -> Result<Option<ResourceResponse>, AppError> {
|
|
110
|
+
let resource = sqlx::query_as!(
|
|
111
|
+
ResourceResponse,
|
|
112
|
+
r#"
|
|
113
|
+
SELECT id, title, description, user_id, created_at, updated_at
|
|
114
|
+
FROM resources
|
|
115
|
+
WHERE id = $1 AND deleted_at IS NULL
|
|
116
|
+
"#,
|
|
117
|
+
id
|
|
118
|
+
)
|
|
119
|
+
.fetch_optional(pool)
|
|
120
|
+
.await?;
|
|
121
|
+
|
|
122
|
+
Ok(resource)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pub async fn insert(
|
|
126
|
+
pool: &PgPool,
|
|
127
|
+
user_id: Uuid,
|
|
128
|
+
title: &str,
|
|
129
|
+
description: Option<&str>,
|
|
130
|
+
) -> Result<ResourceResponse, AppError> {
|
|
131
|
+
let resource = sqlx::query_as!(
|
|
132
|
+
ResourceResponse,
|
|
133
|
+
r#"
|
|
134
|
+
INSERT INTO resources (id, title, description, user_id, created_at, updated_at)
|
|
135
|
+
VALUES (gen_random_uuid(), $1, $2, $3, NOW(), NOW())
|
|
136
|
+
RETURNING id, title, description, user_id, created_at, updated_at
|
|
137
|
+
"#,
|
|
138
|
+
title,
|
|
139
|
+
description,
|
|
140
|
+
user_id,
|
|
141
|
+
)
|
|
142
|
+
.fetch_one(pool)
|
|
143
|
+
.await?;
|
|
144
|
+
|
|
145
|
+
Ok(resource)
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 4. Axum State-Based Dependency Injection
|
|
152
|
+
|
|
153
|
+
```rust
|
|
154
|
+
use axum::{routing::get, Router};
|
|
155
|
+
use sqlx::PgPool;
|
|
156
|
+
use std::sync::Arc;
|
|
157
|
+
|
|
158
|
+
use crate::services::{ResourceService, UserService};
|
|
159
|
+
|
|
160
|
+
#[derive(Clone)]
|
|
161
|
+
pub struct AppState {
|
|
162
|
+
pub db: PgPool,
|
|
163
|
+
pub resource_service: Arc<ResourceService>,
|
|
164
|
+
pub user_service: Arc<UserService>,
|
|
165
|
+
pub jwt_decoding_key: Arc<jsonwebtoken::DecodingKey>,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Validate the required JWT_SECRET at production startup before constructing state.
|
|
169
|
+
impl AppState {
|
|
170
|
+
pub fn new(db: PgPool, jwt_secret: &[u8]) -> Self {
|
|
171
|
+
Self {
|
|
172
|
+
db: db.clone(),
|
|
173
|
+
resource_service: Arc::new(ResourceService::new()),
|
|
174
|
+
user_service: Arc::new(UserService::new()),
|
|
175
|
+
jwt_decoding_key: Arc::new(jsonwebtoken::DecodingKey::from_secret(jwt_secret)),
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
pub fn create_router(state: AppState) -> Router {
|
|
181
|
+
Router::new()
|
|
182
|
+
.route("/api/resources", get(handlers::resource::list).post(handlers::resource::create))
|
|
183
|
+
.route(
|
|
184
|
+
"/api/resources/{id}",
|
|
185
|
+
get(handlers::resource::get_resource)
|
|
186
|
+
.patch(handlers::resource::update)
|
|
187
|
+
.delete(handlers::resource::delete),
|
|
188
|
+
)
|
|
189
|
+
.with_state(state)
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 5. Repository Pattern with SQLx
|
|
196
|
+
|
|
197
|
+
```rust
|
|
198
|
+
use async_trait::async_trait;
|
|
199
|
+
use sqlx::PgPool;
|
|
200
|
+
use uuid::Uuid;
|
|
201
|
+
|
|
202
|
+
use crate::{error::AppError, models::resource::{ResourceCreate, ResourceResponse}};
|
|
203
|
+
|
|
204
|
+
#[async_trait]
|
|
205
|
+
pub trait ResourceRepository: Send + Sync {
|
|
206
|
+
async fn find_by_id(&self, pool: &PgPool, id: Uuid) -> Result<Option<ResourceResponse>, AppError>;
|
|
207
|
+
async fn find_all_by_user(&self, pool: &PgPool, user_id: Uuid, limit: i64, offset: i64) -> Result<Vec<ResourceResponse>, AppError>;
|
|
208
|
+
async fn create(&self, pool: &PgPool, user_id: Uuid, data: &ResourceCreate) -> Result<ResourceResponse, AppError>;
|
|
209
|
+
async fn delete(&self, pool: &PgPool, id: Uuid) -> Result<(), AppError>;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
pub struct PgResourceRepository;
|
|
213
|
+
|
|
214
|
+
#[async_trait]
|
|
215
|
+
impl ResourceRepository for PgResourceRepository {
|
|
216
|
+
async fn find_by_id(&self, pool: &PgPool, id: Uuid) -> Result<Option<ResourceResponse>, AppError> {
|
|
217
|
+
let row = sqlx::query_as!(
|
|
218
|
+
ResourceResponse,
|
|
219
|
+
"SELECT id, title, description, user_id, created_at, updated_at FROM resources WHERE id = $1 AND deleted_at IS NULL",
|
|
220
|
+
id
|
|
221
|
+
)
|
|
222
|
+
.fetch_optional(pool)
|
|
223
|
+
.await?;
|
|
224
|
+
Ok(row)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async fn find_all_by_user(
|
|
228
|
+
&self,
|
|
229
|
+
pool: &PgPool,
|
|
230
|
+
user_id: Uuid,
|
|
231
|
+
limit: i64,
|
|
232
|
+
offset: i64,
|
|
233
|
+
) -> Result<Vec<ResourceResponse>, AppError> {
|
|
234
|
+
let rows = sqlx::query_as!(
|
|
235
|
+
ResourceResponse,
|
|
236
|
+
"SELECT id, title, description, user_id, created_at, updated_at FROM resources WHERE user_id = $1 AND deleted_at IS NULL ORDER BY created_at DESC LIMIT $2 OFFSET $3",
|
|
237
|
+
user_id,
|
|
238
|
+
limit,
|
|
239
|
+
offset,
|
|
240
|
+
)
|
|
241
|
+
.fetch_all(pool)
|
|
242
|
+
.await?;
|
|
243
|
+
Ok(rows)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async fn create(&self, pool: &PgPool, user_id: Uuid, data: &ResourceCreate) -> Result<ResourceResponse, AppError> {
|
|
247
|
+
let row = sqlx::query_as!(
|
|
248
|
+
ResourceResponse,
|
|
249
|
+
"INSERT INTO resources (id, title, description, user_id, created_at, updated_at) VALUES (gen_random_uuid(), $1, $2, $3, NOW(), NOW()) RETURNING id, title, description, user_id, created_at, updated_at",
|
|
250
|
+
data.title,
|
|
251
|
+
data.description,
|
|
252
|
+
user_id,
|
|
253
|
+
)
|
|
254
|
+
.fetch_one(pool)
|
|
255
|
+
.await?;
|
|
256
|
+
Ok(row)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async fn delete(&self, pool: &PgPool, id: Uuid) -> Result<(), AppError> {
|
|
260
|
+
sqlx::query!(
|
|
261
|
+
"UPDATE resources SET deleted_at = NOW() WHERE id = $1",
|
|
262
|
+
id
|
|
263
|
+
)
|
|
264
|
+
.execute(pool)
|
|
265
|
+
.await?;
|
|
266
|
+
Ok(())
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 6. Paginated Query
|
|
274
|
+
|
|
275
|
+
```rust
|
|
276
|
+
use serde::Serialize;
|
|
277
|
+
use sqlx::PgPool;
|
|
278
|
+
use uuid::Uuid;
|
|
279
|
+
|
|
280
|
+
use crate::{error::AppError, models::resource::ResourceResponse};
|
|
281
|
+
|
|
282
|
+
#[derive(Debug, Serialize)]
|
|
283
|
+
pub struct PaginatedResponse<T> {
|
|
284
|
+
pub items: Vec<T>,
|
|
285
|
+
pub total: i64,
|
|
286
|
+
pub page: i64,
|
|
287
|
+
pub size: i64,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
pub async fn list_resources_paginated(
|
|
291
|
+
pool: &PgPool,
|
|
292
|
+
user_id: Uuid,
|
|
293
|
+
page: i64,
|
|
294
|
+
size: i64,
|
|
295
|
+
) -> Result<PaginatedResponse<ResourceResponse>, AppError> {
|
|
296
|
+
let offset = (page - 1) * size;
|
|
297
|
+
|
|
298
|
+
// Run count and data queries concurrently
|
|
299
|
+
let (total, items) = tokio::try_join!(
|
|
300
|
+
async {
|
|
301
|
+
sqlx::query_scalar!(
|
|
302
|
+
"SELECT COUNT(*) FROM resources WHERE user_id = $1 AND deleted_at IS NULL",
|
|
303
|
+
user_id
|
|
304
|
+
)
|
|
305
|
+
.fetch_one(pool)
|
|
306
|
+
.await
|
|
307
|
+
.map_err(AppError::from)
|
|
308
|
+
},
|
|
309
|
+
async {
|
|
310
|
+
sqlx::query_as!(
|
|
311
|
+
ResourceResponse,
|
|
312
|
+
r#"
|
|
313
|
+
SELECT id, title, description, user_id, created_at, updated_at
|
|
314
|
+
FROM resources
|
|
315
|
+
WHERE user_id = $1 AND deleted_at IS NULL
|
|
316
|
+
ORDER BY created_at DESC
|
|
317
|
+
LIMIT $2 OFFSET $3
|
|
318
|
+
"#,
|
|
319
|
+
user_id,
|
|
320
|
+
size,
|
|
321
|
+
offset,
|
|
322
|
+
)
|
|
323
|
+
.fetch_all(pool)
|
|
324
|
+
.await
|
|
325
|
+
.map_err(AppError::from)
|
|
326
|
+
}
|
|
327
|
+
)?;
|
|
328
|
+
|
|
329
|
+
Ok(PaginatedResponse {
|
|
330
|
+
items,
|
|
331
|
+
total: total.unwrap_or(0),
|
|
332
|
+
page,
|
|
333
|
+
size,
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 7. SQLx Migration
|
|
341
|
+
|
|
342
|
+
```sql
|
|
343
|
+
-- migrations/20240101000001_create_resources.sql
|
|
344
|
+
|
|
345
|
+
CREATE TABLE resources (
|
|
346
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
347
|
+
title VARCHAR(200) NOT NULL,
|
|
348
|
+
description TEXT,
|
|
349
|
+
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
350
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
351
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
352
|
+
deleted_at TIMESTAMPTZ
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
CREATE INDEX idx_resources_user_id ON resources(user_id);
|
|
356
|
+
CREATE INDEX idx_resources_created_at ON resources(created_at DESC);
|
|
357
|
+
|
|
358
|
+
-- Trigger to auto-update updated_at
|
|
359
|
+
CREATE OR REPLACE FUNCTION set_updated_at()
|
|
360
|
+
RETURNS TRIGGER AS $$
|
|
361
|
+
BEGIN
|
|
362
|
+
NEW.updated_at = NOW();
|
|
363
|
+
RETURN NEW;
|
|
364
|
+
END;
|
|
365
|
+
$$ LANGUAGE plpgsql;
|
|
366
|
+
|
|
367
|
+
CREATE TRIGGER resources_set_updated_at
|
|
368
|
+
BEFORE UPDATE ON resources
|
|
369
|
+
FOR EACH ROW
|
|
370
|
+
EXECUTE FUNCTION set_updated_at();
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Run migrations via:
|
|
374
|
+
```bash
|
|
375
|
+
sqlx migrate run
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## 8. Integration Test with axum::test
|
|
381
|
+
|
|
382
|
+
```rust
|
|
383
|
+
#[cfg(test)]
|
|
384
|
+
mod tests {
|
|
385
|
+
use axum::{
|
|
386
|
+
body::Body,
|
|
387
|
+
http::{header, Method, Request, StatusCode},
|
|
388
|
+
};
|
|
389
|
+
use serde_json::{json, Value};
|
|
390
|
+
use tower::ServiceExt; // for `oneshot`
|
|
391
|
+
|
|
392
|
+
use crate::{create_router, AppState};
|
|
393
|
+
|
|
394
|
+
// Helper: build a test app connected to a test database
|
|
395
|
+
async fn test_app() -> (axum::Router, sqlx::PgPool) {
|
|
396
|
+
let pool = sqlx::PgPool::connect(&std::env::var("DATABASE_URL_TEST").unwrap())
|
|
397
|
+
.await
|
|
398
|
+
.unwrap();
|
|
399
|
+
sqlx::migrate!("./migrations").run(&pool).await.unwrap();
|
|
400
|
+
let state = AppState::new(pool.clone(), b"test-only-jwt-key-with-at-least-32-bytes");
|
|
401
|
+
let app = create_router(state);
|
|
402
|
+
(app, pool)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
#[tokio::test]
|
|
406
|
+
async fn test_create_resource_returns_201() {
|
|
407
|
+
let (app, _pool) = test_app().await;
|
|
408
|
+
let token = create_test_jwt(); // helper that mints a valid JWT
|
|
409
|
+
|
|
410
|
+
let request = Request::builder()
|
|
411
|
+
.method(Method::POST)
|
|
412
|
+
.uri("/api/resources")
|
|
413
|
+
.header(header::CONTENT_TYPE, "application/json")
|
|
414
|
+
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
|
415
|
+
.body(Body::from(
|
|
416
|
+
serde_json::to_string(&json!({
|
|
417
|
+
"title": "Test Resource",
|
|
418
|
+
"description": "Integration test"
|
|
419
|
+
}))
|
|
420
|
+
.unwrap(),
|
|
421
|
+
))
|
|
422
|
+
.unwrap();
|
|
423
|
+
|
|
424
|
+
let response = app.oneshot(request).await.unwrap();
|
|
425
|
+
|
|
426
|
+
assert_eq!(response.status(), StatusCode::CREATED);
|
|
427
|
+
|
|
428
|
+
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
|
|
429
|
+
.await
|
|
430
|
+
.unwrap();
|
|
431
|
+
let json: Value = serde_json::from_slice(&body).unwrap();
|
|
432
|
+
|
|
433
|
+
assert_eq!(json["title"], "Test Resource");
|
|
434
|
+
assert!(json["id"].is_string());
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
#[tokio::test]
|
|
438
|
+
async fn test_get_resource_not_found_returns_404() {
|
|
439
|
+
let (app, _pool) = test_app().await;
|
|
440
|
+
let token = create_test_jwt();
|
|
441
|
+
let nonexistent_id = uuid::Uuid::new_v4();
|
|
442
|
+
|
|
443
|
+
let request = Request::builder()
|
|
444
|
+
.method(Method::GET)
|
|
445
|
+
.uri(format!("/api/resources/{nonexistent_id}"))
|
|
446
|
+
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
|
447
|
+
.body(Body::empty())
|
|
448
|
+
.unwrap();
|
|
449
|
+
|
|
450
|
+
let response = app.oneshot(request).await.unwrap();
|
|
451
|
+
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
fn create_test_jwt() -> String {
|
|
455
|
+
use jsonwebtoken::{encode, EncodingKey, Header};
|
|
456
|
+
use serde_json::json;
|
|
457
|
+
|
|
458
|
+
let claims = json!({
|
|
459
|
+
"sub": uuid::Uuid::new_v4().to_string(),
|
|
460
|
+
"exp": (chrono::Utc::now() + chrono::Duration::hours(1)).timestamp(),
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
encode(
|
|
464
|
+
&Header::default(),
|
|
465
|
+
&claims,
|
|
466
|
+
&EncodingKey::from_secret(b"test-only-jwt-key-with-at-least-32-bytes"),
|
|
467
|
+
)
|
|
468
|
+
.unwrap()
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# yaml-language-server: $schema=../stack.schema.json
|
|
2
|
+
language: rust
|
|
3
|
+
framework: axum
|
|
4
|
+
orm: sqlx
|
|
5
|
+
source: variant
|
|
6
|
+
|
|
7
|
+
verify:
|
|
8
|
+
detect: Cargo.toml
|
|
9
|
+
syntax:
|
|
10
|
+
cmd: "cargo check --quiet"
|
|
11
|
+
skip_if_missing: cargo
|
|
12
|
+
tests:
|
|
13
|
+
cmd: "cargo test --quiet"
|
|
14
|
+
skip_if_missing: cargo
|
|
15
|
+
raw_sql:
|
|
16
|
+
patterns:
|
|
17
|
+
- "sqlx::query(_as|_scalar)?\\(\"[^\"]*\\{"
|
|
18
|
+
- "format!\\(\"[^\"]*(SELECT|INSERT|UPDATE|DELETE)"
|
|
19
|
+
include_glob: "*.rs"
|
|
20
|
+
exclude_dirs: [target, node_modules]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Backend Agent - Tech Stack Reference (Rust)
|
|
2
|
+
|
|
3
|
+
## Primary Stack
|
|
4
|
+
- **Language**: Rust (latest stable)
|
|
5
|
+
- **Framework**: Axum 0.8+ (preferred) or Actix-web 4+
|
|
6
|
+
- **ORM/Query**: SQLx 0.8+ (compile-time checked queries) or SeaORM
|
|
7
|
+
- **Validation**: validator crate + serde
|
|
8
|
+
- **Database**: PostgreSQL 16+, Redis 7+
|
|
9
|
+
- **Auth**: jsonwebtoken, argon2/bcrypt
|
|
10
|
+
- **Testing**: cargo test, axum-test / actix-rt
|
|
11
|
+
- **Migrations**: SQLx migrate or sea-orm-migration
|
|
12
|
+
- **Async Runtime**: Tokio
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
```
|
|
16
|
+
src/
|
|
17
|
+
handlers/ # HTTP handlers (thin, delegate to services)
|
|
18
|
+
services/ # Business logic
|
|
19
|
+
repositories/ # Database access
|
|
20
|
+
models/ # Domain models and DB entities
|
|
21
|
+
extractors/ # Custom Axum extractors (auth, validation)
|
|
22
|
+
error.rs # Centralized error types
|
|
23
|
+
main.rs # Router setup and server start
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Security Requirements
|
|
27
|
+
- Password hashing: Argon2id via `argon2` crate (bcrypt acceptable for legacy compatibility)
|
|
28
|
+
- JWT: 15min access tokens, 7 day refresh tokens
|
|
29
|
+
- Rate limiting via tower middleware
|
|
30
|
+
- Input validation with serde + validator
|
|
31
|
+
- Compile-time query checking with SQLx
|
|
32
|
+
|
|
33
|
+
## Linter/Formatter
|
|
34
|
+
- **clippy**: lint warnings treated as errors
|
|
35
|
+
- **rustfmt**: consistent formatting
|
|
36
|
+
- **cargo audit**: dependency vulnerability scanning
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Backend Stack Manifest",
|
|
4
|
+
"description": "Per-variant stack declaration and verify contract consumed by `oma verify agent backend` and agent structured lookup.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["language"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": { "type": "string" },
|
|
9
|
+
"language": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Primary implementation language. Shipped variants cover python, node, and rust; the stack-set workflow (`source: detected`) may also produce go, java, kotlin, elixir, ruby, csharp, and others."
|
|
12
|
+
},
|
|
13
|
+
"framework": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Primary web framework (e.g., fastapi, hono, axum)."
|
|
16
|
+
},
|
|
17
|
+
"orm": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Primary ORM/query layer (e.g., sqlalchemy, prisma, sqlx)."
|
|
20
|
+
},
|
|
21
|
+
"source": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"enum": ["variant", "migrated", "user", "detected"],
|
|
24
|
+
"description": "Origin marker. `variant` = shipped with repo, `migrated` = produced by legacy migration, `user` = hand-customized, `detected` = generated by the stack-set workflow from codebase detection."
|
|
25
|
+
},
|
|
26
|
+
"verify": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"description": "Executable contract consumed by cli/commands/verify/verify.ts.",
|
|
29
|
+
"properties": {
|
|
30
|
+
"detect": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Relative path whose existence in the workspace selects this variant (e.g., package.json)."
|
|
33
|
+
},
|
|
34
|
+
"syntax": {
|
|
35
|
+
"$ref": "../../_shared/core/stack-verify.schema.json#/definitions/commandCheck"
|
|
36
|
+
},
|
|
37
|
+
"tests": {
|
|
38
|
+
"$ref": "../../_shared/core/stack-verify.schema.json#/definitions/commandCheck"
|
|
39
|
+
},
|
|
40
|
+
"raw_sql": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"description": "Raw-SQL injection grep scan.",
|
|
43
|
+
"required": ["patterns"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"patterns": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": { "type": "string" },
|
|
48
|
+
"minItems": 1,
|
|
49
|
+
"description": "ERE patterns passed to `grep -E`."
|
|
50
|
+
},
|
|
51
|
+
"include_glob": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "File pattern for `grep --include`."
|
|
54
|
+
},
|
|
55
|
+
"exclude_dirs": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": { "type": "string" },
|
|
58
|
+
"description": "Directory names to exclude via `grep -v`."
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"additionalProperties": false
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"additionalProperties": false
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"additionalProperties": true,
|
|
68
|
+
"definitions": {
|
|
69
|
+
"commandCheck": {
|
|
70
|
+
"$ref": "../../_shared/core/stack-verify.schema.json#/definitions/commandCheck"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|