mirror of
https://github.com/etkecc/synapse-admin.git
synced 2024-11-21 07:15:20 +03:00
Add support for config from /.well-known/matrix/client (#126)
* Add support for config from /.well-known/matrix/client * final fixes, refactoring, updated readme
This commit is contained in:
parent
9adc13e722
commit
c698f57395
6 changed files with 160 additions and 41 deletions
63
README.md
63
README.md
|
@ -90,6 +90,7 @@ with a proper manifest.json generation on build)
|
|||
* [Add option to control user's experimental features](https://github.com/etkecc/synapse-admin/pull/111)
|
||||
* [Add random password generation on user create/edit form](https://github.com/etkecc/synapse-admin/pull/123)
|
||||
* [Add option to set user's rate limits](https://github.com/etkecc/synapse-admin/pull/125)
|
||||
* [Support configuration via /.well-known/matrix/client](https://github.com/etkecc/synapse-admin/pull/126)
|
||||
|
||||
_the list will be updated as new changes are added_
|
||||
|
||||
|
@ -106,7 +107,11 @@ After that open `http://localhost:5173` in your browser, login using the followi
|
|||
|
||||
## Configuration
|
||||
|
||||
You can use `config.json` file to configure synapse-admin
|
||||
You can use `config.json` file to configure Synapse Admin instance,
|
||||
and `/.well-known/matrix/client` file to provide Synapse Admin configuration specifically for your homeserver.
|
||||
In the latter case, any instance of Synapse Admin will automatically pick up the configuration from the homeserver.
|
||||
Note that configuration inside the `/.well-known/matrix/client` file should go under the `cc.etke.synapse-admin` key,
|
||||
and it will override the configuration from the `config.json` file.
|
||||
|
||||
The `config.json` can be injected into a Docker container using a bind mount.
|
||||
|
||||
|
@ -131,6 +136,16 @@ Edit `config.json` to restrict either to a single homeserver:
|
|||
}
|
||||
```
|
||||
|
||||
similar for `/.well-known/matrix/client`:
|
||||
|
||||
```json
|
||||
{
|
||||
"cc.etke.synapse-admin": {
|
||||
"restrictBaseUrl": "https://your-matrixs-erver.example.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
or to a list of homeservers:
|
||||
|
||||
```json
|
||||
|
@ -139,6 +154,16 @@ or to a list of homeservers:
|
|||
}
|
||||
```
|
||||
|
||||
similar for `/.well-known/matrix/client`:
|
||||
|
||||
```json
|
||||
{
|
||||
"cc.etke.synapse-admin": {
|
||||
"restrictBaseUrl": ["https://your-first-matrix-server.example.com", "https://your-second-matrix-server.example.com"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Protecting appservice managed users
|
||||
|
||||
To avoid accidental adjustments of appservice-managed users (e.g., puppets created by a bridge) and breaking the bridge,
|
||||
|
@ -152,6 +177,16 @@ Example for [mautrix-telegram](https://github.com/mautrix/telegram)
|
|||
}
|
||||
```
|
||||
|
||||
similar for `/.well-known/matrix/client`:
|
||||
|
||||
```json
|
||||
{
|
||||
"cc.etke.synapse-admin": {
|
||||
"asManagedUsers": ["^@telegram_[a-zA-Z0-9]+:example\\.com$"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Adding custom menu items
|
||||
|
||||
You can add custom menu items to the main menu by providing a `menu` array in the `config.json`.
|
||||
|
@ -168,6 +203,22 @@ You can add custom menu items to the main menu by providing a `menu` array in th
|
|||
}
|
||||
```
|
||||
|
||||
similar for `/.well-known/matrix/client`:
|
||||
|
||||
```json
|
||||
{
|
||||
"cc.etke.synapse-admin": {
|
||||
"menu": [
|
||||
{
|
||||
"label": "Contact support",
|
||||
"icon": "SupportAgent",
|
||||
"url": "https://github.com/etkecc/synapse-admin/issues"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Where `icon` is one of the [preloaded icons](./src/components/icons.ts)
|
||||
|
||||
### Providing support URL
|
||||
|
@ -182,6 +233,16 @@ Where `icon` is one of the [preloaded icons](./src/components/icons.ts)
|
|||
}
|
||||
```
|
||||
|
||||
similar for `/.well-known/matrix/client`:
|
||||
|
||||
```json
|
||||
{
|
||||
"cc.etke.synapse-admin": {
|
||||
"supportURL": "https://example.com/support"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Supported Synapse
|
||||
|
|
|
@ -1,18 +1,6 @@
|
|||
import { createContext, useContext } from "react";
|
||||
|
||||
interface AppContextType {
|
||||
restrictBaseUrl: string | string[];
|
||||
asManagedUsers: string[];
|
||||
supportURL: string;
|
||||
menu: MenuItem[];
|
||||
}
|
||||
|
||||
interface MenuItem {
|
||||
label: string;
|
||||
icon: string;
|
||||
url: string;
|
||||
}
|
||||
import { Config } from "./components/config";
|
||||
|
||||
export const AppContext = createContext({});
|
||||
|
||||
export const useAppContext = () => useContext(AppContext) as AppContextType;
|
||||
export const useAppContext = () => useContext(AppContext) as Config;
|
||||
|
|
63
src/components/config.ts
Normal file
63
src/components/config.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import storage from "../storage";
|
||||
|
||||
export interface Config {
|
||||
restrictBaseUrl: string | string[];
|
||||
asManagedUsers: string[];
|
||||
supportURL: string;
|
||||
menu: MenuItem[];
|
||||
}
|
||||
|
||||
export interface MenuItem {
|
||||
label: string;
|
||||
icon: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const WellKnownKey = "cc.etke.synapse-admin";
|
||||
|
||||
export const LoadConfig = (context: Config): Config => {
|
||||
if (context.restrictBaseUrl) {
|
||||
storage.setItem("restrict_base_url", JSON.stringify(context.restrictBaseUrl));
|
||||
}
|
||||
|
||||
if (context.asManagedUsers) {
|
||||
storage.setItem("as_managed_users", JSON.stringify(context.asManagedUsers));
|
||||
}
|
||||
|
||||
let menu: MenuItem[] = [];
|
||||
if (context.menu) {
|
||||
menu = context.menu;
|
||||
}
|
||||
if (context.supportURL) {
|
||||
const migratedSupportURL = {
|
||||
label: "Contact support",
|
||||
icon: "SupportAgent",
|
||||
url: context.supportURL,
|
||||
};
|
||||
console.warn("supportURL config option is deprecated. Please, use the menu option instead. Automatically migrated to the new menu option:", migratedSupportURL);
|
||||
menu.push(migratedSupportURL as MenuItem);
|
||||
}
|
||||
if (menu.length > 0) {
|
||||
storage.setItem("menu", JSON.stringify(menu));
|
||||
}
|
||||
|
||||
// below we try to calculate "final" config, which will contain values from context and already set values in storage
|
||||
// because LoadConfig could be called multiple times to get config from different sources
|
||||
let finalAsManagedUsers: string[] = [];
|
||||
try {
|
||||
finalAsManagedUsers = JSON.parse(storage.getItem("as_managed_users") || "");
|
||||
} catch (e) {}
|
||||
|
||||
let finalMenu: MenuItem[] = [];
|
||||
try {
|
||||
finalMenu = JSON.parse(storage.getItem("menu") || "");
|
||||
} catch (e) {}
|
||||
|
||||
return {
|
||||
restrictBaseUrl: storage.getItem("restrict_base_url") || "",
|
||||
asManagedUsers: finalAsManagedUsers,
|
||||
supportURL: storage.getItem("support_url") || "",
|
||||
menu: finalMenu,
|
||||
} as Config;
|
||||
|
||||
}
|
|
@ -3,38 +3,42 @@ import React from "react";
|
|||
import { createRoot } from "react-dom/client";
|
||||
|
||||
import App from "./App";
|
||||
import { AppContext, MenuItem } from "./AppContext";
|
||||
import { Config, WellKnownKey, LoadConfig } from "./components/config";
|
||||
import { AppContext } from "./AppContext";
|
||||
import storage from "./storage";
|
||||
|
||||
fetch("config.json")
|
||||
.then(res => res.json())
|
||||
.then(props => {
|
||||
if (props.asManagedUsers) {
|
||||
storage.setItem("as_managed_users", JSON.stringify(props.asManagedUsers));
|
||||
// load config.json
|
||||
let props: Config = {};
|
||||
try {
|
||||
const resp = await fetch("config.json");
|
||||
const configJSON = await resp.json();
|
||||
console.log("Loaded config.json", configJSON);
|
||||
props = LoadConfig(configJSON as Config);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
let menu: MenuItem[] = [];
|
||||
if (props.menu) {
|
||||
menu = props.menu;
|
||||
// if home_server is set, try to load https://home_server/.well-known/matrix/client
|
||||
const homeserver = storage.getItem("home_server");
|
||||
if (homeserver) {
|
||||
try {
|
||||
const resp = await fetch(`https://${homeserver}/.well-known/matrix/client`);
|
||||
const configWK = await resp.json();
|
||||
if (!configWK[WellKnownKey]) {
|
||||
console.log(`Loaded https://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK);
|
||||
} else {
|
||||
console.log(`Loaded https://${homeserver}.well-known/matrix/client`, configWK);
|
||||
props = LoadConfig(configWK[WellKnownKey] as Config);
|
||||
}
|
||||
if (props.supportURL) {
|
||||
const migratedSupportURL = {
|
||||
label: "Contact support",
|
||||
icon: "SupportAgent",
|
||||
url: props.supportURL,
|
||||
};
|
||||
console.warn("supportURL config option is deprecated. Please, use the menu option instead. Automatically migrated to the new menu option:", migratedSupportURL);
|
||||
menu.push(migratedSupportURL as MenuItem);
|
||||
} catch (e) {
|
||||
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e);
|
||||
}
|
||||
if (menu.length > 0) {
|
||||
storage.setItem("menu", JSON.stringify(menu));
|
||||
}
|
||||
|
||||
return createRoot(document.getElementById("root")).render(
|
||||
createRoot(document.getElementById("root")).render(
|
||||
<React.StrictMode>
|
||||
<AppContext.Provider value={props}>
|
||||
<App />
|
||||
</AppContext.Provider>
|
||||
</React.StrictMode>
|
||||
)
|
||||
});
|
||||
);
|
||||
|
|
|
@ -33,7 +33,7 @@ const LoginPage = () => {
|
|||
const login = useLogin();
|
||||
const notify = useNotify();
|
||||
const { restrictBaseUrl } = useAppContext();
|
||||
const allowSingleBaseUrl = typeof restrictBaseUrl === "string";
|
||||
const allowSingleBaseUrl = typeof restrictBaseUrl === "string" && restrictBaseUrl !== "";
|
||||
const allowMultipleBaseUrls =
|
||||
Array.isArray(restrictBaseUrl) &&
|
||||
restrictBaseUrl.length > 0 &&
|
||||
|
|
|
@ -5,6 +5,9 @@ import { defineConfig } from "vite";
|
|||
|
||||
export default defineConfig({
|
||||
base: "./",
|
||||
build: {
|
||||
target: "esnext",
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
vitePluginVersionMark({
|
||||
|
|
Loading…
Reference in a new issue