@rsweeten/dropbox-sync
Version:
Reusable Dropbox synchronization module with framework adapters
156 lines (133 loc) • 4.96 kB
text/typescript
import { createDropboxSyncClient } from '../core/client'
import type { DropboxCredentials, DropboxSyncClient } from '../core/types'
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'
/**
* Next.js-specific helper to create a Dropbox sync client
* Can be used in both client and server components
*/
export function useNextDropboxSync(
credentials: DropboxCredentials
): DropboxSyncClient {
return createDropboxSyncClient(credentials)
}
/**
* Server-side helper to get credentials from Next.js cookies
*/
export async function getCredentialsFromCookies(): Promise<DropboxCredentials> {
const cookieStore = await cookies()
return {
clientId: process.env.DROPBOX_APP_KEY || '',
clientSecret: process.env.DROPBOX_APP_SECRET,
accessToken: cookieStore.get('dropbox_access_token')?.value,
refreshToken: cookieStore.get('dropbox_refresh_token')?.value,
}
}
/**
* Server action to handle Dropbox OAuth callback
*/
export async function handleOAuthCallback(
request: NextRequest
): Promise<NextResponse> {
const url = new URL(request.url)
const code = url.searchParams.get('code')
if (!code) {
return NextResponse.redirect(new URL('/auth/error', request.url))
}
const redirectUri =
process.env.DROPBOX_REDIRECT_URI ||
`${
process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
}/api/dropbox/auth/callback`
const dropboxSync = createDropboxSyncClient({
clientId: process.env.DROPBOX_APP_KEY || '',
clientSecret: process.env.DROPBOX_APP_SECRET,
})
try {
const tokens = await dropboxSync.auth.exchangeCodeForToken(
code,
redirectUri
)
// Create response with redirect
const response = NextResponse.redirect(new URL('/', request.url))
// Set cookies with the tokens
response.cookies.set({
name: 'dropbox_access_token',
value: tokens.accessToken,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: tokens.expiresAt
? (tokens.expiresAt - Date.now()) / 1000
: 14 * 24 * 60 * 60, // 14 days default
sameSite: 'lax',
path: '/',
})
if (tokens.refreshToken) {
response.cookies.set({
name: 'dropbox_refresh_token',
value: tokens.refreshToken,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 365 * 24 * 60 * 60, // 1 year
sameSite: 'lax',
path: '/',
})
}
// Set a non-httpOnly cookie to indicate connection status to the client
response.cookies.set({
name: 'dropbox_connected',
value: 'true',
secure: process.env.NODE_ENV === 'production',
maxAge: tokens.expiresAt
? (tokens.expiresAt - Date.now()) / 1000
: 14 * 24 * 60 * 60,
sameSite: 'lax',
path: '/',
})
return response
} catch (error) {
console.error('Error completing Dropbox OAuth flow:', error)
return NextResponse.redirect(new URL('/auth/error', request.url))
}
}
/**
* Create API route handlers for a Next.js app
*/
export function createNextDropboxApiHandlers() {
return {
/**
* Handler for status check route
*/
async status() {
const cookieStore = await cookies()
const isConnected = !!cookieStore.get('dropbox_access_token')?.value
return NextResponse.json({ connected: isConnected })
},
/**
* Handler for OAuth start route
*/
async oauthStart() {
const dropboxSync = createDropboxSyncClient({
clientId: process.env.DROPBOX_APP_KEY || '',
})
const redirectUri =
process.env.DROPBOX_REDIRECT_URI ||
`${
process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
}/api/dropbox/auth/callback`
const authUrl = await dropboxSync.auth.getAuthUrl(redirectUri)
return NextResponse.redirect(authUrl)
},
/**
* Handler for logout route
*/
async logout() {
const response = NextResponse.json({ success: true })
// Clear all Dropbox-related cookies
response.cookies.delete('dropbox_access_token')
response.cookies.delete('dropbox_refresh_token')
response.cookies.delete('dropbox_connected')
return response
},
}
}