|
|
|
@ -1,35 +1,35 @@ |
|
|
|
|
import { PluginClient } from '@remixproject/plugin' |
|
|
|
|
import { SharedFolderArgs, TrackDownStreamUpdate, WS } from '../../types' |
|
|
|
|
import { SharedFolderArgs, TrackDownStreamUpdate, WS, Filelist, ResolveDirectory, FileContent } from '../../types' |
|
|
|
|
import * as utils from '../utils' |
|
|
|
|
|
|
|
|
|
const isbinaryfile = require('isbinaryfile') |
|
|
|
|
const fs = require('fs-extra') |
|
|
|
|
|
|
|
|
|
export class RemixdClient extends PluginClient { |
|
|
|
|
methods: ['folderIsReadOnly', 'resolveDirectory', 'get', 'exists', 'isFile'] |
|
|
|
|
trackDownStreamUpdate: TrackDownStreamUpdate |
|
|
|
|
methods: ['folderIsReadOnly', 'resolveDirectory', 'get', 'exists', 'isFile', 'set'] |
|
|
|
|
trackDownStreamUpdate: TrackDownStreamUpdate = {} |
|
|
|
|
websocket: WS |
|
|
|
|
currentSharedFolder: string |
|
|
|
|
readOnly: boolean |
|
|
|
|
|
|
|
|
|
setWebSocket (websocket: WS) { |
|
|
|
|
setWebSocket (websocket: WS): void { |
|
|
|
|
this.websocket = websocket |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sharedFolder (currentSharedFolder: string, readOnly: boolean) { |
|
|
|
|
sharedFolder (currentSharedFolder: string, readOnly: boolean): void { |
|
|
|
|
this.currentSharedFolder = currentSharedFolder |
|
|
|
|
this.readOnly = readOnly |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
list (args: SharedFolderArgs, cb: Function) { |
|
|
|
|
list (args: SharedFolderArgs): Filelist { |
|
|
|
|
try { |
|
|
|
|
cb(null, utils.walkSync(this.currentSharedFolder, {}, this.currentSharedFolder)) |
|
|
|
|
return utils.walkSync(this.currentSharedFolder, {}, this.currentSharedFolder) |
|
|
|
|
} catch (e) { |
|
|
|
|
cb(e.message) |
|
|
|
|
throw new Error(e) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
resolveDirectory (args: SharedFolderArgs) { |
|
|
|
|
resolveDirectory (args: SharedFolderArgs): ResolveDirectory { |
|
|
|
|
try { |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
const result = utils.resolveDirectory(path, this.currentSharedFolder) |
|
|
|
@ -40,33 +40,37 @@ export class RemixdClient extends PluginClient { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
folderIsReadOnly () { |
|
|
|
|
folderIsReadOnly (): boolean { |
|
|
|
|
return this.readOnly |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get (args: SharedFolderArgs) { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(path)) { |
|
|
|
|
reject('File not found ' + path) |
|
|
|
|
} |
|
|
|
|
if (!isRealPath(path)) return |
|
|
|
|
isbinaryfile(path, (error: Error, isBinary: boolean) => { |
|
|
|
|
if (error) console.log(error) |
|
|
|
|
if (isBinary) { |
|
|
|
|
resolve({ content: '<binary content not displayed>', readonly: true }) |
|
|
|
|
} else { |
|
|
|
|
fs.readFile(path, 'utf8', (error: Error, data: string) => { |
|
|
|
|
if (error) console.log(error) |
|
|
|
|
resolve({ content: data, readonly: false }) |
|
|
|
|
}) |
|
|
|
|
get (args: SharedFolderArgs): Promise<FileContent> { |
|
|
|
|
try { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(path)) { |
|
|
|
|
reject('File not found ' + path) |
|
|
|
|
} |
|
|
|
|
if (!isRealPath(path)) return |
|
|
|
|
isbinaryfile(path, (error: Error, isBinary: boolean) => { |
|
|
|
|
if (error) console.log(error) |
|
|
|
|
if (isBinary) { |
|
|
|
|
resolve({ content: '<binary content not displayed>', readonly: true }) |
|
|
|
|
} else { |
|
|
|
|
fs.readFile(path, 'utf8', (error: Error, data: string) => { |
|
|
|
|
if (error) console.log(error) |
|
|
|
|
resolve({ content: data, readonly: false }) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
} catch (error) { |
|
|
|
|
throw new Error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
exists (args: SharedFolderArgs) { |
|
|
|
|
exists (args: SharedFolderArgs): boolean { |
|
|
|
|
try { |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
@ -76,69 +80,95 @@ export class RemixdClient extends PluginClient { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
set (args: SharedFolderArgs, cb: Function) { |
|
|
|
|
if (this.readOnly) return cb('Cannot write file: read-only mode selected') |
|
|
|
|
const isFolder = args.path.endsWith('/') |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (fs.existsSync(path) && !isRealPath(path)) return |
|
|
|
|
if (args.content === 'undefined') { // no !!!!!
|
|
|
|
|
console.log('trying to write "undefined" ! stopping.') |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
this.trackDownStreamUpdate[path] = path |
|
|
|
|
if (isFolder) { |
|
|
|
|
fs.mkdirp(path).then(() => cb()).catch((e: Error) => cb(e)) |
|
|
|
|
} else { |
|
|
|
|
fs.ensureFile(path).then(() => { |
|
|
|
|
fs.writeFile(path, args.content, 'utf8', (error: Error, data: string) => { |
|
|
|
|
if (error) console.log(error) |
|
|
|
|
cb(error, data) |
|
|
|
|
}) |
|
|
|
|
}).catch((e: Error) => cb(e)) |
|
|
|
|
set (args: SharedFolderArgs): Promise<void> { |
|
|
|
|
try { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
if (this.readOnly) reject('Cannot write file: read-only mode selected') |
|
|
|
|
const isFolder = args.path.endsWith('/') |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (fs.existsSync(path) && !isRealPath(path)) reject() |
|
|
|
|
if (args.content === 'undefined') { // no !!!!!
|
|
|
|
|
console.log('trying to write "undefined" ! stopping.') |
|
|
|
|
reject('trying to write "undefined" ! stopping.') |
|
|
|
|
} |
|
|
|
|
this.trackDownStreamUpdate[path] = path |
|
|
|
|
if (isFolder) { |
|
|
|
|
fs.mkdirp(path).then(() => resolve()).catch((e: Error) => reject(e)) |
|
|
|
|
} else { |
|
|
|
|
fs.ensureFile(path).then(() => { |
|
|
|
|
fs.writeFile(path, args.content, 'utf8', (error: Error, data: string) => { |
|
|
|
|
if (error) { |
|
|
|
|
console.log(error) |
|
|
|
|
reject(error) |
|
|
|
|
} |
|
|
|
|
resolve() |
|
|
|
|
}) |
|
|
|
|
}).catch((e: Error) => reject(e)) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
} catch (error) { |
|
|
|
|
throw new Error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rename (args: SharedFolderArgs, cb: Function) { |
|
|
|
|
if (this.readOnly) return cb('Cannot rename file: read-only mode selected') |
|
|
|
|
const oldpath = utils.absolutePath(args.oldPath, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(oldpath)) { |
|
|
|
|
return cb('File not found ' + oldpath) |
|
|
|
|
rename (args: SharedFolderArgs): Promise<boolean> { |
|
|
|
|
try { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
if (this.readOnly) reject('Cannot rename file: read-only mode selected') |
|
|
|
|
const oldpath = utils.absolutePath(args.oldPath, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(oldpath)) { |
|
|
|
|
reject('File not found ' + oldpath) |
|
|
|
|
} |
|
|
|
|
const newpath = utils.absolutePath(args.newPath, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!isRealPath(oldpath)) return |
|
|
|
|
fs.move(oldpath, newpath, (error: Error, data: string) => { |
|
|
|
|
if (error) { |
|
|
|
|
console.log(error) |
|
|
|
|
reject(error.message) |
|
|
|
|
} |
|
|
|
|
resolve(true) |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
} catch (error) { |
|
|
|
|
throw new Error(error) |
|
|
|
|
} |
|
|
|
|
const newpath = utils.absolutePath(args.newPath, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!isRealPath(oldpath)) return |
|
|
|
|
fs.move(oldpath, newpath, (error: Error, data: string) => { |
|
|
|
|
if (error) console.log(error) |
|
|
|
|
cb(error, data) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
remove (args: SharedFolderArgs, cb: Function) { |
|
|
|
|
if (this.readOnly) return cb('Cannot remove file: read-only mode selected') |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(path)) { |
|
|
|
|
return cb('File not found ' + path) |
|
|
|
|
remove (args: SharedFolderArgs): Promise<boolean> { |
|
|
|
|
try { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
if (this.readOnly) reject('Cannot remove file: read-only mode selected') |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(path)) reject('File not found ' + path) |
|
|
|
|
if (!isRealPath(path)) return |
|
|
|
|
return fs.remove(path, (error: Error, data: string) => { |
|
|
|
|
if (error) { |
|
|
|
|
console.log(error) |
|
|
|
|
reject('Failed to remove file/directory: ' + error) |
|
|
|
|
} |
|
|
|
|
resolve(true) |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
} catch (error) { |
|
|
|
|
throw new Error(error) |
|
|
|
|
} |
|
|
|
|
if (!isRealPath(path)) return |
|
|
|
|
fs.remove(path, (error: Error) => { |
|
|
|
|
if (error) { |
|
|
|
|
console.log(error) |
|
|
|
|
return cb('Failed to remove file/directory: ' + error) |
|
|
|
|
} |
|
|
|
|
cb(error, true) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
isDirectory (args: SharedFolderArgs, cb: Function) { |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
isDirectory (args: SharedFolderArgs): boolean { |
|
|
|
|
try { |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
|
cb(null, fs.statSync(path).isDirectory()) |
|
|
|
|
return fs.statSync(path).isDirectory() |
|
|
|
|
} catch (error) { |
|
|
|
|
throw new Error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
isFile (args: SharedFolderArgs) { |
|
|
|
|
isFile (args: SharedFolderArgs): boolean { |
|
|
|
|
try { |
|
|
|
|
const path = utils.absolutePath(args.path, this.currentSharedFolder) |
|
|
|
|
|
|
|
|
@ -149,7 +179,7 @@ export class RemixdClient extends PluginClient { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function isRealPath (path: string) { |
|
|
|
|
function isRealPath (path: string): boolean { |
|
|
|
|
const realPath = fs.realpathSync(path) |
|
|
|
|
const isRealPath = path === realPath |
|
|
|
|
const mes = '[WARN] Symbolic link modification not allowed : ' + path + ' | ' + realPath |
|
|
|
|