Dominik Schwank
8d160cefb0
During a delete the metadata file is updated. As the overwrite property was not set, the metadata couldn't be updated and caused issues.
245 lines
6.7 KiB
TypeScript
245 lines
6.7 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
import * as helpers from './helpers.js';
|
|
import * as interfaces from './interfaces.js';
|
|
import { Directory } from './classes.directory.js';
|
|
import { MetaData } from './classes.metadata.js';
|
|
|
|
/**
|
|
* represents a file in a directory
|
|
*/
|
|
export class File {
|
|
// STATIC
|
|
|
|
/**
|
|
* creates a file in draft mode
|
|
* you need to call .save() to store it in s3
|
|
* @param optionsArg
|
|
*/
|
|
public static async create(optionsArg: {
|
|
directory: Directory;
|
|
name: string;
|
|
contents: Buffer | string | plugins.stream.Readable;
|
|
/**
|
|
* if contents are of type string, you can specify the encoding here
|
|
*/
|
|
encoding?: 'utf8' | 'binary';
|
|
}): Promise<File> {
|
|
const contents =
|
|
typeof optionsArg.contents === 'string'
|
|
? Buffer.from(optionsArg.contents, optionsArg.encoding)
|
|
: optionsArg.contents;
|
|
const file = new File({
|
|
directoryRefArg: optionsArg.directory,
|
|
fileName: optionsArg.name,
|
|
});
|
|
if (contents instanceof plugins.stream.Readable) {
|
|
await optionsArg.directory.fastPutStream({
|
|
path: optionsArg.name,
|
|
stream: contents,
|
|
});
|
|
} else {
|
|
await optionsArg.directory.fastPut({
|
|
path: optionsArg.name,
|
|
contents: contents,
|
|
});
|
|
}
|
|
return file;
|
|
}
|
|
|
|
// INSTANCE
|
|
public parentDirectoryRef: Directory;
|
|
public name: string;
|
|
|
|
/**
|
|
* get the full path to the file
|
|
* @returns the full path to the file
|
|
*/
|
|
public getBasePath(): string {
|
|
return plugins.path.join(this.parentDirectoryRef.getBasePath(), this.name);
|
|
}
|
|
|
|
constructor(optionsArg: { directoryRefArg: Directory; fileName: string }) {
|
|
this.parentDirectoryRef = optionsArg.directoryRefArg;
|
|
this.name = optionsArg.fileName;
|
|
}
|
|
|
|
public async getContentsAsString(): Promise<string> {
|
|
const fileBuffer = await this.getContents();
|
|
return fileBuffer.toString();
|
|
}
|
|
|
|
public async getContents(): Promise<Buffer> {
|
|
const resultBuffer = await this.parentDirectoryRef.bucketRef.fastGet({
|
|
path: this.getBasePath(),
|
|
});
|
|
return resultBuffer;
|
|
}
|
|
|
|
public async getReadStream(typeArg: 'webstream'): Promise<ReadableStream>;
|
|
public async getReadStream(typeArg: 'nodestream'): Promise<plugins.stream.Readable>;
|
|
public async getReadStream(
|
|
typeArg: 'nodestream' | 'webstream'
|
|
): Promise<ReadableStream | plugins.stream.Readable> {
|
|
const readStream = this.parentDirectoryRef.bucketRef.fastGetStream(
|
|
{
|
|
path: this.getBasePath(),
|
|
},
|
|
typeArg as any
|
|
);
|
|
return readStream;
|
|
}
|
|
|
|
/**
|
|
* deletes this file
|
|
*/
|
|
public async delete(optionsArg?: { mode: 'trash' | 'permanent' }) {
|
|
optionsArg = {
|
|
...{
|
|
mode: 'permanent',
|
|
},
|
|
...optionsArg,
|
|
};
|
|
|
|
if (optionsArg.mode === 'permanent') {
|
|
await this.parentDirectoryRef.bucketRef.fastRemove({
|
|
path: this.getBasePath(),
|
|
});
|
|
if (!this.name.endsWith('.metadata')) {
|
|
const metadata = await this.getMetaData();
|
|
await metadata.metadataFile.delete(optionsArg);
|
|
}
|
|
} else if (optionsArg.mode === 'trash') {
|
|
const metadata = await this.getMetaData();
|
|
await metadata.storeCustomMetaData({
|
|
key: 'recycle',
|
|
value: {
|
|
deletedAt: Date.now(),
|
|
originalPath: this.getBasePath(),
|
|
},
|
|
});
|
|
const trash = await this.parentDirectoryRef.bucketRef.getTrash();
|
|
await this.move({
|
|
directory: await trash.getTrashDir(),
|
|
path: await trash.getTrashKeyByOriginalBasePath(this.getBasePath()),
|
|
});
|
|
}
|
|
|
|
await this.parentDirectoryRef.listFiles();
|
|
}
|
|
|
|
/**
|
|
* allows locking the file
|
|
* @param optionsArg
|
|
*/
|
|
public async lock(optionsArg?: { timeoutMillis?: number }) {
|
|
const metadata = await this.getMetaData();
|
|
await metadata.setLock({
|
|
lock: 'locked',
|
|
expires: Date.now() + (optionsArg?.timeoutMillis || 1000),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* actively unlocks a file
|
|
*
|
|
*/
|
|
public async unlock(optionsArg?: {
|
|
/**
|
|
* unlock the file even if not locked from this instance
|
|
*/
|
|
force?: boolean;
|
|
}) {
|
|
const metadata = await this.getMetaData();
|
|
await metadata.removeLock({
|
|
force: optionsArg?.force,
|
|
});
|
|
}
|
|
|
|
public async updateWithContents(optionsArg: {
|
|
contents: Buffer | string | plugins.stream.Readable | ReadableStream;
|
|
encoding?: 'utf8' | 'binary';
|
|
}) {
|
|
if (
|
|
optionsArg.contents instanceof plugins.stream.Readable ||
|
|
optionsArg.contents instanceof ReadableStream
|
|
) {
|
|
await this.parentDirectoryRef.bucketRef.fastPutStream({
|
|
path: this.getBasePath(),
|
|
readableStream: optionsArg.contents,
|
|
overwrite: true,
|
|
});
|
|
} else if (Buffer.isBuffer(optionsArg.contents)) {
|
|
await this.parentDirectoryRef.bucketRef.fastPut({
|
|
path: this.getBasePath(),
|
|
contents: optionsArg.contents,
|
|
overwrite: true,
|
|
});
|
|
} else if (typeof optionsArg.contents === 'string') {
|
|
await this.parentDirectoryRef.bucketRef.fastPut({
|
|
path: this.getBasePath(),
|
|
contents: Buffer.from(optionsArg.contents, optionsArg.encoding),
|
|
overwrite: true,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* moves the file to another directory
|
|
*/
|
|
public async move(pathDescriptorArg: interfaces.IPathDecriptor) {
|
|
let moveToPath = '';
|
|
const isDirectory = await this.parentDirectoryRef.bucketRef.isDirectory(pathDescriptorArg);
|
|
if (isDirectory) {
|
|
moveToPath = await helpers.reducePathDescriptorToPath({
|
|
...pathDescriptorArg,
|
|
path: plugins.path.join(pathDescriptorArg.path!, this.name),
|
|
});
|
|
}
|
|
// lets move the file
|
|
await this.parentDirectoryRef.bucketRef.fastMove({
|
|
sourcePath: this.getBasePath(),
|
|
destinationPath: moveToPath,
|
|
});
|
|
|
|
// lets move the metadatafile
|
|
const metadata = await this.getMetaData();
|
|
await metadata.metadataFile.move(pathDescriptorArg);
|
|
}
|
|
|
|
/**
|
|
* allows updating the metadata of a file
|
|
* @param updatedMetadata
|
|
*/
|
|
public async getMetaData() {
|
|
if (this.name.endsWith('.metadata')) {
|
|
throw new Error('metadata files cannot have metadata');
|
|
}
|
|
const metadata = await MetaData.createForFile({
|
|
file: this,
|
|
});
|
|
return metadata;
|
|
}
|
|
|
|
/**
|
|
* gets the contents as json
|
|
*/
|
|
public async getJsonData() {
|
|
const json = await this.getContentsAsString();
|
|
const parsed = await JSON.parse(json);
|
|
return parsed;
|
|
}
|
|
|
|
public async writeJsonData(dataArg: any) {
|
|
await this.updateWithContents({
|
|
contents: JSON.stringify(dataArg),
|
|
});
|
|
}
|
|
|
|
public async getMagicBytes(optionsArg: { length: number }): Promise<Buffer> {
|
|
return this.parentDirectoryRef.bucketRef.getMagicBytes({
|
|
path: this.getBasePath(),
|
|
length: optionsArg.length,
|
|
});
|
|
}
|
|
}
|