mirror of
https://github.com/etkecc/synapse-admin.git
synced 2024-12-04 10:18:59 +03:00
Rework configuration process
Dynamically loads `config.json` on startup. Fixes #167, #284, #449, #486 Change-Id: I9efb1079c0c88e6e0272c5fda734a367aa8f84a3
This commit is contained in:
parent
ef3836313c
commit
4b1277f653
11 changed files with 254 additions and 46 deletions
5
.env
5
.env
|
@ -1,5 +0,0 @@
|
||||||
# This setting allows to fix the homeserver.
|
|
||||||
# If you set this setting, the user will not be able to select
|
|
||||||
# the server and have to use synapse-admin with this server.
|
|
||||||
|
|
||||||
#REACT_APP_SERVER=https://yourmatrixserver.example.com
|
|
|
@ -1,13 +1,12 @@
|
||||||
# Builder
|
# Builder
|
||||||
FROM node:lts as builder
|
FROM node:lts as builder
|
||||||
LABEL org.opencontainers.image.url=https://github.com/Awesome-Technologies/synapse-admin org.opencontainers.image.source=https://github.com/Awesome-Technologies/synapse-admin
|
LABEL org.opencontainers.image.url=https://github.com/Awesome-Technologies/synapse-admin org.opencontainers.image.source=https://github.com/Awesome-Technologies/synapse-admin
|
||||||
ARG REACT_APP_SERVER
|
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY . /src
|
COPY . /src
|
||||||
RUN yarn --network-timeout=300000 install --immutable
|
RUN yarn --network-timeout=300000 install --immutable
|
||||||
RUN REACT_APP_SERVER=$REACT_APP_SERVER yarn build
|
RUN yarn build
|
||||||
|
|
||||||
|
|
||||||
# App
|
# App
|
||||||
|
|
42
README.md
42
README.md
|
@ -64,11 +64,6 @@ You have three options:
|
||||||
- download dependencies: `yarn install`
|
- download dependencies: `yarn install`
|
||||||
- start web server: `yarn start`
|
- start web server: `yarn start`
|
||||||
|
|
||||||
You can fix the homeserver, so that the user can no longer define it himself.
|
|
||||||
Either you define it at startup (e.g. `REACT_APP_SERVER=https://yourmatrixserver.example.com yarn start`)
|
|
||||||
or by editing it in the [.env](.env) file. See also the
|
|
||||||
[documentation](https://create-react-app.dev/docs/adding-custom-environment-variables/).
|
|
||||||
|
|
||||||
#### Steps for 3)
|
#### Steps for 3)
|
||||||
|
|
||||||
- run the Docker container from the public docker registry: `docker run -p 8080:80 awesometechnologies/synapse-admin` or use the [docker-compose.yml](docker-compose.yml): `docker-compose up -d`
|
- run the Docker container from the public docker registry: `docker run -p 8080:80 awesometechnologies/synapse-admin` or use the [docker-compose.yml](docker-compose.yml): `docker-compose up -d`
|
||||||
|
@ -76,8 +71,6 @@ or by editing it in the [.env](.env) file. See also the
|
||||||
> note: if you're building on an architecture other than amd64 (for example a raspberry pi), make sure to define a maximum ram for node. otherwise the build will fail.
|
> note: if you're building on an architecture other than amd64 (for example a raspberry pi), make sure to define a maximum ram for node. otherwise the build will fail.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
synapse-admin:
|
synapse-admin:
|
||||||
container_name: synapse-admin
|
container_name: synapse-admin
|
||||||
|
@ -88,7 +81,6 @@ or by editing it in the [.env](.env) file. See also the
|
||||||
# - NODE_OPTIONS="--max_old_space_size=1024"
|
# - NODE_OPTIONS="--max_old_space_size=1024"
|
||||||
# # see #266, PUBLIC_URL must be without surrounding quotation marks
|
# # see #266, PUBLIC_URL must be without surrounding quotation marks
|
||||||
# - PUBLIC_URL=/synapse-admin
|
# - PUBLIC_URL=/synapse-admin
|
||||||
# - REACT_APP_SERVER="https://matrix.example.com"
|
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@ -96,6 +88,40 @@ or by editing it in the [.env](.env) file. See also the
|
||||||
|
|
||||||
- browse to http://localhost:8080
|
- browse to http://localhost:8080
|
||||||
|
|
||||||
|
### Restricting available homeserver
|
||||||
|
|
||||||
|
You can restrict the homeserver(s), so that the user can no longer define it himself.
|
||||||
|
|
||||||
|
Edit `config.json` to restrict either to a single homeserver:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"restrictBaseUrl": "https://your-matrixs-erver.example.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
or to a list of homeservers:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"restrictBaseUrl": [
|
||||||
|
"https://your-first-matrix-server.example.com",
|
||||||
|
"https://your-second-matrix-server.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `config.json` can be injected into a Docker container using a bind mount.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
services:
|
||||||
|
synapse-admin:
|
||||||
|
...
|
||||||
|
volumes:
|
||||||
|
./config.json:/app/config.json
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
![Screenshots](./screenshots.jpg)
|
![Screenshots](./screenshots.jpg)
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
synapse-admin:
|
synapse-admin:
|
||||||
container_name: synapse-admin
|
container_name: synapse-admin
|
||||||
hostname: synapse-admin
|
hostname: synapse-admin
|
||||||
image: awesometechnologies/synapse-admin:latest
|
image: awesometechnologies/synapse-admin:latest
|
||||||
# build:
|
# build:
|
||||||
# context: .
|
# context: .
|
||||||
|
|
||||||
# to use the docker-compose as standalone without a local repo clone,
|
# to use the docker-compose as standalone without a local repo clone,
|
||||||
# replace the context definition with this:
|
# replace the context definition with this:
|
||||||
|
@ -18,9 +16,6 @@ services:
|
||||||
# - NODE_OPTIONS="--max_old_space_size=1024"
|
# - NODE_OPTIONS="--max_old_space_size=1024"
|
||||||
# default is .
|
# default is .
|
||||||
# - PUBLIC_URL=/synapse-admin
|
# - PUBLIC_URL=/synapse-admin
|
||||||
# You can use a fixed homeserver, so that the user can no longer
|
|
||||||
# define it himself
|
|
||||||
# - REACT_APP_SERVER="https://matrix.example.com"
|
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
1
public/config.json
Normal file
1
public/config.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -24,10 +24,110 @@
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>Synapse-Admin</title>
|
<title>Synapse-Admin</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS Spinner from https://projects.lukehaas.me/css-loaders/ */
|
||||||
|
|
||||||
|
.loader,
|
||||||
|
.loader:before,
|
||||||
|
.loader:after {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
color: #283593;
|
||||||
|
font-size: 11px;
|
||||||
|
text-indent: -99999em;
|
||||||
|
margin: 55px auto;
|
||||||
|
position: relative;
|
||||||
|
width: 10em;
|
||||||
|
height: 10em;
|
||||||
|
box-shadow: inset 0 0 0 1em;
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
-ms-transform: translateZ(0);
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:before,
|
||||||
|
.loader:after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:before {
|
||||||
|
width: 5.2em;
|
||||||
|
height: 10.2em;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 10.2em 0 0 10.2em;
|
||||||
|
top: -0.1em;
|
||||||
|
left: -0.1em;
|
||||||
|
-webkit-transform-origin: 5.2em 5.1em;
|
||||||
|
transform-origin: 5.2em 5.1em;
|
||||||
|
-webkit-animation: load2 2s infinite ease 1.5s;
|
||||||
|
animation: load2 2s infinite ease 1.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:after {
|
||||||
|
width: 5.2em;
|
||||||
|
height: 10.2em;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 0 10.2em 10.2em 0;
|
||||||
|
top: -0.1em;
|
||||||
|
left: 5.1em;
|
||||||
|
-webkit-transform-origin: 0px 5.1em;
|
||||||
|
transform-origin: 0px 5.1em;
|
||||||
|
-webkit-animation: load2 2s infinite ease;
|
||||||
|
animation: load2 2s infinite ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes load2 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes load2 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root">
|
||||||
|
<div class="loader-container">
|
||||||
|
<div class="loader">Loading...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!--
|
<!--
|
||||||
This HTML file is a template.
|
This HTML file is a template.
|
||||||
If you open it directly in the browser, you will see an empty page.
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
@ -46,4 +146,4 @@
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
5
src/AppContext.jsx
Normal file
5
src/AppContext.jsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
export const AppContext = createContext({});
|
||||||
|
|
||||||
|
export const useAppContext = () => useContext(AppContext);
|
|
@ -26,6 +26,8 @@ import {
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { styled } from "@mui/material/styles";
|
import { styled } from "@mui/material/styles";
|
||||||
import LockIcon from "@mui/icons-material/Lock";
|
import LockIcon from "@mui/icons-material/Lock";
|
||||||
|
|
||||||
|
import { useAppContext } from "../AppContext";
|
||||||
import {
|
import {
|
||||||
getServerVersion,
|
getServerVersion,
|
||||||
getSupportedFeatures,
|
getSupportedFeatures,
|
||||||
|
@ -92,13 +94,18 @@ const FormBox = styled(Box)(({ theme }) => ({
|
||||||
const LoginPage = () => {
|
const LoginPage = () => {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
|
const { restrictBaseUrl } = useAppContext();
|
||||||
|
const allowSingleBaseUrl = typeof restrictBaseUrl === "string";
|
||||||
|
const allowMultipleBaseUrls = Array.isArray(restrictBaseUrl);
|
||||||
|
const allowAnyBaseUrl = !(allowSingleBaseUrl || allowMultipleBaseUrls);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [supportPassAuth, setSupportPassAuth] = useState(true);
|
const [supportPassAuth, setSupportPassAuth] = useState(true);
|
||||||
const [locale, setLocale] = useLocaleState();
|
const [locale, setLocale] = useLocaleState();
|
||||||
const locales = useLocales();
|
const locales = useLocales();
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
const base_url = localStorage.getItem("base_url");
|
const base_url = allowSingleBaseUrl
|
||||||
const cfg_base_url = process.env.REACT_APP_SERVER;
|
? restrictBaseUrl
|
||||||
|
: localStorage.getItem("base_url");
|
||||||
const [ssoBaseUrl, setSSOBaseUrl] = useState("");
|
const [ssoBaseUrl, setSSOBaseUrl] = useState("");
|
||||||
const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href);
|
const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href);
|
||||||
|
|
||||||
|
@ -177,15 +184,24 @@ const LoginPage = () => {
|
||||||
const [matrixVersions, setMatrixVersions] = useState("");
|
const [matrixVersions, setMatrixVersions] = useState("");
|
||||||
|
|
||||||
const handleUsernameChange = _ => {
|
const handleUsernameChange = _ => {
|
||||||
if (formData.base_url || cfg_base_url) return;
|
if (formData.base_url || allowSingleBaseUrl) return;
|
||||||
// check if username is a full qualified userId then set base_url accordingly
|
// check if username is a full qualified userId then set base_url accordingly
|
||||||
const domain = splitMxid(formData.username)?.domain;
|
const domain = splitMxid(formData.username)?.domain;
|
||||||
if (domain) {
|
if (domain) {
|
||||||
getWellKnownUrl(domain).then(url => form.setValue("base_url", url));
|
getWellKnownUrl(domain).then(url => {
|
||||||
|
if (
|
||||||
|
allowAnyBaseUrl ||
|
||||||
|
(allowMultipleBaseUrls && restrictBaseUrl.includes(url))
|
||||||
|
)
|
||||||
|
form.setValue("base_url", url);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (formData.base_url === "" && allowMultipleBaseUrls) {
|
||||||
|
form.setValue("base_url", restrictBaseUrl[0]);
|
||||||
|
}
|
||||||
if (!isValidBaseUrl(formData.base_url)) return;
|
if (!isValidBaseUrl(formData.base_url)) return;
|
||||||
|
|
||||||
getServerVersion(formData.base_url)
|
getServerVersion(formData.base_url)
|
||||||
|
@ -215,7 +231,7 @@ const LoginPage = () => {
|
||||||
setSSOBaseUrl(supportSSO ? formData.base_url : "");
|
setSSOBaseUrl(supportSSO ? formData.base_url : "");
|
||||||
})
|
})
|
||||||
.catch(() => setSSOBaseUrl(""));
|
.catch(() => setSSOBaseUrl(""));
|
||||||
}, [formData.base_url]);
|
}, [formData.base_url, form]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -224,11 +240,11 @@ const LoginPage = () => {
|
||||||
autoFocus
|
autoFocus
|
||||||
name="username"
|
name="username"
|
||||||
label="ra.auth.username"
|
label="ra.auth.username"
|
||||||
|
autoComplete="username"
|
||||||
disabled={loading || !supportPassAuth}
|
disabled={loading || !supportPassAuth}
|
||||||
onBlur={handleUsernameChange}
|
onBlur={handleUsernameChange}
|
||||||
resettable
|
resettable
|
||||||
fullWidth
|
fullWidth
|
||||||
className="input"
|
|
||||||
validate={required()}
|
validate={required()}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -237,10 +253,10 @@ const LoginPage = () => {
|
||||||
name="password"
|
name="password"
|
||||||
label="ra.auth.password"
|
label="ra.auth.password"
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="current-password"
|
||||||
disabled={loading || !supportPassAuth}
|
disabled={loading || !supportPassAuth}
|
||||||
resettable
|
resettable
|
||||||
fullWidth
|
fullWidth
|
||||||
className="input"
|
|
||||||
validate={required()}
|
validate={required()}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -248,12 +264,21 @@ const LoginPage = () => {
|
||||||
<TextInput
|
<TextInput
|
||||||
name="base_url"
|
name="base_url"
|
||||||
label="synapseadmin.auth.base_url"
|
label="synapseadmin.auth.base_url"
|
||||||
disabled={cfg_base_url || loading}
|
select={allowMultipleBaseUrls}
|
||||||
resettable
|
autoComplete="url"
|
||||||
|
disabled={loading}
|
||||||
|
readOnly={allowSingleBaseUrl}
|
||||||
|
resettable={allowAnyBaseUrl}
|
||||||
fullWidth
|
fullWidth
|
||||||
className="input"
|
|
||||||
validate={[required(), validateBaseUrl]}
|
validate={[required(), validateBaseUrl]}
|
||||||
/>
|
>
|
||||||
|
{allowMultipleBaseUrls &&
|
||||||
|
restrictBaseUrl.map(url => (
|
||||||
|
<MenuItem key={url} value={url}>
|
||||||
|
{url}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextInput>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography className="serverVersion">{serverVersion}</Typography>
|
<Typography className="serverVersion">{serverVersion}</Typography>
|
||||||
<Typography className="matrixVersions">{matrixVersions}</Typography>
|
<Typography className="matrixVersions">{matrixVersions}</Typography>
|
||||||
|
@ -263,7 +288,7 @@ const LoginPage = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
defaultValues={{ base_url: cfg_base_url || base_url }}
|
defaultValues={{ base_url: base_url }}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
mode="onTouched"
|
mode="onTouched"
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,14 +1,71 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { render } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
import { AdminContext } from "react-admin";
|
import { AdminContext } from "react-admin";
|
||||||
import LoginPage from "./LoginPage";
|
import LoginPage from "./LoginPage";
|
||||||
|
import { AppContext } from "../AppContext";
|
||||||
|
|
||||||
describe("LoginForm", () => {
|
describe("LoginForm", () => {
|
||||||
it("renders", () => {
|
it("renders with no restriction to homeserver", () => {
|
||||||
render(
|
render(
|
||||||
<AdminContext>
|
<AdminContext>
|
||||||
<LoginPage />
|
<LoginPage />
|
||||||
</AdminContext>
|
</AdminContext>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
screen.getByText("synapseadmin.auth.welcome");
|
||||||
|
screen.getByRole("combobox", { name: "" });
|
||||||
|
screen.getByRole("textbox", { name: "ra.auth.username" });
|
||||||
|
screen.getByText("ra.auth.password");
|
||||||
|
const baseUrlInput = screen.getByRole("textbox", {
|
||||||
|
name: "synapseadmin.auth.base_url",
|
||||||
|
});
|
||||||
|
expect(baseUrlInput.className.split(" ")).not.toContain("Mui-readOnly");
|
||||||
|
screen.getByRole("button", { name: "ra.auth.sign_in" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders with single restricted homeserver", () => {
|
||||||
|
render(
|
||||||
|
<AppContext.Provider
|
||||||
|
value={{ restrictBaseUrl: "https://matrix.example.com" }}
|
||||||
|
>
|
||||||
|
<AdminContext>
|
||||||
|
<LoginPage />
|
||||||
|
</AdminContext>
|
||||||
|
</AppContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText("synapseadmin.auth.welcome");
|
||||||
|
screen.getByRole("combobox", { name: "" });
|
||||||
|
screen.getByRole("textbox", { name: "ra.auth.username" });
|
||||||
|
screen.getByText("ra.auth.password");
|
||||||
|
const baseUrlInput = screen.getByRole("textbox", {
|
||||||
|
name: "synapseadmin.auth.base_url",
|
||||||
|
});
|
||||||
|
expect(baseUrlInput.className.split(" ")).toContain("Mui-readOnly");
|
||||||
|
screen.getByRole("button", { name: "ra.auth.sign_in" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders with multiple restricted homeservers", async () => {
|
||||||
|
render(
|
||||||
|
<AppContext.Provider
|
||||||
|
value={{
|
||||||
|
restrictBaseUrl: [
|
||||||
|
"https://matrix.example.com",
|
||||||
|
"https://matrix.example.org",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AdminContext>
|
||||||
|
<LoginPage />
|
||||||
|
</AdminContext>
|
||||||
|
</AppContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText("synapseadmin.auth.welcome");
|
||||||
|
screen.getByRole("combobox", { name: "" });
|
||||||
|
screen.getByRole("textbox", { name: "ra.auth.username" });
|
||||||
|
screen.getByText("ra.auth.password");
|
||||||
|
screen.getByRole("combobox", { name: "synapseadmin.auth.base_url" });
|
||||||
|
screen.getByRole("button", { name: "ra.auth.sign_in" });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import App from "./App";
|
|
||||||
|
|
||||||
createRoot(document.getElementById("root")).render(
|
import App from "./App";
|
||||||
<React.StrictMode>
|
import { AppContext } from "./AppContext";
|
||||||
<App />
|
|
||||||
</React.StrictMode>
|
fetch("config.json")
|
||||||
);
|
.then(res => res.json())
|
||||||
|
.then(props =>
|
||||||
|
createRoot(document.getElementById("root")).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<AppContext.Provider value={props}>
|
||||||
|
<App />
|
||||||
|
</AppContext.Provider>
|
||||||
|
</React.StrictMode>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
|
@ -3,9 +3,6 @@ import { fetchUtils } from "react-admin";
|
||||||
const authProvider = {
|
const authProvider = {
|
||||||
// called when the user attempts to log in
|
// called when the user attempts to log in
|
||||||
login: async ({ base_url, username, password, loginToken }) => {
|
login: async ({ base_url, username, password, loginToken }) => {
|
||||||
// force homeserver for protection in case the form is manipulated
|
|
||||||
base_url = process.env.REACT_APP_SERVER || base_url;
|
|
||||||
|
|
||||||
console.log("login ");
|
console.log("login ");
|
||||||
const options = {
|
const options = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
Loading…
Reference in a new issue