Skip to content

Commit

Permalink
Node Explorer: Add refresh and change root dir (#119)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Smalley <tyler@tailscale.com>
  • Loading branch information
tylersmalley committed Jul 25, 2023
1 parent 49c9af1 commit bdcb818
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 20 deletions.
35 changes: 27 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@
"command": "tailscale.openAdminConsole",
"group": "overflow",
"when": "view == tailscale-serve-view || view == tailscale-node-explorer-view"
},
{
"command": "tailscale.nodeExplorer.refresh",
"group": "navigation",
"when": "view == tailscale-node-explorer-view"
}
],
"view/item/context": [
Expand All @@ -142,37 +147,42 @@
{
"command": "tailscale.node.setUsername",
"when": "view == tailscale-node-explorer-view && viewItem == tailscale-peer-item",
"group": "1_action@3"
"group": "2_settings@3"
},
{
"command": "tailscale.node.setRootDir",
"when": "(view == tailscale-node-explorer-view && viewItem == tailscale-peer-item) || (view == tailscale-node-explorer-view && viewItem =~ /^file-explorer-item-root/)",
"group": "2_settings@3"
},
{
"command": "tailscale.node.copyIPv4",
"when": "view == tailscale-node-explorer-view && viewItem == tailscale-peer-item",
"group": "2_copy@1"
"group": "3_copy@1"
},
{
"command": "tailscale.node.copyIPv6",
"when": "view == tailscale-node-explorer-view && viewItem == tailscale-peer-item",
"group": "2_copy@1"
"group": "3_copy@1"
},
{
"command": "tailscale.node.copyHostname",
"when": "view == tailscale-node-explorer-view && viewItem == tailscale-peer-item",
"group": "2_copy@1"
"group": "3_copy@1"
},
{
"command": "tailscale.node.openDetailsLink",
"group": "3_control@1",
"group": "4_control@1",
"when": "view == tailscale-node-explorer-view && viewItem == tailscale-peer-item"
},
{
"command": "tailscale.node.openRemoteCodeAtLocation",
"group": "1_action@2",
"when": "view == tailscale-node-explorer-view && viewItem == file-explorer-item"
"when": "view == tailscale-node-explorer-view && viewItem =~ /^file-explorer-item-root/"
},
{
"command": "tailscale.node.fs.delete",
"group": "2_fileAction@2",
"when": "view == tailscale-node-explorer-view && viewItem == file-explorer-item"
"when": "view == tailscale-node-explorer-view && viewItem =~ /^file-explorer-item-child/"
}
]
},
Expand Down Expand Up @@ -228,6 +238,11 @@
"command": "tailscale.node.openTerminal",
"title": "Open SSH"
},
{
"command": "tailscale.nodeExplorer.refresh",
"title": "Refresh",
"icon": "$(refresh)"
},
{
"command": "tailscale.node.openRemoteCode",
"title": "Open Remote Connection"
Expand All @@ -244,6 +259,10 @@
"command": "tailscale.node.setUsername",
"title": "Change SSH Username"
},
{
"command": "tailscale.node.setRootDir",
"title": "Change Root Directory"
},
{
"command": "tailscale.node.openRemoteCodeAtLocation",
"title": "Open Remote Connection"
Expand Down Expand Up @@ -322,7 +341,7 @@
"tailscale.ssh.defaultRootPath": {
"type": "string",
"default": null,
"markdownDescription": "Default root path for SSH connections.",
"markdownDescription": "Default root directory for SSH connections.",
"scope": "window",
"examples": [
"/",
Expand Down
8 changes: 6 additions & 2 deletions src/config-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ export class ConfigManager {
return this._config;
}

setUserForHost(hostname: string, username: string) {
setForHost<TKey extends keyof Host, TValue extends Host[TKey]>(
hostname: string,
key: TKey,
value: TValue
) {
this._config.hosts = this._config.hosts ?? {};
this._config.hosts[hostname] = this._config.hosts[hostname] ?? {};
this._config.hosts[hostname].user = username;
this._config.hosts[hostname][key] = value;

this.saveConfig();
}
Expand Down
28 changes: 26 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as path from 'path';
import * as vscode from 'vscode';

import { ServePanelProvider } from './serve-panel-provider';
Expand All @@ -9,6 +10,7 @@ import { NodeExplorerProvider, PeerTree } from './node-explorer-provider';

import { TSFileSystemProvider } from './ts-file-system-provider';
import { ConfigManager } from './config-manager';
import { SSH } from './utils/ssh';

let tailscaleInstance: Tailscale;

Expand All @@ -18,6 +20,7 @@ export async function activate(context: vscode.ExtensionContext) {
tailscaleInstance = await Tailscale.withInit(vscode);

const configManager = ConfigManager.withGlobalStorageUri(context.globalStorageUri);
const ssh = new SSH(configManager);

// walkthrough completion
tailscaleInstance.serveStatus().then((status) => {
Expand Down Expand Up @@ -58,7 +61,7 @@ export async function activate(context: vscode.ExtensionContext) {
})
);

const nodeExplorerProvider = new NodeExplorerProvider(tailscaleInstance);
const nodeExplorerProvider = new NodeExplorerProvider(tailscaleInstance, ssh);
vscode.window.registerTreeDataProvider('tailscale-node-explorer-view', nodeExplorerProvider);
const view = vscode.window.createTreeView('tailscale-node-explorer-view', {
treeDataProvider: nodeExplorerProvider,
Expand Down Expand Up @@ -107,7 +110,28 @@ export async function activate(context: vscode.ExtensionContext) {
return;
}

configManager.setUserForHost(node.HostName, username);
configManager.setForHost(node.HostName, 'user', username);
})
);

context.subscriptions.push(
vscode.commands.registerCommand('tailscale.node.setRootDir', async (node: PeerTree) => {
const dir = await vscode.window.showInputBox({
prompt: `Enter the root directory to use for ${node.HostName}`,
value: configManager.config?.hosts?.[node.HostName]?.rootDir || '~',
});

if (!dir) {
return;
}

if (!path.isAbsolute(dir) && dir !== '~') {
vscode.window.showErrorMessage(`${dir} is an invalid absolute path`);
return;
}

configManager.setForHost(node.HostName, 'rootDir', dir);
// TODO: trigger refresh to fsFileSystemProvider
})
);

Expand Down
25 changes: 19 additions & 6 deletions src/node-explorer-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as path from 'path';
import { Peer } from './types';
import { Tailscale } from './tailscale/cli';
import { TSFileSystemProvider } from './ts-file-system-provider';
import { SSH } from './utils/ssh';

export class NodeExplorerProvider
implements
Expand All @@ -23,7 +24,10 @@ export class NodeExplorerProvider
private peers: { [hostName: string]: Peer } = {};
private fsProvider: TSFileSystemProvider;

constructor(private readonly ts: Tailscale) {
constructor(
private readonly ts: Tailscale,
private ssh: SSH
) {
this.fsProvider = new TSFileSystemProvider();

this.registerDeleteCommand();
Expand All @@ -34,6 +38,7 @@ export class NodeExplorerProvider
this.registerOpenRemoteCodeCommand();
this.registerOpenRemoteCodeLocationCommand();
this.registerOpenNodeDetailsCommand();
this.registerRefresh();
}

dispose() {}
Expand All @@ -50,7 +55,7 @@ export class NodeExplorerProvider
const dirents = await vscode.workspace.fs.readDirectory(element.uri);
return dirents.map(([name, type]) => {
const childUri = element.uri.with({ path: `${element.uri.path}/${name}` });
return new FileExplorer(name, childUri, type);
return new FileExplorer(name, childUri, type, 'child');
});
}

Expand All @@ -61,7 +66,8 @@ export class NodeExplorerProvider
'File explorer',
// TODO: allow the directory to be configurable
vscode.Uri.parse(`ts://nodes/${element.HostName}/~`),
vscode.FileType.Directory
vscode.FileType.Directory,
'root'
),
];
} else {
Expand Down Expand Up @@ -184,7 +190,7 @@ export class NodeExplorerProvider
registerOpenTerminalCommand() {
vscode.commands.registerCommand('tailscale.node.openTerminal', async (node: PeerTree) => {
const t = vscode.window.createTerminal(node.HostName);
t.sendText(`ssh ${node.HostName}`);
t.sendText(`ssh ${this.ssh.sshHostnameWithUser(node.HostName)}`);
t.show();
});
}
Expand All @@ -203,6 +209,12 @@ export class NodeExplorerProvider
});
}

