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
|
||||
tests/
|
||||
build/
|
||||
dist/
|
||||
lib/
|
||||
node_modules/
|
||||
electron_app/
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@
|
||||
|
||||
# production
|
||||
/build
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.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/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"prettier": "^3.2.5"
|
||||
"prettier": "^3.2.5",
|
||||
"vite": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mui/icons-material": "^5.15.7",
|
||||
@ -31,12 +33,11 @@
|
||||
"ra-language-italian": "^3.13.1",
|
||||
"react": "^18.0.0",
|
||||
"react-admin": "^4.16.9",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-scripts": "^5.0.1"
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "REACT_APP_VERSION=$(git describe --tags) react-scripts start",
|
||||
"build": "REACT_APP_VERSION=$(git describe --tags) react-scripts build",
|
||||
"start": "REACT_APP_VERSION=$(git describe --tags) vite serve",
|
||||
"build": "REACT_APP_VERSION=$(git describe --tags) vite build",
|
||||
"fix:other": "yarn prettier --write",
|
||||
"fix:code": "yarn test:lint --fix",
|
||||
"fix": "yarn fix:code && yarn fix:other",
|
||||
@ -44,8 +45,7 @@
|
||||
"test:code": "react-scripts test",
|
||||
"test:lint": "eslint --ignore-path .gitignore --ext .js,.jsx .",
|
||||
"test:style": "yarn prettier --list-different",
|
||||
"test": "yarn test:style && yarn test:lint && yarn test:code",
|
||||
"eject": "react-scripts eject"
|
||||
"test": "yarn test:style && yarn test:lint && yarn test:code"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
|
@ -46,4 +46,4 @@
|
||||
</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -24,6 +24,8 @@ import frenchMessages from "./i18n/fr";
|
||||
import chineseMessages from "./i18n/zh";
|
||||
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?
|
||||
const messages = {
|
||||
de: germanMessages,
|
||||
@ -42,7 +44,7 @@ const App = () => (
|
||||
disableTelemetry
|
||||
requireAuth
|
||||
loginPage={LoginPage}
|
||||
authProvider={authProvider}
|
||||
authProvider={authProvider(fixed_base_url)}
|
||||
dataProvider={dataProvider}
|
||||
i18nProvider={i18nProvider}
|
||||
>
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
useLogin,
|
||||
useNotify,
|
||||
useLocaleState,
|
||||
useStoreContext,
|
||||
useTranslate,
|
||||
PasswordInput,
|
||||
TextInput,
|
||||
@ -81,15 +82,15 @@ const FormBox = styled(Box)(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const LoginPage = () => {
|
||||
const LoginPage = ({ cfg_base_url }) => {
|
||||
const login = useLogin();
|
||||
const notify = useNotify();
|
||||
const store = useStoreContext();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [supportPassAuth, setSupportPassAuth] = useState(true);
|
||||
const [locale, setLocale] = useLocaleState();
|
||||
const translate = useTranslate();
|
||||
const base_url = localStorage.getItem("base_url");
|
||||
const cfg_base_url = process.env.REACT_APP_SERVER;
|
||||
const base_url = store.getItem("base_url");
|
||||
const [ssoBaseUrl, setSSOBaseUrl] = useState("");
|
||||
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]
|
||||
);
|
||||
const baseUrl = localStorage.getItem("sso_base_url");
|
||||
localStorage.removeItem("sso_base_url");
|
||||
const baseUrl = store.getItem("sso_base_url");
|
||||
store.removeItem("sso_base_url");
|
||||
if (baseUrl) {
|
||||
const auth = {
|
||||
base_url: baseUrl,
|
||||
@ -169,7 +170,7 @@ const LoginPage = () => {
|
||||
};
|
||||
|
||||
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(
|
||||
window.location.href
|
||||
)}`;
|
||||
@ -247,7 +248,7 @@ const LoginPage = () => {
|
||||
name="base_url"
|
||||
component={renderInput}
|
||||
label="synapseadmin.auth.base_url"
|
||||
disabled={cfg_base_url || loading}
|
||||
disabled={cfg_base_url != null || loading}
|
||||
resettable
|
||||
fullWidth
|
||||
className="input"
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import ReactDOM from "react-dom/client";
|
||||
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>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { fetchUtils } from "react-admin";
|
||||
|
||||
const authProvider = {
|
||||
const authProvider = (fixed_base_url, store) => ({
|
||||
// called when the user attempts to log in
|
||||
login: ({ base_url, username, password, loginToken }) => {
|
||||
// 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 ");
|
||||
const options = {
|
||||
@ -12,7 +12,7 @@ const authProvider = {
|
||||
body: JSON.stringify(
|
||||
Object.assign(
|
||||
{
|
||||
device_id: localStorage.getItem("device_id"),
|
||||
device_id: store.getItem("device_id"),
|
||||
initial_device_display_name: "Synapse Admin",
|
||||
},
|
||||
loginToken
|
||||
@ -33,16 +33,16 @@ const authProvider = {
|
||||
// server, since the admin might want to access the admin API via some
|
||||
// private address
|
||||
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 login_api_url = decoded_base_url + "/_matrix/client/r0/login";
|
||||
|
||||
return fetchUtils.fetchJson(login_api_url, options).then(({ json }) => {
|
||||
localStorage.setItem("home_server", json.home_server);
|
||||
localStorage.setItem("user_id", json.user_id);
|
||||
localStorage.setItem("access_token", json.access_token);
|
||||
localStorage.setItem("device_id", json.device_id);
|
||||
store.setItem("home_server", json.home_server);
|
||||
store.setItem("user_id", json.user_id);
|
||||
store.setItem("access_token", json.access_token);
|
||||
store.setItem("device_id", json.device_id);
|
||||
});
|
||||
},
|
||||
// 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
|
||||
getPermissions: () => Promise.resolve(),
|
||||
};
|
||||
});
|
||||
|
||||
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