een-api-toolkit 0.1.2 → 0.1.7
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 +10 -53
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +686 -1
- package/dist/index.js +457 -222
- package/dist/index.js.map +1 -1
- package/docs/AI-CONTEXT.md +293 -1
- package/examples/vue-bridges/.env.example +13 -0
- package/examples/vue-bridges/e2e/app.spec.ts +73 -0
- package/examples/vue-bridges/e2e/auth.spec.ts +206 -0
- package/examples/vue-bridges/index.html +13 -0
- package/examples/vue-bridges/package-lock.json +1583 -0
- package/examples/vue-bridges/package.json +28 -0
- package/examples/vue-bridges/playwright.config.ts +46 -0
- package/examples/vue-bridges/src/App.vue +108 -0
- package/examples/vue-bridges/src/main.ts +23 -0
- package/examples/vue-bridges/src/router/index.ts +68 -0
- package/examples/vue-bridges/src/views/BridgeDetail.vue +279 -0
- package/examples/vue-bridges/src/views/Bridges.vue +297 -0
- package/examples/vue-bridges/src/views/Callback.vue +76 -0
- package/examples/vue-bridges/src/views/Home.vue +150 -0
- package/examples/vue-bridges/src/views/Login.vue +33 -0
- package/examples/vue-bridges/src/views/Logout.vue +66 -0
- package/examples/vue-bridges/src/vite-env.d.ts +12 -0
- package/examples/vue-bridges/tsconfig.json +21 -0
- package/examples/vue-bridges/tsconfig.node.json +10 -0
- package/examples/vue-bridges/vite.config.ts +12 -0
- package/examples/vue-media/.env.example +5 -0
- package/examples/vue-media/e2e/app.spec.ts +55 -0
- package/examples/vue-media/e2e/auth.spec.ts +344 -0
- package/examples/vue-media/index.html +13 -0
- package/examples/vue-media/package-lock.json +1583 -0
- package/examples/vue-media/package.json +28 -0
- package/examples/vue-media/playwright.config.ts +28 -0
- package/examples/vue-media/src/App.vue +122 -0
- package/examples/vue-media/src/main.ts +22 -0
- package/examples/vue-media/src/router/index.ts +61 -0
- package/examples/vue-media/src/views/Callback.vue +76 -0
- package/examples/vue-media/src/views/Home.vue +86 -0
- package/examples/vue-media/src/views/LiveCamera.vue +330 -0
- package/examples/vue-media/src/views/Login.vue +32 -0
- package/examples/vue-media/src/views/Logout.vue +59 -0
- package/examples/vue-media/src/vite-env.d.ts +12 -0
- package/examples/vue-media/tsconfig.json +21 -0
- package/examples/vue-media/tsconfig.node.json +10 -0
- package/examples/vue-media/vite.config.ts +12 -0
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "een-api-toolkit-media-example",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"stop": "npx kill-port 3333",
|
|
8
|
+
"dev": "npm run stop && vite",
|
|
9
|
+
"build": "vue-tsc && vite build",
|
|
10
|
+
"preview": "vite preview",
|
|
11
|
+
"test:e2e": "playwright test",
|
|
12
|
+
"test:e2e:ui": "playwright test --ui"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"een-api-toolkit": "file:../..",
|
|
16
|
+
"pinia": "^2.1.7",
|
|
17
|
+
"vue": "^3.4.0",
|
|
18
|
+
"vue-router": "^4.2.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@playwright/test": "^1.57.0",
|
|
22
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
23
|
+
"dotenv": "^17.2.3",
|
|
24
|
+
"typescript": "~5.8.0",
|
|
25
|
+
"vite": "^7.3.0",
|
|
26
|
+
"vue-tsc": "^3.2.1"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
testDir: './e2e',
|
|
5
|
+
fullyParallel: false,
|
|
6
|
+
forbidOnly: !!process.env.CI,
|
|
7
|
+
retries: process.env.CI ? 2 : 0,
|
|
8
|
+
workers: 1,
|
|
9
|
+
reporter: 'html',
|
|
10
|
+
timeout: 60000,
|
|
11
|
+
use: {
|
|
12
|
+
baseURL: 'http://127.0.0.1:3333',
|
|
13
|
+
trace: 'on-first-retry',
|
|
14
|
+
screenshot: 'only-on-failure'
|
|
15
|
+
},
|
|
16
|
+
projects: [
|
|
17
|
+
{
|
|
18
|
+
name: 'chromium',
|
|
19
|
+
use: { ...devices['Desktop Chrome'] }
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
webServer: {
|
|
23
|
+
command: 'npm run dev',
|
|
24
|
+
url: 'http://127.0.0.1:3333',
|
|
25
|
+
reuseExistingServer: !process.env.CI,
|
|
26
|
+
timeout: 120000
|
|
27
|
+
}
|
|
28
|
+
})
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useAuthStore } from 'een-api-toolkit'
|
|
3
|
+
|
|
4
|
+
const authStore = useAuthStore()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<div id="app">
|
|
9
|
+
<header>
|
|
10
|
+
<h1>EEN Media Example</h1>
|
|
11
|
+
<nav>
|
|
12
|
+
<router-link to="/" data-testid="nav-home">Home</router-link>
|
|
13
|
+
<router-link v-if="!authStore.isAuthenticated" to="/login" data-testid="nav-login">Login</router-link>
|
|
14
|
+
<router-link v-if="authStore.isAuthenticated" to="/live" data-testid="nav-live">Live Camera</router-link>
|
|
15
|
+
<router-link v-if="authStore.isAuthenticated" to="/logout" data-testid="nav-logout">Logout</router-link>
|
|
16
|
+
</nav>
|
|
17
|
+
</header>
|
|
18
|
+
<main>
|
|
19
|
+
<router-view />
|
|
20
|
+
</main>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<style>
|
|
25
|
+
* {
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body {
|
|
32
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
33
|
+
line-height: 1.6;
|
|
34
|
+
color: #333;
|
|
35
|
+
background: #f5f5f5;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#app {
|
|
39
|
+
max-width: 1200px;
|
|
40
|
+
margin: 0 auto;
|
|
41
|
+
padding: 20px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
header {
|
|
45
|
+
background: #2c3e50;
|
|
46
|
+
color: white;
|
|
47
|
+
padding: 20px;
|
|
48
|
+
border-radius: 8px;
|
|
49
|
+
margin-bottom: 20px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
header h1 {
|
|
53
|
+
margin-bottom: 15px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
nav {
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: 15px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
nav a {
|
|
62
|
+
color: white;
|
|
63
|
+
text-decoration: none;
|
|
64
|
+
padding: 8px 16px;
|
|
65
|
+
border-radius: 4px;
|
|
66
|
+
transition: background-color 0.2s;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
nav a:hover {
|
|
70
|
+
background: rgba(255, 255, 255, 0.1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
nav a.router-link-active {
|
|
74
|
+
background: rgba(255, 255, 255, 0.2);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
main {
|
|
78
|
+
background: white;
|
|
79
|
+
padding: 30px;
|
|
80
|
+
border-radius: 8px;
|
|
81
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
h2 {
|
|
85
|
+
color: #2c3e50;
|
|
86
|
+
margin-bottom: 20px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
button {
|
|
90
|
+
background: #3498db;
|
|
91
|
+
color: white;
|
|
92
|
+
border: none;
|
|
93
|
+
padding: 10px 20px;
|
|
94
|
+
border-radius: 4px;
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
font-size: 1rem;
|
|
97
|
+
transition: background-color 0.2s;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
button:hover {
|
|
101
|
+
background: #2980b9;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
button:disabled {
|
|
105
|
+
background: #bdc3c7;
|
|
106
|
+
cursor: not-allowed;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.loading {
|
|
110
|
+
text-align: center;
|
|
111
|
+
padding: 40px;
|
|
112
|
+
color: #666;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.error {
|
|
116
|
+
color: #e74c3c;
|
|
117
|
+
padding: 15px;
|
|
118
|
+
background: #fdf0ef;
|
|
119
|
+
border-radius: 4px;
|
|
120
|
+
margin-bottom: 20px;
|
|
121
|
+
}
|
|
122
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createApp } from 'vue'
|
|
2
|
+
import { createPinia } from 'pinia'
|
|
3
|
+
import { initEenToolkit } from 'een-api-toolkit'
|
|
4
|
+
import App from './App.vue'
|
|
5
|
+
import router from './router'
|
|
6
|
+
|
|
7
|
+
const app = createApp(App)
|
|
8
|
+
const pinia = createPinia()
|
|
9
|
+
|
|
10
|
+
// Install Pinia FIRST (required before initEenToolkit)
|
|
11
|
+
app.use(pinia)
|
|
12
|
+
app.use(router)
|
|
13
|
+
|
|
14
|
+
// Initialize the EEN API Toolkit
|
|
15
|
+
initEenToolkit({
|
|
16
|
+
proxyUrl: import.meta.env.VITE_PROXY_URL,
|
|
17
|
+
clientId: import.meta.env.VITE_EEN_CLIENT_ID,
|
|
18
|
+
redirectUri: import.meta.env.VITE_REDIRECT_URI || 'http://127.0.0.1:3333',
|
|
19
|
+
debug: import.meta.env.VITE_DEBUG === 'true'
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
app.mount('#app')
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
|
+
import { useAuthStore } from 'een-api-toolkit'
|
|
3
|
+
import Home from '../views/Home.vue'
|
|
4
|
+
import Login from '../views/Login.vue'
|
|
5
|
+
import Callback from '../views/Callback.vue'
|
|
6
|
+
import LiveCamera from '../views/LiveCamera.vue'
|
|
7
|
+
import Logout from '../views/Logout.vue'
|
|
8
|
+
|
|
9
|
+
const router = createRouter({
|
|
10
|
+
history: createWebHistory(),
|
|
11
|
+
routes: [
|
|
12
|
+
{
|
|
13
|
+
path: '/',
|
|
14
|
+
name: 'home',
|
|
15
|
+
component: Home,
|
|
16
|
+
// Handle OAuth callback on root path (EEN IDP redirects to http://127.0.0.1:3333)
|
|
17
|
+
beforeEnter: (to, _from, next) => {
|
|
18
|
+
// If URL has code and state params, it's an OAuth callback
|
|
19
|
+
if (to.query.code && to.query.state) {
|
|
20
|
+
next({ name: 'callback', query: to.query })
|
|
21
|
+
} else {
|
|
22
|
+
next()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
path: '/login',
|
|
28
|
+
name: 'login',
|
|
29
|
+
component: Login
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: '/callback',
|
|
33
|
+
name: 'callback',
|
|
34
|
+
component: Callback
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
path: '/live',
|
|
38
|
+
name: 'live',
|
|
39
|
+
component: LiveCamera,
|
|
40
|
+
meta: { requiresAuth: true }
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
path: '/logout',
|
|
44
|
+
name: 'logout',
|
|
45
|
+
component: Logout
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Navigation guard for protected routes
|
|
51
|
+
router.beforeEach((to, _from, next) => {
|
|
52
|
+
const authStore = useAuthStore()
|
|
53
|
+
|
|
54
|
+
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
|
|
55
|
+
next({ name: 'login' })
|
|
56
|
+
} else {
|
|
57
|
+
next()
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export default router
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, ref } from 'vue'
|
|
3
|
+
import { useRouter } from 'vue-router'
|
|
4
|
+
import { handleAuthCallback } from 'een-api-toolkit'
|
|
5
|
+
|
|
6
|
+
const router = useRouter()
|
|
7
|
+
const error = ref<string | null>(null)
|
|
8
|
+
const processing = ref(true)
|
|
9
|
+
|
|
10
|
+
onMounted(async () => {
|
|
11
|
+
const url = new URL(window.location.href)
|
|
12
|
+
const code = url.searchParams.get('code')
|
|
13
|
+
const state = url.searchParams.get('state')
|
|
14
|
+
const errorParam = url.searchParams.get('error')
|
|
15
|
+
|
|
16
|
+
if (errorParam) {
|
|
17
|
+
error.value = `OAuth error: ${errorParam}`
|
|
18
|
+
processing.value = false
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!code || !state) {
|
|
23
|
+
error.value = 'Missing authorization code or state parameter'
|
|
24
|
+
processing.value = false
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = await handleAuthCallback(code, state)
|
|
29
|
+
|
|
30
|
+
if (result.error) {
|
|
31
|
+
error.value = result.error.message
|
|
32
|
+
processing.value = false
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Success - redirect to live camera view
|
|
37
|
+
router.push('/live')
|
|
38
|
+
})
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template>
|
|
42
|
+
<div class="callback">
|
|
43
|
+
<div v-if="processing" class="loading">
|
|
44
|
+
<h2>Authenticating...</h2>
|
|
45
|
+
<p>Please wait while we complete the login process.</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div v-else-if="error" class="error-state">
|
|
49
|
+
<h2>Authentication Failed</h2>
|
|
50
|
+
<p class="error">{{ error }}</p>
|
|
51
|
+
<router-link to="/login">
|
|
52
|
+
<button>Try Again</button>
|
|
53
|
+
</router-link>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style scoped>
|
|
59
|
+
.callback {
|
|
60
|
+
text-align: center;
|
|
61
|
+
max-width: 400px;
|
|
62
|
+
margin: 0 auto;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
h2 {
|
|
66
|
+
margin-bottom: 20px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.loading p {
|
|
70
|
+
color: #666;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.error-state .error {
|
|
74
|
+
margin-bottom: 20px;
|
|
75
|
+
}
|
|
76
|
+
</style>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useAuthStore } from 'een-api-toolkit'
|
|
3
|
+
|
|
4
|
+
const authStore = useAuthStore()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<div class="home">
|
|
9
|
+
<h2>Welcome to the EEN Media Example</h2>
|
|
10
|
+
|
|
11
|
+
<div v-if="!authStore.isAuthenticated" class="login-prompt" data-testid="not-authenticated">
|
|
12
|
+
<p>Please log in to view live camera images.</p>
|
|
13
|
+
<router-link to="/login">
|
|
14
|
+
<button data-testid="login-button">Login with Eagle Eye Networks</button>
|
|
15
|
+
</router-link>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div v-else class="authenticated" data-testid="authenticated">
|
|
19
|
+
<p>You are logged in!</p>
|
|
20
|
+
<router-link to="/live">
|
|
21
|
+
<button data-testid="view-live-button">View Live Camera</button>
|
|
22
|
+
</router-link>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="description">
|
|
26
|
+
<h3>About This Example</h3>
|
|
27
|
+
<p>
|
|
28
|
+
This example demonstrates using the EEN Media API to fetch live images from cameras.
|
|
29
|
+
It showcases the following toolkit functions:
|
|
30
|
+
</p>
|
|
31
|
+
<ul>
|
|
32
|
+
<li><code>getCameras()</code> - List available cameras</li>
|
|
33
|
+
<li><code>getLiveImage()</code> - Fetch live preview images</li>
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<style scoped>
|
|
40
|
+
.home {
|
|
41
|
+
max-width: 600px;
|
|
42
|
+
margin: 0 auto;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.login-prompt,
|
|
46
|
+
.authenticated {
|
|
47
|
+
text-align: center;
|
|
48
|
+
padding: 30px;
|
|
49
|
+
margin-bottom: 30px;
|
|
50
|
+
background: #f8f9fa;
|
|
51
|
+
border-radius: 8px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.login-prompt p,
|
|
55
|
+
.authenticated p {
|
|
56
|
+
margin-bottom: 20px;
|
|
57
|
+
color: #666;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.description {
|
|
61
|
+
padding: 20px;
|
|
62
|
+
background: #f8f9fa;
|
|
63
|
+
border-radius: 8px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.description h3 {
|
|
67
|
+
margin-bottom: 15px;
|
|
68
|
+
color: #2c3e50;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.description ul {
|
|
72
|
+
margin-top: 15px;
|
|
73
|
+
padding-left: 25px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.description li {
|
|
77
|
+
margin-bottom: 8px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.description code {
|
|
81
|
+
background: #e9ecef;
|
|
82
|
+
padding: 2px 6px;
|
|
83
|
+
border-radius: 3px;
|
|
84
|
+
font-family: monospace;
|
|
85
|
+
}
|
|
86
|
+
</style>
|