UNPKG

baqend

Version:

Baqend JavaScript SDK

126 lines (102 loc) 3.56 kB
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)))); }