Skip to content

Commit 17ee931

Browse files
committed
serve/simple: Use message passing instead of HTTP
Implements a frameworks to support asynchronous requests using messages. Signed-off-by: Tyler Smalley <[email protected]>
1 parent 55e3a1e commit 17ee931

File tree

12 files changed

+202
-225
lines changed

12 files changed

+202
-225
lines changed

package.json

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,6 @@
112112
],
113113
"menus": {
114114
"view/title": [
115-
{
116-
"command": "tailscale.refreshServe",
117-
"group": "overflow",
118-
"when": "view == tailscale-serve-view"
119-
},
120115
{
121116
"command": "tailscale.resetServe",
122117
"group": "overflow",
@@ -145,11 +140,6 @@
145140
"title": "Reset",
146141
"category": "Tailscale"
147142
},
148-
{
149-
"command": "tailscale.refreshServe",
150-
"title": "Refresh",
151-
"category": "Tailscale"
152-
},
153143
{
154144
"command": "tailscale.openFunnelPanel",
155145
"title": "Open Funnel Panel",

src/extension.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,10 @@ export async function activate(context: vscode.ExtensionContext) {
4545
tailscaleInstance
4646
);
4747

48-
context.subscriptions.push(
49-
vscode.commands.registerCommand('tailscale.refreshServe', () => {
50-
Logger.info('called tailscale.refreshServe', 'command');
51-
servePanelProvider.refreshState();
52-
})
53-
);
54-
5548
context.subscriptions.push(
5649
vscode.commands.registerCommand('tailscale.resetServe', async () => {
5750
Logger.info('called tailscale.resetServe', 'command');
5851
await tailscaleInstance.serveDelete();
59-
servePanelProvider.refreshState();
60-
6152
vscode.window.showInformationMessage('Serve configuration reset');
6253
})
6354
);

src/serve-panel-provider.ts

Lines changed: 20 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@ export class ServePanelProvider implements vscode.WebviewViewProvider {
1818
this._view.webview.postMessage(message);
1919
}
2020

21-
public async refreshState() {
22-
this.postMessage({
23-
type: 'refreshState',
24-
});
25-
}
26-
2721
resolveWebviewView(webviewView: vscode.WebviewView) {
2822
this._view = webviewView;
2923
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
@@ -33,69 +27,40 @@ export class ServePanelProvider implements vscode.WebviewViewProvider {
3327
};
3428

3529
webviewView.webview.onDidReceiveMessage(async (m: Message) => {
36-
switch (m.type) {
37-
case 'refreshState': {
38-
Logger.info('Called refreshState', 'serve-panel');
39-
await this.refreshState();
40-
break;
41-
}
42-
43-
case 'deleteServe': {
44-
Logger.info('Called deleteServe', 'serve-panel');
45-
try {
46-
await this.ts.serveDelete(m.params);
47-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
48-
} catch (e: any) {
49-
vscode.window.showErrorMessage('Unable to delete serve', e.message);
50-
}
51-
52-
await this.refreshState();
53-
break;
54-
}
55-
56-
case 'addServe': {
57-
Logger.info('Called addServe', 'serve-panel');
58-
await this.ts.serveAdd(m.params);
59-
await this.refreshState();
60-
break;
61-
}
62-
63-
case 'setFunnel': {
64-
Logger.info('Called setFunnel', 'serve-panel');
65-
try {
66-
await this.ts.setFunnel(parseInt(m.params.port), m.params.allow);
67-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
68-
} catch (e: any) {
69-
vscode.window.showErrorMessage('Unable to toggle funnel', e.message);
70-
}
30+
const { type } = m;
31+
Logger.info(`called ${type}`, 'serve-panel');
7132

72-
await this.refreshState();
73-
break;
74-
}
33+
let response;
7534

76-
case 'resetServe': {
77-
Logger.info('Called resetServe', 'serve-panel');
35+
switch (type) {
36+
case 'relayRequest': {
37+
const { id, endpoint, method } = m;
38+
Logger.info(`${id}, ${endpoint}, ${method}`, 'serve-panel');
7839
try {
79-
await this.ts.serveDelete();
80-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
81-
} catch (e: any) {
82-
vscode.window.showErrorMessage('Unable to delete serve', e.message);
40+
response = await this.ts.performFetch(endpoint, method, m.data);
41+
Logger.info(`response: ${JSON.stringify(response)}`, 'serve-panel');
42+
this.postMessage({
43+
id,
44+
endpoint,
45+
method,
46+
type: 'relayResponse',
47+
data: response,
48+
});
49+
} catch (e) {
50+
vscode.window.showErrorMessage(`${e}`);
8351
}
8452

85-
await this.refreshState();
8653
break;
8754
}
8855

8956
case 'writeToClipboard': {
90-
Logger.info('Called writeToClipboard', 'serve-panel');
91-
vscode.env.clipboard.writeText(m.params.text);
57+
vscode.env.clipboard.writeText(m.data.text);
9258
vscode.window.showInformationMessage('Copied to clipboard');
9359
break;
9460
}
9561

9662
case 'openLink': {
97-
Logger.info(`Called openLink: ${m.params.url}`, 'serve-panel');
98-
vscode.env.openExternal(vscode.Uri.parse(m.params.url));
63+
vscode.env.openExternal(vscode.Uri.parse(m.data.url));
9964
break;
10065
}
10166

src/tailscale/cli.ts

Lines changed: 26 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -221,83 +221,52 @@ export class Tailscale {
221221
}
222222

223223
async serveStatus(): Promise<ServeStatus> {
224-
if (!this.url) {
225-
throw new Error('uninitialized client');
226-
}
227-
try {
228-
const resp = await fetch(`${this.url}/serve`, {
229-
headers: {
230-
Authorization: 'Basic ' + this.authkey,
231-
},
232-
});
233-
234-
const status = (await resp.json()) as ServeStatus;
235-
return status;
236-
} catch (e) {
237-
Logger.error(`error calling status: ${JSON.stringify(e, null, 2)}`);
238-
throw e;
239-
}
224+
return (await this.performFetch('/serve')) as ServeStatus;
240225
}
241226

242227
async serveAdd(p: ServeParams) {
243-
if (!this.url) {
244-
throw new Error('uninitialized client');
245-
}
246-
try {
247-
const resp = await fetch(`${this.url}/serve`, {
248-
method: 'POST',
249-
headers: {
250-
Authorization: 'Basic ' + this.authkey,
251-
},
252-
body: JSON.stringify(p),
253-
});
254-
if (!resp.ok) {
255-
throw new Error('/serve failed');
256-
}
257-
} catch (e) {
258-
Logger.info(`error adding serve: ${e}`);
259-
throw e;
260-
}
228+
await this.performFetch('/serve', 'POST', p);
261229
}
262230

263231
async serveDelete(p?: ServeParams) {
264-
if (!this.url) {
265-
throw new Error('uninitialized client');
266-
}
267-
try {
268-
const resp = await fetch(`${this.url}/serve`, {
269-
method: 'DELETE',
270-
headers: {
271-
Authorization: 'Basic ' + this.authkey,
272-
},
273-
body: JSON.stringify(p),
274-
});
275-
if (!resp.ok) {
276-
throw new Error('/serve failed');
277-
}
278-
} catch (e) {
279-
Logger.info(`error deleting serve: ${e}`);
280-
throw e;
281-
}
232+
await this.performFetch('/serve', 'DELETE', p);
282233
}
283234

284235
async setFunnel(port: number, on: boolean) {
236+
await this.performFetch('/funnel', 'POST', { port, on });
237+
}
238+
239+
async performFetch(endpoint: string, method = 'GET', body?: unknown) {
285240
if (!this.url) {
286241
throw new Error('uninitialized client');
287242
}
243+
288244
try {
289-
const resp = await fetch(`${this.url}/funnel`, {
290-
method: 'POST',
245+
const resp = await fetch(`${this.url}${endpoint}`, {
246+
method,
291247
headers: {
292248
Authorization: 'Basic ' + this.authkey,
293249
},
294-
body: JSON.stringify({ port, on }),
250+
body: body !== undefined && typeof body === 'object' ? JSON.stringify(body) : undefined,
295251
});
252+
296253
if (!resp.ok) {
297-
throw new Error('/serve failed');
254+
Logger.error(`${endpoint} failed: ${JSON.stringify(resp)}`);
255+
throw new Error(`${endpoint} failed`);
298256
}
257+
258+
const text = await resp.text();
259+
260+
try {
261+
return JSON.parse(text);
262+
// eslint-disable-next-line no-empty
263+
} catch {
264+
Logger.error(`failed to parse json: ${text}`);
265+
}
266+
267+
return text;
299268
} catch (e) {
300-
Logger.info(`error deleting serve: ${e}`);
269+
Logger.info(`error in ${method} ${endpoint}: ${e}`);
301270
throw e;
302271
}
303272
}

src/types.ts

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -80,73 +80,69 @@ export interface Version {
8080
* Messages sent from the webview to the extension.
8181
*/
8282

83-
interface RefreshState {
84-
type: 'refreshState';
83+
interface RequestBase {
84+
id?: number;
85+
type: string;
86+
data?: unknown;
8587
}
8688

87-
interface DeleteServe {
88-
type: 'deleteServe';
89-
params: ServeParams;
89+
interface RelayRequestBase extends RequestBase {
90+
type: 'relayRequest';
91+
endpoint: string;
92+
method: string;
9093
}
9194

92-
interface AddServe {
93-
type: 'addServe';
94-
params: ServeParams;
95+
interface RelayServeRequest extends RelayRequestBase {
96+
endpoint: '/serve';
97+
method: 'GET' | 'POST' | 'DELETE';
9598
}
9699

97-
interface ResetServe {
98-
type: 'resetServe';
99-
}
100-
101-
interface SetFunnel {
102-
type: 'setFunnel';
103-
params: {
104-
port: string;
105-
allow: boolean;
106-
};
107-
}
108-
109-
interface WriteToClipboard {
100+
interface WriteToClipboard extends RequestBase {
110101
type: 'writeToClipboard';
111-
params: {
102+
data: {
112103
text: string;
113104
};
114105
}
115106

116-
interface OpenLink {
107+
interface OpenLink extends RequestBase {
117108
type: 'openLink';
118-
params: {
109+
data: {
119110
url: string;
120111
};
121112
}
122113

123-
export type Message =
124-
| RefreshState
125-
| DeleteServe
126-
| AddServe
127-
| ResetServe
128-
| SetFunnel
129-
| WriteToClipboard
130-
| OpenLink
131-
| SudoPrompt;
132-
133114
interface SudoPrompt {
115+
id?: number;
134116
type: 'sudoPrompt';
135117
operation: 'add' | 'delete';
136118
params?: ServeParams;
137119
}
138120

121+
export type Message = RelayServeRequest | WriteToClipboard | OpenLink | SudoPrompt;
122+
export type MessageWithId = Omit<Message, 'id'> & { id: number };
123+
139124
/**
140125
* Messages sent from the extension to the webview.
141126
*/
142127

143-
interface UpdateState {
144-
type: 'updateState';
145-
state: ServeConfig;
128+
interface ResponseBase {
129+
id?: number;
130+
type: string;
131+
data?: unknown;
132+
error?: string;
133+
}
134+
135+
interface RelayResponseBase extends Omit<RelayRequestBase, 'type'>, Omit<ResponseBase, 'type'> {
136+
id?: number;
137+
endpoint: string;
138+
method: string;
139+
body?: unknown;
140+
error?: string;
146141
}
147142

148-
interface RefreshState {
149-
type: 'refreshState';
143+
export interface RelayServeResponse extends RelayResponseBase {
144+
type: 'relayResponse';
145+
body?: ServeStatus;
150146
}
151147

152148
interface WebpackOk {
@@ -161,7 +157,8 @@ interface WebpackStillOk {
161157
type: 'webpackStillOk';
162158
}
163159

164-
export type WebviewData = UpdateState | RefreshState | WebpackOk | WebpackInvalid | WebpackStillOk;
160+
export type Responses = RelayServeResponse;
161+
export type WebviewData = Responses | WebpackOk | WebpackInvalid | WebpackStillOk;
165162
export type WebviewEvent = Event & { data: WebviewData };
166163

167164
export interface NewPortNotification {

src/vscode-api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class VSCodeWrapper {
1111
public writeToClipboard(text: string): void {
1212
this.postMessage({
1313
type: 'writeToClipboard',
14-
params: {
14+
data: {
1515
text,
1616
},
1717
});
@@ -20,7 +20,7 @@ class VSCodeWrapper {
2020
public openLink(url: string): void {
2121
this.postMessage({
2222
type: 'openLink',
23-
params: {
23+
data: {
2424
url,
2525
},
2626
});

0 commit comments

Comments
 (0)