baqend
Version:
Baqend JavaScript SDK
126 lines (102 loc) • 3.56 kB
text/typescript
import * as fs from 'fs';
import { ReadStream } from 'fs';
import { EntityManager } from 'baqend';
import * as account from './account';
import { isDir, stat } from './helper';
function splitArg(arg: string): [string | null, string] {
const index = arg.lastIndexOf(':');
// Has app and path part?
if (index >= 0) {
const app = arg.substring(0, index);
const path = arg.substring(index + 1);
// Add www bucket prefix if path is relative
const absolutePath = path.startsWith('/') ? path : `/www/${path}`;
if (!absolutePath.match(/^\/\w+\//)) {
throw new Error('The path must begin with a bucket.');
}
return [app, absolutePath];
}
// Has local part?
return [null, arg];
}
function isDirectory(db: EntityManager | null, path: string): Promise<boolean> {
if (db) {
return Promise.resolve(path.endsWith('/'));
}
return isDir(path);
}
function extractFilename(path: string): string {
const index = path.lastIndexOf('/');
if (index >= 0) {
return path.substring(index + 1);
}
return path;
}
function removeTrailingSlash(path: string): string {
if (path.endsWith('/')) {
return path.substring(0, path.length - 1);
}
return path;
}
function normalizeArgs(sourceDB: EntityManager | null, sourcePath: string,
destDB: EntityManager | null, destPath: string) {
return isDirectory(destDB, destPath).then((directory) => {
let destination = destPath;
if (directory) {
const sourceFilename = extractFilename(sourcePath);
destination = `${removeTrailingSlash(destPath)}/${sourceFilename}`;
}
return [sourcePath, destination];
});
}
function login(sourceApp: string | null, destApp: string | null):
Promise<[EntityManager | null, EntityManager | null]> {
if (sourceApp) {
if (destApp) {
return Promise.all([account.login({ app: sourceApp }), account.login({ app: destApp })]);
}
return Promise.all([account.login({ app: sourceApp }), null]);
}
if (destApp) {
return Promise.all([null, account.login({ app: destApp })]);
}
return Promise.resolve([null, null]);
}
function streamFrom(db: EntityManager | null, path: string): Promise<[ReadStream, number]> {
if (db) {
const file = new db.File({ path });
return file.loadMetadata().then(() => Promise.all([file.download({ type: 'stream' }) as Promise<ReadStream>, file.size!]));
}
return stat(path).then((st) => {
if (!st || !st.isFile()) {
throw new Error(`${path} is not a valid file.`);
}
return [fs.createReadStream(path), st.size];
});
}
function streamTo(db: EntityManager | null, path: string, rs: ReadStream, size: number): Promise<any> {
if (db) {
const file = new db.File({
path, data: rs, size, type: 'stream',
});
return file.upload({ force: true });
}
return new Promise((resolve, reject) => {
const ws = fs.createWriteStream(path);
rs.on('end', resolve);
rs.on('error', reject);
rs.pipe(ws);
});
}
/**
* Copies from arbitrary location to each other.
*/
export function copy(args: { source: string, dest: string }): Promise<any> {
// TODO: Split arguments with destructure in the future
const [sourceApp, sourcePath] = splitArg(args.source);
const [destApp, destPath] = splitArg(args.dest);
return login(sourceApp, destApp)
.then(([sourceDB, destDB]) => normalizeArgs(sourceDB, sourcePath, destDB, destPath)
.then(([nSourcePath, nDestPath]) => streamFrom(sourceDB, nSourcePath)
.then(([rs, size]) => streamTo(destDB, nDestPath, rs, size))));
}