2024-05-20 23:22:21 +00:00
|
|
|
import * as plugins from './plugins.js';
|
2024-05-27 10:56:25 +00:00
|
|
|
import * as helpers from './helpers.js';
|
|
|
|
import * as interfaces from './interfaces.js';
|
2024-05-20 23:22:21 +00:00
|
|
|
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,
|
|
|
|
});
|
2024-06-03 19:35:08 +00:00
|
|
|
if (contents instanceof plugins.stream.Readable) {
|
2024-06-09 14:02:33 +00:00
|
|
|
await optionsArg.directory.fastPutStream({
|
|
|
|
path: optionsArg.name,
|
|
|
|
stream: contents,
|
|
|
|
});
|
2024-06-03 19:35:08 +00:00
|
|
|
} else {
|
2024-05-20 23:22:21 +00:00
|
|
|
await optionsArg.directory.fastPut({
|
|
|
|
path: optionsArg.name,
|
|
|
|
contents: contents,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
// INSTANCE
|
|
|
|
public parentDirectoryRef: Directory;
|
|
|
|
public name: string;
|
|
|
|
|
2024-11-18 14:07:46 +00:00
|
|
|
/**
|
|
|
|
* get the full path to the file
|
|
|
|
* @returns the full path to the file
|
|
|
|
*/
|
2024-05-20 23:22:21 +00:00
|
|
|
public getBasePath(): string {
|
|
|
|
return plugins.path.join(this.parentDirectoryRef.getBasePath(), this.name);
|
2024-06-03 19:35:08 +00:00
|
|
|
}
|
2024-05-20 23:22:21 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-06-03 19:35:08 +00:00
|
|
|
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;
|
2024-05-20 23:22:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-06-03 19:35:08 +00:00
|
|
|
* deletes this file
|
2024-05-20 23:22:21 +00:00
|
|
|
*/
|
2024-11-18 21:08:39 +00:00
|
|
|
public async delete(optionsArg?: { mode: 'trash' | 'permanent' }) {
|
2024-06-03 19:35:08 +00:00
|
|
|
optionsArg = {
|
2024-11-18 21:08:39 +00:00
|
|
|
...{
|
2024-06-03 19:35:08 +00:00
|
|
|
mode: 'permanent',
|
|
|
|
},
|
|
|
|
...optionsArg,
|
2024-11-18 21:08:39 +00:00
|
|
|
};
|
2024-06-03 19:35:08 +00:00
|
|
|
|
|
|
|
if (optionsArg.mode === 'permanent') {
|
2024-05-20 23:22:21 +00:00
|
|
|
await this.parentDirectoryRef.bucketRef.fastRemove({
|
2024-06-03 19:35:08 +00:00
|
|
|
path: this.getBasePath(),
|
|
|
|
});
|
|
|
|
if (!this.name.endsWith('.metadata')) {
|
2024-11-24 01:25:08 +00:00
|
|
|
if (await this.hasMetaData()) {
|
|
|
|
const metadata = await this.getMetaData();
|
|
|
|
await metadata.metadataFile.delete(optionsArg);
|
|
|
|
}
|
2024-06-03 19:35:08 +00:00
|
|
|
}
|
|
|
|
} else if (optionsArg.mode === 'trash') {
|
|
|
|
const metadata = await this.getMetaData();
|
|
|
|
await metadata.storeCustomMetaData({
|
|
|
|
key: 'recycle',
|
|
|
|
value: {
|
|
|
|
deletedAt: Date.now(),
|
|
|
|
originalPath: this.getBasePath(),
|
|
|
|
},
|
|
|
|
});
|
2024-06-10 14:47:20 +00:00
|
|
|
const trash = await this.parentDirectoryRef.bucketRef.getTrash();
|
2024-11-24 01:25:08 +00:00
|
|
|
const trashDir = await trash.getTrashDir();
|
2024-06-03 19:35:08 +00:00
|
|
|
await this.move({
|
2024-11-24 01:25:08 +00:00
|
|
|
directory: trashDir,
|
2024-06-10 14:47:20 +00:00
|
|
|
path: await trash.getTrashKeyByOriginalBasePath(this.getBasePath()),
|
2024-05-20 23:22:21 +00:00
|
|
|
});
|
|
|
|
}
|
2024-11-18 21:08:39 +00:00
|
|
|
|
2024-05-20 23:22:21 +00:00
|
|
|
await this.parentDirectoryRef.listFiles();
|
|
|
|
}
|
|
|
|
|
2024-11-24 18:56:12 +00:00
|
|
|
/**
|
|
|
|
* restores
|
|
|
|
*/
|
|
|
|
public async restore(optionsArg: {
|
|
|
|
useOriginalPath?: boolean;
|
|
|
|
toPath?: string;
|
|
|
|
overwrite?: boolean;
|
|
|
|
} = {}) {
|
|
|
|
optionsArg = {
|
|
|
|
useOriginalPath: (() => {
|
|
|
|
return optionsArg.toPath ? false : true;
|
|
|
|
})(),
|
|
|
|
overwrite: false,
|
|
|
|
...optionsArg,
|
|
|
|
};
|
2024-11-24 18:59:37 +00:00
|
|
|
const metadata = await this.getMetaData();
|
|
|
|
const moveToPath = optionsArg.toPath || (await metadata.getCustomMetaData({
|
2024-11-24 18:56:12 +00:00
|
|
|
key: 'recycle'
|
|
|
|
})).originalPath;
|
2024-11-24 18:59:37 +00:00
|
|
|
await metadata.deleteCustomMetaData({
|
|
|
|
key: 'recycle'
|
|
|
|
})
|
2024-11-24 18:56:12 +00:00
|
|
|
await this.move({
|
|
|
|
path: moveToPath,
|
2024-11-24 18:59:37 +00:00
|
|
|
});
|
2024-11-24 18:56:12 +00:00
|
|
|
}
|
|
|
|
|
2024-05-20 23:22:21 +00:00
|
|
|
/**
|
|
|
|
* allows locking the file
|
|
|
|
* @param optionsArg
|
|
|
|
*/
|
|
|
|
public async lock(optionsArg?: { timeoutMillis?: number }) {
|
|
|
|
const metadata = await this.getMetaData();
|
|
|
|
await metadata.setLock({
|
|
|
|
lock: 'locked',
|
2024-05-21 16:42:55 +00:00
|
|
|
expires: Date.now() + (optionsArg?.timeoutMillis || 1000),
|
2024-05-20 23:22:21 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* actively unlocks a file
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public async unlock(optionsArg?: {
|
|
|
|
/**
|
|
|
|
* unlock the file even if not locked from this instance
|
|
|
|
*/
|
|
|
|
force?: boolean;
|
|
|
|
}) {
|
2024-05-21 16:42:55 +00:00
|
|
|
const metadata = await this.getMetaData();
|
|
|
|
await metadata.removeLock({
|
2024-11-24 18:56:12 +00:00
|
|
|
force: optionsArg?.force || false,
|
2024-05-21 16:42:55 +00:00
|
|
|
});
|
2024-05-20 23:22:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async updateWithContents(optionsArg: {
|
2024-06-03 19:35:08 +00:00
|
|
|
contents: Buffer | string | plugins.stream.Readable | ReadableStream;
|
2024-05-20 23:22:21 +00:00
|
|
|
encoding?: 'utf8' | 'binary';
|
|
|
|
}) {
|
2024-06-03 19:35:08 +00:00
|
|
|
if (
|
|
|
|
optionsArg.contents instanceof plugins.stream.Readable ||
|
|
|
|
optionsArg.contents instanceof ReadableStream
|
|
|
|
) {
|
2024-05-20 23:22:21 +00:00
|
|
|
await this.parentDirectoryRef.bucketRef.fastPutStream({
|
|
|
|
path: this.getBasePath(),
|
2024-06-09 14:02:33 +00:00
|
|
|
readableStream: optionsArg.contents,
|
2024-11-18 21:08:39 +00:00
|
|
|
overwrite: true,
|
2024-05-20 23:22:21 +00:00
|
|
|
});
|
|
|
|
} else if (Buffer.isBuffer(optionsArg.contents)) {
|
|
|
|
await this.parentDirectoryRef.bucketRef.fastPut({
|
|
|
|
path: this.getBasePath(),
|
|
|
|
contents: optionsArg.contents,
|
2024-11-18 21:08:39 +00:00
|
|
|
overwrite: true,
|
2024-05-20 23:22:21 +00:00
|
|
|
});
|
|
|
|
} else if (typeof optionsArg.contents === 'string') {
|
|
|
|
await this.parentDirectoryRef.bucketRef.fastPut({
|
|
|
|
path: this.getBasePath(),
|
|
|
|
contents: Buffer.from(optionsArg.contents, optionsArg.encoding),
|
2024-11-18 21:08:39 +00:00
|
|
|
overwrite: true,
|
2024-05-20 23:22:21 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-27 10:56:25 +00:00
|
|
|
/**
|
|
|
|
* moves the file to another directory
|
|
|
|
*/
|
|
|
|
public async move(pathDescriptorArg: interfaces.IPathDecriptor) {
|
2024-11-24 01:25:08 +00:00
|
|
|
let moveToPath: string = '';
|
2024-05-27 10:56:25 +00:00
|
|
|
const isDirectory = await this.parentDirectoryRef.bucketRef.isDirectory(pathDescriptorArg);
|
|
|
|
if (isDirectory) {
|
|
|
|
moveToPath = await helpers.reducePathDescriptorToPath({
|
|
|
|
...pathDescriptorArg,
|
2024-11-18 14:07:46 +00:00
|
|
|
path: plugins.path.join(pathDescriptorArg.path!, this.name),
|
2024-05-27 10:56:25 +00:00
|
|
|
});
|
2024-11-24 01:25:08 +00:00
|
|
|
} else {
|
|
|
|
moveToPath = await helpers.reducePathDescriptorToPath(pathDescriptorArg);
|
2024-05-27 10:56:25 +00:00
|
|
|
}
|
|
|
|
// lets move the file
|
|
|
|
await this.parentDirectoryRef.bucketRef.fastMove({
|
|
|
|
sourcePath: this.getBasePath(),
|
|
|
|
destinationPath: moveToPath,
|
2024-11-24 01:25:08 +00:00
|
|
|
overwrite: true,
|
2024-05-27 10:56:25 +00:00
|
|
|
});
|
|
|
|
// lets move the metadatafile
|
2024-11-24 01:25:08 +00:00
|
|
|
if (!this.name.endsWith('.metadata')) {
|
|
|
|
const metadata = await this.getMetaData();
|
|
|
|
await this.parentDirectoryRef.bucketRef.fastMove({
|
|
|
|
sourcePath: metadata.metadataFile.getBasePath(),
|
|
|
|
destinationPath: moveToPath + '.metadata',
|
|
|
|
overwrite: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// lets update references of this
|
|
|
|
const baseDirectory = await this.parentDirectoryRef.bucketRef.getBaseDirectory();
|
|
|
|
this.parentDirectoryRef = await baseDirectory.getSubDirectoryByNameStrict(
|
2024-11-24 18:56:12 +00:00
|
|
|
await helpers.reducePathDescriptorToPath(pathDescriptorArg),
|
|
|
|
{
|
|
|
|
couldBeFilePath: true,
|
|
|
|
}
|
2024-11-24 01:25:08 +00:00
|
|
|
);
|
|
|
|
this.name = pathDescriptorArg.path!;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async hasMetaData(): Promise<boolean> {
|
|
|
|
if (!this.name.endsWith('.metadata')) {
|
|
|
|
const hasMetadataBool = MetaData.hasMetaData({
|
|
|
|
file: this,
|
|
|
|
});
|
|
|
|
return hasMetadataBool;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2024-05-27 10:56:25 +00:00
|
|
|
}
|
|
|
|
|
2024-05-20 23:22:21 +00:00
|
|
|
/**
|
|
|
|
* allows updating the metadata of a file
|
|
|
|
* @param updatedMetadata
|
|
|
|
*/
|
|
|
|
public async getMetaData() {
|
2024-05-21 16:42:55 +00:00
|
|
|
if (this.name.endsWith('.metadata')) {
|
|
|
|
throw new Error('metadata files cannot have metadata');
|
|
|
|
}
|
2024-05-20 23:22:21 +00:00
|
|
|
const metadata = await MetaData.createForFile({
|
|
|
|
file: this,
|
|
|
|
});
|
|
|
|
return metadata;
|
|
|
|
}
|
2024-05-21 16:42:55 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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),
|
|
|
|
});
|
|
|
|
}
|
2024-11-18 14:07:46 +00:00
|
|
|
|
|
|
|
public async getMagicBytes(optionsArg: { length: number }): Promise<Buffer> {
|
|
|
|
return this.parentDirectoryRef.bucketRef.getMagicBytes({
|
|
|
|
path: this.getBasePath(),
|
2024-11-18 21:08:39 +00:00
|
|
|
length: optionsArg.length,
|
|
|
|
});
|
2024-11-18 14:07:46 +00:00
|
|
|
}
|
2024-05-20 23:22:21 +00:00
|
|
|
}
|