een-api-toolkit 0.1.13 → 0.2.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/CHANGELOG.md +45 -8
- package/README.md +38 -0
- package/docs/AI-CONTEXT.md +1 -1
- package/examples/vue-bridges/README.md +126 -0
- package/examples/vue-bridges/bridges-screenshot.png +0 -0
- package/examples/vue-bridges/package-lock.json +130 -42
- package/examples/vue-bridges/package.json +2 -2
- package/examples/vue-cameras/README.md +142 -0
- package/examples/vue-cameras/cameras-screenshot.png +0 -0
- package/examples/vue-cameras/package-lock.json +130 -42
- package/examples/vue-cameras/package.json +2 -2
- package/examples/vue-feeds/README.md +162 -0
- package/examples/vue-feeds/e2e/auth.spec.ts +182 -444
- package/examples/vue-feeds/feeds-screenshot.png +0 -0
- package/examples/vue-feeds/package-lock.json +130 -42
- package/examples/vue-feeds/package.json +2 -2
- package/examples/vue-feeds/playwright.config.ts +28 -7
- package/examples/vue-media/README.md +187 -0
- package/examples/vue-media/e2e/auth.spec.ts +218 -298
- package/examples/vue-media/media-screenshot.png +0 -0
- package/examples/vue-media/package-lock.json +130 -42
- package/examples/vue-media/package.json +2 -2
- package/examples/vue-media/playwright.config.ts +28 -7
- package/examples/vue-users/README.md +58 -15
- package/examples/vue-users/package-lock.json +132 -44
- package/examples/vue-users/package.json +3 -3
- package/examples/vue-users/users-screenshot.png +0 -0
- package/package.json +12 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,23 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [0.
|
|
5
|
+
## [0.2.0] - 2026-01-02
|
|
6
6
|
|
|
7
7
|
### Release Summary
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
#### PR #39: Release v0.1.13: Documentation updates and example improvements
|
|
10
|
+
## Summary
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
This release includes documentation improvements and developer experience enhancements:
|
|
13
|
+
|
|
14
|
+
- **Example README files**: Added comprehensive README.md files with screenshots for all 5 example applications (vue-bridges, vue-cameras, vue-feeds, vue-media, vue-users)
|
|
15
|
+
- **Faster dev server startup**: Replaced slow `npx kill-port` (10-15 seconds) with native `lsof` command for instant port cleanup
|
|
16
|
+
- **Documentation fixes**: Fixed vue-users README to use functions instead of removed composables, updated docs/README.md to reference all example apps
|
|
17
|
+
|
|
18
|
+
## Commits included
|
|
19
|
+
|
|
20
|
+
- 9050e88 docs: Update README.md to reference all example apps
|
|
21
|
+
- 105051e docs: Fix vue-users README to use functions instead of composables
|
|
22
|
+
- 8d11c7a chore: Replace npx kill-port with faster native lsof command
|
|
23
|
+
- 741ca61 docs: Add README files and screenshots to example applications
|
|
24
|
+
- f4aa78b Merge pull request #38 from klaushofrichter/develop
|
|
25
|
+
- 6f3b0bf fix: Address code review feedback from PR #38
|
|
26
|
+
- 0432d7a feat: Add Media Session API and live video streaming to vue-feeds
|
|
27
|
+
|
|
28
|
+
## Test Results
|
|
29
|
+
|
|
30
|
+
- ✅ Lint: Passed (1 expected warning for unused vars in test files)
|
|
31
|
+
- ✅ Unit tests: 164 passed
|
|
32
|
+
- ✅ Build: Succeeded
|
|
12
33
|
|
|
13
|
-
|
|
14
|
-
|
|
34
|
+
## Version
|
|
35
|
+
|
|
36
|
+
**0.1.13**
|
|
37
|
+
|
|
38
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Detailed Changes
|
|
15
42
|
|
|
16
43
|
#### Bug Fixes
|
|
17
|
-
- fix: Address code review
|
|
44
|
+
- fix: Address remaining code review recommendations
|
|
45
|
+
- fix: Address code review feedback for PR #40
|
|
46
|
+
- fix: Address code review feedback - macOS note, xargs safety, and skill fix
|
|
47
|
+
|
|
48
|
+
#### Other Changes
|
|
49
|
+
- chore: Bump version to 0.2.0 and add CHANGELOG
|
|
50
|
+
- chore: Update dependencies and fix E2E tests
|
|
51
|
+
- docs: Update README.md to reference all example apps
|
|
52
|
+
- docs: Fix vue-users README to use functions instead of composables
|
|
53
|
+
- chore: Replace npx kill-port with faster native lsof command
|
|
54
|
+
- docs: Add README files and screenshots to example applications
|
|
18
55
|
|
|
19
56
|
### Links
|
|
20
57
|
- [npm package](https://www.npmjs.com/package/een-api-toolkit)
|
|
21
|
-
- [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.1.
|
|
58
|
+
- [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.1.13...v0.2.0)
|
|
22
59
|
|
|
23
60
|
---
|
|
24
|
-
*Released: 2026-01-
|
|
61
|
+
*Released: 2026-01-02 09:30:09 CST*
|
package/README.md
CHANGED
|
@@ -199,6 +199,44 @@ npm run dev # Runs at http://127.0.0.1:3333
|
|
|
199
199
|
|
|
200
200
|
> **Note:** Examples require a running OAuth proxy. See [Quick Start](#2-set-up-oauth-proxy).
|
|
201
201
|
|
|
202
|
+
## Running E2E Tests
|
|
203
|
+
|
|
204
|
+
Each example includes Playwright E2E tests that test the full OAuth login flow. Tests are designed to skip gracefully when the OAuth proxy or credentials are unavailable.
|
|
205
|
+
|
|
206
|
+
### Prerequisites
|
|
207
|
+
|
|
208
|
+
1. **OAuth Proxy**: Start the proxy server:
|
|
209
|
+
```bash
|
|
210
|
+
./scripts/restart-proxy.sh # Starts proxy at http://127.0.0.1:8787
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
2. **Environment Variables**: Create a `.env` file in the project root:
|
|
214
|
+
```bash
|
|
215
|
+
VITE_PROXY_URL=http://127.0.0.1:8787
|
|
216
|
+
VITE_EEN_CLIENT_ID=your_client_id
|
|
217
|
+
TEST_USER=your_test_email
|
|
218
|
+
TEST_PASSWORD=your_test_password
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Running Tests
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# Run E2E tests for a specific example
|
|
225
|
+
cd examples/vue-users
|
|
226
|
+
npm run test:e2e
|
|
227
|
+
|
|
228
|
+
# Run with UI for debugging
|
|
229
|
+
npm run test:e2e:ui
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Test Behavior
|
|
233
|
+
|
|
234
|
+
- **Proxy unavailable**: OAuth tests are automatically skipped with message "OAuth proxy not accessible"
|
|
235
|
+
- **Credentials missing**: OAuth tests are skipped with message "Test credentials not available"
|
|
236
|
+
- **Basic tests**: Tests that don't require OAuth (e.g., checking login button visibility) always run
|
|
237
|
+
|
|
238
|
+
> **Note:** All development and testing has been done on macOS. The `lsof` command used in scripts may behave differently on other platforms.
|
|
239
|
+
|
|
202
240
|
## External Resources
|
|
203
241
|
|
|
204
242
|
- [EEN Developer Portal](https://developer.eagleeyenetworks.com/)
|
package/docs/AI-CONTEXT.md
CHANGED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# EEN API Toolkit - Vue Bridges Example
|
|
2
|
+
|
|
3
|
+
A Vue 3 example demonstrating how to list and view EEN bridge devices using the een-api-toolkit.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Features Demonstrated
|
|
8
|
+
|
|
9
|
+
- OAuth authentication flow (login, callback, logout)
|
|
10
|
+
- Protected routes with navigation guards
|
|
11
|
+
- `getBridges()` function for listing bridges with pagination
|
|
12
|
+
- `getBridge()` function for fetching bridge details
|
|
13
|
+
- Status filtering (online, offline, error, idle)
|
|
14
|
+
- Bridge card grid with status badges
|
|
15
|
+
- Detailed bridge view with device info and network info
|
|
16
|
+
|
|
17
|
+
## APIs Used
|
|
18
|
+
|
|
19
|
+
- `getBridges()` - List bridges with filtering and pagination
|
|
20
|
+
- `getBridge()` - Get single bridge by ID
|
|
21
|
+
- `useAuthStore()` - Authentication state management
|
|
22
|
+
- `initEenToolkit()` - Toolkit initialization
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
### Prerequisites
|
|
27
|
+
|
|
28
|
+
1. **Start the OAuth proxy** (required for authentication):
|
|
29
|
+
|
|
30
|
+
The OAuth proxy is a separate project that handles token management securely.
|
|
31
|
+
Clone and run it from: https://github.com/klaushofrichter/een-oauth-proxy
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# In a separate terminal, from the een-oauth-proxy directory
|
|
35
|
+
npm install
|
|
36
|
+
npm run dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The proxy should be running at `http://localhost:8787`.
|
|
40
|
+
|
|
41
|
+
### Example Setup
|
|
42
|
+
|
|
43
|
+
All commands below should be run from this example directory (`examples/vue-bridges/`):
|
|
44
|
+
|
|
45
|
+
2. Copy the environment file:
|
|
46
|
+
```bash
|
|
47
|
+
# From examples/vue-bridges/
|
|
48
|
+
cp .env.example .env
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. Edit `.env` with your EEN credentials:
|
|
52
|
+
```env
|
|
53
|
+
VITE_EEN_CLIENT_ID=your-client-id
|
|
54
|
+
VITE_PROXY_URL=http://localhost:8787
|
|
55
|
+
# DO NOT change the redirect URI - EEN IDP only permits this URL
|
|
56
|
+
VITE_REDIRECT_URI=http://127.0.0.1:3333
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
4. Install dependencies and start:
|
|
60
|
+
```bash
|
|
61
|
+
# From examples/vue-bridges/
|
|
62
|
+
npm install
|
|
63
|
+
npm run dev
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
5. Open http://127.0.0.1:3333 in your browser.
|
|
67
|
+
|
|
68
|
+
**Important:** The EEN Identity Provider only permits `http://127.0.0.1:3333` as the OAuth redirect URI. Do not use `localhost` or other ports.
|
|
69
|
+
|
|
70
|
+
**Note:** Development and testing was done on macOS. The `npm run stop` command uses `lsof`, which is not available on Windows. Windows users should manually stop any process on port 3333 or use `npx kill-port 3333` instead.
|
|
71
|
+
|
|
72
|
+
## Project Structure
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
src/
|
|
76
|
+
├── main.ts # App entry, toolkit initialization
|
|
77
|
+
├── App.vue # Root component with navigation
|
|
78
|
+
├── router/
|
|
79
|
+
│ └── index.ts # Vue Router with auth guards
|
|
80
|
+
└── views/
|
|
81
|
+
├── Home.vue # Home page with login prompt
|
|
82
|
+
├── Login.vue # OAuth login redirect
|
|
83
|
+
├── Callback.vue # OAuth callback handler
|
|
84
|
+
├── Bridges.vue # Bridge list with filtering
|
|
85
|
+
├── BridgeDetail.vue # Single bridge details
|
|
86
|
+
└── Logout.vue # Logout handler
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Key Code Examples
|
|
90
|
+
|
|
91
|
+
### Listing Bridges with Filtering (Bridges.vue)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { getBridges, type Bridge, type ListBridgesParams } from 'een-api-toolkit'
|
|
95
|
+
|
|
96
|
+
const params = ref<ListBridgesParams>({
|
|
97
|
+
pageSize: 20,
|
|
98
|
+
include: ['deviceInfo', 'status', 'networkInfo']
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
async function fetchBridges() {
|
|
102
|
+
const result = await getBridges(params.value)
|
|
103
|
+
if (result.error) {
|
|
104
|
+
error.value = result.error
|
|
105
|
+
} else {
|
|
106
|
+
bridges.value = result.data.results
|
|
107
|
+
nextPageToken.value = result.data.nextPageToken
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Fetching Bridge Details (BridgeDetail.vue)
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { getBridge } from 'een-api-toolkit'
|
|
116
|
+
|
|
117
|
+
const result = await getBridge(bridgeId, {
|
|
118
|
+
include: ['deviceInfo', 'status', 'networkInfo']
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
if (result.error) {
|
|
122
|
+
error.value = result.error
|
|
123
|
+
} else {
|
|
124
|
+
bridge.value = result.data
|
|
125
|
+
}
|
|
126
|
+
```
|
|
Binary file
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"version": "0.0.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"een-api-toolkit": "file:../..",
|
|
12
|
-
"pinia": "^
|
|
12
|
+
"pinia": "^3.0.4",
|
|
13
13
|
"vue": "^3.4.0",
|
|
14
14
|
"vue-router": "^4.2.0"
|
|
15
15
|
},
|
|
@@ -23,21 +23,22 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"../..": {
|
|
26
|
-
"version": "0.1.
|
|
26
|
+
"version": "0.1.13",
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@playwright/test": "^1.57.0",
|
|
30
|
-
"@types/node": "^
|
|
31
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
32
|
-
"@typescript-eslint/parser": "^
|
|
30
|
+
"@types/node": "^25.0.3",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^8.51.0",
|
|
32
|
+
"@typescript-eslint/parser": "^8.51.0",
|
|
33
33
|
"@vitejs/plugin-vue": "^6.0.0",
|
|
34
|
-
"@vue/tsconfig": "^0.
|
|
34
|
+
"@vue/tsconfig": "^0.8.1",
|
|
35
35
|
"dotenv": "^17.2.3",
|
|
36
|
-
"eslint": "^
|
|
37
|
-
"eslint-plugin-vue": "^
|
|
36
|
+
"eslint": "^9.39.2",
|
|
37
|
+
"eslint-plugin-vue": "^10.6.2",
|
|
38
|
+
"globals": "^17.0.0",
|
|
38
39
|
"husky": "^9.1.7",
|
|
39
40
|
"jsdom": "^27.4.0",
|
|
40
|
-
"pinia": "^
|
|
41
|
+
"pinia": "^3.0.4",
|
|
41
42
|
"tsx": "^4.21.0",
|
|
42
43
|
"typedoc": "^0.28.15",
|
|
43
44
|
"typedoc-plugin-markdown": "^4.9.0",
|
|
@@ -990,6 +991,30 @@
|
|
|
990
991
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
|
991
992
|
"license": "MIT"
|
|
992
993
|
},
|
|
994
|
+
"node_modules/@vue/devtools-kit": {
|
|
995
|
+
"version": "7.7.9",
|
|
996
|
+
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
|
|
997
|
+
"integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
|
|
998
|
+
"license": "MIT",
|
|
999
|
+
"dependencies": {
|
|
1000
|
+
"@vue/devtools-shared": "^7.7.9",
|
|
1001
|
+
"birpc": "^2.3.0",
|
|
1002
|
+
"hookable": "^5.5.3",
|
|
1003
|
+
"mitt": "^3.0.1",
|
|
1004
|
+
"perfect-debounce": "^1.0.0",
|
|
1005
|
+
"speakingurl": "^14.0.1",
|
|
1006
|
+
"superjson": "^2.2.2"
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
"node_modules/@vue/devtools-shared": {
|
|
1010
|
+
"version": "7.7.9",
|
|
1011
|
+
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
|
|
1012
|
+
"integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
|
|
1013
|
+
"license": "MIT",
|
|
1014
|
+
"dependencies": {
|
|
1015
|
+
"rfdc": "^1.4.1"
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
993
1018
|
"node_modules/@vue/language-core": {
|
|
994
1019
|
"version": "3.2.1",
|
|
995
1020
|
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.1.tgz",
|
|
@@ -1063,6 +1088,30 @@
|
|
|
1063
1088
|
"dev": true,
|
|
1064
1089
|
"license": "MIT"
|
|
1065
1090
|
},
|
|
1091
|
+
"node_modules/birpc": {
|
|
1092
|
+
"version": "2.9.0",
|
|
1093
|
+
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
|
|
1094
|
+
"integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
|
|
1095
|
+
"license": "MIT",
|
|
1096
|
+
"funding": {
|
|
1097
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1098
|
+
}
|
|
1099
|
+
},
|
|
1100
|
+
"node_modules/copy-anything": {
|
|
1101
|
+
"version": "4.0.5",
|
|
1102
|
+
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
|
|
1103
|
+
"integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
|
|
1104
|
+
"license": "MIT",
|
|
1105
|
+
"dependencies": {
|
|
1106
|
+
"is-what": "^5.2.0"
|
|
1107
|
+
},
|
|
1108
|
+
"engines": {
|
|
1109
|
+
"node": ">=18"
|
|
1110
|
+
},
|
|
1111
|
+
"funding": {
|
|
1112
|
+
"url": "https://github.com/sponsors/mesqueeb"
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1066
1115
|
"node_modules/csstype": {
|
|
1067
1116
|
"version": "3.2.3",
|
|
1068
1117
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
|
@@ -1179,6 +1228,24 @@
|
|
|
1179
1228
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
1180
1229
|
}
|
|
1181
1230
|
},
|
|
1231
|
+
"node_modules/hookable": {
|
|
1232
|
+
"version": "5.5.3",
|
|
1233
|
+
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
|
1234
|
+
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
|
|
1235
|
+
"license": "MIT"
|
|
1236
|
+
},
|
|
1237
|
+
"node_modules/is-what": {
|
|
1238
|
+
"version": "5.5.0",
|
|
1239
|
+
"resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
|
|
1240
|
+
"integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
|
|
1241
|
+
"license": "MIT",
|
|
1242
|
+
"engines": {
|
|
1243
|
+
"node": ">=18"
|
|
1244
|
+
},
|
|
1245
|
+
"funding": {
|
|
1246
|
+
"url": "https://github.com/sponsors/mesqueeb"
|
|
1247
|
+
}
|
|
1248
|
+
},
|
|
1182
1249
|
"node_modules/magic-string": {
|
|
1183
1250
|
"version": "0.30.21",
|
|
1184
1251
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
|
@@ -1188,6 +1255,12 @@
|
|
|
1188
1255
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
|
1189
1256
|
}
|
|
1190
1257
|
},
|
|
1258
|
+
"node_modules/mitt": {
|
|
1259
|
+
"version": "3.0.1",
|
|
1260
|
+
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
|
1261
|
+
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
|
1262
|
+
"license": "MIT"
|
|
1263
|
+
},
|
|
1191
1264
|
"node_modules/muggle-string": {
|
|
1192
1265
|
"version": "0.4.1",
|
|
1193
1266
|
"resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
|
|
@@ -1220,6 +1293,12 @@
|
|
|
1220
1293
|
"dev": true,
|
|
1221
1294
|
"license": "MIT"
|
|
1222
1295
|
},
|
|
1296
|
+
"node_modules/perfect-debounce": {
|
|
1297
|
+
"version": "1.0.0",
|
|
1298
|
+
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
|
|
1299
|
+
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
|
1300
|
+
"license": "MIT"
|
|
1301
|
+
},
|
|
1223
1302
|
"node_modules/picocolors": {
|
|
1224
1303
|
"version": "1.1.1",
|
|
1225
1304
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
@@ -1240,20 +1319,19 @@
|
|
|
1240
1319
|
}
|
|
1241
1320
|
},
|
|
1242
1321
|
"node_modules/pinia": {
|
|
1243
|
-
"version": "
|
|
1244
|
-
"resolved": "https://registry.npmjs.org/pinia/-/pinia-
|
|
1245
|
-
"integrity": "sha512-
|
|
1322
|
+
"version": "3.0.4",
|
|
1323
|
+
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
|
|
1324
|
+
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
|
1246
1325
|
"license": "MIT",
|
|
1247
1326
|
"dependencies": {
|
|
1248
|
-
"@vue/devtools-api": "^
|
|
1249
|
-
"vue-demi": "^0.14.10"
|
|
1327
|
+
"@vue/devtools-api": "^7.7.7"
|
|
1250
1328
|
},
|
|
1251
1329
|
"funding": {
|
|
1252
1330
|
"url": "https://github.com/sponsors/posva"
|
|
1253
1331
|
},
|
|
1254
1332
|
"peerDependencies": {
|
|
1255
|
-
"typescript": ">=4.
|
|
1256
|
-
"vue": "^
|
|
1333
|
+
"typescript": ">=4.5.0",
|
|
1334
|
+
"vue": "^3.5.11"
|
|
1257
1335
|
},
|
|
1258
1336
|
"peerDependenciesMeta": {
|
|
1259
1337
|
"typescript": {
|
|
@@ -1261,6 +1339,15 @@
|
|
|
1261
1339
|
}
|
|
1262
1340
|
}
|
|
1263
1341
|
},
|
|
1342
|
+
"node_modules/pinia/node_modules/@vue/devtools-api": {
|
|
1343
|
+
"version": "7.7.9",
|
|
1344
|
+
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
|
|
1345
|
+
"integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
|
|
1346
|
+
"license": "MIT",
|
|
1347
|
+
"dependencies": {
|
|
1348
|
+
"@vue/devtools-kit": "^7.7.9"
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1264
1351
|
"node_modules/playwright": {
|
|
1265
1352
|
"version": "1.57.0",
|
|
1266
1353
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz",
|
|
@@ -1321,6 +1408,12 @@
|
|
|
1321
1408
|
"node": "^10 || ^12 || >=14"
|
|
1322
1409
|
}
|
|
1323
1410
|
},
|
|
1411
|
+
"node_modules/rfdc": {
|
|
1412
|
+
"version": "1.4.1",
|
|
1413
|
+
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
|
1414
|
+
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
|
1415
|
+
"license": "MIT"
|
|
1416
|
+
},
|
|
1324
1417
|
"node_modules/rollup": {
|
|
1325
1418
|
"version": "4.54.0",
|
|
1326
1419
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
|
|
@@ -1372,6 +1465,27 @@
|
|
|
1372
1465
|
"node": ">=0.10.0"
|
|
1373
1466
|
}
|
|
1374
1467
|
},
|
|
1468
|
+
"node_modules/speakingurl": {
|
|
1469
|
+
"version": "14.0.1",
|
|
1470
|
+
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
|
|
1471
|
+
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
|
|
1472
|
+
"license": "BSD-3-Clause",
|
|
1473
|
+
"engines": {
|
|
1474
|
+
"node": ">=0.10.0"
|
|
1475
|
+
}
|
|
1476
|
+
},
|
|
1477
|
+
"node_modules/superjson": {
|
|
1478
|
+
"version": "2.2.6",
|
|
1479
|
+
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
|
|
1480
|
+
"integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
|
|
1481
|
+
"license": "MIT",
|
|
1482
|
+
"dependencies": {
|
|
1483
|
+
"copy-anything": "^4"
|
|
1484
|
+
},
|
|
1485
|
+
"engines": {
|
|
1486
|
+
"node": ">=16"
|
|
1487
|
+
}
|
|
1488
|
+
},
|
|
1375
1489
|
"node_modules/tinyglobby": {
|
|
1376
1490
|
"version": "0.2.15",
|
|
1377
1491
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
|
@@ -1521,32 +1635,6 @@
|
|
|
1521
1635
|
}
|
|
1522
1636
|
}
|
|
1523
1637
|
},
|
|
1524
|
-
"node_modules/vue-demi": {
|
|
1525
|
-
"version": "0.14.10",
|
|
1526
|
-
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
|
1527
|
-
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
|
1528
|
-
"hasInstallScript": true,
|
|
1529
|
-
"license": "MIT",
|
|
1530
|
-
"bin": {
|
|
1531
|
-
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
1532
|
-
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
1533
|
-
},
|
|
1534
|
-
"engines": {
|
|
1535
|
-
"node": ">=12"
|
|
1536
|
-
},
|
|
1537
|
-
"funding": {
|
|
1538
|
-
"url": "https://github.com/sponsors/antfu"
|
|
1539
|
-
},
|
|
1540
|
-
"peerDependencies": {
|
|
1541
|
-
"@vue/composition-api": "^1.0.0-rc.1",
|
|
1542
|
-
"vue": "^3.0.0-0 || ^2.6.0"
|
|
1543
|
-
},
|
|
1544
|
-
"peerDependenciesMeta": {
|
|
1545
|
-
"@vue/composition-api": {
|
|
1546
|
-
"optional": true
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
},
|
|
1550
1638
|
"node_modules/vue-router": {
|
|
1551
1639
|
"version": "4.6.4",
|
|
1552
1640
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"stop": "
|
|
7
|
+
"stop": "lsof -ti :3333 2>/dev/null | xargs -r kill -9 || echo 'Port 3333 is free'",
|
|
8
8
|
"dev": "npm run stop && vite",
|
|
9
9
|
"build": "vue-tsc && vite build",
|
|
10
10
|
"preview": "vite preview",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"een-api-toolkit": "file:../..",
|
|
16
|
-
"pinia": "^
|
|
16
|
+
"pinia": "^3.0.4",
|
|
17
17
|
"vue": "^3.4.0",
|
|
18
18
|
"vue-router": "^4.2.0"
|
|
19
19
|
},
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# EEN API Toolkit - Vue Cameras Example
|
|
2
|
+
|
|
3
|
+
A Vue 3 example demonstrating how to list and view EEN cameras using the een-api-toolkit.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Features Demonstrated
|
|
8
|
+
|
|
9
|
+
- OAuth authentication flow (login, callback, logout)
|
|
10
|
+
- Protected routes with navigation guards
|
|
11
|
+
- `getCameras()` function for listing cameras with pagination
|
|
12
|
+
- `getCamera()` function for fetching camera details
|
|
13
|
+
- Status filtering (online, streaming, offline, device offline, bridge offline, error)
|
|
14
|
+
- Camera card grid with status badges
|
|
15
|
+
- Detailed camera view with device info
|
|
16
|
+
|
|
17
|
+
## APIs Used
|
|
18
|
+
|
|
19
|
+
- `getCameras()` - List cameras with filtering and pagination
|
|
20
|
+
- `getCamera()` - Get single camera by ID
|
|
21
|
+
- `useAuthStore()` - Authentication state management
|
|
22
|
+
- `initEenToolkit()` - Toolkit initialization
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
### Prerequisites
|
|
27
|
+
|
|
28
|
+
1. **Start the OAuth proxy** (required for authentication):
|
|
29
|
+
|
|
30
|
+
The OAuth proxy is a separate project that handles token management securely.
|
|
31
|
+
Clone and run it from: https://github.com/klaushofrichter/een-oauth-proxy
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# In a separate terminal, from the een-oauth-proxy directory
|
|
35
|
+
npm install
|
|
36
|
+
npm run dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The proxy should be running at `http://localhost:8787`.
|
|
40
|
+
|
|
41
|
+
### Example Setup
|
|
42
|
+
|
|
43
|
+
All commands below should be run from this example directory (`examples/vue-cameras/`):
|
|
44
|
+
|
|
45
|
+
2. Copy the environment file:
|
|
46
|
+
```bash
|
|
47
|
+
# From examples/vue-cameras/
|
|
48
|
+
cp .env.example .env
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. Edit `.env` with your EEN credentials:
|
|
52
|
+
```env
|
|
53
|
+
VITE_EEN_CLIENT_ID=your-client-id
|
|
54
|
+
VITE_PROXY_URL=http://localhost:8787
|
|
55
|
+
# DO NOT change the redirect URI - EEN IDP only permits this URL
|
|
56
|
+
VITE_REDIRECT_URI=http://127.0.0.1:3333
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
4. Install dependencies and start:
|
|
60
|
+
```bash
|
|
61
|
+
# From examples/vue-cameras/
|
|
62
|
+
npm install
|
|
63
|
+
npm run dev
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
5. Open http://127.0.0.1:3333 in your browser.
|
|
67
|
+
|
|
68
|
+
**Important:** The EEN Identity Provider only permits `http://127.0.0.1:3333` as the OAuth redirect URI. Do not use `localhost` or other ports.
|
|
69
|
+
|
|
70
|
+
**Note:** Development and testing was done on macOS. The `npm run stop` command uses `lsof`, which is not available on Windows. Windows users should manually stop any process on port 3333 or use `npx kill-port 3333` instead.
|
|
71
|
+
|
|
72
|
+
## Project Structure
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
src/
|
|
76
|
+
├── main.ts # App entry, toolkit initialization
|
|
77
|
+
├── App.vue # Root component with navigation
|
|
78
|
+
├── router/
|
|
79
|
+
│ └── index.ts # Vue Router with auth guards
|
|
80
|
+
└── views/
|
|
81
|
+
├── Home.vue # Home page with login prompt
|
|
82
|
+
├── Login.vue # OAuth login redirect
|
|
83
|
+
├── Callback.vue # OAuth callback handler
|
|
84
|
+
├── Cameras.vue # Camera list with filtering
|
|
85
|
+
├── CameraDetail.vue # Single camera details
|
|
86
|
+
└── Logout.vue # Logout handler
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Key Code Examples
|
|
90
|
+
|
|
91
|
+
### Listing Cameras with Filtering (Cameras.vue)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { getCameras, type Camera, type ListCamerasParams } from 'een-api-toolkit'
|
|
95
|
+
|
|
96
|
+
const params = ref<ListCamerasParams>({
|
|
97
|
+
pageSize: 20,
|
|
98
|
+
include: ['deviceInfo', 'status']
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
async function fetchCameras() {
|
|
102
|
+
const result = await getCameras(params.value)
|
|
103
|
+
if (result.error) {
|
|
104
|
+
error.value = result.error
|
|
105
|
+
} else {
|
|
106
|
+
cameras.value = result.data.results
|
|
107
|
+
nextPageToken.value = result.data.nextPageToken
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Fetching Camera Details (CameraDetail.vue)
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { getCamera } from 'een-api-toolkit'
|
|
116
|
+
|
|
117
|
+
const result = await getCamera(cameraId, {
|
|
118
|
+
include: ['deviceInfo', 'status']
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
if (result.error) {
|
|
122
|
+
error.value = result.error
|
|
123
|
+
} else {
|
|
124
|
+
camera.value = result.data
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Status Filtering
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// Watch for status filter changes
|
|
132
|
+
watch(statusFilter, async (newStatus) => {
|
|
133
|
+
if (newStatus) {
|
|
134
|
+
setParams({
|
|
135
|
+
pageSize: 20,
|
|
136
|
+
include: ['deviceInfo', 'status'],
|
|
137
|
+
status__in: [newStatus]
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
await fetchCameras()
|
|
141
|
+
})
|
|
142
|
+
```
|
|
Binary file
|