@veristone/nuxt-v-app 0.1.0 → 0.1.1

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.
@@ -207,12 +207,28 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
207
207
  showError(title, message)
208
208
  }
209
209
 
210
- // Get auth token for mutations
210
+ /**
211
+ * Get auth token from cookies
212
+ * Reads the stack_auth_access cookie for Bearer token
213
+ */
214
+ const getAuthToken = (): string | null => {
215
+ const tokenCookie = useCookie('stack_auth_access')
216
+ return tokenCookie.value || null
217
+ }
218
+
219
+ // Get auth headers with Bearer token
211
220
  const getAuthHeaders = () => {
212
- return {
221
+ const headers: Record<string, string> = {
213
222
  'Content-Type': 'application/json',
214
223
  'X-Client-Version': 'v-app-1.0'
215
224
  }
225
+
226
+ const token = getAuthToken()
227
+ if (token) {
228
+ headers['Authorization'] = `Bearer ${token}`
229
+ }
230
+
231
+ return headers
216
232
  }
217
233
 
218
234
  // Get base URL
@@ -342,11 +358,12 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
342
358
  })
343
359
  }
344
360
 
345
- const result = await $fetch<T>(`${getBaseUrl()}${constructUrl(endpoint)}`, {
346
- method: 'POST',
347
- headers: getMutationHeaders(body),
348
- body
349
- })
361
+ const result = await $fetch<T>(constructUrl(endpoint), {
362
+ method: 'POST',
363
+ baseURL: getBaseUrl(),
364
+ headers: getMutationHeaders(body),
365
+ body
366
+ })
350
367
 
351
368
  notifyOk('Success', 'Record created successfully.')
352
369
  if (onCreated) onCreated(result)
@@ -394,11 +411,12 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
394
411
  mergedParams.order = sortConfig.value.direction
395
412
  }
396
413
 
397
- const response = await $fetch<any>(`${getBaseUrl()}${constructUrl(endpoint)}`, {
398
- method: 'GET',
399
- headers: getAuthHeaders(),
400
- params: mergedParams
401
- })
414
+ const response = await $fetch<any>(constructUrl(endpoint), {
415
+ method: 'GET',
416
+ baseURL: getBaseUrl(),
417
+ headers: getAuthHeaders(),
418
+ params: mergedParams
419
+ })
402
420
 
403
421
  // Handle paginated responses: { data: [...], pagination: {...} }
404
422
  // Return just the data array if response is paginated, otherwise return as-is
@@ -429,10 +447,11 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
429
447
  lastError.value = null
430
448
 
431
449
  try {
432
- const result = await $fetch<T>(`${getBaseUrl()}${constructUrl(endpoint, id)}`, {
433
- method: 'GET',
434
- headers: getAuthHeaders()
435
- })
450
+ const result = await $fetch<T>(constructUrl(endpoint, id), {
451
+ method: 'GET',
452
+ baseURL: getBaseUrl(),
453
+ headers: getAuthHeaders()
454
+ })
436
455
 
437
456
  detailData.value = result
438
457
  syncFormData(result)
@@ -464,11 +483,12 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
464
483
  })
465
484
  }
466
485
 
467
- const result = await $fetch<T>(`${getBaseUrl()}${constructUrl(endpoint, id)}`, {
468
- method: 'PUT',
469
- headers: getMutationHeaders(body),
470
- body
471
- })
486
+ const result = await $fetch<T>(constructUrl(endpoint, id), {
487
+ method: 'PUT',
488
+ baseURL: getBaseUrl(),
489
+ headers: getMutationHeaders(body),
490
+ body
491
+ })
472
492
 
473
493
  detailData.value = result
474
494
  notifyOk('Saved', 'Changes saved successfully.')
@@ -502,11 +522,12 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
502
522
  })
503
523
  }
504
524
 
505
- const result = await $fetch<T>(`${getBaseUrl()}${constructUrl(endpoint, id)}`, {
506
- method: 'PATCH',
507
- headers: getMutationHeaders(body),
508
- body
509
- })
525
+ const result = await $fetch<T>(constructUrl(endpoint, id), {
526
+ method: 'PATCH',
527
+ baseURL: getBaseUrl(),
528
+ headers: getMutationHeaders(body),
529
+ body
530
+ })
510
531
 
511
532
  detailData.value = result
512
533
  notifyOk('Saved', 'Record updated.')
@@ -528,10 +549,11 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
528
549
  isDeleting.value = true
