Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
36f9ce6b07 | ||
|
83c9704633 | ||
|
8c1546cd5a |
@ -1,6 +1,7 @@
|
|||||||
# Exclude a bunch of stuff which can make the build context a larger than it needs to be
|
# Exclude a bunch of stuff which can make the build context a larger than it needs to be
|
||||||
tests/
|
tests/
|
||||||
build/
|
build/
|
||||||
|
dist/
|
||||||
lib/
|
lib/
|
||||||
node_modules/
|
node_modules/
|
||||||
electron_app/
|
electron_app/
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
/dist
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
149
index.html
Normal file
149
index.html
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="shortcut icon" href="./favicon.ico" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
|
||||||
|
/>
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Synapse-Admin"
|
||||||
|
/>
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="./manifest.json" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<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>
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root">
|
||||||
|
<div class="loader-container">
|
||||||
|
<div class="loader">Loading...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer
|
||||||
|
style="position: relative; z-index: 2; height: 2em; margin-top: -2em; line-height: 2em; background-color: #eee; border: 0.5px solid #ddd">
|
||||||
|
<a id="copyright" href="https://github.com/Awesome-Technologies/synapse-admin"
|
||||||
|
style="margin-left: 1em; color: #888; font-family: Roboto, Helvetica, Arial, sans-serif; font-weight: 100; font-size: 0.8em; text-decoration: none;">
|
||||||
|
Synapse-Admin <b>(%REACT_APP_VERSION%)</b> by Awesome Technologies Innovationslabor GmbH
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
<script type="module" src="/src/index.jsx"></script>
|
||||||
|
</html>
|
14
package.json
14
package.json
@ -13,12 +13,14 @@
|
|||||||
"@testing-library/jest-dom": "^6.0.0",
|
"@testing-library/jest-dom": "^6.0.0",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
|
"@vitejs/plugin-react": "^4.0.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"prettier": "^3.2.5"
|
"prettier": "^3.2.5",
|
||||||
|
"vite": "^4.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mui/icons-material": "^5.15.7",
|
"@mui/icons-material": "^5.15.7",
|
||||||
@ -31,12 +33,11 @@
|
|||||||
"ra-language-italian": "^3.13.1",
|
"ra-language-italian": "^3.13.1",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-admin": "^4.16.9",
|
"react-admin": "^4.16.9",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0"
|
||||||
"react-scripts": "^5.0.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "REACT_APP_VERSION=$(git describe --tags) react-scripts start",
|
"start": "REACT_APP_VERSION=$(git describe --tags) vite serve",
|
||||||
"build": "REACT_APP_VERSION=$(git describe --tags) react-scripts build",
|
"build": "REACT_APP_VERSION=$(git describe --tags) vite build",
|
||||||
"fix:other": "yarn prettier --write",
|
"fix:other": "yarn prettier --write",
|
||||||
"fix:code": "yarn test:lint --fix",
|
"fix:code": "yarn test:lint --fix",
|
||||||
"fix": "yarn fix:code && yarn fix:other",
|
"fix": "yarn fix:code && yarn fix:other",
|
||||||
@ -44,8 +45,7 @@
|
|||||||
"test:code": "react-scripts test",
|
"test:code": "react-scripts test",
|
||||||
"test:lint": "eslint --ignore-path .gitignore --ext .js,.jsx .",
|
"test:lint": "eslint --ignore-path .gitignore --ext .js,.jsx .",
|
||||||
"test:style": "yarn prettier --list-different",
|
"test:style": "yarn prettier --list-different",
|
||||||
"test": "yarn test:style && yarn test:lint && yarn test:code",
|
"test": "yarn test:style && yarn test:lint && yarn test:code"
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
|
@ -24,6 +24,8 @@ import frenchMessages from "./i18n/fr";
|
|||||||
import chineseMessages from "./i18n/zh";
|
import chineseMessages from "./i18n/zh";
|
||||||
import italianMessages from "./i18n/it";
|
import italianMessages from "./i18n/it";
|
||||||
|
|
||||||
|
const fixed_base_url = undefined; // FIXME: process.env.REACT_APP_SERVER;
|
||||||
|
|
||||||
// TODO: Can we use lazy loading together with browser locale?
|
// TODO: Can we use lazy loading together with browser locale?
|
||||||
const messages = {
|
const messages = {
|
||||||
de: germanMessages,
|
de: germanMessages,
|
||||||
@ -42,7 +44,7 @@ const App = () => (
|
|||||||
disableTelemetry
|
disableTelemetry
|
||||||
requireAuth
|
requireAuth
|
||||||
loginPage={LoginPage}
|
loginPage={LoginPage}
|
||||||
authProvider={authProvider}
|
authProvider={authProvider(fixed_base_url)}
|
||||||
dataProvider={dataProvider}
|
dataProvider={dataProvider}
|
||||||
i18nProvider={i18nProvider}
|
i18nProvider={i18nProvider}
|
||||||
>
|
>
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
useLogin,
|
useLogin,
|
||||||
useNotify,
|
useNotify,
|
||||||
useLocaleState,
|
useLocaleState,
|
||||||
|
useStoreContext,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
TextInput,
|
TextInput,
|
||||||
@ -81,15 +82,15 @@ const FormBox = styled(Box)(({ theme }) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const LoginPage = () => {
|
const LoginPage = ({ cfg_base_url }) => {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
|
const store = useStoreContext();
|
||||||
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 translate = useTranslate();
|
const translate = useTranslate();
|
||||||
const base_url = localStorage.getItem("base_url");
|
const base_url = store.getItem("base_url");
|
||||||
const cfg_base_url = process.env.REACT_APP_SERVER;
|
|
||||||
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);
|
||||||
|
|
||||||
@ -102,8 +103,8 @@ const LoginPage = () => {
|
|||||||
"",
|
"",
|
||||||
window.location.href.replace(loginToken[0], "#").split("#")[0]
|
window.location.href.replace(loginToken[0], "#").split("#")[0]
|
||||||
);
|
);
|
||||||
const baseUrl = localStorage.getItem("sso_base_url");
|
const baseUrl = store.getItem("sso_base_url");
|
||||||
localStorage.removeItem("sso_base_url");
|
store.removeItem("sso_base_url");
|
||||||
if (baseUrl) {
|
if (baseUrl) {
|
||||||
const auth = {
|
const auth = {
|
||||||
base_url: baseUrl,
|
base_url: baseUrl,
|
||||||
@ -169,7 +170,7 @@ const LoginPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSSO = () => {
|
const handleSSO = () => {
|
||||||
localStorage.setItem("sso_base_url", ssoBaseUrl);
|
store.setItem("sso_base_url", ssoBaseUrl);
|
||||||
const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent(
|
const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent(
|
||||||
window.location.href
|
window.location.href
|
||||||
)}`;
|
)}`;
|
||||||
@ -247,7 +248,7 @@ const LoginPage = () => {
|
|||||||
name="base_url"
|
name="base_url"
|
||||||
component={renderInput}
|
component={renderInput}
|
||||||
label="synapseadmin.auth.base_url"
|
label="synapseadmin.auth.base_url"
|
||||||
disabled={cfg_base_url || loading}
|
disabled={cfg_base_url != null || loading}
|
||||||
resettable
|
resettable
|
||||||
fullWidth
|
fullWidth
|
||||||
className="input"
|
className="input"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
createRoot(document.getElementById("root")).render(
|
const REACT_APP_SERVER = import.meta.env.VITE_APP_SERVER;
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { fetchUtils } from "react-admin";
|
import { fetchUtils } from "react-admin";
|
||||||
|
|
||||||
const authProvider = {
|
const authProvider = (fixed_base_url, store) => ({
|
||||||
// called when the user attempts to log in
|
// called when the user attempts to log in
|
||||||
login: ({ base_url, username, password, loginToken }) => {
|
login: ({ base_url, username, password, loginToken }) => {
|
||||||
// force homeserver for protection in case the form is manipulated
|
// force homeserver for protection in case the form is manipulated
|
||||||
base_url = process.env.REACT_APP_SERVER || base_url;
|
base_url = fixed_base_url || base_url;
|
||||||
|
|
||||||
console.log("login ");
|
console.log("login ");
|
||||||
const options = {
|
const options = {
|
||||||
@ -12,7 +12,7 @@ const authProvider = {
|
|||||||
body: JSON.stringify(
|
body: JSON.stringify(
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{
|
{
|
||||||
device_id: localStorage.getItem("device_id"),
|
device_id: store.getItem("device_id"),
|
||||||
initial_device_display_name: "Synapse Admin",
|
initial_device_display_name: "Synapse Admin",
|
||||||
},
|
},
|
||||||
loginToken
|
loginToken
|
||||||
@ -33,16 +33,16 @@ const authProvider = {
|
|||||||
// server, since the admin might want to access the admin API via some
|
// server, since the admin might want to access the admin API via some
|
||||||
// private address
|
// private address
|
||||||
base_url = base_url.replace(/\/+$/g, "");
|
base_url = base_url.replace(/\/+$/g, "");
|
||||||
localStorage.setItem("base_url", base_url);
|
store.setItem("base_url", base_url);
|
||||||
|
|
||||||
const decoded_base_url = window.decodeURIComponent(base_url);
|
const decoded_base_url = window.decodeURIComponent(base_url);
|
||||||
const login_api_url = decoded_base_url + "/_matrix/client/r0/login";
|
const login_api_url = decoded_base_url + "/_matrix/client/r0/login";
|
||||||
|
|
||||||
return fetchUtils.fetchJson(login_api_url, options).then(({ json }) => {
|
return fetchUtils.fetchJson(login_api_url, options).then(({ json }) => {
|
||||||
localStorage.setItem("home_server", json.home_server);
|
store.setItem("home_server", json.home_server);
|
||||||
localStorage.setItem("user_id", json.user_id);
|
store.setItem("user_id", json.user_id);
|
||||||
localStorage.setItem("access_token", json.access_token);
|
store.setItem("access_token", json.access_token);
|
||||||
localStorage.setItem("device_id", json.device_id);
|
store.setItem("device_id", json.device_id);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// called when the user clicks on the logout button
|
// called when the user clicks on the logout button
|
||||||
@ -86,6 +86,6 @@ const authProvider = {
|
|||||||
},
|
},
|
||||||
// called when the user navigates to a new location, to check for permissions / roles
|
// called when the user navigates to a new location, to check for permissions / roles
|
||||||
getPermissions: () => Promise.resolve(),
|
getPermissions: () => Promise.resolve(),
|
||||||
};
|
});
|
||||||
|
|
||||||
export default authProvider;
|
export default authProvider;
|
||||||
|
6
vite.config.js
Normal file
6
vite.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user