Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions src/features/sync/lib/Sync.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,18 +264,19 @@ export class SyncService extends AuthenticatedDropboxService {
}
}
}

private async uploadFileInAssembly(dbxPath: string, uploadUrl: string, copilotApi: CopilotAPI) {
logger.info('SyncService#uploadFileInAssembly :: Uploading file to Assembly', dbxPath)

const dbx = this.dbxClient.getDropboxClient()
const fileMetaData = await dbx.filesDownload({ path: dbxPath }) // get metadata for the files
logger.info('SyncService#uploadFileInAssembly :: File metadata downloaded', dbxPath)

const downloadBody = await this.dbxClient.downloadFile(
DBX_URL_PATH.fileDownload,
dbxPath,
z.string().parse(this.connectionToken.rootNamespaceId),
)
const downloadBody = await this.dbxClient.downloadFile({
urlPath: DBX_URL_PATH.fileDownload,
filePath: dbxPath,
rootNamespaceId: z.string().parse(this.connectionToken.rootNamespaceId),
})
logger.info('SyncService#uploadFileInAssembly :: Found downloadBody', Boolean(downloadBody))

// upload file to assembly
Expand Down Expand Up @@ -339,6 +340,7 @@ export class SyncService extends AuthenticatedDropboxService {
portalId: this.user.portalId,
assemblyFileId: file.id,
}

const dbxFileInfo = await this.createAndUploadFileInDropbox(dbxRootPath, file.object, file)
await this.mapFilesService.insertFileMap({
...filePayload,
Expand Down Expand Up @@ -423,12 +425,12 @@ export class SyncService extends AuthenticatedDropboxService {
// download file from Assembly
const resp = await fetch(file.downloadUrl)
// upload file to dropbox
const dbxResponse = await this.dbxClient.uploadFile(
DBX_URL_PATH.fileUpload,
path,
resp.body,
z.string().parse(this.connectionToken.rootNamespaceId),
)
const dbxResponse = await this.dbxClient.uploadFile({
urlPath: DBX_URL_PATH.fileUpload,
filePath: path,
body: resp.body,
rootNamespaceId: z.string().parse(this.connectionToken.rootNamespaceId),
})
logger.info('SyncService#uploadFileInDropbox :: File uploaded to', path)
return {
dbxFileId: dbxResponse.id,
Expand Down
2 changes: 2 additions & 0 deletions src/features/webhook/assembly/api/webhook.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { AssemblyWebhookService } from '@/features/webhook/assembly/lib/webhook.
import { DISPATCHABLE_HANDLEABLE_EVENT } from '@/features/webhook/assembly/utils/types'
import User from '@/lib/copilot/models/User.model'
import logger from '@/lib/logger'
import { sleep } from '@/utils/sleep'

export const handleWebhookEvent = async (req: NextRequest) => {
await sleep(800) // prevent ping-pong case of webhooks
const token = req.nextUrl.searchParams.get('token')

const user = await User.authenticate(token)
Expand Down
6 changes: 6 additions & 0 deletions src/features/webhook/assembly/lib/webhook.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ export class AssemblyWebhookService extends AuthenticatedDropboxService {
logger.info('AssemblyWebhookService#handleFileCreated :: Filtered entries', filteredEntries)

if (filteredEntries.length) {
// refresh dropbox access token
await this.dbxClient.dbxAuthClient.refreshAccessToken(this.connectionToken.refreshToken)

await syncAssemblyFileToDropbox.batchTrigger(filteredEntries)
await this.updateLastSynced(channelSyncId)
}
Expand Down Expand Up @@ -169,6 +172,9 @@ export class AssemblyWebhookService extends AuthenticatedDropboxService {
const user = this.user
const connectionToken = this.connectionToken
if (file) {
// refresh dropbox access token
await this.dbxClient.dbxAuthClient.refreshAccessToken(this.connectionToken.refreshToken)

const payload: AssemblyToDropboxSyncFilesPayload = {
file: CopilotFileWithObjectSchema.parse(file),
opts: {
Expand Down
3 changes: 3 additions & 0 deletions src/features/webhook/dropbox/api/webhook.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import status from 'http-status'
import { type NextRequest, NextResponse } from 'next/server'
import env from '@/config/server.env'
import { processDropboxChanges } from '@/trigger/processFileSync'
import { sleep } from '@/utils/sleep'

export const handleWebhookUrlVerification = (req: NextRequest) => {
try {
Expand All @@ -24,6 +25,8 @@ export const handleWebhookUrlVerification = (req: NextRequest) => {
}

export const handleWebhookEvents = async (req: NextRequest) => {
await sleep(800) // prevent ping-pong case of webhooks

const signature = req.headers.get('X-Dropbox-Signature')
if (!signature)
return NextResponse.json({ error: 'Missing signature' }, { status: status.BAD_REQUEST })
Expand Down
5 changes: 3 additions & 2 deletions src/lib/dropbox/DropboxAuthClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ export class DropboxAuthClient {
})
}

refreshAccessToken(refreshToken: string) {
// @function checkAndRefreshAccessToken() in-built function that gets a fresh access token. Refresh token never expires unless revoked manually.
async refreshAccessToken(refreshToken: string) {
this.authInstance.setRefreshToken(refreshToken)
this.authInstance.checkAndRefreshAccessToken()
return await this.authInstance.checkAndRefreshAccessToken() // returns promise
}

async _getAuthUrl(state: string) {
Expand Down
35 changes: 24 additions & 11 deletions src/lib/dropbox/DropboxClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { dropboxArgHeader } from '@/utils/header'

export class DropboxClient {
protected readonly clientInstance: Dropbox
private dbxAuthClient: DropboxAuthClient
readonly dbxAuthClient: DropboxAuthClient

constructor(
refreshToken: string,
Expand All @@ -27,17 +27,17 @@ export class DropboxClient {
/**
* Function returns the instance of Dropbox client after checking and refreshing (if required) the access token
* @returns instance of Dropbox client
* @function checkAndRefreshAccessToken() in-built function that gets a fresh access token. Refresh token never expires unless revoked manually.
*/
createDropboxClient(
refreshToken: string,
rootNamespaceId?: string | null,
type: DropboxClientTypeValue = DropboxClientType.ROOT,
): Dropbox {
this.dbxAuthClient.refreshAccessToken(refreshToken)
this.dbxAuthClient.authInstance.setRefreshToken(refreshToken)

const options: { auth: DropboxAuth; pathRoot?: string } = {
const options: { auth: DropboxAuth; refreshToken: string; pathRoot?: string } = {
auth: this.dbxAuthClient.authInstance,
refreshToken,
}

// If we have a root namespace, set the header
Expand Down Expand Up @@ -104,7 +104,15 @@ export class DropboxClient {
return entries
}

async _downloadFile(urlPath: string, filePath: string, rootNamespaceId: string) {
async _downloadFile({
urlPath,
filePath,
rootNamespaceId,
}: {
urlPath: string
filePath: string
rootNamespaceId: string
}) {
const headers = {
Authorization: `Bearer ${this.dbxAuthClient.authInstance.getAccessToken()}`,
'Dropbox-API-Path-Root': dropboxArgHeader({
Expand All @@ -125,12 +133,17 @@ export class DropboxClient {
* Description: this function streams the file to Dropbox. @param body is the readable stream of the file.
* For the stream to work we need to add the Content-Type: 'application/octet-stream' in the headers.
*/
async _uploadFile(
urlPath: string,
filePath: string,
body: NodeJS.ReadableStream | null,
rootNamespaceId: string,
): Promise<DropboxFileMetadata> {
async _uploadFile({
urlPath,
filePath,
body,
rootNamespaceId,
}: {
urlPath: string
filePath: string
body: NodeJS.ReadableStream | null
rootNamespaceId: string
}): Promise<DropboxFileMetadata> {
const args = {
path: filePath,
autorename: false,
Expand Down
20 changes: 13 additions & 7 deletions src/trigger/processFileSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ export const initiateDropboxToAssemblySync = task({
const mapFilesService = new MapFilesService(user, connectionToken)

// 1. get all the files folder from dropbox
const dbxClient = new DropboxClient(
connectionToken.refreshToken,
connectionToken.rootNamespaceId,
).getDropboxClient()
const dbx = new DropboxClient(connectionToken.refreshToken, connectionToken.rootNamespaceId)
const dbxClient = dbx.getDropboxClient()

let dbxFiles = await dbxClient.filesListFolder({
path: dbxRootPath,
Expand All @@ -94,6 +92,9 @@ export const initiateDropboxToAssemblySync = task({

// 2. loop over the dropbox files
while (dbxFiles.result.entries.length) {
// refresh access token for every batch
await dbx.dbxAuthClient.refreshAccessToken(connectionToken.refreshToken)

const parsedDbxFiles = DropboxFileListFolderResultEntriesSchema.safeParse(
dbxFiles.result.entries,
)
Expand Down Expand Up @@ -174,6 +175,11 @@ export const handleChannelFileChanges = task({
},
run: async (payload: HandleChannelFilePayload) => {
const { files, channelSyncId, dbxRootPath, assemblyChannelId, user, connectionToken } = payload

// refresh dropbox access token
const dbxAuth = new DropboxAuthClient()
await dbxAuth.refreshAccessToken(connectionToken.refreshToken)

const mapFilesService = new MapFilesService(user, connectionToken)
const mappedFiles = await mapFilesService.getAllFileMaps(
and(
Expand Down Expand Up @@ -312,16 +318,16 @@ export const initiateAssemblyToDropboxSync = task({

const { user, connectionToken, dbxRootPath, assemblyChannelId } = payload
const mapFilesService = new MapFilesService(user, connectionToken)

// refresh dropbox access token
const dbxAuth = new DropboxAuthClient()
dbxAuth.refreshAccessToken(connectionToken.refreshToken)

// 1. get al the files from the assembly
const copilotApi = new CopilotAPI(payload.user.token)
let files = await copilotApi.listFiles(payload.assemblyChannelId)

while (files.data.length) {
// refresh dropbox access token for every batch
await dbxAuth.refreshAccessToken(connectionToken.refreshToken)

// 2. check and filter out all the mapped files
const filteredEntries = await mapFilesService.checkAndFilterAssemblyFiles(
files.data,
Expand Down