feat(core): add SSH data access proxy CLI and core managers
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import { SshClient } from './classes.sshclient.js';
|
||||
import type { IMountRequest, TDapMountBackend } from './types.js';
|
||||
|
||||
export class MountManager {
|
||||
constructor(private sshClient = new SshClient()) {}
|
||||
|
||||
public parseRemoteSpec(remoteSpec: string): { host: string; remotePath: string } {
|
||||
const separatorIndex = remoteSpec.indexOf(':');
|
||||
if (separatorIndex <= 0) {
|
||||
throw new Error('Remote mount spec must look like <host>:<remotePath>');
|
||||
}
|
||||
const host = remoteSpec.slice(0, separatorIndex);
|
||||
const remotePath = remoteSpec.slice(separatorIndex + 1) || '.';
|
||||
return { host, remotePath };
|
||||
}
|
||||
|
||||
public buildSshfsArgs(request: IMountRequest): string[] {
|
||||
return [`${request.host}:${request.remotePath}`, request.localPath];
|
||||
}
|
||||
|
||||
public buildRcloneArgs(request: IMountRequest): string[] {
|
||||
return [
|
||||
'mount',
|
||||
`:sftp:${request.remotePath}`,
|
||||
request.localPath,
|
||||
'--sftp-ssh',
|
||||
`ssh ${request.host}`,
|
||||
'--sftp-shell-type',
|
||||
'none',
|
||||
'--vfs-cache-mode',
|
||||
'writes',
|
||||
];
|
||||
}
|
||||
|
||||
public async mount(request: IMountRequest): Promise<number> {
|
||||
await plugins.fs.mkdir(request.localPath, { recursive: true });
|
||||
const backend = request.backend ?? (await this.detectBackend());
|
||||
if (backend === 'sshfs') {
|
||||
return this.sshClient.spawnInteractive('sshfs', this.buildSshfsArgs(request));
|
||||
}
|
||||
return this.sshClient.spawnInteractive('rclone', this.buildRcloneArgs(request));
|
||||
}
|
||||
|
||||
public async mountDetached(request: IMountRequest): Promise<number> {
|
||||
await plugins.fs.mkdir(request.localPath, { recursive: true });
|
||||
const backend = request.backend ?? (await this.detectBackend());
|
||||
if (backend === 'sshfs') {
|
||||
return this.sshClient.spawnInteractive('sshfs', this.buildSshfsArgs(request));
|
||||
}
|
||||
return this.sshClient.spawnDetached('rclone', this.buildRcloneArgs(request));
|
||||
}
|
||||
|
||||
public async unmount(localPath: string): Promise<number> {
|
||||
if (process.platform === 'darwin') {
|
||||
return this.sshClient.spawnInteractive('umount', [localPath]);
|
||||
}
|
||||
if (await this.sshClient.commandExists('fusermount3')) {
|
||||
return this.sshClient.spawnInteractive('fusermount3', ['-u', localPath]);
|
||||
}
|
||||
if (await this.sshClient.commandExists('fusermount')) {
|
||||
return this.sshClient.spawnInteractive('fusermount', ['-u', localPath]);
|
||||
}
|
||||
return this.sshClient.spawnInteractive('umount', [localPath]);
|
||||
}
|
||||
|
||||
public async detectBackend(): Promise<TDapMountBackend> {
|
||||
if (await this.sshClient.commandExists('sshfs')) {
|
||||
return 'sshfs';
|
||||
}
|
||||
if (await this.sshClient.commandExists('rclone')) {
|
||||
return 'rclone';
|
||||
}
|
||||
throw new Error('No mount backend found. Install sshfs, or install rclone with FUSE/macFUSE support.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user