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
This commit is contained in:
Alex Weininger 2021-04-30 05:18:12 -07:00 committed by GitHub
parent 4886416bae
commit 5fa1a5e661
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 63 additions and 26 deletions

View file

@ -6,6 +6,10 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
## [Unreleased] ## [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 ## [0.0.5] - 2021-4-30
### Fixed ### Fixed
- Sometimes views would not refresh after adding/removing a project [PR](https://github.com/streamlux/vscode-appwrite/pull/7) - Sometimes views would not refresh after adding/removing a project [PR](https://github.com/streamlux/vscode-appwrite/pull/7)

View file

@ -58,6 +58,7 @@ After connecting to an Appwrite project, your `appwrite.projects` setting will c
"endpoint": "https://[Domain]/v1", "endpoint": "https://[Domain]/v1",
"projectId": "[Project ID]", "projectId": "[Project ID]",
"secret": "API key with all scopes", "secret": "API key with all scopes",
"selfSigned": "boolean", // set to true if you're connecting to Appwrite over localhost
"nickname": "My project" "nickname": "My project"
} }
``` ```

View file

@ -2,7 +2,7 @@
"name": "vscode-appwrite", "name": "vscode-appwrite",
"displayName": "Appwrite", "displayName": "Appwrite",
"description": "Manage your Appwrite resources right from VS Code!", "description": "Manage your Appwrite resources right from VS Code!",
"version": "0.0.5", "version": "0.0.6",
"engines": { "engines": {
"vscode": "^1.55.0" "vscode": "^1.55.0"
}, },

25
src/appwrite.d.ts vendored
View file

