fix(desktop): accept instance hosts without an https prefix
This commit is contained in:
1 parent
d81ee60c58
commit
de7e006e57
9 files changed
+58
-10
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { useState, type FormEvent } from "react";
|
||||
import { LockKeyhole } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { resolveInstanceUrlInput } from "@edgeever/shared";
|
||||
import { normalizeInstanceUrl } from "@edgeever/shared";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { GitHubRepositoryLink } from "@/components/GitHubRepositoryLink";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -39,7 +39,7 @@ export const LoginScreen = ({ error, instanceUrl: initialInstanceUrl, isSubmitti
|
||||
}
|
||||
|
||||
onSubmit({
|
||||
...(initialInstanceUrl !== undefined ? { instanceUrl: resolveInstanceUrlInput(instanceUrl) } : {}),
|
||||
...(initialInstanceUrl !== undefined ? { instanceUrl: normalizeInstanceUrl(instanceUrl) } : {}),
|
||||
username: username.trim(),
|
||||
password,
|
||||
});
|
||||
@@ -83,11 +83,14 @@ export const LoginScreen = ({ error, instanceUrl: initialInstanceUrl, isSubmitti
|
||||
<label className="block">
|
||||
<span className="mb-2 block text-sm font-semibold text-slate-700">{t("login.desktopInstanceUrl")}</span>
|
||||
<Input
|
||||
autoCapitalize="none"
|
||||
autoComplete="url"
|
||||
autoCorrect="off"
|
||||
className="h-11 rounded-lg bg-slate-50/50 px-3.5 focus-visible:bg-card focus-visible:ring-2 focus-visible:ring-emerald-500/20"
|
||||
inputMode="url"
|
||||
placeholder={t("login.instanceUrlPlaceholder")}
|
||||
required
|
||||
type="url"
|
||||
spellCheck={false}
|
||||
value={instanceUrl}
|
||||
onChange={(event) => setInstanceUrl(event.target.value)}
|
||||
/>
|
||||
|
||||
@@ -74,7 +74,7 @@ const {
|
||||
|
||||
describe("desktop instance setup", () => {
|
||||
test("can retry with a valid URL after invalid input", async () => {
|
||||
await expect(saveDesktopApiBaseUrl("not-an-instance")).rejects.toThrow();
|
||||
await expect(saveDesktopApiBaseUrl("ftp://example.com")).rejects.toThrow();
|
||||
|
||||
const saving = saveDesktopApiBaseUrl(" https://notes.example.com/ ");
|
||||
await Promise.resolve();
|
||||
@@ -100,6 +100,25 @@ describe("desktop instance setup", () => {
|
||||
await expect(saving).resolves.toBe("https://demo.edgeever.org");
|
||||
});
|
||||
|
||||
test("prefixes https when the instance host has no protocol", async () => {
|
||||
calls.length = 0;
|
||||
const saving = saveDesktopApiBaseUrl(" example.workers.dev/ ");
|
||||
await Promise.resolve();
|
||||
expect(calls).toEqual([["bridge:start", "https://example.workers.dev"]]);
|
||||
completeSave();
|
||||
await expect(saving).resolves.toBe("https://example.workers.dev");
|
||||
expect(storage.get(DESKTOP_API_BASE_URL_STORAGE_KEY)).toBe("https://example.workers.dev");
|
||||
});
|
||||
|
||||
test("keeps an explicit http instance URL", async () => {
|
||||
calls.length = 0;
|
||||
const saving = saveDesktopApiBaseUrl("http://127.0.0.1:8787/");
|
||||
await Promise.resolve();
|
||||
expect(calls).toEqual([["bridge:start", "http://127.0.0.1:8787"]]);
|
||||
completeSave();
|
||||
await expect(saving).resolves.toBe("http://127.0.0.1:8787");
|
||||
});
|
||||
|
||||
test("clears the cached session when the login form changes instances", async () => {
|
||||
calls.length = 0;
|
||||
window.edgeeverDesktop.apiBaseUrl = "https://notes.example.com";
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
type EdgeEverClientRequestContext,
|
||||
} from "@edgeever/client";
|
||||
import type { AuthSession } from "@edgeever/shared";
|
||||
import { resolveInstanceUrlInput } from "@edgeever/shared";
|
||||
import { normalizeInstanceUrl } from "@edgeever/shared";
|
||||
import { createClientUuid } from "./client-id";
|
||||
|
||||
export { ApiRequestError };
|
||||
@@ -121,7 +121,7 @@ export class DesktopInstanceUrlError extends Error {
|
||||
}
|
||||
|
||||
export const saveDesktopApiBaseUrl = async (value: string) => {
|
||||
const normalized = resolveInstanceUrlInput(value).replace(/\/$/, "");
|
||||
const normalized = normalizeInstanceUrl(value);
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(normalized);
|
||||
|
||||
@@ -262,7 +262,7 @@ export const enUS = {
|
||||
desktopInstanceUrl: "Instance URL",
|
||||
desktopInstanceContinue: "Continue",
|
||||
desktopChangeInstance: "Change instance URL",
|
||||
desktopInstanceUrlInvalid: "Enter a valid instance URL beginning with http:// or https://.",
|
||||
desktopInstanceUrlInvalid: "Enter a valid instance URL.",
|
||||
instanceUrlPlaceholder: "https://demo.edgeever.org",
|
||||
},
|
||||
nav: {
|
||||
|
||||
@@ -262,7 +262,7 @@ export const ja = {
|
||||
desktopInstanceUrl: "インスタンス URL",
|
||||
desktopInstanceContinue: "続ける",
|
||||
desktopChangeInstance: "インスタンス URL を変更",
|
||||
desktopInstanceUrlInvalid: "http:// または https:// で始まる有効なインスタンス URL を入力してください。",
|
||||
desktopInstanceUrlInvalid: "有効なインスタンス URL を入力してください。",
|
||||
instanceUrlPlaceholder: "https://demo.edgeever.org",
|
||||
},
|
||||
nav: {
|
||||
|
||||
@@ -262,7 +262,7 @@ export const zhCN = {
|
||||
desktopInstanceUrl: "实例地址",
|
||||
desktopInstanceContinue: "继续",
|
||||
desktopChangeInstance: "更换实例地址",
|
||||
desktopInstanceUrlInvalid: "请输入以 http:// 或 https:// 开头的有效实例地址。",
|
||||
desktopInstanceUrlInvalid: "请输入有效的实例地址。",
|
||||
instanceUrlPlaceholder: "https://demo.edgeever.org",
|
||||
},
|
||||
nav: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { PUBLIC_DEMO_INSTANCE_URL, resolveInstanceUrlInput } from "./public-demo.ts";
|
||||
import { PUBLIC_DEMO_INSTANCE_URL, normalizeInstanceUrl, resolveInstanceUrlInput } from "./public-demo.ts";
|
||||
|
||||
describe("public demo instance URL", () => {
|
||||
test("maps the App Review 'demo' alias to the public instance", () => {
|
||||
@@ -16,4 +16,14 @@ describe("public demo instance URL", () => {
|
||||
expect(resolveInstanceUrlInput("demo.example.com")).toBe("demo.example.com");
|
||||
expect(resolveInstanceUrlInput("")).toBe("");
|
||||
});
|
||||
|
||||
test("prefixes https when a host has no protocol", () => {
|
||||
expect(normalizeInstanceUrl("example.workers.dev")).toBe("https://example.workers.dev");
|
||||
expect(normalizeInstanceUrl(" example.workers.dev/ ")).toBe("https://example.workers.dev");
|
||||
expect(normalizeInstanceUrl("https://notes.example.com/")).toBe("https://notes.example.com");
|
||||
expect(normalizeInstanceUrl("http://127.0.0.1:8787")).toBe("http://127.0.0.1:8787");
|
||||
expect(normalizeInstanceUrl("ftp://example.com")).toBe("ftp://example.com");
|
||||
expect(normalizeInstanceUrl("demo")).toBe(PUBLIC_DEMO_INSTANCE_URL);
|
||||
expect(normalizeInstanceUrl("")).toBe("");
|
||||
});
|
||||
});
|
||||
@@ -16,3 +16,13 @@ export const resolveInstanceUrlInput = (value: string) => {
|
||||
}
|
||||
return publicDemoAliasHost(trimmed) === "demo" ? PUBLIC_DEMO_INSTANCE_URL : trimmed;
|
||||
};
|
||||
|
||||
/** Resolve aliases, default missing schemes to https://, and drop trailing slashes. */
|
||||
export const normalizeInstanceUrl = (value: string) => {
|
||||
const resolved = resolveInstanceUrlInput(value);
|
||||
if (!resolved) {
|
||||
return resolved;
|
||||
}
|
||||
const withProtocol = /:\/\//.test(resolved) ? resolved : `https://${resolved}`;
|
||||
return withProtocol.replace(/\/+$/, "");
|
||||
};
|
||||
@@ -11,4 +11,10 @@ describe("login screen chrome", () => {
|
||||
expect(loginScreen).toContain("backdrop-blur-md");
|
||||
expect(loginScreen).not.toContain("slate-900-rgb");
|
||||
});
|
||||
|
||||
test("accepts instance hosts without a protocol", () => {
|
||||
expect(loginScreen).toContain("normalizeInstanceUrl");
|
||||
expect(loginScreen).toContain('inputMode="url"');
|
||||
expect(loginScreen).not.toContain('type="url"');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user