529
550
 
530
551
  try {
531
- await $fetch(`${getBaseUrl()}${constructUrl(endpoint, id)}`, {
532
- method: 'DELETE',
533
- headers: getAuthHeaders()
534
- })
552
+ await $fetch(constructUrl(endpoint, id), {
553
+ method: 'DELETE',
554
+ baseURL: getBaseUrl(),
555
+ headers: getAuthHeaders()
556
+ })
535
557
 
536
558
  notifyOk('Deleted', 'Record removed permanently.')
537
559
  if (onDeleted) onDeleted()
@@ -559,11 +581,12 @@ export const useVCrud = <T = any>(endpoint: string, options: VCrudOptions<T> = {
559
581
  isDeleting.value = true
560
582
 
561
583
  try {
562
- await $fetch(`${getBaseUrl()}${constructUrl(endpoint)}/bulk-delete`, {
563
- method: 'DELETE',
564
- headers: getAuthHeaders(),
565
- body: { ids }
566
- })
584
+ await $fetch(constructUrl(endpoint) + '/bulk-delete', {
585
+ method: 'DELETE',
586
+ baseURL: getBaseUrl(),
587
+ headers: getAuthHeaders(),
588
+ body: { ids }
589
+ })
567
590
 
568
591
  notifyOk('Batch Deleted', `${ids.length} records removed.`)
569
592
  if (onDeleted) onDeleted()
@@ -5,6 +5,31 @@
5
5
  import type { UseFetchOptions } from 'nuxt/app'
6
6
  import { useVToast } from './useVToast'
7
7
 
8
+ /**
9
+ * Get auth token from cookies
10
+ * Reads the stack_auth_access cookie for Bearer token
11
+ */
12
+ function getAuthToken(): string | null {
13
+ const tokenCookie = useCookie('stack_auth_access')
14
+ return tokenCookie.value || null
15
+ }
16
+
17
+ /**
18
+ * Get default auth headers including Bearer token
19
+ */
20
+ function getAuthHeaders(): Record<string, string> {
21
+ const headers: Record<string, string> = {
22
+ 'X-Client-Version': 'v-app-1.0'
23
+ }
24
+
25
+ const token = getAuthToken()
26
+ if (token) {
27
+ headers['Authorization'] = `Bearer ${token}`
28
+ }
29
+
30
+ return headers
31
+ }
32
+
8
33
  export async function useVFetch<T>(
9
34
  endpoint: string,
10
35
  opts: UseFetchOptions<T> = {},
@@ -13,13 +38,13 @@ export async function useVFetch<T>(
13
38
  const { error: notifyError } = useVToast()
14
39
  const runtimeConfig = useRuntimeConfig()
15
40
 
16
- // Construct Base Config
41
+ // Construct Base Config with auth headers
17
42
  const fetchConfig: UseFetchOptions<T> = {
18
43
  baseURL: (runtimeConfig.public.apiUrl as string) || '/',
19
44
  retry: 1,
20
45
  retryStatusCodes: [408, 409, 429, 500, 502, 503, 504], // Extended retry codes
21
46
  headers: {
22
- 'X-Client-Version': 'v-app-1.0',
47
+ ...getAuthHeaders(),
23
48
  ...opts.headers
24
49
  },
25
50
 
@@ -0,0 +1,223 @@
1
+ <script setup lang="ts">
2
+ const endpoint = 'http://localhost:3002/admin/portfolio/loans'
3
+
4
+ const crud = useVCrud(endpoint, { immediate: false })
5
+
6
+ // Store results for each method
7
+ const results = ref<Record<string, any>>({})
8
+ const loading = ref<Record<string, boolean>>({})
9
+ const errors = ref<Record<string, string>>({})
10
+
11
+ // Test GET (findAll)
12
+ const testGet = async () => {
13
+ loading.value.get = true
14
+ errors.value.get = ''
15
+ try {
16
+ const data = await crud.findAll()
17
+ results.value.get = data
18
+ } catch (err: any) {
19
+ errors.value.get = err.message || 'Failed'
20
+ } finally {
21
+ loading.value.get = false
22
+ }
23
+ }
24
+
25
+ // Test GET single (findOne) - using first ID if available
26
+ const testGetOne = async () => {
27
+ const testId = 'test-id-123'
28
+ loading.value.getOne = true
29
+ errors.value.getOne = ''
30
+ try {
31
+ const data = await crud.findOne(testId)
32
+ results.value.getOne = data
33
+ } catch (err: any) {
34
+ errors.value.getOne = err.message || 'Failed'
35
+ } finally {
36
+ loading.value.getOne = false
37
+ }
38
+ }
39
+
40
+ // Test POST (create)
41
+ const testPost = async () => {
42
+ loading.value.post = true
43
+ errors.value.post = ''
44
+ try {
45
+ const data = await crud.create({ name: 'Test Loan', amount: 1000 })
46
+ results.value.post = data
47
+ } catch (err: any) {
48
+ errors.value.post = err.message || 'Failed'
49
+ } finally {
50
+ loading.value.post = false
51
+ }
52
+ }
53
+
54
+ // Test PUT (update)
55
+ const testPut = async () => {
56
+ const testId = 'test-id-123'
57
+ loading.value.put = true
58
+ errors.value.put = ''
59
+ try {
60
+ const data = await crud.update(testId, { name: 'Updated Loan', amount: 2000 })
61
+ results.value.put = data
62
+ } catch (err: any) {
63
+ errors.value.put = err.message || 'Failed'
64
+ } finally {
65
+ loading.value.put = false
66
+ }
67
+ }
68
+
69
+ // Test PATCH
70
+ const testPatch = async () => {
71
+ const testId = 'test-id-123'
72
+ loading.value.patch = true
73
+ errors.value.patch = ''
74
+ try {
75
+ const data = await crud.patch(testId, { amount: 1500 })
76
+ results.value.patch = data
77
+ } catch (err: any) {
78
+ errors.value.patch = err.message || 'Failed'
79
+ } finally {
80
+ loading.value.patch = false
81
+ }
82
+ }
83
+
84
+ // Test DELETE
85
+ const testDelete = async () => {
86
+ const testId = 'test-id-123'
87
+ loading.value.delete = true
88
+ errors.value.delete = ''
89
+ try {
90
+ const data = await crud.remove(testId)
91
+ results.value.delete = data
92
+ } catch (err: any) {
93
+ errors.value.delete = err.message || 'Failed'
94
+ } finally {
95
+ loading.value.delete = false
96
+ }
97
+ }
98
+
99
+ // Run all tests
100
+ const testAll = async () => {
101
+ await testGet()
102
+ await testGetOne()
103
+ await testPost()
104
+ await testPut()
105
+ await testPatch()
106
+ await testDelete()
107
+ }
108
+ </script>
109
+
110
+ <template>
111
+ <div class="p-8 space-y-8">
112
+ <div class="flex items-center justify-between">
113
+ <h1 class="text-2xl font-bold">API Methods Test</h1>
114
+ <button
115
+ @click="testAll"
116
+ class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
117
+ >
118
+ Test All Methods
119
+ </button>
120
+ </div>
121
+
122
+ <!-- GET (findAll) -->
123
+ <div class="border p-4 rounded">
124
+ <div class="flex items-center justify-between mb-4">
125
+ <h2 class="text-xl font-semibold">GET (findAll)</h2>
126
+ <button
127
+ @click="testGet"
128
+ :disabled="loading.get"
129
+ class="px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600 disabled:opacity-50"
130
+ >
131
+ {{ loading.get ? 'Loading...' : 'Test GET' }}
132
+ </button>
133
+ </div>
134
+ <div v-if="errors.get" class="text-red-500 mb-2">Error: {{ errors.get }}</div>
135
+ <pre v-if="results.get" class="text-sm bg-gray-100 p-4 rounded overflow-auto max-h-48">{{ JSON.stringify(results.get, null, 2) }}</pre>
136
+ </div>
137
+
138
+ <!-- GET One (findOne) -->
139
+ <div class="border p-4 rounded">
140
+ <div class="flex items-center justify-between mb-4">
141
+ <h2 class="text-xl font-semibold">GET Single (findOne)</h2>
142
+ <button
143
+ @click="testGetOne"
144
+ :disabled="loading.getOne"
145
+ class="px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600 disabled:opacity-50"
146
+ >
147
+ {{ loading.getOne ? 'Loading...' : 'Test GET One' }}
148
+ </button>
149
+ </div>
150
+ <div class="text-sm text-gray-500 mb-2">Testing with ID: test-id-123</div>
151
+ <div v-if="errors.getOne" class="text-red-500 mb-2">Error: {{ errors.getOne }}</div>
152
+ <pre v-if="results.getOne" class="text-sm bg-gray-100 p-4 rounded overflow-auto max-h-48">{{ JSON.stringify(results.getOne, null, 2) }}</pre>
153
+ </div>
154
+
155
+ <!-- POST (create) -->
156
+ <div class="border p-4 rounded">
157
+ <div class="flex items-center justify-between mb-4">
158
+ <h2 class="text-xl font-semibold">POST (create)</h2>
159
+ <button
160
+ @click="testPost"
161
+ :disabled="loading.post"
162
+ class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50"
163
+ >
164
+ {{ loading.post ? 'Loading...' : 'Test POST' }}
165
+ </button>
166
+ </div>
167
+ <div class="text-sm text-gray-500 mb-2">Payload: { name: 'Test Loan', amount: 1000 }</div>
168
+ <div v-if="errors.post" class="text-red-500 mb-2">Error: {{ errors.post }}</div>
169
+ <pre v-if="results.post" class="text-sm bg-gray-100 p-4 rounded overflow-auto max-h-48">{{ JSON.stringify(results.post, null, 2) }}</pre>
170
+ </div>
171
+
172
+ <!-- PUT (update) -->
173
+ <div class="border p-4 rounded">
174
+ <div class="flex items-center justify-between mb-4">
175
+ <h2 class="text-xl font-semibold">PUT (update)</h2>
176
+ <button
177
+ @click="testPut"
178
+ :disabled="loading.put"
179
+ class="px-3 py-1 bg-yellow-500 text-white rounded hover:bg-yellow-600 disabled:opacity-50"
180
+ >
181
+ {{ loading.put ? 'Loading...' : 'Test PUT' }}
182
+ </button>
183
+ </div>
184
+ <div class="text-sm text-gray-500 mb-2">Testing with ID: test-id-123 | Payload: { name: 'Updated Loan', amount: 2000 }</div>
185
+ <div v-if="errors.put" class="text-red-500 mb-2">Error: {{ errors.put }}</div>
186
+ <pre v-if="results.put" class="text-sm bg-gray-100 p-4 rounded overflow-auto max-h-48">{{ JSON.stringify(results.put, null, 2) }}</pre>
187
+ </div>
188
+
189
+ <!-- PATCH -->
190
+ <div class="border p-4 rounded">
191
+ <div class="flex items-center justify-between mb-4">
192
+ <h2 class="text-xl font-semibold">PATCH</h2>
193
+ <button
194
+ @click="testPatch"
195
+ :disabled="loading.patch"
196
+ class="px-3 py-1 bg-purple-500 text-white rounded hover:bg-purple-600 disabled:opacity-50"
197
+ >
198
+ {{ loading.patch ? 'Loading...' : 'Test PATCH' }}
199
+ </button>
200
+ </div>
201
+ <div class="text-sm text-gray-500 mb-2">Testing with ID: test-id-123 | Payload: { amount: 1500 }</div>
202
+ <div v-if="errors.patch" class="text-red-500 mb-2">Error: {{ errors.patch }}</div>
203
+ <pre v-if="results.patch" class="text-sm bg-gray-100 p-4 rounded overflow-auto max-h-48">{{ JSON.stringify(results.patch, null, 2) }}</pre>
204
+ </div>
205
+
206
+ <!-- DELETE -->
207
+ <div class="border p-4 rounded">
208
+ <div class="flex items-center justify-between mb-4">
209
+ <h2 class="text-xl font-semibold">DELETE (remove)</h2>
210
+ <button
211
+ @click="testDelete"
212
+ :disabled="loading.delete"
213
+ class="px-3 py-1 bg-red-500 text-white rounded hover:bg-red-600 disabled:opacity-50"
214
+ >
215
+ {{ loading.delete ? 'Loading...' : 'Test DELETE' }}
216
+ </button>
217
+ </div>
218
+ <div class="text-sm text-gray-500 mb-2">Testing with ID: test-id-123</div>
219
+ <div v-if="errors.delete" class="text-red-500 mb-2">Error: {{ errors.delete }}</div>
220
+ <pre v-if="results.delete" class="text-sm bg-gray-100 p-4 rounded overflow-auto max-h-48">{{ JSON.stringify(results.delete, null, 2) }}</pre>
221
+ </div>
222
+ </div>
223
+ </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veristone/nuxt-v-app",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "description": "Veristone Nuxt App Layer - Shared components, composables, and layouts",
6
6
  "keywords": [