diff --git a/Makefile b/Makefile index ad76fe1b8c..e7da6e6b70 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ GOBIND=env PATH="$(GOBIN):$(PATH)" "$(GOMOBILE)" bind IMPORT_HOST=github.com IMPORT_PATH=$(IMPORT_HOST)/Jigsaw-Code/outline-client -.PHONY: android apple linux windows +.PHONY: android apple linux windows browser all: android apple linux windows @@ -73,3 +73,6 @@ $(XGO): go.mod go.mod: tools.go go mod tidy touch go.mod + +browser: + echo 'browser environment: nothing to do' diff --git a/src/www/app/app.ts b/src/www/app/app.ts index a22c3cd10d..e5802c0633 100644 --- a/src/www/app/app.ts +++ b/src/www/app/app.ts @@ -218,6 +218,8 @@ export class App { buttonHandler = () => { this.showErrorDetailDialog(error); }; + } else if (error instanceof errors.SessionConfigError) { + toastMessage = error.message; } else { const hasErrorDetails = Boolean(error.message || error.cause); toastMessage = this.localize('error-unexpected'); diff --git a/src/www/app/outline_server_repository/access_key_serialization.ts b/src/www/app/outline_server_repository/access_key_serialization.ts index 17dcc560ce..a3c57f7b97 100644 --- a/src/www/app/outline_server_repository/access_key_serialization.ts +++ b/src/www/app/outline_server_repository/access_key_serialization.ts @@ -36,8 +36,14 @@ export function staticKeyToShadowsocksSessionConfig(staticKey: string): Shadowso } } -function parseShadowsocksSessionConfigJson(maybeJsonText: string): ShadowsocksSessionConfig | null { - const {method, password, server, server_port, prefix} = JSON.parse(maybeJsonText); +function parseShadowsocksSessionConfigJson(responseBody: string): ShadowsocksSessionConfig | null { + const responseJson = JSON.parse(responseBody); + + if ('error' in responseJson) { + throw new errors.SessionConfigError(responseJson.error.message); + } + + const {method, password, server, server_port, prefix} = responseJson; // These are the mandatory keys. const missingKeys = []; @@ -80,6 +86,10 @@ export async function fetchShadowsocksSessionConfig(configLocation: URL): Promis return parseShadowsocksSessionConfigJson(responseBody); } catch (cause) { + if (cause instanceof errors.SessionConfigError) { + throw cause; + } + throw new errors.ServerAccessKeyInvalid('Failed to parse VPN information fetched from dynamic access key.', { cause, }); diff --git a/src/www/model/errors.ts b/src/www/model/errors.ts index a6f35d0089..09fef3ab9b 100644 --- a/src/www/model/errors.ts +++ b/src/www/model/errors.ts @@ -45,6 +45,12 @@ export class SessionConfigFetchFailed extends CustomError { } } +export class SessionConfigError extends CustomError { + constructor(message: string) { + super(message); + } +} + export class ServerAccessKeyInvalid extends CustomError { constructor(message: string, options?: {cause?: Error}) { super(message, options);