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:
commit
f181632913
@ -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 && (
|
||||
|
@ -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: {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user