UNPKG

@rsweeten/dropbox-sync

Version:

Reusable Dropbox synchronization module with framework adapters

271 lines (236 loc) 8.91 kB
import { createDropboxSyncClient } from '../core/client' import type { DropboxCredentials, DropboxSyncClient } from '../core/types' import { useCookie, useRuntimeConfig } from 'nuxt/app' import type { H3Event } from 'h3' /** * Helper to safely access runtime config properties */ function getConfigValue<T>(obj: any, path: string, defaultValue: T): T { const parts = path.split('.') let current = obj for (const part of parts) { if (current === undefined || current === null) { return defaultValue } current = current[part] } return (current as T) || defaultValue } /** * Nuxt-specific helper for creating a Dropbox sync client * Can be used in both client and server components */ export function useNuxtDropboxSync( credentials?: Partial<DropboxCredentials> ): DropboxSyncClient { // Get Nuxt runtime config (for client ID and secret) const config = useRuntimeConfig() // Create base credentials with defaults from runtime config const baseCredentials: DropboxCredentials = { clientId: getConfigValue<string>(config, 'public.dropboxAppKey', ''), clientSecret: getConfigValue<string | undefined>( config, 'dropboxAppSecret', '' ), ...credentials, } // On client-side, attempt to get tokens from cookies if (process.client) { const accessToken = useCookie('dropbox_access_token') const refreshToken = useCookie('dropbox_refresh_token') if (accessToken.value && !baseCredentials.accessToken) { baseCredentials.accessToken = accessToken.value as string } if (refreshToken.value && !baseCredentials.refreshToken) { baseCredentials.refreshToken = refreshToken.value as string } } return createDropboxSyncClient(baseCredentials) } /** * Server-side helper to get credentials from Nuxt server event */ export function getCredentialsFromCookies(event: H3Event): DropboxCredentials { const config = useRuntimeConfig() // Get cookies from Nuxt server event const accessToken = getCookie(event, 'dropbox_access_token') const refreshToken = getCookie(event, 'dropbox_refresh_token') return { clientId: getConfigValue<string>(config, 'public.dropboxAppKey', ''), clientSecret: getConfigValue<string | undefined>( config, 'dropboxAppSecret', '' ), accessToken, refreshToken, } } /** * Create API event handlers for a Nuxt app */ export function createNuxtApiHandlers() { return { /** * Handler for status check endpoint */ async status(event: H3Event) { const accessToken = getCookie(event, 'dropbox_access_token') const isConnected = !!accessToken return { connected: isConnected } }, /** * Handler for OAuth start endpoint */ async oauthStart(event: H3Event) { const config = useRuntimeConfig() const dropboxSync = createDropboxSyncClient({ clientId: getConfigValue<string>( config, 'public.dropboxAppKey', '' ), }) const redirectUri = getConfigValue<string>(config, 'dropboxRedirectUri', '') || `${getConfigValue<string>( config, 'public.appUrl', 'http://localhost:3000' )}/api/dropbox/auth/callback` const authUrl = await dropboxSync.auth.getAuthUrl(redirectUri) return sendRedirect(event, authUrl) }, /** * Handler for OAuth callback endpoint */ async oauthCallback(event: H3Event) { const config = useRuntimeConfig() // Get the authorization code from query parameters const query = getQuery(event) const code = query.code as string if (!code) { return sendRedirect(event, '/auth/error') } const redirectUri = getConfigValue<string>(config, 'dropboxRedirectUri', '') || `${getConfigValue<string>( config, 'public.appUrl', 'http://localhost:3000' )}/api/dropbox/auth/callback` const dropboxSync = createDropboxSyncClient({ clientId: getConfigValue<string>( config, 'public.dropboxAppKey', '' ), clientSecret: getConfigValue<string | undefined>( config, 'dropboxAppSecret', '' ), }) try { const tokens = await dropboxSync.auth.exchangeCodeForToken( code, redirectUri ) // Set cookies with the tokens setCookie(event, 'dropbox_access_token', tokens.accessToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: tokens.expiresAt ? Math.floor((tokens.expiresAt - Date.now()) / 1000) : 14 * 24 * 60 * 60, // 14 days default sameSite: 'lax', path: '/', }) if (tokens.refreshToken) { setCookie( event, 'dropbox_refresh_token', 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 setCookie(event, 'dropbox_connected', 'true', { secure: process.env.NODE_ENV === 'production', maxAge: tokens.expiresAt ? Math.floor((tokens.expiresAt - Date.now()) / 1000) : 14 * 24 * 60 * 60, sameSite: 'lax', path: '/', }) return sendRedirect(event, '/') } catch (error) { console.error('Error completing Dropbox OAuth flow:', error) return sendRedirect(event, '/auth/error') } }, /** * Handler for logout endpoint */ async logout(event: H3Event) { // Clear all Dropbox-related cookies deleteCookie(event, 'dropbox_access_token') deleteCookie(event, 'dropbox_refresh_token') deleteCookie(event, 'dropbox_connected') return { success: true } }, } } /** * Helper functions to work with Nuxt's H3Event * These import statements need to be added to avoid reference errors */ const { getCookie, setCookie, deleteCookie } = useNuxtCookies() const { getQuery, sendRedirect } = useNuxtServer() /** * Helper to access h3 cookie methods */ function useNuxtCookies() { return { getCookie: (event: H3Event, name: string) => { // Import inside function to avoid module loading issues const { getCookie } = require('h3') return getCookie(event, name) }, setCookie: ( event: H3Event, name: string, value: string, options?: any ) => { const { setCookie } = require('h3') return setCookie(event, name, value, options) }, deleteCookie: (event: H3Event, name: string, options?: any) => { const { deleteCookie } = require('h3') return deleteCookie(event, name, { ...options, path: '/' }) }, } } /** * Helper to access h3 server methods */ function useNuxtServer() { return { getQuery: (event: H3Event) => { const { getQuery } = require('h3') return getQuery(event) }, sendRedirect: (event: H3Event, location: string) => { const { sendRedirect } = require('h3') return sendRedirect(event, location) }, } }