een-api-toolkit 0.0.8 → 0.0.13

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.
@@ -0,0 +1,767 @@
1
+ # EEN API Toolkit - AI Reference
2
+
3
+ > **Version:** 0.0.13
4
+ > **Generated:** 2025-12-29
5
+ >
6
+ > This file is optimized for AI assistants. It contains all API signatures,
7
+ > types, and usage patterns in a single, parseable document.
8
+ >
9
+ > For the full EEN API documentation, see the
10
+ > [Eagle Eye Networks Developer Portal](https://developer.eagleeyenetworks.com).
11
+
12
+ ---
13
+
14
+ ## Quick Reference
15
+
16
+ ### Configuration
17
+
18
+ | Function | Purpose |
19
+ |----------|---------|
20
+ | `initEenToolkit(config)` | Initialize the toolkit with proxy URL and client ID |
21
+
22
+ ### Authentication Functions
23
+
24
+ | Function | Purpose | Returns |
25
+ |----------|---------|---------|
26
+ | `getAuthUrl()` | Generate OAuth authorization URL | `string` |
27
+ | `handleAuthCallback(code, state)` | Exchange auth code for token | `Result<TokenResponse>` |
28
+ | `refreshToken()` | Refresh the access token | `Result<{accessToken, expiresIn}>` |
29
+ | `revokeToken()` | Revoke token and logout | `Result<void>` |
30
+
31
+ ### User Functions
32
+
33
+ | Function | Purpose | Returns |
34
+ |----------|---------|---------|
35
+ | `getCurrentUser()` | Get current user profile | `Result<UserProfile>` |
36
+ | `getUsers(params?)` | List all users (paginated) | `Result<PaginatedResult<User>>` |
37
+ | `getUser(userId, params?)` | Get a specific user | `Result<User>` |
38
+
39
+ ### Vue 3 Composables
40
+
41
+ | Composable | Purpose | Returns |
42
+ |------------|---------|---------|
43
+ | `useCurrentUser(options?)` | Reactive current user | `{ user, loading, error, refresh }` |
44
+ | `useUsers(params?, options?)` | Reactive user list with pagination | `{ users, loading, hasNextPage, fetchNextPage, ... }` |
45
+ | `useUser(userId, options?)` | Reactive single user | `{ user, loading, error, refresh }` |
46
+
47
+ ---
48
+
49
+ ## Critical Requirements
50
+
51
+ ### OAuth Redirect URI (IMPORTANT)
52
+
53
+ The EEN Identity Provider performs an **exact string match** on the redirect URI. Applications MUST follow these rules:
54
+
55
+ | Requirement | Correct | Incorrect |
56
+ |-------------|---------|-----------|
57
+ | Host | `127.0.0.1` | `localhost` |
58
+ | Path | None (root path only) | `/callback` |
59
+ | Trailing slash | No | `http://127.0.0.1:3333/` |
60
+
61
+ **The only valid redirect URI is: `http://127.0.0.1:3333`**
62
+
63
+ **Configure at:** [EEN Developer Portal - My Application](https://developer.eagleeyenetworks.com/page/my-application)
64
+
65
+ ### Application Requirements
66
+
67
+ 1. **Handle OAuth callbacks on the root path (`/`)** - not `/callback`
68
+ 2. **Run dev server on `127.0.0.1`** - not `localhost`
69
+ 3. **Register exactly `http://127.0.0.1:3333` with EEN at the Developer Portal**
70
+
71
+ ### Router Pattern for OAuth Callbacks
72
+
73
+ The root path must detect OAuth params and handle the callback:
74
+
75
+ ```typescript
76
+ // router/index.ts
77
+ {
78
+ path: '/',
79
+ name: 'home',
80
+ component: Home,
81
+ beforeEnter: (to, _from, next) => {
82
+ // If URL has OAuth params, redirect to callback handler
83
+ if (to.query.code && to.query.state) {
84
+ next({ name: 'callback', query: to.query })
85
+ } else {
86
+ next()
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### Vite Dev Server Configuration
93
+
94
+ ```typescript
95
+ // vite.config.ts
96
+ export default defineConfig({
97
+ server: {
98
+ host: '127.0.0.1', // MUST use 127.0.0.1, not localhost
99
+ port: 3333,
100
+ strictPort: true
101
+ }
102
+ })
103
+ ```
104
+
105
+ ### Common OAuth Errors
106
+
107
+ | Error | Cause | Solution |
108
+ |-------|-------|----------|
109
+ | "Redirect URI mismatch" | URI doesn't match exactly | Use `http://127.0.0.1:3333` (no path, no trailing slash) |
110
+ | Redirected back to login | Router guard blocks callback | Allow OAuth params through on root path |
111
+ | Callback not processed | Wrong path or host | Handle callback on `/`, use `127.0.0.1` |
112
+
113
+ ---
114
+
115
+ ## Core Types
116
+
117
+ ### Result<T>
118
+
119
+ All API functions return a `Result<T>` type - they never throw exceptions.
120
+
121
+ ```typescript
122
+ type Result<T> =
123
+ | { data: T; error: null } // Success
124
+ | { data: null; error: EenError } // Failure
125
+
126
+ interface EenError {
127
+ code: ErrorCode
128
+ message: string
129
+ status?: number
130
+ details?: unknown
131
+ }
132
+
133
+ type ErrorCode =
134
+ | 'AUTH_REQUIRED' // No valid token - redirect to login
135
+ | 'AUTH_FAILED' // Authentication failed
136
+ | 'TOKEN_EXPIRED' // Token expired - will auto-refresh
137
+ | 'API_ERROR' // EEN API returned an error
138
+ | 'NETWORK_ERROR' // Network request failed
139
+ | 'VALIDATION_ERROR' // Invalid parameters
140
+ | 'NOT_FOUND' // Resource not found (404)
141
+ | 'FORBIDDEN' // Access denied (403)
142
+ | 'RATE_LIMITED' // Too many requests (429)
143
+ | 'UNKNOWN_ERROR' // Unexpected error
144
+ ```
145
+
146
+ ### Pagination Types
147
+
148
+ ```typescript
149
+ interface PaginationParams {
150
+ pageSize?: number // Results per page (default varies, typically 100)
151
+ pageToken?: string // Token for fetching a specific page
152
+ }
153
+
154
+ interface PaginatedResult<T> {
155
+ results: T[]
156
+ nextPageToken?: string // Token for next page (undefined if last page)
157
+ prevPageToken?: string // Token for previous page
158
+ totalSize?: number // Total count (not always provided)
159
+ }
160
+ ```
161
+
162
+ ### Configuration Type
163
+
164
+ ```typescript
165
+ interface EenToolkitConfig {
166
+ proxyUrl?: string // OAuth proxy URL (required for API calls)
167
+ clientId?: string // EEN OAuth client ID
168
+ redirectUri?: string // OAuth redirect URI (default: http://127.0.0.1:3333)
169
+ debug?: boolean // Enable debug logging
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Entity Types
176
+
177
+ ### User
178
+
179
+ ```typescript
180
+ interface User {
181
+ id: string
182
+ email: string
183
+ firstName: string
184
+ lastName: string
185
+ accountId?: string
186
+ timeZone?: string // IANA timezone (e.g., "America/Los_Angeles")
187
+ language?: string // ISO 639-1 code (e.g., "en")
188
+ phone?: string
189
+ mobilePhone?: string
190
+ permissions?: string[] // Requires include: ['permissions']
191
+ lastLogin?: string // ISO 8601 timestamp
192
+ isActive?: boolean
193
+ createdAt?: string
194
+ updatedAt?: string
195
+ }
196
+
197
+ interface UserProfile {
198
+ id: string
199
+ email: string
200
+ firstName: string
201
+ lastName: string
202
+ accountId?: string
203
+ timeZone?: string
204
+ language?: string
205
+ }
206
+ ```
207
+
208
+ ### Parameter Types
209
+
210
+ ```typescript
211
+ interface ListUsersParams {
212
+ pageSize?: number // Results per page (default: 100, max: 1000)
213
+ pageToken?: string // Pagination token
214
+ include?: string[] // Additional fields (e.g., ['permissions'])
215
+ }
216
+
217
+ interface GetUserParams {
218
+ include?: string[] // Additional fields to include
219
+ }
220
+ ```
221
+
222
+ ---
223
+
224
+ ## API Reference
225
+
226
+ ### initEenToolkit
227
+
228
+ Initialize the toolkit. Call this before using any API functions.
229
+
230
+ ```typescript
231
+ import { initEenToolkit } from 'een-api-toolkit'
232
+
233
+ // In main.ts
234
+ initEenToolkit({
235
+ proxyUrl: import.meta.env.VITE_PROXY_URL,
236
+ clientId: import.meta.env.VITE_EEN_CLIENT_ID,
237
+ debug: true // optional
238
+ })
239
+ ```
240
+
241
+ ### getAuthUrl
242
+
243
+ Generate the OAuth authorization URL. Redirect the user here to start login.
244
+
245
+ ```typescript
246
+ import { getAuthUrl } from 'een-api-toolkit'
247
+
248
+ function login() {
249
+ window.location.href = getAuthUrl()
250
+ }
251
+ ```
252
+
253
+ ### handleAuthCallback
254
+
255
+ Handle the OAuth callback after user authorizes. Call this when user returns to your redirect URI.
256
+
257
+ ```typescript
258
+ import { handleAuthCallback } from 'een-api-toolkit'
259
+
260
+ // In your callback route handler
261
+ const url = new URL(window.location.href)
262
+ const code = url.searchParams.get('code')
263
+ const state = url.searchParams.get('state')
264
+
265
+ if (code && state) {
266
+ const { data, error } = await handleAuthCallback(code, state)
267
+
268
+ if (error) {
269
+ console.error('Auth failed:', error.message)
270
+ return
271
+ }
272
+
273
+ // User is now authenticated
274
+ router.push('/dashboard')
275
+ }
276
+ ```
277
+
278
+ ### getCurrentUser
279
+
280
+ Get the current authenticated user's profile.
281
+
282
+ ```typescript
283
+ import { getCurrentUser } from 'een-api-toolkit'
284
+
285
+ const { data, error } = await getCurrentUser()
286
+
287
+ if (error) {
288
+ if (error.code === 'AUTH_REQUIRED') {
289
+ router.push('/login')
290
+ }
291
+ return
292
+ }
293
+
294
+ console.log(`Welcome, ${data.firstName} ${data.lastName}`)
295
+ ```
296
+
297
+ ### getUsers
298
+
299
+ List users with optional pagination.
300
+
301
+ ```typescript
302
+ import { getUsers } from 'een-api-toolkit'
303
+
304
+ // Basic usage
305
+ const { data, error } = await getUsers()
306
+
307
+ // With pagination
308
+ const { data } = await getUsers({ pageSize: 50 })
309
+
310
+ // Fetch all users
311
+ let allUsers: User[] = []
312
+ let pageToken: string | undefined
313
+
314
+ do {
315
+ const { data, error } = await getUsers({ pageSize: 100, pageToken })
316
+ if (error) break
317
+ allUsers.push(...data.results)
318
+ pageToken = data.nextPageToken
319
+ } while (pageToken)
320
+ ```
321
+
322
+ ### getUser
323
+
324
+ Get a specific user by ID.
325
+
326
+ ```typescript
327
+ import { getUser } from 'een-api-toolkit'
328
+
329
+ const { data, error } = await getUser('user-id-123')
330
+
331
+ if (error) {
332
+ if (error.code === 'NOT_FOUND') {
333
+ console.log('User not found')
334
+ }
335
+ return
336
+ }
337
+
338
+ // With permissions
339
+ const { data: userWithPerms } = await getUser('user-id-123', {
340
+ include: ['permissions']
341
+ })
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Vue 3 Composables
347
+
348
+ ### useCurrentUser
349
+
350
+ Reactive composable for the current authenticated user.
351
+
352
+ ```vue
353
+ <script setup>
354
+ import { useCurrentUser } from 'een-api-toolkit'
355
+
356
+ const { user, loading, error, refresh } = useCurrentUser()
357
+
358
+ // Options:
359
+ // { immediate: false } - don't fetch on mount
360
+ </script>
361
+
362
+ <template>
363
+ <div v-if="loading">Loading...</div>
364
+ <div v-else-if="error">Error: {{ error.message }}</div>
365
+ <div v-else-if="user">
366
+ <h1>Welcome, {{ user.firstName }}!</h1>
367
+ <button @click="refresh">Refresh</button>
368
+ </div>
369
+ </template>
370
+ ```
371
+
372
+ **Returns:**
373
+ - `user: Ref<UserProfile | null>` - The user profile
374
+ - `loading: Ref<boolean>` - Fetch in progress
375
+ - `error: Ref<EenError | null>` - Last error
376
+ - `fetch(): Promise<Result<UserProfile>>` - Fetch user
377
+ - `refresh(): Promise<Result<UserProfile>>` - Alias for fetch
378
+
379
+ ### useUsers
380
+
381
+ Reactive composable for listing users with pagination.
382
+
383
+ ```vue
384
+ <script setup>
385
+ import { useUsers } from 'een-api-toolkit'
386
+
387
+ const {
388
+ users,
389
+ loading,
390
+ error,
391
+ hasNextPage,
392
+ fetchNextPage,
393
+ refresh
394
+ } = useUsers({ pageSize: 20 })
395
+ </script>
396
+
397
+ <template>
398
+ <ul>
399
+ <li v-for="user in users" :key="user.id">
400
+ {{ user.firstName }} {{ user.lastName }}
401
+ </li>
402
+ </ul>
403
+ <button v-if="hasNextPage" @click="fetchNextPage">Load More</button>
404
+ </template>
405
+ ```
406
+
407
+ **Returns:**
408
+ - `users: Ref<User[]>` - Array of users
409
+ - `loading: Ref<boolean>` - Fetch in progress
410
+ - `error: Ref<EenError | null>` - Last error
411
+ - `hasNextPage: ComputedRef<boolean>` - More pages available
412
+ - `hasPrevPage: ComputedRef<boolean>` - Previous page available
413
+ - `nextPageToken: Ref<string | undefined>` - Next page token
414
+ - `totalSize: Ref<number | undefined>` - Total count
415
+ - `fetch(params?): Promise<Result>` - Fetch with params
416
+ - `refresh(): Promise<Result>` - Refresh current page
417
+ - `fetchNextPage(): Promise<Result | undefined>` - Fetch next page
418
+ - `fetchPrevPage(): Promise<Result | undefined>` - Fetch previous page
419
+ - `setParams(params): void` - Update default params
420
+
421
+ ### useUser
422
+
423
+ Reactive composable for a single user by ID.
424
+
425
+ ```vue
426
+ <script setup>
427
+ import { useUser } from 'een-api-toolkit'
428
+ import { useRoute } from 'vue-router'
429
+
430
+ const route = useRoute()
431
+
432
+ // Static ID
433
+ const { user, loading, error } = useUser('user-123')
434
+
435
+ // Reactive ID from route
436
+ const { user: routeUser } = useUser(() => route.params.id as string)
437
+
438
+ // With options
439
+ const { user: userWithPerms } = useUser('user-123', {
440
+ include: ['permissions']
441
+ })
442
+ </script>
443
+ ```
444
+
445
+ **Returns:**
446
+ - `user: Ref<User | null>` - The user
447
+ - `loading: Ref<boolean>` - Fetch in progress
448
+ - `error: Ref<EenError | null>` - Last error
449
+ - `fetch(params?): Promise<Result<User>>` - Fetch user
450
+ - `refresh(): Promise<Result<User>>` - Refresh user
451
+
452
+ ---
453
+
454
+ ## Common Patterns
455
+
456
+ ### Error Handling
457
+
458
+ ```typescript
459
+ // All functions return Result<T>, never throw
460
+ const { data, error } = await getUsers()
461
+
462
+ if (error) {
463
+ switch (error.code) {
464
+ case 'AUTH_REQUIRED':
465
+ router.push('/login')
466
+ break
467
+ case 'NETWORK_ERROR':
468
+ showRetryDialog()
469
+ break
470
+ case 'RATE_LIMITED':
471
+ await sleep(1000)
472
+ return retry()
473
+ default:
474
+ showError(error.message)
475
+ }
476
+ return
477
+ }
478
+
479
+ // TypeScript knows data is not null here
480
+ processUsers(data.results)
481
+ ```
482
+
483
+ ### Pagination
484
+
485
+ ```typescript
486
+ // Manual pagination - fetch all
487
+ async function fetchAllUsers(): Promise<User[]> {
488
+ const allUsers: User[] = []
489
+ let pageToken: string | undefined
490
+
491
+ do {
492
+ const { data, error } = await getUsers({ pageSize: 100, pageToken })
493
+ if (error) break // Stop on error, return what we have
494
+ allUsers.push(...data.results)
495
+ pageToken = data.nextPageToken
496
+ } while (pageToken)
497
+
498
+ return allUsers
499
+ }
500
+ ```
501
+
502
+ ### Auth Guard (Vue Router)
503
+
504
+ ```typescript
505
+ import { useAuthStore } from 'een-api-toolkit'
506
+
507
+ router.beforeEach((to, from, next) => {
508
+ const authStore = useAuthStore()
509
+
510
+ if (to.meta.requiresAuth && !authStore.isAuthenticated) {
511
+ next('/login')
512
+ } else {
513
+ next()
514
+ }
515
+ })
516
+ ```
517
+
518
+ ### Composable with Conditional Fetch
519
+
520
+ ```vue
521
+ <script setup>
522
+ import { useCurrentUser } from 'een-api-toolkit'
523
+ import { onMounted } from 'vue'
524
+
525
+ // Don't fetch on mount
526
+ const { user, fetch } = useCurrentUser({ immediate: false })
527
+
528
+ onMounted(async () => {
529
+ // Only fetch if some condition is met
530
+ if (shouldFetchUser()) {
531
+ await fetch()
532
+ }
533
+ })
534
+ </script>
535
+ ```
536
+
537
+ ### In-Place Login (Single Page Callback)
538
+
539
+ When handling the OAuth callback on the same page that displays user data (without navigation), you must manually refresh the composable after authentication:
540
+
541
+ > **⚠️ Important:** When handling OAuth callbacks on the same page, you must call `refresh()` after `handleAuthCallback()` to update the user data.
542
+
543
+ ```vue
544
+ <script setup lang="ts">
545
+ import { onMounted } from 'vue'
546
+ import { useCurrentUser, handleAuthCallback } from 'een-api-toolkit'
547
+
548
+ // Composable initializes before token exists
549
+ const { user, loading, error, refresh } = useCurrentUser({ immediate: false })
550
+
551
+ onMounted(async () => {
552
+ const urlParams = new URLSearchParams(window.location.search)
553
+ const code = urlParams.get('code')
554
+ const state = urlParams.get('state')
555
+
556
+ if (code && state) {
557
+ const result = await handleAuthCallback(code, state)
558
+
559
+ if (result.error) {
560
+ console.error('Login failed:', result.error.message)
561
+ return
562
+ }
563
+
564
+ // Clean URL after successful auth
565
+ window.history.replaceState({}, document.title, window.location.pathname)
566
+ }
567
+
568
+ // Refresh user data - runs after successful auth callback,
569
+ // or on initial load if no callback was processed
570
+ await refresh()
571
+ })
572
+ </script>
573
+
574
+ <template>
575
+ <div v-if="loading">Loading...</div>
576
+ <div v-else-if="error">{{ error.message }}</div>
577
+ <div v-else-if="user">Welcome, {{ user.firstName }}!</div>
578
+ <div v-else>Please log in</div>
579
+ </template>
580
+ ```
581
+
582
+ **Key Points:**
583
+ - `useCurrentUser()` initializes with the auth state at the moment it's called
584
+ - `handleAuthCallback()` updates the token but doesn't trigger composable re-fetch
585
+ - Always call `refresh()` after auth state changes if staying on the same page
586
+
587
+ ---
588
+
589
+ ## Anti-Patterns (What NOT to Do)
590
+
591
+ ### DON'T: Use try/catch for API errors
592
+
593
+ ```typescript
594
+ // WRONG - functions don't throw
595
+ try {
596
+ const users = await getUsers()
597
+ } catch (e) {
598
+ // This will never catch API errors!
599
+ }
600
+
601
+ // CORRECT
602
+ const { data, error } = await getUsers()
603
+ if (error) handleError(error)
604
+ ```
605
+
606
+ ### DON'T: Ignore the error check
607
+
608
+ ```typescript
609
+ // WRONG - data might be null
610
+ const { data } = await getUsers()
611
+ data.results.forEach(...) // TypeError if error occurred!
612
+
613
+ // CORRECT
614
+ const { data, error } = await getUsers()
615
+ if (error) return
616
+ data.results.forEach(...) // Safe - TypeScript knows data is not null
617
+ ```
618
+
619
+ ### DON'T: Call initEenToolkit multiple times
620
+
621
+ ```typescript
622
+ // WRONG - calling in component
623
+ export default {
624
+ setup() {
625
+ initEenToolkit({ ... }) // Called every time component mounts!
626
+ }
627
+ }
628
+
629
+ // CORRECT - call once in main.ts
630
+ // main.ts
631
+ initEenToolkit({ ... })
632
+ app.mount('#app')
633
+ ```
634
+
635
+ ### DON'T: Access data before checking error
636
+
637
+ ```typescript
638
+ // WRONG - unsafe access
639
+ const { data, error } = await getUser(id)
640
+ console.log(data.email) // TypeError if error!
641
+ if (error) { ... }
642
+
643
+ // CORRECT - check error first
644
+ const { data, error } = await getUser(id)
645
+ if (error) {
646
+ console.error(error.message)
647
+ return
648
+ }
649
+ console.log(data.email) // Safe
650
+ ```
651
+
652
+ ---
653
+
654
+ ## Setup Guide
655
+
656
+ ### Installation
657
+
658
+ ```bash
659
+ npm install een-api-toolkit
660
+ ```
661
+
662
+ ### Prerequisites (IMPORTANT)
663
+
664
+ The toolkit uses **Pinia for state management internally**. You must install and configure Pinia before initializing the toolkit:
665
+
666
+ ```bash
667
+ npm install pinia
668
+ ```
669
+
670
+ **Pinia must be installed on the Vue app instance BEFORE calling `initEenToolkit()` or using any composables** (`useCurrentUser`, `useUsers`, `useUser`). Failing to do so will result in:
671
+
672
+ ```
673
+ Error: [🍍]: "getActivePinia()" was called but there was no active Pinia.
674
+ Are you trying to use a store before calling "app.use(pinia)"?
675
+ ```
676
+
677
+ ### Environment Variables
678
+
679
+ Create a `.env` file:
680
+
681
+ ```env
682
+ VITE_PROXY_URL=https://your-proxy.workers.dev
683
+ VITE_EEN_CLIENT_ID=your-een-client-id
684
+ # IMPORTANT: EEN IDP only permits this exact redirect URI
685
+ VITE_REDIRECT_URI=http://127.0.0.1:3333
686
+ VITE_DEBUG=true
687
+ ```
688
+
689
+ > **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.
690
+
691
+ ### Basic Setup (main.ts)
692
+
693
+ ```typescript
694
+ import { createApp } from 'vue'
695
+ import { createPinia } from 'pinia'
696
+ import { initEenToolkit } from 'een-api-toolkit'
697
+ import App from './App.vue'
698
+
699
+ const app = createApp(App)
700
+ const pinia = createPinia()
701
+
702
+ // IMPORTANT: Install Pinia BEFORE initializing the toolkit
703
+ app.use(pinia)
704
+
705
+ // Now initialize the toolkit (Pinia must already be installed)
706
+ initEenToolkit({
707
+ proxyUrl: import.meta.env.VITE_PROXY_URL,
708
+ clientId: import.meta.env.VITE_EEN_CLIENT_ID,
709
+ redirectUri: import.meta.env.VITE_REDIRECT_URI,
710
+ debug: import.meta.env.VITE_DEBUG === 'true'
711
+ })
712
+
713
+ app.mount('#app')
714
+ ```
715
+
716
+ ### OAuth Callback Route
717
+
718
+ ```typescript
719
+ // router/index.ts
720
+ {
721
+ path: '/callback',
722
+ component: () => import('./views/Callback.vue')
723
+ }
724
+ ```
725
+
726
+ ```vue
727
+ <!-- views/Callback.vue -->
728
+ <script setup>
729
+ import { onMounted } from 'vue'
730
+ import { useRouter } from 'vue-router'
731
+ import { handleAuthCallback } from 'een-api-toolkit'
732
+
733
+ const router = useRouter()
734
+
735
+ onMounted(async () => {
736
+ const url = new URL(window.location.href)
737
+ const code = url.searchParams.get('code')
738
+ const state = url.searchParams.get('state')
739
+
740
+ if (!code || !state) {
741
+ router.push('/login?error=missing_params')
742
+ return
743
+ }
744
+
745
+ const { error } = await handleAuthCallback(code, state)
746
+
747
+ if (error) {
748
+ router.push(`/login?error=${error.code}`)
749
+ return
750
+ }
751
+
752
+ router.push('/dashboard')
753
+ })
754
+ </script>
755
+
756
+ <template>
757
+ <div>Authenticating...</div>
758
+ </template>
759
+ ```
760
+
761
+ ---
762
+
763
+ ## External Resources
764
+
765
+ - [Eagle Eye Networks Developer Portal](https://developer.eagleeyenetworks.com)
766
+ - [EEN API v3.0 Reference](https://developer.eagleeyenetworks.com/reference/using-the-api)
767
+ - [GitHub Repository](https://github.com/klaushofrichter/een-api-toolkit)