-
-
Notifications
You must be signed in to change notification settings - Fork 103
/
file.ts
104 lines (94 loc) · 3.25 KB
/
file.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { ObjectMetadata, ObjectResponse } from '../types/types'
import xattr from 'fs-xattr'
import fs from 'fs-extra'
import path from 'path'
import { promisify } from 'util'
import stream from 'stream'
import { getConfig } from '../utils/config'
import { GenericStorageBackend } from './generic'
const pipeline = promisify(stream.pipeline)
export class FileBackend implements GenericStorageBackend {
client: null
filePath: string
constructor() {
const { fileStoragePath } = getConfig()
if (!fileStoragePath) {
throw new Error('FILE_STORAGE_BACKEND_PATH env variable not set')
}
this.filePath = fileStoragePath
}
getMetadata(file: string, attribute: string): Promise<string | undefined> {
return xattr.get(file, attribute).then((value) => {
return value?.toString() ?? undefined
})
}
setMetadata(file: string, attribute: string, value: string): Promise<void> {
return xattr.set(file, attribute, value)
}
async getObject(bucketName: string, key: string): Promise<ObjectResponse> {
const file = path.resolve(this.filePath, `${bucketName}/${key}`)
const body = await fs.readFile(file)
const data = await fs.stat(file)
const cacheControl = await this.getMetadata(file, 'user.supabase.cache-control')
const contentType = await this.getMetadata(file, 'user.supabase.content-type')
const lastModified = new Date(0)
lastModified.setUTCMilliseconds(data.mtimeMs)
return {
metadata: {
cacheControl,
mimetype: contentType,
lastModified: lastModified,
// contentRange: data.ContentRange, @todo: support range requests
httpStatusCode: 200,
},
body,
}
}
async uploadObject(
bucketName: string,
key: string,
body: NodeJS.ReadableStream,
contentType: string,
cacheControl: string
): Promise<ObjectMetadata> {
const file = path.resolve(this.filePath, `${bucketName}/${key}`)
await fs.ensureFile(file)
const destFile = fs.createWriteStream(file)
await pipeline(body, destFile)
await Promise.all([
this.setMetadata(file, 'user.supabase.content-type', contentType),
this.setMetadata(file, 'user.supabase.cache-control', cacheControl),
])
return {
httpStatusCode: 200,
}
}
async deleteObject(bucket: string, key: string): Promise<ObjectMetadata> {
const file = path.resolve(this.filePath, `${bucket}/${key}`)
await fs.remove(file)
return {}
}
async copyObject(bucket: string, source: string, destination: string): Promise<ObjectMetadata> {
const srcFile = path.resolve(this.filePath, `${bucket}/${source}`)
const destFile = path.resolve(this.filePath, `${bucket}/${destination}`)
await fs.copyFile(srcFile, destFile)
return {
httpStatusCode: 200,
}
}
async deleteObjects(bucket: string, prefixes: string[]): Promise<ObjectMetadata> {
const promises = prefixes.map((prefix) => {
return fs.rm(path.resolve(this.filePath, bucket, prefix))
})
await Promise.all(promises)
return {}
}
async headObject(bucket: string, key: string): Promise<ObjectMetadata> {
const file = path.resolve(this.filePath, `${bucket}/${key}`)
const data = await fs.stat(file)
return {
httpStatusCode: 200,
size: data.size,
}
}
}