registerRefresh(): void {
vscode.commands.registerCommand('tailscale.nodeExplorer.refresh', () => {
this._onDidChangeTreeData.fire(undefined);
});
}

openRemoteCodeWindow(host: string, reuseWindow: boolean) {
vscode.commands.executeCommand('vscode.newWindow', {
remoteAuthority: `ssh-remote+${host}`,
Expand Down Expand Up @@ -252,6 +264,7 @@ export class FileExplorer extends vscode.TreeItem {
public readonly label: string,
public readonly uri: vscode.Uri,
public readonly type: vscode.FileType,
public readonly context?: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState = type ===
vscode.FileType.Directory
? vscode.TreeItemCollapsibleState.Collapsed
Expand All @@ -266,9 +279,9 @@ export class FileExplorer extends vscode.TreeItem {
arguments: [this.uri],
};
}
}

contextValue = 'file-explorer-item';
this.contextValue = `file-explorer-item${context ? '-' : ''}${context}`;
}
}

export class PeerTree extends PeerBaseTreeItem {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/ssh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class SSH {
});

if (username && this.configManager) {
this.configManager.setUserForHost(hostname, username);
this.configManager.setForHost(hostname, 'user', username);
}

return username;
Expand Down Expand Up @@ -125,7 +125,7 @@ export class SSH {
}
}

sshHostnameWithUser(hostname: string) {
public sshHostnameWithUser(hostname: string) {
const { hosts } = this.configManager?.config || {};
const userForHost = hosts?.[hostname]?.user?.trim();
const defaultUser = vscode.workspace.getConfiguration('ssh').get<string>('defaultUser')?.trim();
Expand Down

0 comments on commit bdcb818

Please sign in to comment.