@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,155 @@
|
|
1
|
+
import { createDropboxSyncClient } from '../core/client'
|
2
|
+
import type { DropboxCredentials, DropboxSyncClient } from '../core/types'
|
3
|
+
import { cookies } from 'next/headers'
|
4
|
+
import { NextRequest, NextResponse } from 'next/server'
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Next.js-specific helper to create a Dropbox sync client
|
8
|
+
* Can be used in both client and server components
|
9
|
+
*/
|
10
|
+
export function useNextDropboxSync(
|
11
|
+
credentials: DropboxCredentials
|
12
|
+
): DropboxSyncClient {
|
13
|
+
return createDropboxSyncClient(credentials)
|
14
|
+
}
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Server-side helper to get credentials from Next.js cookies
|
18
|
+
*/
|
19
|
+
export async function getCredentialsFromCookies(): Promise<DropboxCredentials> {
|
20
|
+
const cookieStore = await cookies()
|
21
|
+
|
22
|
+
return {
|
23
|
+
clientId: process.env.DROPBOX_APP_KEY || '',
|
24
|
+
clientSecret: process.env.DROPBOX_APP_SECRET,
|
25
|
+
accessToken: cookieStore.get('dropbox_access_token')?.value,
|
26
|
+
refreshToken: cookieStore.get('dropbox_refresh_token')?.value,
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Server action to handle Dropbox OAuth callback
|
32
|
+
*/
|
33
|
+
export async function handleOAuthCallback(
|
34
|
+
request: NextRequest
|
35
|
+
): Promise<NextResponse> {
|
36
|
+
const url = new URL(request.url)
|
37
|
+
const code = url.searchParams.get('code')
|
38
|
+
|
39
|
+
if (!code) {
|
40
|
+
return NextResponse.redirect(new URL('/auth/error', request.url))
|
41
|
+
}
|
42
|
+
|
43
|
+
const redirectUri =
|
44
|
+
process.env.DROPBOX_REDIRECT_URI ||
|
45
|
+
`${
|
46
|
+
process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
|
47
|
+
}/api/dropbox/auth/callback`
|
48
|
+
|
49
|
+
const dropboxSync = createDropboxSyncClient({
|
50
|
+
clientId: process.env.DROPBOX_APP_KEY || '',
|
51
|
+
clientSecret: process.env.DROPBOX_APP_SECRET,
|
52
|
+
})
|
53
|
+
|
54
|
+
try {
|
55
|
+
const tokens = await dropboxSync.auth.exchangeCodeForToken(
|
56
|
+
code,
|
57
|
+
redirectUri
|
58
|
+
)
|
59
|
+
|
60
|
+
// Create response with redirect
|
61
|
+
const response = NextResponse.redirect(new URL('/', request.url))
|
62
|
+
|
63
|
+
// Set cookies with the tokens
|
64
|
+
response.cookies.set({
|
65
|
+
name: 'dropbox_access_token',
|
66
|
+
value: tokens.accessToken,
|
67
|
+
httpOnly: true,
|
68
|
+
secure: process.env.NODE_ENV === 'production',
|
69
|
+
maxAge: tokens.expiresAt
|
70
|
+
? (tokens.expiresAt - Date.now()) / 1000
|
71
|
+
: 14 * 24 * 60 * 60, // 14 days default
|
72
|
+
sameSite: 'lax',
|
73
|
+
path: '/',
|
74
|
+
})
|
75
|
+
|
76
|
+
if (tokens.refreshToken) {
|
77
|
+
response.cookies.set({
|
78
|
+
name: 'dropbox_refresh_token',
|
79
|
+
value: tokens.refreshToken,
|
80
|
+
httpOnly: true,
|
81
|
+
secure: process.env.NODE_ENV === 'production',
|
82
|
+
maxAge: 365 * 24 * 60 * 60, // 1 year
|
83
|
+
sameSite: 'lax',
|
84
|
+
path: '/',
|
85
|
+
})
|
86
|
+
}
|
87
|
+
|
88
|
+
// Set a non-httpOnly cookie to indicate connection status to the client
|
89
|
+
response.cookies.set({
|
90
|
+
name: 'dropbox_connected',
|
91
|
+
value: 'true',
|
92
|
+
secure: process.env.NODE_ENV === 'production',
|
93
|
+
maxAge: tokens.expiresAt
|
94
|
+
? (tokens.expiresAt - Date.now()) / 1000
|
95
|
+
: 14 * 24 * 60 * 60,
|
96
|
+
sameSite: 'lax',
|
97
|
+
path: '/',
|
98
|
+
})
|
99
|
+
|
100
|
+
return response
|
101
|
+
} catch (error) {
|
102
|
+
console.error('Error completing Dropbox OAuth flow:', error)
|
103
|
+
return NextResponse.redirect(new URL('/auth/error', request.url))
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Create API route handlers for a Next.js app
|
109
|
+
*/
|
110
|
+
export function createNextDropboxApiHandlers() {
|
111
|
+
return {
|
112
|
+
/**
|
113
|
+
* Handler for status check route
|
114
|
+
*/
|
115
|
+
async status() {
|
116
|
+
const cookieStore = await cookies()
|
117
|
+
const isConnected = !!cookieStore.get('dropbox_access_token')?.value
|
118
|
+
|
119
|
+
return NextResponse.json({ connected: isConnected })
|
120
|
+
},
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Handler for OAuth start route
|
124
|
+
*/
|
125
|
+
async oauthStart() {
|
126
|
+
const dropboxSync = createDropboxSyncClient({
|
127
|
+
clientId: process.env.DROPBOX_APP_KEY || '',
|
128
|
+
})
|
129
|
+
|
130
|
+
const redirectUri =
|
131
|
+
process.env.DROPBOX_REDIRECT_URI ||
|
132
|
+
`${
|
133
|
+
process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
|
134
|
+
}/api/dropbox/auth/callback`
|
135
|
+
|
136
|
+
const authUrl = await dropboxSync.auth.getAuthUrl(redirectUri)
|
137
|
+
|
138
|
+
return NextResponse.redirect(authUrl)
|
139
|
+
},
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Handler for logout route
|
143
|
+
*/
|
144
|
+
async logout() {
|
145
|
+
const response = NextResponse.json({ success: true })
|
146
|
+
|
147
|
+
// Clear all Dropbox-related cookies
|
148
|
+
response.cookies.delete('dropbox_access_token')
|
149
|
+
response.cookies.delete('dropbox_refresh_token')
|
150
|
+
response.cookies.delete('dropbox_connected')
|
151
|
+
|
152
|
+
return response
|
153
|
+
},
|
154
|
+
}
|
155
|
+
}
|
@@ -0,0 +1,270 @@
|
|
1
|
+
import { createDropboxSyncClient } from '../core/client'
|
2
|
+
import type { DropboxCredentials, DropboxSyncClient } from '../core/types'
|
3
|
+
import { useCookie, useRuntimeConfig } from 'nuxt/app'
|
4
|
+
import type { H3Event } from 'h3'
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Helper to safely access runtime config properties
|
8
|
+
*/
|
9
|
+
function getConfigValue<T>(obj: any, path: string, defaultValue: T): T {
|
10
|
+
const parts = path.split('.')
|
11
|
+
let current = obj
|
12
|
+
|
13
|
+
for (const part of parts) {
|
14
|
+
if (current === undefined || current === null) {
|
15
|
+
return defaultValue
|
16
|
+
}
|
17
|
+
current = current[part]
|
18
|
+
}
|
19
|
+
|
20
|
+
return (current as T) || defaultValue
|
21
|
+
}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Nuxt-specific helper for creating a Dropbox sync client
|
25
|
+
* Can be used in both client and server components
|
26
|
+
*/
|
27
|
+
export function useNuxtDropboxSync(
|
28
|
+
credentials?: Partial<DropboxCredentials>
|
29
|
+
): DropboxSyncClient {
|
30
|
+
// Get Nuxt runtime config (for client ID and secret)
|
31
|
+
const config = useRuntimeConfig()
|
32
|
+
|
33
|
+
// Create base credentials with defaults from runtime config
|
34
|
+
const baseCredentials: DropboxCredentials = {
|
35
|
+
clientId: getConfigValue<string>(config, 'public.dropboxAppKey', ''),
|
36
|
+
clientSecret: getConfigValue<string | undefined>(
|
37
|
+
config,
|
38
|
+
'dropboxAppSecret',
|
39
|
+
''
|
40
|
+
),
|
41
|
+
...credentials,
|
42
|
+
}
|
43
|
+
|
44
|
+
// On client-side, attempt to get tokens from cookies
|
45
|
+
if (process.client) {
|
46
|
+
const accessToken = useCookie('dropbox_access_token')
|
47
|
+
const refreshToken = useCookie('dropbox_refresh_token')
|
48
|
+
|
49
|
+
if (accessToken.value && !baseCredentials.accessToken) {
|
50
|
+
baseCredentials.accessToken = accessToken.value as string
|
51
|
+
}
|
52
|
+
|
53
|
+
if (refreshToken.value && !baseCredentials.refreshToken) {
|
54
|
+
baseCredentials.refreshToken = refreshToken.value as string
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
return createDropboxSyncClient(baseCredentials)
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Server-side helper to get credentials from Nuxt server event
|
63
|
+
*/
|
64
|
+
export function getCredentialsFromCookies(event: H3Event): DropboxCredentials {
|
65
|
+
const config = useRuntimeConfig()
|
66
|
+
|
67
|
+
// Get cookies from Nuxt server event
|
68
|
+
const accessToken = getCookie(event, 'dropbox_access_token')
|
69
|
+
const refreshToken = getCookie(event, 'dropbox_refresh_token')
|
70
|
+
|
71
|
+
return {
|
72
|
+
clientId: getConfigValue<string>(config, 'public.dropboxAppKey', ''),
|
73
|
+
clientSecret: getConfigValue<string | undefined>(
|
74
|
+
config,
|
75
|
+
'dropboxAppSecret',
|
76
|
+
''
|
77
|
+
),
|
78
|
+
accessToken,
|
79
|
+
refreshToken,
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Create API event handlers for a Nuxt app
|
85
|
+
*/
|
86
|
+
export function createNuxtApiHandlers() {
|
87
|
+
return {
|
88
|
+
/**
|
89
|
+
* Handler for status check endpoint
|
90
|
+
*/
|
91
|
+
async status(event: H3Event) {
|
92
|
+
const accessToken = getCookie(event, 'dropbox_access_token')
|
93
|
+
const isConnected = !!accessToken
|
94
|
+
|
95
|
+
return { connected: isConnected }
|
96
|
+
},
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Handler for OAuth start endpoint
|
100
|
+
*/
|
101
|
+
async oauthStart(event: H3Event) {
|
102
|
+
const config = useRuntimeConfig()
|
103
|
+
|
104
|
+
const dropboxSync = createDropboxSyncClient({
|
105
|
+
clientId: getConfigValue<string>(
|
106
|
+
config,
|
107
|
+
'public.dropboxAppKey',
|
108
|
+
''
|
109
|
+
),
|
110
|
+
})
|
111
|
+
|
112
|
+
const redirectUri =
|
113
|
+
getConfigValue<string>(config, 'dropboxRedirectUri', '') ||
|
114
|
+
`${getConfigValue<string>(
|
115
|
+
config,
|
116
|
+
'public.appUrl',
|
117
|
+
'http://localhost:3000'
|
118
|
+
)}/api/dropbox/auth/callback`
|
119
|
+
|
120
|
+
const authUrl = await dropboxSync.auth.getAuthUrl(redirectUri)
|
121
|
+
|
122
|
+
return sendRedirect(event, authUrl)
|
123
|
+
},
|
124
|
+
|
125
|
+
/**
|
126
|
+
* Handler for OAuth callback endpoint
|
127
|
+
*/
|
128
|
+
async oauthCallback(event: H3Event) {
|
129
|
+
const config = useRuntimeConfig()
|
130
|
+
|
131
|
+
// Get the authorization code from query parameters
|
132
|
+
const query = getQuery(event)
|
133
|
+
const code = query.code as string
|
134
|
+
|
135
|
+
if (!code) {
|
136
|
+
return sendRedirect(event, '/auth/error')
|
137
|
+
}
|
138
|
+
|
139
|
+
const redirectUri =
|
140
|
+
getConfigValue<string>(config, 'dropboxRedirectUri', '') ||
|
141
|
+
`${getConfigValue<string>(
|
142
|
+
config,
|
143
|
+
'public.appUrl',
|
144
|
+
'http://localhost:3000'
|
145
|
+
)}/api/dropbox/auth/callback`
|
146
|
+
|
147
|
+
const dropboxSync = createDropboxSyncClient({
|
148
|
+
clientId: getConfigValue<string>(
|
149
|
+
config,
|
150
|
+
'public.dropboxAppKey',
|
151
|
+
''
|
152
|
+
),
|
153
|
+
clientSecret: getConfigValue<string | undefined>(
|
154
|
+
config,
|
155
|
+
'dropboxAppSecret',
|
156
|
+
''
|
157
|
+
),
|
158
|
+
})
|
159
|
+
|
160
|
+
try {
|
161
|
+
const tokens = await dropboxSync.auth.exchangeCodeForToken(
|
162
|
+
code,
|
163
|
+
redirectUri
|
164
|
+
)
|
165
|
+
|
166
|
+
// Set cookies with the tokens
|
167
|
+
setCookie(event, 'dropbox_access_token', tokens.accessToken, {
|
168
|
+
httpOnly: true,
|
169
|
+
secure: process.env.NODE_ENV === 'production',
|
170
|
+
maxAge: tokens.expiresAt
|
171
|
+
? Math.floor((tokens.expiresAt - Date.now()) / 1000)
|
172
|
+
: 14 * 24 * 60 * 60, // 14 days default
|
173
|
+
sameSite: 'lax',
|
174
|
+
path: '/',
|
175
|
+
})
|
176
|
+
|
177
|
+
if (tokens.refreshToken) {
|
178
|
+
setCookie(
|
179
|
+
event,
|
180
|
+
'dropbox_refresh_token',
|
181
|
+
tokens.refreshToken,
|
182
|
+
{
|
183
|
+
httpOnly: true,
|
184
|
+
secure: process.env.NODE_ENV === 'production',
|
185
|
+
maxAge: 365 * 24 * 60 * 60, // 1 year
|
186
|
+
sameSite: 'lax',
|
187
|
+
path: '/',
|
188
|
+
}
|
189
|
+
)
|
190
|
+
}
|
191
|
+
|
192
|
+
// Set a non-httpOnly cookie to indicate connection status to the client
|
193
|
+
setCookie(event, 'dropbox_connected', 'true', {
|
194
|
+
secure: process.env.NODE_ENV === 'production',
|
195
|
+
maxAge: tokens.expiresAt
|
196
|
+
? Math.floor((tokens.expiresAt - Date.now()) / 1000)
|
197
|
+
: 14 * 24 * 60 * 60,
|
198
|
+
sameSite: 'lax',
|
199
|
+
path: '/',
|
200
|
+
})
|
201
|
+
|
202
|
+
return sendRedirect(event, '/')
|
203
|
+
} catch (error) {
|
204
|
+
console.error('Error completing Dropbox OAuth flow:', error)
|
205
|
+
return sendRedirect(event, '/auth/error')
|
206
|
+
}
|
207
|
+
},
|
208
|
+
|
209
|
+
/**
|
210
|
+
* Handler for logout endpoint
|
211
|
+
*/
|
212
|
+
async logout(event: H3Event) {
|
213
|
+
// Clear all Dropbox-related cookies
|
214
|
+
deleteCookie(event, 'dropbox_access_token')
|
215
|
+
deleteCookie(event, 'dropbox_refresh_token')
|
216
|
+
deleteCookie(event, 'dropbox_connected')
|
217
|
+
|
218
|
+
return { success: true }
|
219
|
+
},
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* Helper functions to work with Nuxt's H3Event
|
225
|
+
* These import statements need to be added to avoid reference errors
|
226
|
+
*/
|
227
|
+
const { getCookie, setCookie, deleteCookie } = useNuxtCookies()
|
228
|
+
const { getQuery, sendRedirect } = useNuxtServer()
|
229
|
+
|
230
|
+
/**
|
231
|
+
* Helper to access h3 cookie methods
|
232
|
+
*/
|
233
|
+
function useNuxtCookies() {
|
234
|
+
return {
|
235
|
+
getCookie: (event: H3Event, name: string) => {
|
236
|
+
// Import inside function to avoid module loading issues
|
237
|
+
const { getCookie } = require('h3')
|
238
|
+
return getCookie(event, name)
|
239
|
+
},
|
240
|
+
setCookie: (
|
241
|
+
event: H3Event,
|
242
|
+
name: string,
|
243
|
+
value: string,
|
244
|
+
options?: any
|
245
|
+
) => {
|
246
|
+
const { setCookie } = require('h3')
|
247
|
+
return setCookie(event, name, value, options)
|
248
|
+
},
|
249
|
+
deleteCookie: (event: H3Event, name: string, options?: any) => {
|
250
|
+
const { deleteCookie } = require('h3')
|
251
|
+
return deleteCookie(event, name, { ...options, path: '/' })
|
252
|
+
},
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
/**
|
257
|
+
* Helper to access h3 server methods
|
258
|
+
*/
|
259
|
+
function useNuxtServer() {
|
260
|
+
return {
|
261
|
+
getQuery: (event: H3Event) => {
|
262
|
+
const { getQuery } = require('h3')
|
263
|
+
return getQuery(event)
|
264
|
+
},
|
265
|
+
sendRedirect: (event: H3Event, location: string) => {
|
266
|
+
const { sendRedirect } = require('h3')
|
267
|
+
return sendRedirect(event, location)
|
268
|
+
},
|
269
|
+
}
|
270
|
+
}
|
@@ -0,0 +1,168 @@
|
|
1
|
+
import { createDropboxSyncClient } from '../core/client'
|
2
|
+
import type { DropboxCredentials, DropboxSyncClient } from '../core/types'
|
3
|
+
import type { Cookies } from '@sveltejs/kit'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* SvelteKit-specific helper for creating a Dropbox sync client
|
7
|
+
* Can be used in both client and server contexts
|
8
|
+
*/
|
9
|
+
export function useSvelteDropboxSync(
|
10
|
+
credentials: DropboxCredentials
|
11
|
+
): DropboxSyncClient {
|
12
|
+
return createDropboxSyncClient(credentials)
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Helper to get credentials from SvelteKit cookies
|
17
|
+
*/
|
18
|
+
export function getCredentialsFromCookies(
|
19
|
+
cookies: Cookies
|
20
|
+
): DropboxCredentials {
|
21
|
+
return {
|
22
|
+
clientId: process.env.DROPBOX_APP_KEY || '',
|
23
|
+
clientSecret: process.env.DROPBOX_APP_SECRET,
|
24
|
+
accessToken: cookies.get('dropbox_access_token'),
|
25
|
+
refreshToken: cookies.get('dropbox_refresh_token'),
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Create server-side handlers for SvelteKit
|
31
|
+
*/
|
32
|
+
export function createSvelteKitHandlers() {
|
33
|
+
return {
|
34
|
+
/**
|
35
|
+
* Handler for status check endpoint
|
36
|
+
*/
|
37
|
+
async status({ cookies }: { cookies: Cookies }) {
|
38
|
+
const isConnected = !!cookies.get('dropbox_access_token')
|
39
|
+
|
40
|
+
return new Response(JSON.stringify({ connected: isConnected }), {
|
41
|
+
status: 200,
|
42
|
+
headers: {
|
43
|
+
'Content-Type': 'application/json',
|
44
|
+
},
|
45
|
+
})
|
46
|
+
},
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Handler for OAuth start endpoint
|
50
|
+
*/
|
51
|
+
async oauthStart() {
|
52
|
+
const dropboxSync = createDropboxSyncClient({
|
53
|
+
clientId: process.env.DROPBOX_APP_KEY || '',
|
54
|
+
})
|
55
|
+
|
56
|
+
const redirectUri =
|
57
|
+
process.env.DROPBOX_REDIRECT_URI ||
|
58
|
+
`${
|
59
|
+
process.env.PUBLIC_APP_URL || 'http://localhost:5173'
|
60
|
+
}/api/dropbox/auth/callback`
|
61
|
+
|
62
|
+
const authUrl = await dropboxSync.auth.getAuthUrl(redirectUri)
|
63
|
+
|
64
|
+
return new Response(null, {
|
65
|
+
status: 302,
|
66
|
+
headers: {
|
67
|
+
Location: authUrl,
|
68
|
+
},
|
69
|
+
})
|
70
|
+
},
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Handler for OAuth callback endpoint
|
74
|
+
*/
|
75
|
+
async oauthCallback({ url, cookies }: { url: URL; cookies: Cookies }) {
|
76
|
+
const code = url.searchParams.get('code')
|
77
|
+
|
78
|
+
if (!code) {
|
79
|
+
return new Response(null, {
|
80
|
+
status: 302,
|
81
|
+
headers: {
|
82
|
+
Location: '/auth/error',
|
83
|
+
},
|
84
|
+
})
|
85
|
+
}
|
86
|
+
|
87
|
+
const redirectUri =
|
88
|
+
process.env.DROPBOX_REDIRECT_URI ||
|
89
|
+
`${
|
90
|
+
process.env.PUBLIC_APP_URL || 'http://localhost:5173'
|
91
|
+
}/api/dropbox/auth/callback`
|
92
|
+
|
93
|
+
const dropboxSync = createDropboxSyncClient({
|
94
|
+
clientId: process.env.DROPBOX_APP_KEY || '',
|
95
|
+
clientSecret: process.env.DROPBOX_APP_SECRET,
|
96
|
+
})
|
97
|
+
|
98
|
+
try {
|
99
|
+
const tokens = await dropboxSync.auth.exchangeCodeForToken(
|
100
|
+
code,
|
101
|
+
redirectUri
|
102
|
+
)
|
103
|
+
|
104
|
+
// Set cookies with the tokens
|
105
|
+
cookies.set('dropbox_access_token', tokens.accessToken, {
|
106
|
+
path: '/',
|
107
|
+
httpOnly: true,
|
108
|
+
secure: process.env.NODE_ENV === 'production',
|
109
|
+
maxAge: tokens.expiresAt
|
110
|
+
? Math.floor((tokens.expiresAt - Date.now()) / 1000)
|
111
|
+
: 14 * 24 * 60 * 60, // 14 days default
|
112
|
+
sameSite: 'lax',
|
113
|
+
})
|
114
|
+
|
115
|
+
if (tokens.refreshToken) {
|
116
|
+
cookies.set('dropbox_refresh_token', tokens.refreshToken, {
|
117
|
+
path: '/',
|
118
|
+
httpOnly: true,
|
119
|
+
secure: process.env.NODE_ENV === 'production',
|
120
|
+
maxAge: 365 * 24 * 60 * 60, // 1 year
|
121
|
+
sameSite: 'lax',
|
122
|
+
})
|
123
|
+
}
|
124
|
+
|
125
|
+
cookies.set('dropbox_connected', 'true', {
|
126
|
+
path: '/',
|
127
|
+
secure: process.env.NODE_ENV === 'production',
|
128
|
+
maxAge: tokens.expiresAt
|
129
|
+
? Math.floor((tokens.expiresAt - Date.now()) / 1000)
|
130
|
+
: 14 * 24 * 60 * 60,
|
131
|
+
sameSite: 'lax',
|
132
|
+
})
|
133
|
+
|
134
|
+
return new Response(null, {
|
135
|
+
status: 302,
|
136
|
+
headers: {
|
137
|
+
Location: '/',
|
138
|
+
},
|
139
|
+
})
|
140
|
+
} catch (error) {
|
141
|
+
console.error('Error completing Dropbox OAuth flow:', error)
|
142
|
+
|
143
|
+
return new Response(null, {
|
144
|
+
status: 302,
|
145
|
+
headers: {
|
146
|
+
Location: '/auth/error',
|
147
|
+
},
|
148
|
+
})
|
149
|
+
}
|
150
|
+
},
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Handler for logout endpoint
|
154
|
+
*/
|
155
|
+
async logout({ cookies }: { cookies: Cookies }) {
|
156
|
+
cookies.delete('dropbox_access_token', { path: '/' })
|
157
|
+
cookies.delete('dropbox_refresh_token', { path: '/' })
|
158
|
+
cookies.delete('dropbox_connected', { path: '/' })
|
159
|
+
|
160
|
+
return new Response(JSON.stringify({ success: true }), {
|
161
|
+
status: 200,
|
162
|
+
headers: {
|
163
|
+
'Content-Type': 'application/json',
|
164
|
+
},
|
165
|
+
})
|
166
|
+
},
|
167
|
+
}
|
168
|
+
}
|