@ -243,18 +243,18 @@ type Membership = {
}; };
export type FilesList = { export type FilesList = {
sum: number; sum: number;
files: File[]; files: File[];
}; };
export type File = { export type File = {
'$id': string; $id: string;
'$permissions': Permissions; $permissions: Permissions;
name: string; name: string;
dateCreated: number; dateCreated: number;
signature: string; signature: string;
mimeType: string; mimeType: string;
sizeOriginal: number; sizeOriginal: number;
}; };
export type Collection = { export type Collection = {
@ -266,14 +266,14 @@ export type Collection = {
rules: Rule[]; rules: Rule[];
}; };
export type CreatedCollection = Partial<Collection> & Pick<Collection, 'name'>; export type CreatedCollection = Partial<Collection> & Pick<Collection, "name">;
export type CollectionsList = { export type CollectionsList = {
sum: number; sum: number;
collections: Collection[]; collections: Collection[];
}; };
export type CreatedRule = Omit<Rule, '$id' | '$collection' | 'default' | 'list'>; export type CreatedRule = Omit<Rule, "$id" | "$collection" | "default" | "list">;
export type Rule = { export type Rule = {
$id: string; $id: string;
@ -299,6 +299,7 @@ export type Client = {
setProject: (projectId: string) => Client; setProject: (projectId: string) => Client;
// Your secret API key // Your secret API key
setKey: (key: string) => Client; setKey: (key: string) => Client;
setSelfSigned: (value: boolean) => void;
}; };
export type UsersClient = { export type UsersClient = {
deleteUser: (id: string) => Promise<any>; deleteUser: (id: string) => Promise<any>;
@ -358,7 +359,7 @@ export type StorageClient = {
createFile: (file: any, read: string[], write: string[]) => Promise<any>; createFile: (file: any, read: string[], write: string[]) => Promise<any>;
listFiles: () => Promise<any>; listFiles: () => Promise<any>;
getFile: (fileId: string) => Promise<any>; getFile: (fileId: string) => Promise<any>;
} };
export type SDK = { export type SDK = {
Client: new () => Client; Client: new () => Client;

View file

@ -13,10 +13,10 @@ export let healthClient: Health | undefined;
export let databaseClient: Database | undefined; export let databaseClient: Database | undefined;
export let storageClient: Storage | undefined; export let storageClient: Storage | undefined;
function initAppwriteClient({ endpoint, projectId, secret }: AppwriteProjectConfiguration) { function initAppwriteClient({ endpoint, projectId, secret, selfSigned }: AppwriteProjectConfiguration) {
client = new AppwriteSDK.Client(); client = new AppwriteSDK.Client();
clientConfig = { endpoint, projectId, secret }; clientConfig = { endpoint, projectId, secret };
client.setEndpoint(endpoint).setProject(projectId).setKey(secret); client.setEndpoint(endpoint).setProject(projectId).setKey(secret).setSelfSigned(selfSigned);
usersClient = new Users(client); usersClient = new Users(client);
healthClient = new Health(client); healthClient = new Health(client);

View file

@ -6,6 +6,7 @@ export type AppwriteProjectConfiguration = {
endpoint: string; endpoint: string;
console?: string; console?: string;
projectId: string; projectId: string;
selfSigned: boolean;
secret: string; secret: string;
}; };

View file

@ -6,6 +6,7 @@ import * as dayjs from "dayjs";
import * as localizedFormat from "dayjs/plugin/localizedFormat"; import * as localizedFormat from "dayjs/plugin/localizedFormat";
import { healthTooltips } from "../../appwrite/Health"; import { healthTooltips } from "../../appwrite/Health";
import { AppwriteHealth } from "../../appwrite"; import { AppwriteHealth } from "../../appwrite";
import { promiseWithTimeout } from "../../utils/promiseWithTimeout";
// dayjs.extend(relativeTime); // dayjs.extend(relativeTime);
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
@ -27,14 +28,16 @@ export class HealthTreeItemProvider implements vscode.TreeDataProvider<vscode.Tr
} }
async getChildren(element?: HealthTreeItem): Promise<vscode.TreeItem[]> { async getChildren(element?: HealthTreeItem): Promise<vscode.TreeItem[]> {
if (healthClient === undefined) { if (healthClient === undefined) {
return []; return [];
} }
// get children for root // get children for root
if (element === undefined) { if (element === undefined) {
const health = await healthClient.checkup(); const health = await promiseWithTimeout<AppwriteHealth | undefined>(10000, async () => await healthClient?.checkup(), 'Health request timed out');
if (health === undefined) {
return [];
}
ext.outputChannel?.append(JSON.stringify(health, null, 4)); ext.outputChannel?.append(JSON.stringify(health, null, 4));
const healthItems = Object.entries(health).map(([service, status]) => { const healthItems = Object.entries(health).map(([service, status]) => {
return new HealthTreeItem(service, status, healthTooltips[service as keyof AppwriteHealth]); return new HealthTreeItem(service, status, healthTooltips[service as keyof AppwriteHealth]);

View file

@ -1,38 +1,54 @@
import { window } from "vscode"; import { window } from "vscode";
import { AppwriteProjectConfiguration } from "../settings"; import { AppwriteProjectConfiguration, getActiveProjectConfiguration } from "../settings";
export async function addProjectWizard(): Promise<AppwriteProjectConfiguration | undefined> { export async function addProjectWizard(): Promise<AppwriteProjectConfiguration | undefined> {
const config = await getActiveProjectConfiguration();
const endpoint = await window.showInputBox({ const endpoint = await window.showInputBox({
placeHolder: "Endpoint", placeHolder: "Endpoint",
prompt: "Enter your Appwrite API endping", value: config?.endpoint ?? "https://localhost/v1",
ignoreFocusOut: true valueSelection: undefined,
prompt: "Enter your Appwrite API endpoint (ex: https://localhost/v1)",
ignoreFocusOut: true,
}); });
if (endpoint === undefined) { if (endpoint === undefined) {
return; return;
} }
const projectId = await window.showInputBox({ const projectId = await window.showInputBox({
placeHolder: "Project Id", placeHolder: "Project Id",
prompt: "Enter your Appwrite project id", prompt: "Enter your Appwrite project id (ex: 5df5acd0d48c2)",
ignoreFocusOut: true ignoreFocusOut: true,
}); });
if (projectId === undefined) { if (projectId === undefined) {
return; return;
} }
const secret = await window.showInputBox({ const secret = await window.showInputBox({
placeHolder: "API key secret", placeHolder: "API key secret",
prompt: "Enter your Appwrite API key secret", prompt: "Enter your Appwrite API key secret (with all scopes)",
ignoreFocusOut: true ignoreFocusOut: true,
}); });
if (secret === undefined) { if (secret === undefined) {
return; 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({ const nickname = await window.showInputBox({
prompt: "(Optional) Project name", prompt: "(Optional) Project name",
ignoreFocusOut: true ignoreFocusOut: true,
}); });
if (endpoint && projectId && secret) { if (endpoint && projectId && secret) {
return { endpoint, projectId, secret, nickname }; return { endpoint, projectId, secret, nickname, selfSigned: selfSigned.label === "Yes" };
} }
return undefined; return undefined;
} }

View file

@ -0,0 +1,11 @@
export const promiseWithTimeout = <T>(timeoutMs: number, promise: () => Promise<T>, failureMessage?: string): Promise<T> => {
let timeoutHandle: NodeJS.Timeout;
const timeoutPromise = new Promise<never>((resolve, reject) => {
timeoutHandle = setTimeout(() => reject(new Error(failureMessage)), timeoutMs);
});
return Promise.race([promise(), timeoutPromise]).then((result) => {
clearTimeout(timeoutHandle);
return result;
});
};