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:
parent
4886416bae
commit
5fa1a5e661
9 changed files with 63 additions and 26 deletions
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
11
src/appwrite.d.ts
vendored
11
src/appwrite.d.ts
vendored
|
@ -248,8 +248,8 @@ export type FilesList = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
11
src/utils/promiseWithTimeout.ts
Normal file
11
src/utils/promiseWithTimeout.ts
Normal 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;
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in a new issue