mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-01-31 15:37:09 +01:00
feat: add cloud save backend
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import { Object, ObjectBackend, ObjectMetadata, ObjectReference, Source } from "./objectHandler";
|
||||
import {
|
||||
Object,
|
||||
ObjectBackend,
|
||||
ObjectMetadata,
|
||||
ObjectReference,
|
||||
Source,
|
||||
} from "./objectHandler";
|
||||
|
||||
import sanitize from "sanitize-filename";
|
||||
|
||||
@@ -44,6 +50,12 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
|
||||
return false;
|
||||
}
|
||||
async startWriteStream(id: ObjectReference) {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
if (!fs.existsSync(objectPath)) return undefined;
|
||||
|
||||
return fs.createWriteStream(objectPath);
|
||||
}
|
||||
async create(
|
||||
id: string,
|
||||
source: Source,
|
||||
@@ -68,6 +80,23 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
|
||||
return id;
|
||||
}
|
||||
async createWithWriteStream(id: string, metadata: ObjectMetadata) {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
const metadataPath = path.join(
|
||||
this.baseMetadataPath,
|
||||
`${sanitize(id)}.json`
|
||||
);
|
||||
if (fs.existsSync(objectPath) || fs.existsSync(metadataPath))
|
||||
return undefined;
|
||||
|
||||
// Write metadata
|
||||
fs.writeFileSync(metadataPath, JSON.stringify(metadata));
|
||||
|
||||
// Create file so write passes
|
||||
fs.writeFileSync(objectPath, "");
|
||||
|
||||
return this.startWriteStream(id);
|
||||
}
|
||||
async delete(id: ObjectReference): Promise<boolean> {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
if (!fs.existsSync(objectPath)) return true;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { parse as getMimeTypeBuffer } from "file-type-mime";
|
||||
import { Readable } from "stream";
|
||||
import Stream, { Readable, Writable } from "stream";
|
||||
import { getMimeType as getMimeTypeStream } from "stream-mime-type";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
@@ -46,11 +46,16 @@ export abstract class ObjectBackend {
|
||||
// They don't check permissions to provide any utilities
|
||||
abstract fetch(id: ObjectReference): Promise<Source | undefined>;
|
||||
abstract write(id: ObjectReference, source: Source): Promise<boolean>;
|
||||
abstract startWriteStream(id: ObjectReference): Promise<Writable | undefined>;
|
||||
abstract create(
|
||||
id: string,
|
||||
source: Source,
|
||||
metadata: ObjectMetadata
|
||||
): Promise<ObjectReference | undefined>;
|
||||
abstract createWithWriteStream(
|
||||
id: string,
|
||||
metadata: ObjectMetadata
|
||||
): Promise<Writable | undefined>;
|
||||
abstract delete(id: ObjectReference): Promise<boolean>;
|
||||
abstract fetchMetadata(
|
||||
id: ObjectReference
|
||||
@@ -60,30 +65,31 @@ export abstract class ObjectBackend {
|
||||
metadata: ObjectMetadata
|
||||
): Promise<boolean>;
|
||||
|
||||
private async fetchMimeType(source: Source) {
|
||||
if (source instanceof ReadableStream) {
|
||||
source = Readable.from(source);
|
||||
}
|
||||
if (source instanceof Readable) {
|
||||
const { stream, mime } = await getMimeTypeStream(source);
|
||||
return { source: Readable.from(stream), mime: mime };
|
||||
}
|
||||
if (source instanceof Buffer) {
|
||||
const mime =
|
||||
getMimeTypeBuffer(new Uint8Array(source).buffer)?.mime ??
|
||||
"application/octet-stream";
|
||||
return { source: source, mime };
|
||||
}
|
||||
|
||||
return { source: undefined, mime: undefined };
|
||||
}
|
||||
|
||||
async createFromSource(
|
||||
id: string,
|
||||
sourceFetcher: () => Promise<Source>,
|
||||
metadata: { [key: string]: string },
|
||||
permissions: Array<string>
|
||||
) {
|
||||
async function fetchMimeType(source: Source) {
|
||||
if (source instanceof ReadableStream) {
|
||||
source = Readable.from(source);
|
||||
}
|
||||
if (source instanceof Readable) {
|
||||
const { stream, mime } = await getMimeTypeStream(source);
|
||||
return { source: Readable.from(stream), mime: mime };
|
||||
}
|
||||
if (source instanceof Buffer) {
|
||||
const mime =
|
||||
getMimeTypeBuffer(new Uint8Array(source).buffer)?.mime ??
|
||||
"application/octet-stream";
|
||||
return { source: source, mime };
|
||||
}
|
||||
|
||||
return { source: undefined, mime: undefined };
|
||||
}
|
||||
const { source, mime } = await fetchMimeType(await sourceFetcher());
|
||||
const { source, mime } = await this.fetchMimeType(await sourceFetcher());
|
||||
if (!mime)
|
||||
throw new Error("Unable to calculate MIME type - is the source empty?");
|
||||
|
||||
@@ -94,6 +100,18 @@ export abstract class ObjectBackend {
|
||||
});
|
||||
}
|
||||
|
||||
async createWithStream(
|
||||
id: string,
|
||||
metadata: { [key: string]: string },
|
||||
permissions: Array<string>
|
||||
) {
|
||||
return this.createWithWriteStream(id, {
|
||||
permissions,
|
||||
userMetadata: metadata,
|
||||
mime: "application/octet-stream",
|
||||
});
|
||||
}
|
||||
|
||||
async fetchWithPermissions(id: ObjectReference, userId?: string) {
|
||||
const metadata = await this.fetchMetadata(id);
|
||||
if (!metadata) return;
|
||||
|
||||
Reference in New Issue
Block a user