@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.
Files changed (41) hide show
  1. package/README.md +315 -0
  2. package/dist/adapters/angular.d.ts +56 -0
  3. package/dist/adapters/angular.js +207 -0
  4. package/dist/adapters/next.d.ts +36 -0
  5. package/dist/adapters/next.js +120 -0
  6. package/dist/adapters/nuxt.d.ts +36 -0
  7. package/dist/adapters/nuxt.js +190 -0
  8. package/dist/adapters/svelte.d.ts +39 -0
  9. package/dist/adapters/svelte.js +134 -0
  10. package/dist/core/auth.d.ts +3 -0
  11. package/dist/core/auth.js +84 -0
  12. package/dist/core/client.d.ts +5 -0
  13. package/dist/core/client.js +37 -0
  14. package/dist/core/socket.d.ts +2 -0
  15. package/dist/core/socket.js +62 -0
  16. package/dist/core/sync.d.ts +3 -0
  17. package/dist/core/sync.js +340 -0
  18. package/dist/core/types.d.ts +73 -0
  19. package/dist/core/types.js +1 -0
  20. package/dist/index.d.ts +11 -0
  21. package/dist/index.js +14 -0
  22. package/examples/angular-app/dropbox-sync.service.ts +244 -0
  23. package/examples/next-app/api-routes.ts +109 -0
  24. package/examples/next-app/dropbox-client.ts +122 -0
  25. package/examples/nuxt-app/api-routes.ts +26 -0
  26. package/examples/nuxt-app/dropbox-plugin.ts +15 -0
  27. package/examples/nuxt-app/nuxt.config.ts +23 -0
  28. package/examples/svelte-app/dropbox-store.ts +174 -0
  29. package/examples/svelte-app/routes.server.ts +120 -0
  30. package/package.json +66 -0
  31. package/src/adapters/angular.ts +217 -0
  32. package/src/adapters/next.ts +155 -0
  33. package/src/adapters/nuxt.ts +270 -0
  34. package/src/adapters/svelte.ts +168 -0
  35. package/src/core/auth.ts +148 -0
  36. package/src/core/client.ts +52 -0
  37. package/src/core/socket.ts +73 -0
  38. package/src/core/sync.ts +476 -0
  39. package/src/core/types.ts +83 -0
  40. package/src/index.ts +32 -0
  41. 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
+ })