From 5fa1a5e661ab2f9cd921213f4b814859fca985e8 Mon Sep 17 00:00:00 2001 From: Alex Weininger Date: Fri, 30 Apr 2021 05:18:12 -0700 Subject: [PATCH] Allow users to specify communication with self-signed certificates to fix connection to Appwrite on local machine (#10) * fix self signed certs * add health request time out of 10 seconds --- CHANGELOG.md | 4 +++ README.md | 1 + package.json | 2 +- src/appwrite.d.ts | 25 +++++++++-------- src/client.ts | 4 +-- src/settings.ts | 1 + src/tree/health/HealthTreeItemProvider.ts | 7 +++-- src/ui/AddProjectWizard.ts | 34 +++++++++++++++++------ src/utils/promiseWithTimeout.ts | 11 ++++++++ 9 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 src/utils/promiseWithTimeout.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 14eaed8..d8c0371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Unreleased] +## [0.0.6] - 2021-4-30 +### Fixed +- Fixed a bug where the extension could not connect to Appwrite instances over localhost beacuse of self-signed certificates. + ## [0.0.5] - 2021-4-30 ### Fixed - Sometimes views would not refresh after adding/removing a project [PR](https://github.com/streamlux/vscode-appwrite/pull/7) diff --git a/README.md b/README.md index 2ef3ce0..98f3339 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ After connecting to an Appwrite project, your `appwrite.projects` setting will c "endpoint": "https://[Domain]/v1", "projectId": "[Project ID]", "secret": "API key with all scopes", + "selfSigned": "boolean", // set to true if you're connecting to Appwrite over localhost "nickname": "My project" } ``` diff --git a/package.json b/package.json index 8241e85..c830aa9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-appwrite", "displayName": "Appwrite", "description": "Manage your Appwrite resources right from VS Code!", - "version": "0.0.5", + "version": "0.0.6", "engines": { "vscode": "^1.55.0" }, diff --git a/src/appwrite.d.ts b/src/appwrite.d.ts index 96c72f5..e84fab3 100644 --- a/src/appwrite.d.ts +++ b/src/appwrite.d.ts @@ -243,18 +243,18 @@ type Membership = { }; export type FilesList = { - sum: number; - files: File[]; + sum: number; + files: File[]; }; export type File = { - '$id': string; - '$permissions': Permissions; - name: string; - dateCreated: number; - signature: string; - mimeType: string; - sizeOriginal: number; + $id: string; + $permissions: Permissions; + name: string; + dateCreated: number; + signature: string; + mimeType: string; + sizeOriginal: number; }; export type Collection = { @@ -266,14 +266,14 @@ export type Collection = { rules: Rule[]; }; -export type CreatedCollection = Partial & Pick; +export type CreatedCollection = Partial & Pick; export type CollectionsList = { sum: number; collections: Collection[]; }; -export type CreatedRule = Omit; +export type CreatedRule = Omit; export type Rule = { $id: string; @@ -299,6 +299,7 @@ export type Client = { setProject: (projectId: string) => Client; // Your secret API key setKey: (key: string) => Client; + setSelfSigned: (value: boolean) => void; }; export type UsersClient = { deleteUser: (id: string) => Promise; @@ -358,7 +359,7 @@ export type StorageClient = { createFile: (file: any, read: string[], write: string[]) => Promise; listFiles: () => Promise; getFile: (fileId: string) => Promise; -} +}; export type SDK = { Client: new () => Client; diff --git a/src/client.ts b/src/client.ts index 3313fcb..0772412 100644 --- a/src/client.ts +++ b/src/client.ts @@ -13,10 +13,10 @@ export let healthClient: Health | undefined; export let databaseClient: Database | undefined; export let storageClient: Storage | undefined; -function initAppwriteClient({ endpoint, projectId, secret }: AppwriteProjectConfiguration) { +function initAppwriteClient({ endpoint, projectId, secret, selfSigned }: AppwriteProjectConfiguration) { client = new AppwriteSDK.Client(); clientConfig = { endpoint, projectId, secret }; - client.setEndpoint(endpoint).setProject(projectId).setKey(secret); + client.setEndpoint(endpoint).setProject(projectId).setKey(secret).setSelfSigned(selfSigned); usersClient = new Users(client); healthClient = new Health(client); diff --git a/src/settings.ts b/src/settings.ts index 55bd14b..72a7474 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -6,6 +6,7 @@ export type AppwriteProjectConfiguration = { endpoint: string; console?: string; projectId: string; + selfSigned: boolean; secret: string; }; diff --git a/src/tree/health/HealthTreeItemProvider.ts b/src/tree/health/HealthTreeItemProvider.ts index 95970b3..bbb95b0 100644 --- a/src/tree/health/HealthTreeItemProvider.ts +++ b/src/tree/health/HealthTreeItemProvider.ts @@ -6,6 +6,7 @@ import * as dayjs from "dayjs"; import * as localizedFormat from "dayjs/plugin/localizedFormat"; import { healthTooltips } from "../../appwrite/Health"; import { AppwriteHealth } from "../../appwrite"; +import { promiseWithTimeout } from "../../utils/promiseWithTimeout"; // dayjs.extend(relativeTime); dayjs.extend(localizedFormat); @@ -27,14 +28,16 @@ export class HealthTreeItemProvider implements vscode.TreeDataProvider { - if (healthClient === undefined) { return []; } // get children for root if (element === undefined) { - const health = await healthClient.checkup(); + const health = await promiseWithTimeout(10000, async () => await healthClient?.checkup(), 'Health request timed out'); + if (health === undefined) { + return []; + } ext.outputChannel?.append(JSON.stringify(health, null, 4)); const healthItems = Object.entries(health).map(([service, status]) => { return new HealthTreeItem(service, status, healthTooltips[service as keyof AppwriteHealth]); diff --git a/src/ui/AddProjectWizard.ts b/src/ui/AddProjectWizard.ts index 8b36ed5..8e4d84e 100644 --- a/src/ui/AddProjectWizard.ts +++ b/src/ui/AddProjectWizard.ts @@ -1,38 +1,54 @@ import { window } from "vscode"; -import { AppwriteProjectConfiguration } from "../settings"; +import { AppwriteProjectConfiguration, getActiveProjectConfiguration } from "../settings"; export async function addProjectWizard(): Promise { + const config = await getActiveProjectConfiguration(); const endpoint = await window.showInputBox({ placeHolder: "Endpoint", - prompt: "Enter your Appwrite API endping", - ignoreFocusOut: true + value: config?.endpoint ?? "https://localhost/v1", + valueSelection: undefined, + prompt: "Enter your Appwrite API endpoint (ex: https://localhost/v1)", + ignoreFocusOut: true, }); if (endpoint === undefined) { return; } const projectId = await window.showInputBox({ placeHolder: "Project Id", - prompt: "Enter your Appwrite project id", - ignoreFocusOut: true + prompt: "Enter your Appwrite project id (ex: 5df5acd0d48c2)", + ignoreFocusOut: true, }); if (projectId === undefined) { return; } const secret = await window.showInputBox({ placeHolder: "API key secret", - prompt: "Enter your Appwrite API key secret", - ignoreFocusOut: true + prompt: "Enter your Appwrite API key secret (with all scopes)", + ignoreFocusOut: true, }); if (secret === undefined) { return; } + const selfSigned = await window.showQuickPick( + [ + { label: "Yes", description: "If running Appwrite on localhost, or local IP" }, + { label: "No", description: "If connecting to a remote Appwrite instance" }, + ], + { + placeHolder: "Allow communication with self-signed SSL certificates? (Select 'Yes' for connecting to Appwrite on localhost)", + ignoreFocusOut: true, + } + ); + if (selfSigned === undefined) { + return; + } const nickname = await window.showInputBox({ prompt: "(Optional) Project name", - ignoreFocusOut: true + ignoreFocusOut: true, }); if (endpoint && projectId && secret) { - return { endpoint, projectId, secret, nickname }; + return { endpoint, projectId, secret, nickname, selfSigned: selfSigned.label === "Yes" }; } return undefined; } diff --git a/src/utils/promiseWithTimeout.ts b/src/utils/promiseWithTimeout.ts new file mode 100644 index 0000000..321664a --- /dev/null +++ b/src/utils/promiseWithTimeout.ts @@ -0,0 +1,11 @@ +export const promiseWithTimeout = (timeoutMs: number, promise: () => Promise, failureMessage?: string): Promise => { + let timeoutHandle: NodeJS.Timeout; + const timeoutPromise = new Promise((resolve, reject) => { + timeoutHandle = setTimeout(() => reject(new Error(failureMessage)), timeoutMs); + }); + + return Promise.race([promise(), timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle); + return result; + }); +};