2020-02-21 06:36:55 +00:00
|
|
|
const { Injectable } = require('flitter-di')
|
|
|
|
const moment = require('moment')
|
2020-03-03 22:10:10 +00:00
|
|
|
const path = require('path')
|
2020-02-21 06:36:55 +00:00
|
|
|
|
|
|
|
class UniversalPath extends Injectable {
|
|
|
|
static get services() {
|
|
|
|
return [...super.services, 'hosts']
|
|
|
|
}
|
|
|
|
|
|
|
|
static PATH_TYPE_FILE = Symbol('file')
|
|
|
|
static PATH_TYPE_DIRECTORY = Symbol('directory')
|
|
|
|
static PATH_TYPE_UNKNOWN = Symbol('unknown')
|
|
|
|
_directory_classify_command = 'test -d'
|
|
|
|
_file_classify_command = 'test -f'
|
|
|
|
_datetime_format = 'Y-MM-DD_HH-mm-ss-SSSS'
|
|
|
|
|
|
|
|
static fromString(path_string) {
|
|
|
|
const parts = path_string.split('::')
|
|
|
|
const hostname = parts.length > 1 ? parts[0] : 'localhost'
|
|
|
|
const path = parts.length > 1 ? parts[1] : parts[0]
|
|
|
|
const host = this.prototype.hosts.get(hostname)
|
|
|
|
return new this(host, path)
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(host, host_path, type) {
|
|
|
|
super()
|
|
|
|
this._host = host
|
|
|
|
this._path = host_path
|
|
|
|
this._type = type ? type : this.constructor.PATH_TYPE_UNKNOWN
|
|
|
|
}
|
|
|
|
|
|
|
|
get host() {
|
|
|
|
return this._host
|
|
|
|
}
|
|
|
|
|
|
|
|
get path() {
|
|
|
|
return this._path
|
|
|
|
}
|
|
|
|
|
|
|
|
get type() {
|
|
|
|
return this._type
|
|
|
|
}
|
|
|
|
|
|
|
|
async resolve() {
|
|
|
|
return this._host.resolve_path(this._path)
|
|
|
|
}
|
|
|
|
|
|
|
|
is_directory() {
|
|
|
|
return this.type === this.constructor.PATH_TYPE_DIRECTORY
|
|
|
|
}
|
|
|
|
|
|
|
|
is_file() {
|
|
|
|
return this.type === this.constructor.PATH_TYPE_FILE
|
|
|
|
}
|
|
|
|
|
|
|
|
is_valid() {
|
|
|
|
return this.is_file() || this.is_directory()
|
|
|
|
}
|
|
|
|
|
2020-03-03 22:10:10 +00:00
|
|
|
async directory() {
|
|
|
|
await this.classify()
|
|
|
|
if ( this.is_directory() ) return this.path
|
|
|
|
return path.dirname(this.path)
|
|
|
|
}
|
|
|
|
|
2020-03-04 03:06:59 +00:00
|
|
|
async parent() {
|
|
|
|
await this.classify()
|
2020-03-05 16:25:45 +00:00
|
|
|
if ( this.is_directory() ) return path.join(this.path, '..')
|
2020-03-04 03:06:59 +00:00
|
|
|
return this.directory()
|
|
|
|
}
|
|
|
|
|
2020-03-05 16:25:45 +00:00
|
|
|
async child(subpath) {
|
|
|
|
await this.classify()
|
|
|
|
if ( !this.is_directory() ) throw new Error('Cannot get sub-path: not a directory: '+this.path)
|
|
|
|
return this.constructor.fromString(path.join(String(this), subpath))
|
|
|
|
}
|
|
|
|
|
2020-03-04 03:06:59 +00:00
|
|
|
name() {
|
|
|
|
return path.basename(this.path)
|
|
|
|
}
|
|
|
|
|
2020-02-21 06:36:55 +00:00
|
|
|
async classify() {
|
|
|
|
const dir_result = await this._host.execute(`${this._directory_classify_command} ${this._path}`)
|
|
|
|
if ( dir_result.exit_code === 0 ) {
|
|
|
|
this._type = this.constructor.PATH_TYPE_DIRECTORY
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const file_result = await this._host.execute(`${this._file_classify_command} ${this._path}`)
|
|
|
|
if ( file_result.exit_code === 0 ) {
|
|
|
|
this._type = this.constructor.PATH_TYPE_FILE
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this._type = this.constructor.PATH_TYPE_UNKNOWN
|
|
|
|
}
|
|
|
|
|
|
|
|
async open_read_stream() {
|
|
|
|
await this.classify()
|
|
|
|
if ( this._type === this.constructor.PATH_TYPE_FILE ) {
|
|
|
|
return this._host.open_file_read_stream(this._path)
|
|
|
|
} else {
|
|
|
|
throw new Error('Maestro does not support generation of read streams for this type of path: '+this._type.description)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async open_write_stream() {
|
|
|
|
await this.classify()
|
|
|
|
if (
|
|
|
|
[this.constructor.PATH_TYPE_FILE, this.constructor.PATH_TYPE_UNKNOWN].includes(this._type)
|
|
|
|
) {
|
|
|
|
return this._host.open_file_write_stream(this._path)
|
|
|
|
} else {
|
|
|
|
throw new Error('Maestro does not support generation of write streams for this type of path: '+this._type.description)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async copy_from(other_path) {
|
|
|
|
const read = await other_path.open_read_stream()
|
|
|
|
const write = await this.open_write_stream()
|
|
|
|
await read.pipe(write)
|
|
|
|
}
|
|
|
|
|
|
|
|
async copy_to(other_path) {
|
|
|
|
const read = await this.open_read_stream()
|
|
|
|
const write = await other_path.open_write_stream()
|
|
|
|
await read.pipe(write)
|
|
|
|
}
|
|
|
|
|
|
|
|
async unlink() {
|
|
|
|
await this.host.delete_path(this._path)
|
|
|
|
}
|
|
|
|
|
|
|
|
async move_from(other_path) {
|
|
|
|
await this.copy_from(other_path)
|
|
|
|
await other_path.unlink()
|
|
|
|
}
|
|
|
|
|
|
|
|
async move_to(other_path) {
|
|
|
|
await this.copy_to(other_path)
|
|
|
|
await this.unlink()
|
|
|
|
}
|
|
|
|
|
|
|
|
async touch(directory = false) {
|
|
|
|
await this.classify()
|
|
|
|
if ( !directory ) {
|
|
|
|
const base_dir = this._path.split('/').slice(0, -1).join('/')
|
|
|
|
await this._host.run(`mkdir -p ${base_dir}`)
|
|
|
|
await this._host.run(`touch ${this._path}`)
|
|
|
|
} else {
|
|
|
|
await this._host.run(`mkdir -p ${this._path}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async echo(text) {
|
|
|
|
await this.classify()
|
|
|
|
if ( this.is_directory() ) {
|
|
|
|
throw new Error('Cannot echo to a directory.')
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !this.is_valid() ) {
|
|
|
|
await this.touch()
|
|
|
|
}
|
|
|
|
|
|
|
|
const ws = await this.open_write_stream()
|
|
|
|
ws.write(text)
|
|
|
|
return new Promise(resolve => {
|
|
|
|
ws.on('finish', () => resolve())
|
|
|
|
ws.end()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-03 22:47:51 +00:00
|
|
|
async permissions(set_level = false, recursive = false) {
|
|
|
|
if ( set_level ) {
|
|
|
|
await this._host.set_permissions_for_path(this, set_level, recursive)
|
|
|
|
} else {
|
|
|
|
return this._host.get_permissions_for_path(this)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async ownership(set_owners = false, recursive = false) {
|
|
|
|
if ( set_owners ) {
|
|
|
|
const current = await this.ownership()
|
|
|
|
const target = {...current, ...set_owners}
|
|
|
|
await this._host.set_ownership_for_path(this, target, recursive)
|
|
|
|
} else {
|
|
|
|
return this._host.get_ownership_for_path(this)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-21 06:36:55 +00:00
|
|
|
async hash() {
|
|
|
|
const line = await this._host.run_line_result(`sha256sum ${this._path}`)
|
|
|
|
return line.split(' ')[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
async backup() {
|
|
|
|
const now = moment().format(this._datetime_format)
|
|
|
|
const backup_path = `${this}.maestro-backup-${now}`
|
|
|
|
const backup_target = this.constructor.fromString(backup_path)
|
|
|
|
await this.copy_to(backup_target)
|
|
|
|
}
|
|
|
|
|
|
|
|
toString() {
|
|
|
|
return `${this._host.name}::${this._path}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = exports = UniversalPath
|