feat(api): generate TypeScript API client from OpenAPI specification
Create type-safe TypeScript API client automatically generated from the OpenAPI specification. Includes generated schema types and documented client wrapper for type-safe backend communication. Ref: T008 (specs/001-modbus-relay-control)
This commit is contained in:
93
README.md
93
README.md
@@ -1,5 +1,7 @@
|
||||
# STA - Smart Temperature & Appliance Control
|
||||
|
||||
> **🤖 AI-Assisted Development Notice**: This project uses Claude Code as a development assistant for task planning, code organization, and workflow management. However, all code is human-written, reviewed, and validated by the project maintainer. AI is used as a productivity tool, not as the author of the implementation.
|
||||
|
||||
Web-based Modbus relay control system for managing 8-channel relay modules over TCP.
|
||||
|
||||
> **⚠️ Development Status**: This project is in early development. Core features are currently being implemented following a specification-driven approach.
|
||||
@@ -10,26 +12,33 @@ STA will provide a modern web interface for controlling Modbus-compatible relay
|
||||
|
||||
## Current Status
|
||||
|
||||
**Implemented:**
|
||||
- ✅ Basic Rust web server with Poem framework
|
||||
**Phase 1 Complete - Foundation:**
|
||||
- ✅ Monorepo structure (backend + frontend at root)
|
||||
- ✅ Rust web server with Poem 3.1 framework
|
||||
- ✅ Configuration system (YAML + environment variables)
|
||||
- ✅ Modbus TCP settings structure
|
||||
- ✅ Modbus TCP and relay settings structures
|
||||
- ✅ Health check and metadata API endpoints
|
||||
- ✅ OpenAPI documentation with Swagger UI
|
||||
- ✅ Rate limiting middleware
|
||||
- ✅ SQLite schema and repository for relay labels
|
||||
- ✅ Vue 3 + TypeScript frontend scaffolding with Vite
|
||||
- ✅ Type-safe API client generation from OpenAPI specs
|
||||
|
||||
**In Progress:**
|
||||
- 🚧 Domain model for relay control (Phase 2)
|
||||
- 🚧 Modbus TCP client implementation (Phase 3)
|
||||
**Phase 2 In Progress - Domain Layer:**
|
||||
- 🚧 Domain types with Type-Driven Development (RelayId, RelayState, RelayLabel)
|
||||
- 🚧 100% test coverage for domain layer
|
||||
|
||||
**Planned Features:**
|
||||
- 📋 8-Channel Relay Control: Individual and bulk relay control (on/off/toggle)
|
||||
- 📋 Real-Time Monitoring: Live relay state updates via HTTP polling
|
||||
- 📋 Custom Labels: Name your relays for easy identification
|
||||
- 📋 Health Monitoring: Connection status and device health tracking
|
||||
- 📋 Vue 3 + TypeScript frontend
|
||||
**Planned - Phases 3-8:**
|
||||
- 📋 Modbus TCP client with tokio-modbus (Phase 3)
|
||||
- 📋 Mock controller for testing (Phase 3)
|
||||
- 📋 Health monitoring service (Phase 3)
|
||||
- 📋 US1: Monitor & toggle relay states - MVP (Phase 4)
|
||||
- 📋 US2: Bulk relay controls (Phase 5)
|
||||
- 📋 US3: Health status display (Phase 6)
|
||||
- 📋 US4: Relay labeling (Phase 7)
|
||||
- 📋 Production deployment (Phase 8)
|
||||
|
||||
See [tasks.md](specs/001-modbus-relay-control/tasks.md) for detailed implementation roadmap.
|
||||
See [tasks.md](specs/001-modbus-relay-control/tasks.md) for detailed implementation roadmap (94 tasks across 8 phases).
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -111,31 +120,41 @@ The server provides OpenAPI documentation via Swagger UI:
|
||||
|
||||
## Project Structure
|
||||
|
||||
**Current:**
|
||||
**Monorepo Layout:**
|
||||
```
|
||||
src/
|
||||
├── lib.rs - Library entry point
|
||||
├── main.rs - Binary entry point
|
||||
├── startup.rs - Application builder and server configuration
|
||||
├── settings.rs - Configuration management
|
||||
├── telemetry.rs - Logging and tracing setup
|
||||
├── route/ - HTTP endpoint handlers
|
||||
│ ├── health.rs - Health check endpoints
|
||||
│ └── meta.rs - Application metadata
|
||||
└── middleware/ - Custom middleware
|
||||
└── rate_limit.rs
|
||||
|
||||
specs/ - Feature specifications and documentation
|
||||
settings/ - YAML configuration files
|
||||
```
|
||||
|
||||
**Planned (Hexagonal Architecture):**
|
||||
```
|
||||
src/
|
||||
├── domain/ - Business logic and domain models (Phase 2)
|
||||
├── application/ - Use cases and orchestration (Phase 3-4)
|
||||
├── infrastructure/ - Modbus, persistence, external services (Phase 3)
|
||||
└── presentation/ - API endpoints and DTOs (Phase 4)
|
||||
sta/ # Repository root
|
||||
├── backend/ # Rust backend workspace member
|
||||
│ ├── src/
|
||||
│ │ ├── lib.rs - Library entry point
|
||||
│ │ ├── main.rs - Binary entry point
|
||||
│ │ ├── startup.rs - Application builder and server config
|
||||
│ │ ├── settings.rs - Configuration management
|
||||
│ │ ├── telemetry.rs - Logging and tracing setup
|
||||
│ │ ├── domain/ - Business logic (Phase 2 in progress)
|
||||
│ │ │ └── relay/ - Relay domain types and repository traits
|
||||
│ │ ├── application/ - Use cases (planned Phase 3-4)
|
||||
│ │ ├── infrastructure/ - External integrations (Phase 3)
|
||||
│ │ │ └── persistence/ - SQLite repository implementation
|
||||
│ │ ├── presentation/ - API layer (planned Phase 4)
|
||||
│ │ ├── route/ - HTTP endpoint handlers
|
||||
│ │ │ ├── health.rs - Health check endpoints
|
||||
│ │ │ └── meta.rs - Application metadata
|
||||
│ │ └── middleware/ - Custom middleware
|
||||
│ │ └── rate_limit.rs
|
||||
│ ├── settings/ - YAML configuration files
|
||||
│ └── tests/ - Integration tests
|
||||
├── src/ # Frontend source (Vue/TypeScript)
|
||||
│ └── api/ - Type-safe API client
|
||||
├── specs/ # Feature specifications and documentation
|
||||
│ ├── constitution.md - Architectural principles
|
||||
│ └── 001-modbus-relay-control/
|
||||
│ ├── spec.md - Feature specification
|
||||
│ ├── plan.md - Implementation plan
|
||||
│ ├── tasks.md - Task breakdown (94 tasks)
|
||||
│ └── research-cors.md - CORS configuration research
|
||||
├── package.json - Frontend dependencies
|
||||
├── vite.config.ts - Vite build configuration
|
||||
└── justfile - Build commands
|
||||
```
|
||||
|
||||
## Technology Stack
|
||||
|
||||
@@ -6,15 +6,18 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"generate:api": "curl -s http://localhost:3100/specs > openapi.yaml && openapi-typescript openapi.yaml -o src/api/schema.ts && rm openapi.yaml"
|
||||
},
|
||||
"dependencies": {
|
||||
"openapi-fetch": "^0.15.0",
|
||||
"vue": "^3.5.24"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.1",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"openapi-typescript": "^7.10.1",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^7.2.4",
|
||||
"vue-tsc": "^3.1.4"
|
||||
|
||||
240
pnpm-lock.yaml
generated
240
pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
openapi-fetch:
|
||||
specifier: ^0.15.0
|
||||
version: 0.15.0
|
||||
vue:
|
||||
specifier: ^3.5.24
|
||||
version: 3.5.26(typescript@5.9.3)
|
||||
@@ -21,6 +24,9 @@ importers:
|
||||
'@vue/tsconfig':
|
||||
specifier: ^0.8.1
|
||||
version: 0.8.1(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3))
|
||||
openapi-typescript:
|
||||
specifier: ^7.10.1
|
||||
version: 7.10.1(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: ~5.9.3
|
||||
version: 5.9.3
|
||||
@@ -33,6 +39,10 @@ importers:
|
||||
|
||||
packages:
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-string-parser@7.27.1':
|
||||
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -209,6 +219,16 @@ packages:
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@redocly/ajv@8.17.1':
|
||||
resolution: {integrity: sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==}
|
||||
|
||||
'@redocly/config@0.22.2':
|
||||
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
|
||||
|
||||
'@redocly/openapi-core@1.34.6':
|
||||
resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==}
|
||||
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.53':
|
||||
resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==}
|
||||
|
||||
@@ -387,12 +407,44 @@ packages:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
agent-base@7.1.4:
|
||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
alien-signals@3.1.2:
|
||||
resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
|
||||
|
||||
ansi-colors@4.1.3:
|
||||
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
brace-expansion@2.0.2:
|
||||
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
||||
|
||||
change-case@5.4.4:
|
||||
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
|
||||
|
||||
colorette@1.4.0:
|
||||
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
entities@7.0.0:
|
||||
resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==}
|
||||
engines: {node: '>=0.12'}
|
||||
@@ -405,6 +457,12 @@ packages:
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
fast-uri@3.1.0:
|
||||
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -419,9 +477,38 @@ packages:
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
https-proxy-agent@7.0.6:
|
||||
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
index-to-position@1.2.0:
|
||||
resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
js-levenshtein@1.1.6:
|
||||
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
json-schema-traverse@1.0.0:
|
||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
minimatch@5.1.6:
|
||||
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
muggle-string@0.4.1:
|
||||
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||
|
||||
@@ -430,6 +517,22 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
openapi-fetch@0.15.0:
|
||||
resolution: {integrity: sha512-OjQUdi61WO4HYhr9+byCPMj0+bgste/LtSBEcV6FzDdONTs7x0fWn8/ndoYwzqCsKWIxEZwo4FN/TG1c1rI8IQ==}
|
||||
|
||||
openapi-typescript-helpers@0.0.15:
|
||||
resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==}
|
||||
|
||||
openapi-typescript@7.10.1:
|
||||
resolution: {integrity: sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: ^5.x
|
||||
|
||||
parse-json@8.3.0:
|
||||
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
path-browserify@1.0.1:
|
||||
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||
|
||||
@@ -440,10 +543,18 @@ packages:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pluralize@8.0.0:
|
||||
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
postcss@8.5.6:
|
||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
rollup@4.54.0:
|
||||
resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
@@ -453,10 +564,18 @@ packages:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
supports-color@10.2.2:
|
||||
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
type-fest@4.41.0:
|
||||
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
@@ -522,8 +641,21 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
yaml-ast-parser@0.0.43:
|
||||
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
|
||||
|
||||
yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5': {}
|
||||
@@ -617,6 +749,29 @@ snapshots:
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@redocly/ajv@8.17.1':
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
fast-uri: 3.1.0
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
'@redocly/config@0.22.2': {}
|
||||
|
||||
'@redocly/openapi-core@1.34.6(supports-color@10.2.2)':
|
||||
dependencies:
|
||||
'@redocly/ajv': 8.17.1
|
||||
'@redocly/config': 0.22.2
|
||||
colorette: 1.4.0
|
||||
https-proxy-agent: 7.0.6(supports-color@10.2.2)
|
||||
js-levenshtein: 1.1.6
|
||||
js-yaml: 4.1.1
|
||||
minimatch: 5.1.6
|
||||
pluralize: 8.0.0
|
||||
yaml-ast-parser: 0.0.43
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.53': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.54.0':
|
||||
@@ -778,10 +933,32 @@ snapshots:
|
||||
typescript: 5.9.3
|
||||
vue: 3.5.26(typescript@5.9.3)
|
||||
|
||||
agent-base@7.1.4: {}
|
||||
|
||||
alien-signals@3.1.2: {}
|
||||
|
||||
ansi-colors@4.1.3: {}
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
brace-expansion@2.0.2:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
change-case@5.4.4: {}
|
||||
|
||||
colorette@1.4.0: {}
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
debug@4.4.3(supports-color@10.2.2):
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
optionalDependencies:
|
||||
supports-color: 10.2.2
|
||||
|
||||
entities@7.0.0: {}
|
||||
|
||||
esbuild@0.27.2:
|
||||
@@ -815,6 +992,10 @@ snapshots:
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-uri@3.1.0: {}
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.3):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.3
|
||||
@@ -822,26 +1003,77 @@ snapshots:
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
https-proxy-agent@7.0.6(supports-color@10.2.2):
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
index-to-position@1.2.0: {}
|
||||
|
||||
js-levenshtein@1.1.6: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
json-schema-traverse@1.0.0: {}
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
minimatch@5.1.6:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.2
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
muggle-string@0.4.1: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
openapi-fetch@0.15.0:
|
||||
dependencies:
|
||||
openapi-typescript-helpers: 0.0.15
|
||||
|
||||
openapi-typescript-helpers@0.0.15: {}
|
||||
|
||||
openapi-typescript@7.10.1(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@redocly/openapi-core': 1.34.6(supports-color@10.2.2)
|
||||
ansi-colors: 4.1.3
|
||||
change-case: 5.4.4
|
||||
parse-json: 8.3.0
|
||||
supports-color: 10.2.2
|
||||
typescript: 5.9.3
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
parse-json@8.3.0:
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
index-to-position: 1.2.0
|
||||
type-fest: 4.41.0
|
||||
|
||||
path-browserify@1.0.1: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
pluralize@8.0.0: {}
|
||||
|
||||
postcss@8.5.6:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
rollup@4.54.0:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@@ -872,11 +1104,15 @@ snapshots:
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
supports-color@10.2.2: {}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
|
||||
type-fest@4.41.0: {}
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
@@ -910,3 +1146,7 @@ snapshots:
|
||||
'@vue/shared': 3.5.26
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
yaml-ast-parser@0.0.43: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
61
src/api/README.md
Normal file
61
src/api/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# API Client
|
||||
|
||||
This directory contains the auto-generated TypeScript API client for the STA backend.
|
||||
|
||||
## Files
|
||||
|
||||
- `schema.ts` - Auto-generated OpenAPI type definitions (do not edit manually)
|
||||
- `client.ts` - API client instance with type-safe methods
|
||||
|
||||
## Regenerating the Client
|
||||
|
||||
To regenerate the TypeScript client after backend API changes:
|
||||
|
||||
1. Start the backend server:
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
2. Download the OpenAPI spec:
|
||||
```bash
|
||||
curl http://localhost:3100/specs > openapi.yaml
|
||||
```
|
||||
|
||||
3. Generate TypeScript types:
|
||||
```bash
|
||||
pnpm exec openapi-typescript openapi.yaml -o src/api/schema.ts
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
```typescript
|
||||
import { apiClient } from '@/api/client';
|
||||
|
||||
// GET request
|
||||
const { data, error } = await apiClient.GET('/api/health');
|
||||
if (error) {
|
||||
console.error('Health check failed:', error);
|
||||
} else {
|
||||
console.log('Server is healthy');
|
||||
}
|
||||
|
||||
// GET request with response data
|
||||
const { data: meta, error: metaError } = await apiClient.GET('/api/meta');
|
||||
if (metaError) {
|
||||
console.error('Failed to get metadata:', metaError);
|
||||
} else {
|
||||
console.log('App name:', meta.name);
|
||||
console.log('App version:', meta.version);
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The API base URL can be configured via the `VITE_API_BASE_URL` environment variable.
|
||||
Create a `.env` file in the project root:
|
||||
|
||||
```env
|
||||
VITE_API_BASE_URL=http://localhost:3100
|
||||
```
|
||||
|
||||
For production builds, set the environment variable to point to your deployed backend.
|
||||
31
src/api/client.ts
Normal file
31
src/api/client.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* API client for the STA backend.
|
||||
*
|
||||
* This client is generated from the OpenAPI specification and provides
|
||||
* type-safe access to all backend endpoints.
|
||||
*
|
||||
* Usage:
|
||||
* ```typescript
|
||||
* import { apiClient } from '@/api/client';
|
||||
*
|
||||
* const { data, error } = await apiClient.GET('/api/health');
|
||||
* ```
|
||||
*/
|
||||
|
||||
import createClient from 'openapi-fetch';
|
||||
import type { paths } from './schema';
|
||||
|
||||
// Get the API base URL from environment variables or default to localhost
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3100';
|
||||
|
||||
/**
|
||||
* Typed API client instance.
|
||||
*
|
||||
* All requests are type-checked against the OpenAPI schema.
|
||||
*/
|
||||
export const apiClient = createClient<paths>({ baseUrl: API_BASE_URL });
|
||||
|
||||
/**
|
||||
* Re-export the types for convenience
|
||||
*/
|
||||
export type { paths, components } from './schema';
|
||||
106
src/api/schema.ts
Normal file
106
src/api/schema.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* This file was auto-generated by openapi-typescript.
|
||||
* Do not make direct changes to the file.
|
||||
*/
|
||||
|
||||
export interface paths {
|
||||
"/api/health": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Success */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Too Many Requests - rate limit exceeded */
|
||||
429: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/meta": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Success */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json; charset=utf-8": components["schemas"]["Meta"];
|
||||
};
|
||||
};
|
||||
/** @description Too Many Requests - rate limit exceeded */
|
||||
429: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
}
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
schemas: {
|
||||
/** Meta */
|
||||
Meta: {
|
||||
version: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
responses: never;
|
||||
parameters: never;
|
||||
requestBodies: never;
|
||||
headers: never;
|
||||
pathItems: never;
|
||||
}
|
||||
export type $defs = Record<string, never>;
|
||||
export type operations = Record<string, never>;
|
||||
Reference in New Issue
Block a user