streamflix-api / src /shared /modules /upload /dropbox-storage /dropbox-storage.service.ts
Akshar2325
style(dropbox-storage): format code for improved readability
6f84fbe
Raw
History Blame
6.27 kB
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Dropbox } from 'dropbox';
@Injectable()
export class DropboxStorageService {
private readonly logger = new Logger(DropboxStorageService.name);
private dbx: Dropbox;
private appFolder: string;
constructor(private configService: ConfigService) {
const accessToken = this.configService.get<string>(
'dropboxStorage.accessToken',
);
this.appFolder =
this.configService.get<string>('dropboxStorage.appFolder') ||
'/StreamflixDev';
if (!accessToken) {
throw new Error('DROPBOX_ACCESS_TOKEN is not configured');
}
this.dbx = new Dropbox({ accessToken });
this.logger.log('Dropbox Storage Service initialized');
}
/**
* Get folder path based on file type
*/
private getFolderPath(fileType: string): string {
const folderMap = {
IMAGE: 'images',
VIDEO: 'videos',
DOCUMENT: 'documents',
};
return folderMap[fileType] || 'others';
}
/**
* Upload file to Dropbox
*/
async uploadFile(params: {
fileBuffer: Buffer;
filename: string;
fileType?: string;
customFilename?: string;
}) {
const { fileBuffer, filename, fileType, customFilename } = params;
const subfolder = this.getFolderPath(fileType || 'IMAGE');
const timestamp = Date.now();
// Use custom filename if provided, otherwise use original filename
const finalFilename = customFilename || filename;
const sanitizedFilename = finalFilename.replace(/[^a-zA-Z0-9.-]/g, '_');
const dropboxPath = `${this.appFolder}/${subfolder}/${timestamp}-${sanitizedFilename}`;
try {
const res = await this.dbx.filesUpload({
path: dropboxPath,
contents: fileBuffer,
mode: { '.tag': 'add' },
autorename: true,
mute: false,
strict_conflict: false,
});
this.logger.log(`File uploaded: ${res.result.path_lower}`);
// Create shared link
const sharedUrl = await this.createSharedLink(
res.result.path_lower || dropboxPath,
);
const directUrl = this.toDirectUrl(sharedUrl);
return {
success: true,
name: res.result.name,
dropboxPath: res.result.path_lower,
size: (res.result as any).size,
publicUrl: directUrl,
sharedUrl: sharedUrl,
};
} catch (error) {
this.logger.error('Upload failed:', error);
throw error;
}
}
/**
* Create shared link for a file
*/
async createSharedLink(path: string): Promise<string> {
try {
const res = await this.dbx.sharingCreateSharedLinkWithSettings({ path });
return res.result.url;
} catch (err: any) {
// Handle case where link already exists
if (
err?.error?.error?.['.tag'] === 'shared_link_already_exists' ||
err?.error?.error_summary?.includes('shared_link_already_exists')
) {
const list = await this.dbx.sharingListSharedLinks({ path });
if (list.result.links && list.result.links.length > 0) {
return list.result.links[0].url;
}
}
throw err;
}
}
/**
* Convert Dropbox preview URL to direct download URL
*/
private toDirectUrl(sharedUrl: string): string {
// Handle new Dropbox shared link format (scl/fi/)
if (sharedUrl.includes('dropbox.com/scl/fi/')) {
// Replace dl=0 with raw=1 for direct access
return sharedUrl.replace('dl=0', 'raw=1');
}
// Handle old format
if (sharedUrl.includes('?dl=0')) {
return sharedUrl.replace('?dl=0', '?raw=1');
}
if (!sharedUrl.includes('?')) {
return `${sharedUrl}?raw=1`;
}
return sharedUrl;
}
/**
* Get file metadata and shared link
*/
async getFileInfo(path: string) {
try {
const metadata = await this.dbx.filesGetMetadata({ path });
const sharedUrl = await this.createSharedLink(path);
const directUrl = this.toDirectUrl(sharedUrl);
return {
success: true,
name: metadata.result.name,
path: metadata.result.path_lower,
size: (metadata.result as any).size,
publicUrl: directUrl,
sharedUrl: sharedUrl,
};
} catch (error) {
this.logger.error('Get file info failed:', error);
throw error;
}
}
/**
* Delete file from Dropbox
*/
async deleteFile(path: string) {
try {
const res = await this.dbx.filesDeleteV2({ path });
this.logger.log(`File deleted: ${path}`);
return {
success: true,
deletedPath: path,
metadata: res.result.metadata,
};
} catch (error) {
this.logger.error('Delete failed:', error);
throw error;
}
}
/**
* Delete file by public URL
*/
async deleteFileByUrl(url: string) {
const path = this.extractPathFromUrl(url);
if (!path) {
throw new Error('Invalid Dropbox URL');
}
return this.deleteFile(path);
}
/**
* Extract Dropbox path from URL
*/
private extractPathFromUrl(url: string): string | null {
try {
// Handle direct URLs like: https://www.dropbox.com/s/xxxxx/filename.jpg?raw=1
// or shared URLs: https://www.dropbox.com/s/xxxxx/filename.jpg?dl=0
const urlObj = new URL(url);
const pathname = urlObj.pathname;
// Extract filename from URL
const parts = pathname.split('/');
const filename = parts[parts.length - 1];
if (!filename) {
return null;
}
// Try to match against app folder structure
// This is a simplified approach - you might need to store the full path in DB
return `${this.appFolder}/images/${filename}`;
} catch (error) {
this.logger.error('Failed to extract path from URL:', error);
return null;
}
}
/**
* List files in a folder
*/
async listFiles(folderPath?: string) {
const path = folderPath || this.appFolder;
try {
const res = await this.dbx.filesListFolder({ path });
return {
success: true,
files: res.result.entries,
hasMore: res.result.has_more,
};
} catch (error) {
this.logger.error('List files failed:', error);
throw error;
}
}
}