1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-25 11:42:30 +01:00

Merge pull request #144 from spacebarchat/feat/instancepicker

Instance picker
This commit is contained in:
Puyodead1 2023-08-08 21:26:42 -04:00 committed by GitHub
commit f181632913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 3 deletions

View File

@ -27,6 +27,8 @@ import HCaptcha, { HeaderContainer } from "../components/HCaptcha";
import MFA from "../components/MFA";
import ForgotPasswordModal from "../components/modals/ForgotPasswordModal";
import { AUTH_NO_BRANDING, useAppStore } from "../stores/AppStore";
import { Globals } from "../utils/Globals";
import REST from "../utils/REST";
import {
IAPILoginRequest,
IAPILoginResponse,
@ -38,6 +40,7 @@ import { messageFromFieldError } from "../utils/messageFromFieldError";
type FormValues = {
login: string;
password: string;
instance: string;
captcha_key?: string;
};
@ -49,6 +52,7 @@ function LoginPage() {
const [mfaData, setMfaData] =
React.useState<IAPILoginResponseMFARequired>();
const captchaRef = React.useRef<HCaptchaLib>(null);
const [debounce, setDebounce] = React.useState<NodeJS.Timeout | null>(null);
const { openModal } = useModals();
const {
@ -57,6 +61,7 @@ function LoginPage() {
formState: { errors },
setError,
setValue,
clearErrors,
} = useForm<FormValues>();
const resetCaptcha = () => {
@ -64,6 +69,14 @@ function LoginPage() {
setValue("captcha_key", undefined);
};
const getValidURL = (url: string) => {
try {
return new URL(url);
} catch (e) {
return undefined;
}
};
const onSubmit = handleSubmit((data) => {
setLoading(true);
setCaptchaSiteKey(undefined);
@ -71,7 +84,8 @@ function LoginPage() {
app.rest
.post<IAPILoginRequest, IAPILoginResponse>(Routes.login(), {
...data,
login: data.login,
password: data.password,
undelete: false,
})
.then((r) => {
@ -157,6 +171,30 @@ function LoginPage() {
onSubmit();
};
const handleInstanceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// set as validating
if (debounce) clearTimeout(debounce);
const doRequest = async () => {
const url = getValidURL(e.target.value);
if (!url) return;
const endpoints = await REST.getEndpointsFromDomain(url);
if (!endpoints)
return setError("instance", {
type: "manual",
message: "Instance could not be resolved",
});
console.debug(`Instance lookup has set routes to`, endpoints);
Globals.routeSettings = endpoints; // hmm
Globals.save();
clearErrors("instance");
};
setDebounce(setTimeout(doRequest, 500));
};
const forgotPassword = () => {
openModal(ForgotPasswordModal);
};
@ -196,6 +234,33 @@ function LoginPage() {
marginBottom={true}
style={{ marginTop: 0 }}
>
<LabelWrapper error={!!errors.instance}>
<InputLabel>Instance</InputLabel>
{errors.instance && (
<InputErrorText>
<>
<Divider>-</Divider>
{errors.instance.message}
</>
</InputErrorText>
)}
</LabelWrapper>
<InputWrapper>
<Input
type="url"
{...register("instance", {
required: true,
value: Globals.routeSettings.wellknown,
})}
placeholder="Instance Root URL"
onChange={handleInstanceChange}
error={!!errors.instance}
disabled={loading}
/>
</InputWrapper>
</InputContainer>
<InputContainer marginBottom={false}>
<LabelWrapper error={!!errors.login}>
<InputLabel>Email</InputLabel>
{errors.login && (

View File

@ -1,13 +1,15 @@
interface RouteSettings {
export interface RouteSettings {
api: string;
cdn: string;
gateway: string;
wellknown: string;
}
export const DefaultRouteSettings: RouteSettings = {
api: "https://api.old.server.spacebar.chat/api",
cdn: "https://cdn.old.server.spacebar.chat",
gateway: "wss://gateway.old.server.spacebar.chat",
wellknown: "https://spacebar.chat",
};
export const Globals: {

View File

@ -3,7 +3,7 @@
// import {DomainStore} from '../stores/DomainStore';
import AppStore from "../stores/AppStore";
import { Globals } from "./Globals";
import { Globals, RouteSettings } from "./Globals";
export default class REST {
private app: AppStore;
@ -27,6 +27,47 @@ export default class REST {
}
}
public static async getEndpointsFromDomain(
url: URL,
): Promise<RouteSettings | null> {
let endpoints = await this.getInstanceDomains(url);
if (endpoints) return { ...endpoints, wellknown: url.toString() };
// get endpoints from .well-known
let wellKnown;
try {
wellKnown = await fetch(`${url.origin}/.well-known/spacebar`)
.then((x) => x.json())
.then((x) => new URL(x.api));
} catch (e) {
return null;
}
// well-known was found
endpoints = await this.getInstanceDomains(wellKnown);
if (!endpoints) return null;
return { ...endpoints, wellknown: url.toString() };
}
static async getInstanceDomains(
url: URL,
): Promise<Omit<RouteSettings, "wellknown"> | null> {
try {
const endpoints = await fetch(
`${url.toString()}${
url.pathname.includes("api") ? "" : "api"
}/policies/instance/domains`,
).then((x) => x.json());
return {
api: endpoints.apiEndpoint,
gateway: endpoints.gateway,
cdn: endpoints.cdn,
};
} catch (e) {
return null;
}
}
public static makeAPIUrl(
path: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any