feat: Add support for private files to file module (#8169)

This commit is contained in:
Stevche Radevski
2024-07-18 09:46:10 +02:00
committed by GitHub
parent 76173afdfc
commit c58a35f0c0
11 changed files with 98 additions and 63 deletions
@@ -6,11 +6,14 @@ import path from "path"
export class LocalFileService extends AbstractFileProviderService {
static identifier = "localfs"
protected uploadDir_: string
protected privateUploadDir_: string
protected backendUrl_: string
constructor(_, options: LocalFileServiceOptions) {
super()
this.uploadDir_ = options?.upload_dir || path.join(process.cwd(), "static")
this.privateUploadDir_ =
options?.private_upload_dir || path.join(process.cwd(), "private")
this.backendUrl_ = options?.backend_url || "http://localhost:9000/static"
}
@@ -29,14 +32,19 @@ export class LocalFileService extends AbstractFileProviderService {
}
const parsedFilename = path.parse(file.filename)
await this.ensureDirExists(parsedFilename.dir)
const baseDir =
file.access === "public" ? this.uploadDir_ : this.privateUploadDir_
const dir = await this.ensureDirExists(baseDir, parsedFilename.dir)
const fileKey = path.join(
parsedFilename.dir,
`${Date.now()}-${parsedFilename.base}`
// We append "private" to the file key so deletions and presigned URLs can know which folder to look into
`${Date.now()}-${parsedFilename.base}${
file.access === "public" ? "" : "-private"
}`
)
const filePath = this.getUploadFilePath(fileKey)
const filePath = this.getUploadFilePath(baseDir, fileKey)
const fileUrl = this.getUploadFileUrl(fileKey)
const content = Buffer.from(file.content as string, "binary")
@@ -49,7 +57,11 @@ export class LocalFileService extends AbstractFileProviderService {
}
async delete(file: FileTypes.ProviderDeleteFileDTO): Promise<void> {
const filePath = this.getUploadFilePath(file.fileKey)
const baseDir = file.fileKey.endsWith("-private")
? this.privateUploadDir_
: this.uploadDir_
const filePath = this.getUploadFilePath(baseDir, file.fileKey)
try {
await fs.access(filePath, fs.constants.F_OK)
await fs.unlink(filePath)
@@ -60,26 +72,34 @@ export class LocalFileService extends AbstractFileProviderService {
return
}
// For private files, we simply return the file path, which can then be loaded manually by the backend.
// The local file provider doesn't support presigned URLs for private files.
async getPresignedDownloadUrl(
fileData: FileTypes.ProviderGetFileDTO
file: FileTypes.ProviderGetFileDTO
): Promise<string> {
const isPrivate = file.fileKey.endsWith("-private")
const baseDir = isPrivate ? this.privateUploadDir_ : this.uploadDir_
const filePath = this.getUploadFilePath(baseDir, file.fileKey)
try {
await fs.access(
this.getUploadFilePath(fileData.fileKey),
fs.constants.F_OK
)
await fs.access(filePath, fs.constants.F_OK)
} catch {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`File with key ${fileData.fileKey} not found`
`File with key ${file.fileKey} not found`
)
}
return this.getUploadFileUrl(fileData.fileKey)
if (isPrivate) {
return filePath
}
return this.getUploadFileUrl(file.fileKey)
}
private getUploadFilePath = (fileKey: string) => {
return path.join(this.uploadDir_, fileKey)
private getUploadFilePath = (baseDir: string, fileKey: string) => {
return path.join(baseDir, fileKey)
}
private getUploadFileUrl = (fileKey: string) => {
@@ -88,12 +108,14 @@ export class LocalFileService extends AbstractFileProviderService {
return baseUrl.href
}
private async ensureDirExists(dirPath: string) {
const relativePath = path.join(this.uploadDir_, dirPath)
private async ensureDirExists(baseDir: string, dirPath: string) {
const relativePath = path.join(baseDir, dirPath)
try {
await fs.access(relativePath, fs.constants.F_OK)
} catch (e) {
await fs.mkdir(relativePath, { recursive: true })
}
return relativePath
}
}