-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1064 from autonomys/add-download-file-ep
Add download file endpoint
- Loading branch information
Showing
10 changed files
with
246 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { Controller, Get, NotFoundException, Param, Res } from '@nestjs/common'; | ||
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { Response } from 'express'; | ||
import { Repository } from 'typeorm'; | ||
import { Chunks } from '../entities/files/chunks.entity'; | ||
import { Files } from '../entities/files/files.entity'; | ||
import { FileRetrieverService } from '../services/file-retriever.sevice'; | ||
|
||
@ApiTags('Files') | ||
@Controller('files') | ||
export class FilesController { | ||
constructor( | ||
private fileRetrieverService: FileRetrieverService, | ||
@InjectRepository(Files) | ||
private filesRepository: Repository<Files>, | ||
@InjectRepository(Chunks) | ||
private chunksRepository: Repository<Chunks>, | ||
) {} | ||
|
||
@Get(':cid') | ||
@ApiOperation({ | ||
operationId: 'getFile', | ||
summary: 'Download file by CID', | ||
}) | ||
@ApiParam({ name: 'cid', description: 'CID of the file' }) | ||
@ApiResponse({ | ||
status: 200, | ||
description: 'Returns the file content as a byte stream', | ||
headers: { | ||
'Content-Type': { | ||
description: 'The MIME type of the file', | ||
example: 'application/octet-stream', | ||
}, | ||
}, | ||
}) | ||
async getFile( | ||
@Param('cid') cid: string, | ||
@Res() res: Response, | ||
): Promise<void> { | ||
const file = await this.filesRepository.findOne({ | ||
where: { | ||
id: cid, | ||
}, | ||
}); | ||
if (!file) { | ||
throw new NotFoundException(`File with CID ${cid} not found`); | ||
} | ||
|
||
const chunk = await this.chunksRepository.findOne({ | ||
where: { | ||
id: cid, | ||
}, | ||
}); | ||
|
||
const contentType = file.contentType() || 'application/octet-stream'; | ||
res.setHeader('Content-Type', contentType); | ||
res.setHeader('Content-Length', file.size); | ||
res.setHeader('Content-Disposition', `filename="${file.name}"`); | ||
|
||
const isCompressedAndPlainText = | ||
chunk?.upload_options?.encryption?.algorithm === undefined && | ||
chunk?.upload_options?.compression?.algorithm !== undefined; | ||
if (isCompressedAndPlainText) { | ||
res.setHeader('Content-Encoding', 'deflate'); | ||
} | ||
|
||
const fileBuffer = await this.fileRetrieverService.getBuffer(cid); | ||
|
||
res.send(fileBuffer); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { Column, Entity } from 'typeorm'; | ||
import { BaseEntity } from '../consensus/base.entity'; | ||
import { UploadOptions } from './uploadOptions.valueObject'; | ||
|
||
@Entity('chunks', { schema: 'files' }) | ||
export class Chunks extends BaseEntity { | ||
@ApiProperty() | ||
@Column('text') | ||
id: string; | ||
|
||
@ApiProperty() | ||
@Column('enum', { | ||
enum: [ | ||
'FileChunk', | ||
'File', | ||
'Folder', | ||
'FileInlink', | ||
'FolderInlink', | ||
'Metadata', | ||
'MetadataInlink', | ||
'MetadataChunk', | ||
], | ||
}) | ||
type: string; | ||
|
||
@ApiProperty() | ||
@Column('numeric') | ||
link_depth: number; | ||
|
||
@ApiProperty() | ||
@Column('numeric') | ||
size: number; | ||
|
||
@ApiProperty() | ||
@Column('varchar', { length: 255 }) | ||
name: string; | ||
|
||
@ApiProperty() | ||
@Column('text') | ||
data: string; | ||
|
||
@ApiProperty() | ||
@Column('jsonb') | ||
upload_options: UploadOptions; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { Column, Entity } from 'typeorm'; | ||
import { BaseEntity } from '../consensus/base.entity'; | ||
|
||
@Entity('file_cids', { schema: 'files' }) | ||
export class FileCids extends BaseEntity { | ||
@ApiProperty() | ||
@Column('varchar', { length: 161 }) | ||
id: string; | ||
|
||
@ApiProperty() | ||
@Column('varchar', { length: 161 }) | ||
parent_cid: string; | ||
|
||
@ApiProperty() | ||
@Column('varchar', { length: 161 }) | ||
child_cid: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { lookup } from 'mime-types'; | ||
import { AfterInsert, AfterLoad, AfterUpdate, Column, Entity } from 'typeorm'; | ||
import { BaseEntity } from '../consensus/base.entity'; | ||
|
||
@Entity('files', { schema: 'files' }) | ||
export class Files extends BaseEntity { | ||
@ApiProperty() | ||
@Column('varchar', { length: 80 }) | ||
id: string; | ||
|
||
@ApiProperty() | ||
@Column('numeric') | ||
size: number; | ||
|
||
@ApiProperty() | ||
@Column('varchar', { length: 255, nullable: true }) | ||
name: string; | ||
|
||
@AfterLoad() | ||
@AfterInsert() | ||
@AfterUpdate() | ||
contentType(): string | undefined { | ||
return lookup(this.name) || undefined; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
indexers/api/src/entities/files/uploadOptions.valueObject.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
export type UploadOptions = { | ||
compression?: CompressionOptions; | ||
encryption?: EncryptionOptions; | ||
}; | ||
|
||
export type CompressionOptions = { | ||
algorithm: CompressionAlgorithm; | ||
level?: number; | ||
chunkSize?: number; | ||
}; | ||
|
||
export type EncryptionOptions = { | ||
algorithm: EncryptionAlgorithm; | ||
chunkSize?: number; | ||
}; | ||
|
||
export enum EncryptionAlgorithm { | ||
AES_256_GCM = 'AES_256_GCM', | ||
} | ||
|
||
export enum CompressionAlgorithm { | ||
ZLIB = 'ZLIB', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { In, Repository } from 'typeorm'; | ||
import { Chunks } from '../entities/files/chunks.entity'; | ||
import { FileCids } from '../entities/files/file-cids.entity'; | ||
|
||
@Injectable() | ||
export class FileRetrieverService { | ||
constructor( | ||
@InjectRepository(FileCids) | ||
private fileCidsRepository: Repository<FileCids>, | ||
@InjectRepository(Chunks) | ||
private chunksRepository: Repository<Chunks>, | ||
) {} | ||
|
||
async getBuffer(fileId: string): Promise<Buffer> { | ||
const children = await this.fileCidsRepository.find({ | ||
where: { | ||
parent_cid: fileId, | ||
}, | ||
}); | ||
|
||
const chunks = await this.chunksRepository.find({ | ||
where: { | ||
id: In(children.map((child) => child.child_cid)), | ||
}, | ||
}); | ||
|
||
return Buffer.concat( | ||
chunks.map((chunk) => | ||
Buffer.from( | ||
Object.values(JSON.parse(chunk.data) as Record<string, number>), | ||
), | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1001,6 +1001,11 @@ | |
resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" | ||
integrity sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== | ||
|
||
"@types/mime-types@^2.1.4": | ||
version "2.1.4" | ||
resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.4.tgz#93a1933e24fed4fb9e4adc5963a63efcbb3317a2" | ||
integrity sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w== | ||
|
||
"@types/mime@^1": | ||
version "1.3.5" | ||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" | ||
|
@@ -3683,7 +3688,7 @@ [email protected]: | |
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" | ||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== | ||
|
||
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: | ||
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34: | ||
version "2.1.35" | ||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" | ||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== | ||
|