@rsweeten/dropbox-sync 0.1.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/README.md +315 -0
- package/dist/adapters/angular.d.ts +56 -0
- package/dist/adapters/angular.js +207 -0
- package/dist/adapters/next.d.ts +36 -0
- package/dist/adapters/next.js +120 -0
- package/dist/adapters/nuxt.d.ts +36 -0
- package/dist/adapters/nuxt.js +190 -0
- package/dist/adapters/svelte.d.ts +39 -0
- package/dist/adapters/svelte.js +134 -0
- package/dist/core/auth.d.ts +3 -0
- package/dist/core/auth.js +84 -0
- package/dist/core/client.d.ts +5 -0
- package/dist/core/client.js +37 -0
- package/dist/core/socket.d.ts +2 -0
- package/dist/core/socket.js +62 -0
- package/dist/core/sync.d.ts +3 -0
- package/dist/core/sync.js +340 -0
- package/dist/core/types.d.ts +73 -0
- package/dist/core/types.js +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +14 -0
- package/examples/angular-app/dropbox-sync.service.ts +244 -0
- package/examples/next-app/api-routes.ts +109 -0
- package/examples/next-app/dropbox-client.ts +122 -0
- package/examples/nuxt-app/api-routes.ts +26 -0
- package/examples/nuxt-app/dropbox-plugin.ts +15 -0
- package/examples/nuxt-app/nuxt.config.ts +23 -0
- package/examples/svelte-app/dropbox-store.ts +174 -0
- package/examples/svelte-app/routes.server.ts +120 -0
- package/package.json +66 -0
- package/src/adapters/angular.ts +217 -0
- package/src/adapters/next.ts +155 -0
- package/src/adapters/nuxt.ts +270 -0
- package/src/adapters/svelte.ts +168 -0
- package/src/core/auth.ts +148 -0
- package/src/core/client.ts +52 -0
- package/src/core/socket.ts +73 -0
- package/src/core/sync.ts +476 -0
- package/src/core/types.ts +83 -0
- package/src/index.ts +32 -0
- package/tsconfig.json +16 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
// Angular service implementation for Dropbox sync
|
2
|
+
import { Injectable } from '@angular/core'
|
3
|
+
import { BehaviorSubject, Observable, of, fromEvent } from 'rxjs'
|
4
|
+
import { catchError, map, tap } from 'rxjs/operators'
|
5
|
+
import { HttpClient } from '@angular/common/http'
|
6
|
+
// For a private module, you would import from your private registry or local path
|
7
|
+
// import { DropboxSyncService, getCredentialsFromEnvironment } from '@yourcompany/dropbox-sync';
|
8
|
+
import { DropboxSyncService, getCredentialsFromEnvironment } from 'dropbox-sync'
|
9
|
+
import { environment } from '../environments/environment'
|
10
|
+
|
11
|
+
@Injectable({
|
12
|
+
providedIn: 'root',
|
13
|
+
})
|
14
|
+
export class DropboxService {
|
15
|
+
// State observables
|
16
|
+
private _connected = new BehaviorSubject<boolean>(false)
|
17
|
+
private _syncing = new BehaviorSubject<boolean>(false)
|
18
|
+
private _progress = new BehaviorSubject<number>(0)
|
19
|
+
private _message = new BehaviorSubject<string>('')
|
20
|
+
private _error = new BehaviorSubject<string | null>(null)
|
21
|
+
|
22
|
+
// Public observable properties
|
23
|
+
readonly connected$ = this._connected.asObservable()
|
24
|
+
readonly syncing$ = this._syncing.asObservable()
|
25
|
+
readonly progress$ = this._progress.asObservable()
|
26
|
+
readonly message$ = this._message.asObservable()
|
27
|
+
readonly error$ = this._error.asObservable()
|
28
|
+
|
29
|
+
private syncStats = {
|
30
|
+
total: 0,
|
31
|
+
uploads: 0,
|
32
|
+
downloads: 0,
|
33
|
+
completed: 0,
|
34
|
+
}
|
35
|
+
|
36
|
+
constructor(
|
37
|
+
private http: HttpClient,
|
38
|
+
private dropboxSyncService: DropboxSyncService
|
39
|
+
) {
|
40
|
+
// Initialize the Dropbox sync client
|
41
|
+
this.initialize()
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Initialize the Dropbox sync client and check connection status
|
46
|
+
*/
|
47
|
+
initialize(): void {
|
48
|
+
// Get credentials from environment and localStorage
|
49
|
+
const credentials = getCredentialsFromEnvironment(environment)
|
50
|
+
|
51
|
+
// Initialize the service
|
52
|
+
this.dropboxSyncService.initialize(credentials)
|
53
|
+
|
54
|
+
// Check connection status
|
55
|
+
this.checkConnection().subscribe()
|
56
|
+
|
57
|
+
// Set up socket event listeners
|
58
|
+
this.setupSocketEventListeners()
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Check if connected to Dropbox
|
63
|
+
*/
|
64
|
+
checkConnection(): Observable<boolean> {
|
65
|
+
return this.http
|
66
|
+
.get<{ connected: boolean }>('/api/dropbox/status')
|
67
|
+
.pipe(
|
68
|
+
map((response) => response.connected),
|
69
|
+
tap((connected) => this._connected.next(connected)),
|
70
|
+
catchError((error) => {
|
71
|
+
console.error('Error checking Dropbox connection:', error)
|
72
|
+
this._connected.next(false)
|
73
|
+
return of(false)
|
74
|
+
})
|
75
|
+
)
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Start the OAuth flow to connect to Dropbox
|
80
|
+
*/
|
81
|
+
connect(): void {
|
82
|
+
window.location.href = '/api/dropbox/auth/start'
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Disconnect from Dropbox
|
87
|
+
*/
|
88
|
+
disconnect(): Observable<boolean> {
|
89
|
+
return this.http
|
90
|
+
.post<{ success: boolean }>('/api/dropbox/logout', {})
|
91
|
+
.pipe(
|
92
|
+
tap((response) => {
|
93
|
+
if (response.success) {
|
94
|
+
this._connected.next(false)
|
95
|
+
localStorage.removeItem('dropbox_access_token')
|
96
|
+
localStorage.removeItem('dropbox_refresh_token')
|
97
|
+
localStorage.removeItem('dropbox_connected')
|
98
|
+
}
|
99
|
+
}),
|
100
|
+
map((response) => response.success),
|
101
|
+
catchError((error) => {
|
102
|
+
console.error('Error disconnecting from Dropbox:', error)
|
103
|
+
return of(false)
|
104
|
+
})
|
105
|
+
)
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Start syncing files with Dropbox
|
110
|
+
*/
|
111
|
+
startSync(options?: { localDir?: string; dropboxDir?: string }): void {
|
112
|
+
try {
|
113
|
+
// Reset state
|
114
|
+
this._syncing.next(true)
|
115
|
+
this._progress.next(0)
|
116
|
+
this._message.next('Initializing Dropbox sync...')
|
117
|
+
this._error.next(null)
|
118
|
+
|
119
|
+
// Get socket from the sync service and emit sync event
|
120
|
+
const client = this.dropboxSyncService.getClient()
|
121
|
+
client.socket.connect()
|
122
|
+
client.socket.emit('dropbox:sync', options)
|
123
|
+
} catch (error: any) {
|
124
|
+
console.error('Error starting Dropbox sync:', error)
|
125
|
+
this._error.next(error.message || 'Error starting synchronization')
|
126
|
+
this._syncing.next(false)
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Cancel ongoing sync process
|
132
|
+
*/
|
133
|
+
cancelSync(): void {
|
134
|
+
try {
|
135
|
+
const client = this.dropboxSyncService.getClient()
|
136
|
+
client.sync.cancelSync()
|
137
|
+
this._syncing.next(false)
|
138
|
+
this._message.next('Sync cancelled by user')
|
139
|
+
} catch (error: any) {
|
140
|
+
console.error('Error cancelling sync:', error)
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* Check which files need to be synced without performing sync
|
146
|
+
*/
|
147
|
+
checkSyncNeeded(options?: {
|
148
|
+
localDir?: string
|
149
|
+
dropboxDir?: string
|
150
|
+
}): Observable<{ upload: number; download: number }> {
|
151
|
+
try {
|
152
|
+
const client = this.dropboxSyncService.getClient()
|
153
|
+
|
154
|
+
return this.dropboxSyncService.createSyncQueue(options).pipe(
|
155
|
+
map(({ uploadQueue, downloadQueue }) => ({
|
156
|
+
upload: uploadQueue.length,
|
157
|
+
download: downloadQueue.length,
|
158
|
+
})),
|
159
|
+
catchError((error) => {
|
160
|
+
console.error('Error checking sync status:', error)
|
161
|
+
return of({ upload: 0, download: 0 })
|
162
|
+
})
|
163
|
+
)
|
164
|
+
} catch (error) {
|
165
|
+
console.error('Error creating sync queue:', error)
|
166
|
+
return of({ upload: 0, download: 0 })
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* Set up socket event listeners for real-time sync updates
|
172
|
+
*/
|
173
|
+
private setupSocketEventListeners(): void {
|
174
|
+
try {
|
175
|
+
const client = this.dropboxSyncService.getClient()
|
176
|
+
const socketProgress =
|
177
|
+
this.dropboxSyncService.setupSocketListeners()
|
178
|
+
|
179
|
+
// Subscribe to the socket events observable
|
180
|
+
socketProgress.subscribe({
|
181
|
+
next: (data: any) => {
|
182
|
+
switch (data.type) {
|
183
|
+
case 'progress':
|
184
|
+
this._progress.next(data.data.progress)
|
185
|
+
this._message.next(data.data.message)
|
186
|
+
break
|
187
|
+
|
188
|
+
case 'queue':
|
189
|
+
this.syncStats = {
|
190
|
+
total: data.data.total,
|
191
|
+
uploads: data.data.totalUploads,
|
192
|
+
downloads: data.data.totalDownloads,
|
193
|
+
completed: 0,
|
194
|
+
}
|
195
|
+
break
|
196
|
+
|
197
|
+
case 'complete':
|
198
|
+
this._progress.next(100)
|
199
|
+
this._message.next(data.data.message)
|
200
|
+
this._syncing.next(false)
|
201
|
+
|
202
|
+
if (data.data.stats) {
|
203
|
+
this.syncStats.completed = this.syncStats.total
|
204
|
+
}
|
205
|
+
break
|
206
|
+
|
207
|
+
case 'error':
|
208
|
+
this._error.next(data.data.message)
|
209
|
+
if (!data.data.continue) {
|
210
|
+
this._syncing.next(false)
|
211
|
+
}
|
212
|
+
break
|
213
|
+
}
|
214
|
+
},
|
215
|
+
error: (error) => {
|
216
|
+
this._error.next(
|
217
|
+
typeof error === 'string'
|
218
|
+
? error
|
219
|
+
: 'An error occurred during synchronization'
|
220
|
+
)
|
221
|
+
this._syncing.next(false)
|
222
|
+
},
|
223
|
+
complete: () => {
|
224
|
+
// Sync process completed successfully
|
225
|
+
console.log('Dropbox sync process completed')
|
226
|
+
},
|
227
|
+
})
|
228
|
+
} catch (error) {
|
229
|
+
console.error('Error setting up socket listeners:', error)
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
/**
|
234
|
+
* Get current sync statistics
|
235
|
+
*/
|
236
|
+
getSyncStats(): {
|
237
|
+
total: number
|
238
|
+
uploads: number
|
239
|
+
downloads: number
|
240
|
+
completed: number
|
241
|
+
} {
|
242
|
+
return { ...this.syncStats }
|
243
|
+
}
|
244
|
+
}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
// Example Next.js API routes implementation
|
2
|
+
import { createNextDropboxApiHandlers, handleOAuthCallback } from 'dropbox-sync'
|
3
|
+
import { Server } from 'socket.io'
|
4
|
+
import type { NextApiRequest, NextApiResponse } from 'next'
|
5
|
+
|
6
|
+
// Create the API handlers
|
7
|
+
const dropboxHandlers = createNextDropboxApiHandlers()
|
8
|
+
|
9
|
+
// Status endpoint
|
10
|
+
export async function GET_status() {
|
11
|
+
return dropboxHandlers.status()
|
12
|
+
}
|
13
|
+
|
14
|
+
// OAuth start endpoint
|
15
|
+
export async function GET_oauthStart() {
|
16
|
+
return dropboxHandlers.oauthStart()
|
17
|
+
}
|
18
|
+
|
19
|
+
// OAuth callback endpoint
|
20
|
+
export async function GET_oauthCallback(req: Request) {
|
21
|
+
return handleOAuthCallback(req)
|
22
|
+
}
|
23
|
+
|
24
|
+
// Logout endpoint
|
25
|
+
export async function POST_logout() {
|
26
|
+
return dropboxHandlers.logout()
|
27
|
+
}
|
28
|
+
|
29
|
+
// Socket.IO setup for App Router in Next.js 13+
|
30
|
+
let io: Server
|
31
|
+
|
32
|
+
export async function setupSocketIO(res: NextApiResponse) {
|
33
|
+
if (!io) {
|
34
|
+
// @ts-ignore - NextApiResponse is compatible but TypeScript doesn't know
|
35
|
+
const httpServer = res.socket?.server
|
36
|
+
|
37
|
+
if (httpServer && !httpServer.io) {
|
38
|
+
io = new Server(httpServer)
|
39
|
+
httpServer.io = io
|
40
|
+
|
41
|
+
// Handle Dropbox sync events
|
42
|
+
io.on('connection', (socket) => {
|
43
|
+
console.log('Client connected:', socket.id)
|
44
|
+
|
45
|
+
// Handle sync start request
|
46
|
+
socket.on('dropbox:sync', async () => {
|
47
|
+
try {
|
48
|
+
// Emit queue information
|
49
|
+
socket.emit('sync:queue', {
|
50
|
+
total: 10, // This would be dynamic in real implementation
|
51
|
+
totalUploads: 5,
|
52
|
+
totalDownloads: 5,
|
53
|
+
})
|
54
|
+
|
55
|
+
// Simulate sync process (in real app, this would use the Dropbox client)
|
56
|
+
let progress = 0
|
57
|
+
const interval = setInterval(() => {
|
58
|
+
progress += 10
|
59
|
+
|
60
|
+
if (progress <= 100) {
|
61
|
+
socket.emit('sync:progress', {
|
62
|
+
progress,
|
63
|
+
message: `Processing files... ${progress}%`,
|
64
|
+
type:
|
65
|
+
progress % 20 === 0
|
66
|
+
? 'upload'
|
67
|
+
: 'download',
|
68
|
+
})
|
69
|
+
}
|
70
|
+
|
71
|
+
if (progress >= 100) {
|
72
|
+
clearInterval(interval)
|
73
|
+
socket.emit('sync:complete', {
|
74
|
+
message: 'Sync completed successfully',
|
75
|
+
stats: {
|
76
|
+
uploaded: 5,
|
77
|
+
downloaded: 5,
|
78
|
+
},
|
79
|
+
})
|
80
|
+
}
|
81
|
+
}, 500)
|
82
|
+
} catch (error: any) {
|
83
|
+
console.error('Error syncing with Dropbox:', error)
|
84
|
+
socket.emit('sync:error', {
|
85
|
+
message:
|
86
|
+
error.message ||
|
87
|
+
'An error occurred during sync',
|
88
|
+
})
|
89
|
+
}
|
90
|
+
})
|
91
|
+
|
92
|
+
// Handle sync cancel request
|
93
|
+
socket.on('sync:cancel', () => {
|
94
|
+
console.log('Sync cancelled by client:', socket.id)
|
95
|
+
socket.emit('sync:complete', {
|
96
|
+
message: 'Sync cancelled by user',
|
97
|
+
})
|
98
|
+
})
|
99
|
+
|
100
|
+
// Handle disconnect
|
101
|
+
socket.on('disconnect', () => {
|
102
|
+
console.log('Client disconnected:', socket.id)
|
103
|
+
})
|
104
|
+
})
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
return res
|
109
|
+
}
|
@@ -0,0 +1,122 @@
|
|
1
|
+
// Example Next.js implementation
|
2
|
+
// For a private module, you would import from your private registry or local path
|
3
|
+
// import { useNextDropboxSync, getCredentialsFromCookies } from '@yourcompany/dropbox-sync';
|
4
|
+
import { useNextDropboxSync, getCredentialsFromCookies } from 'dropbox-sync'
|
5
|
+
import { useState, useEffect } from 'react'
|
6
|
+
|
7
|
+
// Client-side component
|
8
|
+
export function useDropboxSync() {
|
9
|
+
const [isConnected, setIsConnected] = useState(false)
|
10
|
+
const [isSyncing, setIsSyncing] = useState(false)
|
11
|
+
const [progress, setProgress] = useState(0)
|
12
|
+
const [message, setMessage] = useState('')
|
13
|
+
const [error, setError] = useState<string | null>(null)
|
14
|
+
|
15
|
+
// Initialize the Dropbox client
|
16
|
+
const dropboxSync = useNextDropboxSync({
|
17
|
+
clientId: process.env.NEXT_PUBLIC_DROPBOX_APP_KEY || '',
|
18
|
+
// Access token will be added automatically in server components via cookies
|
19
|
+
})
|
20
|
+
|
21
|
+
// Check connection status
|
22
|
+
useEffect(() => {
|
23
|
+
async function checkConnection() {
|
24
|
+
try {
|
25
|
+
const response = await fetch('/api/dropbox/status')
|
26
|
+
const data = await response.json()
|
27
|
+
setIsConnected(data.connected)
|
28
|
+
} catch (error) {
|
29
|
+
console.error('Error checking connection:', error)
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
checkConnection()
|
34
|
+
}, [])
|
35
|
+
|
36
|
+
// Set up socket listeners for real-time updates
|
37
|
+
useEffect(() => {
|
38
|
+
if (!dropboxSync) return
|
39
|
+
|
40
|
+
// Connect to socket
|
41
|
+
dropboxSync.socket.connect()
|
42
|
+
|
43
|
+
// Listen for progress updates
|
44
|
+
dropboxSync.socket.on('sync:progress', (data) => {
|
45
|
+
setProgress(data.progress)
|
46
|
+
setMessage(data.message)
|
47
|
+
})
|
48
|
+
|
49
|
+
// Listen for completion
|
50
|
+
dropboxSync.socket.on('sync:complete', (data) => {
|
51
|
+
setProgress(100)
|
52
|
+
setMessage(data.message)
|
53
|
+
setIsSyncing(false)
|
54
|
+
})
|
55
|
+
|
56
|
+
// Listen for errors
|
57
|
+
dropboxSync.socket.on('sync:error', (data) => {
|
58
|
+
setError(data.message)
|
59
|
+
setIsSyncing(false)
|
60
|
+
})
|
61
|
+
|
62
|
+
// Cleanup on unmount
|
63
|
+
return () => {
|
64
|
+
dropboxSync.socket.off('sync:progress')
|
65
|
+
dropboxSync.socket.off('sync:complete')
|
66
|
+
dropboxSync.socket.off('sync:error')
|
67
|
+
}
|
68
|
+
}, [dropboxSync])
|
69
|
+
|
70
|
+
// Connect to Dropbox
|
71
|
+
const connectDropbox = () => {
|
72
|
+
window.location.href = '/api/dropbox/auth/start'
|
73
|
+
}
|
74
|
+
|
75
|
+
// Disconnect from Dropbox
|
76
|
+
const disconnectDropbox = async () => {
|
77
|
+
try {
|
78
|
+
await fetch('/api/dropbox/logout', { method: 'POST' })
|
79
|
+
setIsConnected(false)
|
80
|
+
} catch (error) {
|
81
|
+
console.error('Error disconnecting:', error)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
// Start sync process
|
86
|
+
const startSync = async () => {
|
87
|
+
try {
|
88
|
+
setIsSyncing(true)
|
89
|
+
setProgress(0)
|
90
|
+
setMessage('Initializing Dropbox sync...')
|
91
|
+
setError(null)
|
92
|
+
|
93
|
+
// Emit sync start event via Socket.IO
|
94
|
+
dropboxSync.socket.emit('dropbox:sync')
|
95
|
+
} catch (error: any) {
|
96
|
+
console.error('Error starting sync:', error)
|
97
|
+
setError(error.message || 'Error starting sync')
|
98
|
+
setIsSyncing(false)
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
// Cancel sync process
|
103
|
+
const cancelSync = () => {
|
104
|
+
if (!isSyncing) return
|
105
|
+
|
106
|
+
dropboxSync.sync.cancelSync()
|
107
|
+
setIsSyncing(false)
|
108
|
+
setMessage('Sync cancelled')
|
109
|
+
}
|
110
|
+
|
111
|
+
return {
|
112
|
+
isConnected,
|
113
|
+
isSyncing,
|
114
|
+
progress,
|
115
|
+
message,
|
116
|
+
error,
|
117
|
+
connectDropbox,
|
118
|
+
disconnectDropbox,
|
119
|
+
startSync,
|
120
|
+
cancelSync,
|
121
|
+
}
|
122
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
// Example Nuxt API routes for Dropbox integration
|
2
|
+
import { createNuxtApiHandlers } from '../../src'
|
3
|
+
import { defineEventHandler } from 'h3'
|
4
|
+
|
5
|
+
// Create the Dropbox API handlers
|
6
|
+
const dropboxHandlers = createNuxtApiHandlers()
|
7
|
+
|
8
|
+
// Status endpoint
|
9
|
+
export const statusHandler = defineEventHandler(async (event) => {
|
10
|
+
return await dropboxHandlers.status(event)
|
11
|
+
})
|
12
|
+
|
13
|
+
// OAuth start endpoint
|
14
|
+
export const oauthStartHandler = defineEventHandler(async (event) => {
|
15
|
+
return await dropboxHandlers.oauthStart(event)
|
16
|
+
})
|
17
|
+
|
18
|
+
// OAuth callback endpoint
|
19
|
+
export const oauthCallbackHandler = defineEventHandler(async (event) => {
|
20
|
+
return await dropboxHandlers.oauthCallback(event)
|
21
|
+
})
|
22
|
+
|
23
|
+
// Logout endpoint
|
24
|
+
export const logoutHandler = defineEventHandler(async (event) => {
|
25
|
+
return await dropboxHandlers.logout(event)
|
26
|
+
})
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// Example Nuxt plugin for Dropbox sync
|
2
|
+
import { useNuxtDropboxSync, getNuxtCredentialsFromCookies } from '../../src'
|
3
|
+
import { defineNuxtPlugin } from 'nuxt/app'
|
4
|
+
|
5
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
6
|
+
// Create the Dropbox client
|
7
|
+
const dropboxClient = useNuxtDropboxSync()
|
8
|
+
|
9
|
+
// Provide the client to the app
|
10
|
+
return {
|
11
|
+
provide: {
|
12
|
+
dropbox: dropboxClient,
|
13
|
+
},
|
14
|
+
}
|
15
|
+
})
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { defineNuxtConfig } from 'nuxt/config'
|
2
|
+
|
3
|
+
// Example Nuxt configuration for Dropbox integration
|
4
|
+
export default defineNuxtConfig({
|
5
|
+
// Runtime config for Dropbox integration
|
6
|
+
runtimeConfig: {
|
7
|
+
// Private keys (server-only)
|
8
|
+
dropboxAppSecret: process.env.DROPBOX_APP_SECRET,
|
9
|
+
dropboxRedirectUri: process.env.DROPBOX_REDIRECT_URI,
|
10
|
+
|
11
|
+
// Public keys (exposed to the client)
|
12
|
+
public: {
|
13
|
+
dropboxAppKey: process.env.DROPBOX_APP_KEY,
|
14
|
+
appUrl: process.env.APP_URL || 'http://localhost:3000',
|
15
|
+
},
|
16
|
+
},
|
17
|
+
|
18
|
+
// Auto-import components
|
19
|
+
components: true,
|
20
|
+
|
21
|
+
// Register the Dropbox plugin
|
22
|
+
plugins: ['~/plugins/dropbox.ts'],
|
23
|
+
})
|