mirror of
https://github.com/etkecc/synapse-admin.git
synced 2024-11-21 15:25:22 +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 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 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)
|
* [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_
|
_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
|
## 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.
|
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:
|
or to a list of homeservers:
|
||||||
|
|
||||||
```json
|
```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
|
### Protecting appservice managed users
|
||||||
|
|
||||||
To avoid accidental adjustments of appservice-managed users (e.g., puppets created by a bridge) and breaking the bridge,
|
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
|
### Adding custom menu items
|
||||||
|
|
||||||
You can add custom menu items to the main menu by providing a `menu` array in the `config.json`.
|
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)
|
Where `icon` is one of the [preloaded icons](./src/components/icons.ts)
|
||||||
|
|
||||||
### Providing support URL
|
### 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
|
## Usage
|
||||||
|
|
||||||
### Supported Synapse
|
### Supported Synapse
|
||||||
|
|
|
@ -1,18 +1,6 @@
|
||||||
import { createContext, useContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
|
import { Config } from "./components/config";
|
||||||
interface AppContextType {
|
|
||||||
restrictBaseUrl: string | string[];
|
|
||||||
asManagedUsers: string[];
|
|
||||||
supportURL: string;
|
|
||||||
menu: MenuItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MenuItem {
|
|
||||||
label: string;
|
|
||||||
icon: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AppContext = createContext({});
|
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 { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { AppContext, MenuItem } from "./AppContext";
|
import { Config, WellKnownKey, LoadConfig } from "./components/config";
|
||||||
|
import { AppContext } from "./AppContext";
|
||||||
import storage from "./storage";
|
import storage from "./storage";
|
||||||
|
|
||||||
fetch("config.json")
|
// load config.json
|
||||||
.then(res => res.json())
|
let props: Config = {};
|
||||||
.then(props => {
|
try {
|
||||||
if (props.asManagedUsers) {
|
const resp = await fetch("config.json");
|
||||||
storage.setItem("as_managed_users", JSON.stringify(props.asManagedUsers));
|
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 home_server is set, try to load https://home_server/.well-known/matrix/client
|
||||||
if (props.menu) {
|
const homeserver = storage.getItem("home_server");
|
||||||
menu = props.menu;
|
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) {
|
} catch (e) {
|
||||||
const migratedSupportURL = {
|
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e);
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (menu.length > 0) {
|
|
||||||
storage.setItem("menu", JSON.stringify(menu));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return createRoot(document.getElementById("root")).render(
|
createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AppContext.Provider value={props}>
|
<AppContext.Provider value={props}>
|
||||||
<App />
|
<App />
|
||||||
</AppContext.Provider>
|
</AppContext.Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
)
|
);
|
||||||
});
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ const LoginPage = () => {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const { restrictBaseUrl } = useAppContext();
|
const { restrictBaseUrl } = useAppContext();
|
||||||
const allowSingleBaseUrl = typeof restrictBaseUrl === "string";
|
const allowSingleBaseUrl = typeof restrictBaseUrl === "string" && restrictBaseUrl !== "";
|
||||||
const allowMultipleBaseUrls =
|
const allowMultipleBaseUrls =
|
||||||
Array.isArray(restrictBaseUrl) &&
|
Array.isArray(restrictBaseUrl) &&
|
||||||
restrictBaseUrl.length > 0 &&
|
restrictBaseUrl.length > 0 &&
|
||||||
|
|
|
@ -5,6 +5,9 @@ import { defineConfig } from "vite";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: "./",
|
base: "./",
|
||||||
|
build: {
|
||||||
|
target: "esnext",
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
vitePluginVersionMark({
|
vitePluginVersionMark({
|
||||||
|
|
Loading…
Reference in a new issue