Compare commits

38 Commits

Author SHA1 Message Date
Aine
687e3364b0 Merge pull request #195 from etkecc/add-account-suspention
Add support for Account Suspension (MSC3823)
2024-12-11 18:51:39 +02:00
Aine
149f5737dd update readme 2024-12-11 18:51:20 +02:00
Aine
c2867e44c6 Merge branch 'main' into add-account-suspention 2024-12-11 18:46:04 +02:00
Aine
f58562cd5b Merge pull request #214 from etkecc/add-e.164-mxids-support
* Support E.164-based Matrix IDs (MSC4009)

* update README
2024-12-11 18:28:04 +02:00
Aine
92845a8640 update README 2024-12-11 18:27:41 +02:00
Aine
4fd18007de Support E.164-based Matrix IDs (MSC4009) 2024-12-11 18:24:08 +02:00
Aine
005be8eec5 Merge branch 'main' into add-account-suspention 2024-12-10 22:32:07 +02:00
Borislav Pantaleev
402457640c Add "Media" tab for rooms (#196)
* WIP on Media tab to rooms

* handle error for downloadFile if it's missing

* Add warning message for rooms Media tab

* Fix error message for download media of rooms

* update readme
2024-12-09 21:56:45 +02:00
Aine
c3dd0facd2 Merge branch 'main' into add-account-suspention 2024-12-09 14:42:38 +02:00
dependabot[bot]
2fa5600d24 Bump vite from 5.4.11 to 6.0.3 (#200)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.11 to 6.0.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.0.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:50:28 +02:00
Aine
c35416ec13 add safeguard for custom menu 2024-12-09 11:42:05 +02:00
dependabot[bot]
9e6e31a728 Bump vite-plugin-version-mark from 0.1.3 to 0.1.4 (#197)
Bumps [vite-plugin-version-mark](https://github.com/ZhongxuYang/vite-plugin-version-mark) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/ZhongxuYang/vite-plugin-version-mark/releases)
- [Changelog](https://github.com/ZhongxuYang/vite-plugin-version-mark/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ZhongxuYang/vite-plugin-version-mark/compare/v0.1.3...v0.1.4)

---
updated-dependencies:
- dependency-name: vite-plugin-version-mark
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:14:50 +02:00
dependabot[bot]
d7677af085 Bump ra-language-farsi from 5.0.0 to 5.1.0 (#199)
Bumps [ra-language-farsi](https://github.com/hamidfzm/ra-language-farsi) from 5.0.0 to 5.1.0.
- [Release notes](https://github.com/hamidfzm/ra-language-farsi/releases)
- [Commits](https://github.com/hamidfzm/ra-language-farsi/compare/v5.0.0...v5.1.0)

---
updated-dependencies:
- dependency-name: ra-language-farsi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:13:45 +02:00
dependabot[bot]
dc0ba15828 Bump prettier from 3.4.1 to 3.4.2 (#202)
Bumps [prettier](https://github.com/prettier/prettier) from 3.4.1 to 3.4.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.4.1...3.4.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:09:53 +02:00
dependabot[bot]
6527565a18 Bump @testing-library/react from 16.0.1 to 16.1.0 (#203)
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 16.0.1 to 16.1.0.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v16.0.1...v16.1.0)

---
updated-dependencies:
- dependency-name: "@testing-library/react"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:09:39 +02:00
dependabot[bot]
c0a4004fd3 Bump ra-language-french from 5.4.0 to 5.4.1 (#205)
Bumps [ra-language-french](https://github.com/marmelab/react-admin) from 5.4.0 to 5.4.1.
- [Release notes](https://github.com/marmelab/react-admin/releases)
- [Changelog](https://github.com/marmelab/react-admin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/marmelab/react-admin/compare/v5.4.0...v5.4.1)

---
updated-dependencies:
- dependency-name: ra-language-french
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:09:26 +02:00
dependabot[bot]
97384d1ebb Bump @mui/material from 6.1.9 to 6.1.10 (#207)
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 6.1.9 to 6.1.10.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v6.1.10/packages/mui-material)

---
updated-dependencies:
- dependency-name: "@mui/material"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:09:14 +02:00
dependabot[bot]
41768eb157 Bump @tanstack/react-query from 5.62.0 to 5.62.3 (#208)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.62.0 to 5.62.3.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.62.3/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:05:16 +02:00
dependabot[bot]
0c89493b58 Bump @mui/icons-material from 6.1.8 to 6.1.9 (#209)
Bumps [@mui/icons-material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material) from 6.1.8 to 6.1.9.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v6.1.9/packages/mui-icons-material)

---
updated-dependencies:
- dependency-name: "@mui/icons-material"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:05:03 +02:00
dependabot[bot]
d5d1be794d Bump react-admin from 5.4.0 to 5.4.1 (#210)
Bumps [react-admin](https://github.com/marmelab/react-admin) from 5.4.0 to 5.4.1.
- [Release notes](https://github.com/marmelab/react-admin/releases)
- [Changelog](https://github.com/marmelab/react-admin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/marmelab/react-admin/compare/v5.4.0...v5.4.1)

---
updated-dependencies:
- dependency-name: react-admin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 11:04:42 +02:00
dependabot[bot]
b2dc623448 Bump typescript-eslint from 8.16.0 to 8.17.0 (#211)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.16.0 to 8.17.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.17.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 10:59:57 +02:00
dependabot[bot]
fa90a82745 Bump react-hook-form from 7.53.2 to 7.54.0 (#212)
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.53.2 to 7.54.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.53.2...v7.54.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 10:59:46 +02:00
dependabot[bot]
b55532e93e Bump ra-core from 5.4.0 to 5.4.1 (#213)
Bumps [ra-core](https://github.com/marmelab/react-admin) from 5.4.0 to 5.4.1.
- [Release notes](https://github.com/marmelab/react-admin/releases)
- [Changelog](https://github.com/marmelab/react-admin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/marmelab/react-admin/compare/v5.4.0...v5.4.1)

---
updated-dependencies:
- dependency-name: ra-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 10:59:28 +02:00
Aine
880270bfe6 Add support for MSC3823 - Account Suspension 2024-12-07 14:47:26 +02:00
Borislav Pantaleev
7c21692a1e Add server status page and a badge in AppBar (#182)
* WIP: Add server status and running process indicators

* Finish ServerStatusPage and ServerRunningProcess

* fix typos, add running process info to the server status page

* Remove ServerRunningProcess and integrate it into ServerStatusBadge

* remove divider in menu

* display time as started X minutes ago

* add documentation; clearly state what new components are; update readme

* change wording a bit, cross-link with docs/README.md

* use returned HTML

* Finish ServerStatus page and badges

* Fix types

* cleanup

* remove some code

* adjust config load
2024-12-04 00:14:29 +02:00
dependabot[bot]
3b69e78bb8 Bump eslint from 9.15.0 to 9.16.0 (#193)
Bumps [eslint](https://github.com/eslint/eslint) from 9.15.0 to 9.16.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.15.0...v9.16.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:30:24 +02:00
dependabot[bot]
a6caa0714e Bump prettier from 3.3.3 to 3.4.1 (#191)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.3 to 3.4.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.3...3.4.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:30:10 +02:00
dependabot[bot]
83473dd3fa Bump @mui/utils from 5.16.6 to 5.16.8 (#185)
Bumps [@mui/utils](https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils) from 5.16.6 to 5.16.8.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/v5.16.8/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v5.16.8/packages/mui-utils)

---
updated-dependencies:
- dependency-name: "@mui/utils"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:29:56 +02:00
dependabot[bot]
aad90760a5 Bump @types/node from 22.9.3 to 22.10.1 (#189)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.9.3 to 22.10.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:21:02 +02:00
dependabot[bot]
cb853bf342 Bump @vitejs/plugin-react from 4.3.3 to 4.3.4 (#188)
Bumps [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) from 4.3.3 to 4.3.4.
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/v4.3.4/packages/plugin-react)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:20:47 +02:00
dependabot[bot]
8d45ae2886 Bump @tanstack/react-query from 5.61.3 to 5.62.0 (#187)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.61.3 to 5.62.0.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.62.0/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:20:31 +02:00
dependabot[bot]
2e151cf198 Bump vite-plugin-version-mark from 0.1.2 to 0.1.3 (#186)
Bumps [vite-plugin-version-mark](https://github.com/ZhongxuYang/vite-plugin-version-mark) from 0.1.2 to 0.1.3.
- [Release notes](https://github.com/ZhongxuYang/vite-plugin-version-mark/releases)
- [Changelog](https://github.com/ZhongxuYang/vite-plugin-version-mark/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ZhongxuYang/vite-plugin-version-mark/compare/v0.1.2...v0.1.3)

---
updated-dependencies:
- dependency-name: vite-plugin-version-mark
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:20:16 +02:00
dependabot[bot]
52c17bc5ca Bump @mui/material from 6.1.8 to 6.1.9 (#184)
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 6.1.8 to 6.1.9.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v6.1.9/packages/mui-material)

---
updated-dependencies:
- dependency-name: "@mui/material"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:19:57 +02:00
dependabot[bot]
d79d825ab4 Bump typescript-eslint from 8.15.0 to 8.16.0 (#183)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.15.0 to 8.16.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.16.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 10:19:42 +02:00
Aine
28b80d7fa2 Show 50 elements by default in the list of each resource 2024-11-30 21:49:05 +02:00
Aine
9735ffbc19 fix readme link 2024-11-30 17:05:29 +02:00
Aine
f4d0e19d21 Allow prefilling of any fields of the login form using GET params (#181)
* Allow prefilling of any fields of the login form using GET params

* update readme
2024-11-29 19:17:52 +02:00
Aine
14d1c904c0 add docs/README.md 2024-11-29 16:58:30 +02:00
36 changed files with 1336 additions and 590 deletions

View File

@@ -21,6 +21,7 @@
* [Fork differences](#fork-differences)
* [Changes](#changes)
* [exclusive for etke.cc customers](#exclusive-for-etkecc-customers)
* [Development](#development)
* [Support](#support)
* [Configuration](#configuration)
@@ -58,6 +59,8 @@ The full list is described below in the [Changes](#changes) section.
### Changes
_the list will be updated as new changes are added_
The following changes are already implemented:
* 🛑 [Prevent admins from deleting themselves](https://github.com/etkecc/synapse-admin/pull/1)
@@ -97,11 +100,20 @@ The following changes are already implemented:
* 🛑 [Prevent accidental user overwrites](https://github.com/etkecc/synapse-admin/pull/139)
* 🔍 [Allow providing login form details via GET params](https://github.com/etkecc/synapse-admin/pull/140)
* 🎨 [Add preferred theme colors to login page and footer](https://github.com/etkecc/synapse-admin/pull/155)
* 🔰 [Add "Assign Admin" button to the rooms](https://github.com/etkecc/synapse-admin/pull/156)
* 🛡️ [Add "Assign Admin" button to the rooms](https://github.com/etkecc/synapse-admin/pull/156)
* 🖼️ [Add rooms' avatars](https://github.com/etkecc/synapse-admin/pull/158)
* 🤖 [User Badges](https://github.com/etkecc/synapse-admin/pull/160)
* 🏷️ [User Badges](https://github.com/etkecc/synapse-admin/pull/160)
* 🔄 [Allow prefilling any fields on the login form via GET params](https://github.com/etkecc/synapse-admin/pull/181)
* 🎞️ [Add "Media" tab for rooms](https://github.com/etkecc/synapse-admin/pull/196)
* 📞 [Support E.164-based Matrix IDs (MSC4009)](https://github.com/etkecc/synapse-admin/pull/214)
* 🛑 [Add support for Account Suspension (MSC3823)](https://github.com/etkecc/synapse-admin/pull/195)
_the list will be updated as new changes are added_
#### exclusive for [etke.cc](https://etke.cc) customers
We at [etke.cc](https://etke.cc) attempting to develop everything open-source, but some things are too specific to be used by anyone else.
The following list contains such features - they are only available for [etke.cc](https://etke.cc) customers.
* 📊 [Server Status indicator and page](https://github.com/etkecc/synapse-admin/pull/182)
### Development
@@ -111,7 +123,8 @@ _the list will be updated as new changes are added_
This command initializes the development environment (local Synapse server and Postgres DB),
and launches the app in a dev mode at `http://localhost:5173`
After that open `http://localhost:5173` in your browser, login using the following credentials:
After that open [http://localhost:5173](http://localhost:5173?username=admin&password=admin&server=http://localhost:8008) in your browser,
login using the following credentials:
* Login: admin
* Password: admin
@@ -148,13 +161,9 @@ services:
### Prefilling login form
You can prefill `username` and `homeserver` fields on the login page using GET parameters, example:
You can prefill all fields on the login page using GET parameters.
```
https://matrix.example.com/synapse-admin/?username=admin&server=matrix.example.com
```
That way `username` and `homeserver` fields will be pre-filled with `admin` and `https://matrix.example.com` respectively.
[Documentation](./docs/prefill-login-form.md)
### Restricting available homeserver

View File

@@ -1,6 +1,6 @@
services:
synapse:
image: ghcr.io/element-hq/synapse:latest
image: ghcr.io/element-hq/synapse:develop
entrypoint: python
command: "-m synapse.app.homeserver -c /config/homeserver.yaml"
ports:

39
docs/README.md Normal file
View File

@@ -0,0 +1,39 @@
# Documentation
Synapse Admin documentation is under construction right now, so PRs are greatly appreciated!
Table of contents:
<!-- vim-markdown-toc GFM -->
* [Configuration](#configuration)
* [Features](#features)
* [Deployment](#deployment)
<!-- vim-markdown-toc -->
## Configuration
[Full configuration documentation](./config.md)
Specific configuration options:
* [Restricting available homeserver](./restrict-hs.md)
* [System / Appservice-managed Users](./system-users.md)
* [Custom Menu Items](./custom-menu.md)
## Features
* [User Badges](./user-badges.md)
* [Prefilling the Login Form](./prefill-login-form.md)
for [etke.cc](https://etke.cc) customers only:
> **Note:** The following features are only available for etke.cc customers. Due to specifics of the implementation,
they are not available for any other Synapse Admin deployments.
* [Server Status icon](../src/components/etke.cc/README.md#server-status-icon)
* [Server Status page](../src/components/etke.cc/README.md#server-status-page)
## Deployment
* [Serving Synapse Admin behind a reverse proxy](./reverse-proxy.md)

View File

@@ -0,0 +1,48 @@
# Prefilling the Login Form
In some cases you may wish to prefill/preset the login form fields when sharing a link to a Synapse Admin instance.
This can be done by adding the following query parameters to the URL:
* `username` - The username to prefill in the username field.
* `server` - The server to prefill in the homeserver url field.
The following query params will work only if the Synapse Admin is loaded from `localhost` or `127.0.0.1`:
* `password` - The password to prefill in the password field (credentials auth). **NEVER** use this in production.
* `accessToken` - The access token to prefill in the access token field (access token auth). **NEVER** use this in production.
> **WARNING**: Never use the `password` or `accessToken` query parameters in production as they can be easily extracted
from the URL. These are only meant for development purposes and local environments.
## Examples
### Production
```bash
https://admin.etke.cc?username=admin&server=https://matrix.example.com
```
This will open `Credentials` (username/password) login form with the username field prefilled with `admin` and the
Homeserver URL field prefilled with `https://matrix.example.com`.
### Development and Local environments
**With Password**
```bash
http://localhost:8080?username=admin&server=https://matrix.example.com&password=secret
```
This will open `Credentials` (username/password) login form with the username field prefilled with `admin`, the
Homeserver URL field prefilled with `https://matrix.example.com` and the password field prefilled with `secret`.
**With Access Token**
```bash
http://localhost:8080?server=https://matrix.example.com&accessToken=secret
```
This will open `Access Token` login form with the Homeserver URL field prefilled with `https://matrix.example.com` and
the access token field prefilled with `secret`.

View File

@@ -14,17 +14,17 @@
"@eslint/js": "^9.13.0",
"@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.0",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.14",
"@types/lodash": "^4.17.13",
"@types/node": "^22.9.3",
"@types/node": "^22.10.1",
"@types/papaparse": "^5.3.15",
"@types/react": "^18.3.12",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.15.0",
"@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.15.0",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.16.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
@@ -34,38 +34,38 @@
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
"prettier": "^3.3.3",
"prettier": "^3.4.2",
"react-test-renderer": "^18.3.1",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.7.2",
"typescript-eslint": "^8.15.0",
"vite": "^5.4.11",
"vite-plugin-version-mark": "^0.1.2"
"typescript-eslint": "^8.17.0",
"vite": "^6.0.3",
"vite-plugin-version-mark": "^0.1.4"
},
"dependencies": {
"@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.5",
"@haleos/ra-language-german": "^1.0.0",
"@haxqer/ra-language-chinese": "^4.16.2",
"@mui/icons-material": "^6.1.8",
"@mui/material": "^6.1.8",
"@mui/utils": "^5.16.6",
"@tanstack/react-query": "^5.61.3",
"@mui/icons-material": "^6.1.9",
"@mui/material": "^6.1.10",
"@mui/utils": "^5.16.8",
"@tanstack/react-query": "^5.62.3",
"history": "^5.3.0",
"lodash": "^4.17.21",
"papaparse": "^5.4.1",
"ra-core": "^5.3.4",
"ra-core": "^5.4.1",
"ra-i18n-polyglot": "^5.3.4",
"ra-language-english": "^5.3.4",
"ra-language-farsi": "^5.0.0",
"ra-language-french": "^5.4.0",
"ra-language-farsi": "^5.1.0",
"ra-language-french": "^5.4.1",
"ra-language-italian": "^3.13.1",
"ra-language-russian": "^4.14.2",
"react": "^18.3.1",
"react-admin": "^5.4.0",
"react-admin": "^5.4.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.2",
"react-hook-form": "^7.54.0",
"react-is": "^18.3.1",
"react-router": "^6.26.2",
"react-router-dom": "^6.28.0"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -2,7 +2,7 @@ import { merge } from "lodash";
import polyglotI18nProvider from "ra-i18n-polyglot";
import { Admin, CustomRoutes, Resource, resolveBrowserLocale } from "react-admin";
import { createContext, useContext } from "react";
import { Route } from "react-router-dom";
import AdminLayout from "./components/AdminLayout";
@@ -24,7 +24,7 @@ import users from "./resources/users";
import authProvider from "./synapse/authProvider";
import dataProvider from "./synapse/dataProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Config } from "./utils/config";
import ServerStatusPage from "./components/etke.cc/ServerStatusPage";
// TODO: Can we use lazy loading together with browser locale?
const messages = {
@@ -64,6 +64,7 @@ export const App = () => (
>
<CustomRoutes>
<Route path="/import_users" element={<UserImport />} />
<Route path="/server_status" element={<ServerStatusPage />} />
</CustomRoutes>
<Resource {...users} />
<Resource {...rooms} />
@@ -86,8 +87,4 @@ export const App = () => (
</QueryClientProvider>
);
export const AppContext = createContext({});
export const useAppContext = () => useContext(AppContext) as Config;
export default App;

7
src/Context.tsx Normal file
View File

@@ -0,0 +1,7 @@
import { createContext, useContext } from "react";
import { Config } from "./utils/config";
export const AppContext = createContext<Config>({} as Config);
export const useAppContext = () => useContext(AppContext) as Config;

View File

@@ -4,6 +4,7 @@ import { useEffect, useState, Suspense } from "react";
import { Icons, DefaultIcon } from "../utils/icons";
import { MenuItem, GetConfig, ClearConfig } from "../utils/config";
import Footer from "./Footer";
import ServerStatusBadge from "./etke.cc/ServerStatusBadge";
const AdminUserMenu = () => {
const [open, setOpen] = useState(false);
@@ -48,6 +49,7 @@ const AdminUserMenu = () => {
const AdminAppBar = () => {
return (<AppBar userMenu={<AdminUserMenu />}>
<TitlePortal />
<ServerStatusBadge />
<InspectorButton />
</AppBar>);
};
@@ -59,7 +61,7 @@ const AdminMenu = (props) => {
return (
<Menu {...props}>
<Menu.ResourceItems />
{menu.map((item, index) => {
{menu && menu.map((item, index) => {
const { url, icon, label } = item;
const IconComponent = Icons[icon] as React.ComponentType<any> | undefined;

View File

@@ -0,0 +1,30 @@
# etke.cc-specific components
This directory contains [etke.cc](https://etke.cc)-specific components, unusable for any other purposes and/or configuration.
We at [etke.cc](https://etke.cc) attempting to develop everything open-source, but some things are too specific to be used by anyone else. This directory contains such components - they are only available for [etke.cc](https://etke.cc) customers.
Due to the specifics mentioned above, these components are documented here rather than in the [docs](../../../docs/README.md), plus they are not supported as part of the Synapse Admin open-source project (i.e.: no issues, no PRs, no support, no requests, etc.).
## Components
### Server Status icon
![Server Status icon](../../../screenshots/etke.cc/server-status/indicator.webp)
In the application bar the new monitoring icon is displayed that shows the current server status, and has the following color dot (and tooltip indicators):
* 🟢 (green) - the server is up and running, everything is fine, no issues detected
* 🟡 (yellow) - the server is up and running, but there is a command in progress (likely [maintenance](https://etke.cc/help/extras/scheduler/#maintenance)), so some temporary issues may occur - that's totally fine
* 🔴 (red) - there is at least 1 issue with one of the server's components
### Server Status page
![Server Status Page](../../../screenshots/etke.cc/server-status/page.webp)
When you click on the [Server Status icon](#server-status-icon) in the application bar, you will be redirected to the
Server Status page. This page contains the following information:
* Overall server status (up/updating/has issues)
* Details about the currently running command (if any)
* Details about the server's components statuses (up/down with error details and suggested actions) by categories

View File

@@ -0,0 +1,169 @@
import { Avatar, Badge, Theme, Tooltip } from "@mui/material";
import { useEffect } from "react";
import { useAppContext } from "../../Context";
import { Button, useDataProvider, useStore } from "react-admin";
import { styled } from '@mui/material/styles';
import MonitorHeartIcon from '@mui/icons-material/MonitorHeart';
import { BadgeProps } from "@mui/material/Badge";
import { useNavigate } from "react-router";
import { useTheme } from "@mui/material/styles";
import { ServerProcessResponse, ServerStatusResponse } from "../../synapse/dataProvider";
interface StyledBadgeProps extends BadgeProps {
backgroundColor: string;
badgeColor: string
theme?: Theme;
}
const StyledBadge = styled(Badge, { shouldForwardProp: (prop) => !['badgeColor', 'backgroundColor'].includes(prop as string) })<StyledBadgeProps>
(({ theme, backgroundColor, badgeColor }) => ({
'& .MuiBadge-badge': {
backgroundColor: backgroundColor,
color: badgeColor,
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
'&::after': {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: '50%',
animation: 'ripple 2.5s infinite ease-in-out',
border: '1px solid currentColor',
content: '""',
},
},
'@keyframes ripple': {
'0%': {
transform: 'scale(.8)',
opacity: 1,
},
'100%': {
transform: 'scale(2.4)',
opacity: 0,
},
},
}));
// every 5 minutes
const SERVER_STATUS_INTERVAL_TIME = 5 * 60 * 1000;
// every 5 seconds
const SERVER_CURRENT_PROCCESS_INTERVAL_TIME = 5 * 1000;
const useServerStatus = () => {
const [serverStatus, setServerStatus] = useStore<ServerStatusResponse>("serverStatus", { ok: false, success: false, host: "", results: [] });
const { etkeccAdmin } = useAppContext();
const dataProvider = useDataProvider();
const isOkay = serverStatus.ok;
const successCheck = serverStatus.success;
const checkServerStatus = async () => {
const serverStatus: ServerStatusResponse = await dataProvider.getServerStatus(etkeccAdmin);
setServerStatus({
ok: serverStatus.ok,
success: serverStatus.success,
host: serverStatus.host,
results: serverStatus.results,
});
};
useEffect(() => {
let serverStatusInterval: NodeJS.Timeout;
if (etkeccAdmin) {
checkServerStatus();
setTimeout(() => {
// start the interval after 10 seconds to avoid too many requests
serverStatusInterval = setInterval(checkServerStatus, SERVER_STATUS_INTERVAL_TIME);
}, 10000);
} else {
setServerStatus({ ok: false, success: false, host: "", results: [] });
}
return () => {
if (serverStatusInterval) {
clearInterval(serverStatusInterval);
}
}
}, [etkeccAdmin]);
return { isOkay, successCheck };
};
const useCurrentServerProcess = () => {
const [serverProcess, setServerProcess] = useStore<ServerProcessResponse>("serverProcess", { command: "", locked_at: "" });
const { etkeccAdmin } = useAppContext();
const dataProvider = useDataProvider();
const { command, locked_at } = serverProcess;
const checkServerRunningProcess = async () => {
const serverProcess: ServerProcessResponse = await dataProvider.getServerRunningProcess(etkeccAdmin);
setServerProcess({
...serverProcess,
command: serverProcess.command,
locked_at: serverProcess.locked_at
});
}
useEffect(() => {
let serverCheckInterval: NodeJS.Timeout;
if (etkeccAdmin) {
checkServerRunningProcess();
setTimeout(() => {
serverCheckInterval = setInterval(checkServerRunningProcess, SERVER_CURRENT_PROCCESS_INTERVAL_TIME);
}, 5000);
} else {
setServerProcess({ command: "", locked_at: "" });
}
return () => {
if (serverCheckInterval) {
clearInterval(serverCheckInterval);
}
}
}, [etkeccAdmin]);
return { command, locked_at };
};
const ServerStatusBadge = () => {
const { isOkay, successCheck } = useServerStatus();
const { command, locked_at } = useCurrentServerProcess();
const theme = useTheme();
const navigate = useNavigate();
if (!successCheck) {
return null;
}
const handleServerStatusClick = () => {
navigate("/server_status");
};
let tooltipText = "Click to view Server Status";
let badgeBackgroundColor = isOkay ? theme.palette.success.light : theme.palette.error.main;
let badgeColor = isOkay ? theme.palette.success.light : theme.palette.error.main;
if (command && locked_at) {
badgeBackgroundColor = theme.palette.warning.main;
badgeColor = theme.palette.warning.main;
tooltipText = `Running: ${command}; ${tooltipText}`;
}
return <Button onClick={handleServerStatusClick} size="medium" sx={{ minWidth: "auto", ".MuiButton-startIcon": { m: 0 }}}>
<Tooltip title={tooltipText} sx={{ cursor: "pointer" }}>
<StyledBadge
overlap="circular"
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
variant="dot"
backgroundColor={badgeBackgroundColor}
badgeColor={badgeColor}
>
<Avatar sx={{ height: 24, width: 24, background: theme.palette.mode === "dark" ? theme.palette.background.default : "#2196f3" }}>
<MonitorHeartIcon sx={{ height: 22, width: 22, color: theme.palette.common.white }} />
</Avatar>
</StyledBadge>
</Tooltip>
</Button>
};
export default ServerStatusBadge;

View File

@@ -0,0 +1,144 @@
import { useStore } from "ra-core";
import { Box, Stack, Typography, Paper, Link, Chip, Divider, Tooltip, ChipProps } from "@mui/material";
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from "@mui/icons-material/Close";
import EngineeringIcon from '@mui/icons-material/Engineering';
import { ServerProcessResponse, ServerStatusComponent, ServerStatusResponse } from "../../synapse/dataProvider";
const getTimeSince = (date: string) => {
const now = new Date();
const past = new Date(date);
const diffInMinutes = Math.floor((now.getTime() - past.getTime()) / (1000 * 60));
if (diffInMinutes < 1) return "a couple of seconds";
if (diffInMinutes === 1) return "1 minute";
return `${diffInMinutes} minutes`;
};
const StatusChip = ({ isOkay, size = "medium", command }: { isOkay: boolean, size?: "small" | "medium", command?: string }) => {
let label = "OK";
let icon = <CheckIcon />;
let color: ChipProps["color"] = "success";
if (!isOkay) {
label = "Error";
icon = <CloseIcon />;
color = "error";
}
if (command) {
label = command;
color = "warning";
icon = <EngineeringIcon />;
}
return (
<Chip icon={icon} label={label} color={color} variant="outlined" size={size} />
);
};
const ServerComponentText = ({ text }: { text: string }) => {
return <Typography variant="body1" dangerouslySetInnerHTML={{ __html: text }} />;
};
const ServerStatusPage = () => {
const [serverStatus, setServerStatus] = useStore<ServerStatusResponse>("serverStatus", {
ok: false,
success: false,
host: "",
results: [],
});
const [ serverProcess, setServerProcess ] = useStore<ServerProcessResponse>("serverProcess", { command: "", locked_at: "" });
const { command, locked_at } = serverProcess;
const successCheck = serverStatus.success;
const isOkay = serverStatus.ok;
const host = serverStatus.host;
const results = serverStatus.results;
let groupedResults: Record<string, ServerStatusComponent[]> = {};
for (const result of results) {
if (!groupedResults[result.category]) {
groupedResults[result.category] = [];
}
groupedResults[result.category].push(result);
}
if (!successCheck) {
return (
<Paper elevation={3} sx={{ p: 3, mt: 3 }}>
<Stack direction="row" spacing={2} alignItems="center">
<CloseIcon color="error" />
<Typography color="error">
Unable to fetch server status. Please try again later.
</Typography>
</Stack>
</Paper>
);
}
return (
<Stack spacing={3} mt={3}>
<Stack spacing={1} direction="row" alignItems="center">
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<Typography variant="h4">Status:</Typography>
<StatusChip isOkay={isOkay} command={command} />
</Box>
<Typography variant="h5" color="primary" fontWeight="medium">
{host}
</Typography>
</Stack>
{command && locked_at && (
<Stack spacing={1} direction="row" alignItems="center">
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<Typography variant="h5">Currently running:</Typography>
<Typography variant="h5" color="text.secondary">
<Link href={"https://etke.cc/help/extras/scheduler/#"+command} target="_blank">
{command}
</Link>
<Tooltip title={locked_at.toString()}>
<Typography component="span" color="text.secondary" sx={{ display: "inline-block", ml: 1 }}>(started {getTimeSince(locked_at)} ago)</Typography>
</Tooltip>
</Typography>
</Box>
</Stack>
)}
<Stack spacing={2} direction="row">
{Object.keys(groupedResults).map((category, idx) => (
<Box key={`category_${category}`} sx={{ flex: 1 }}>
<Typography variant="h5" mb={1}>
{category}
</Typography>
<Paper elevation={2} sx={{ p: 3 }}>
<Stack spacing={1} divider={<Divider />}>
{groupedResults[category].map((result, idx) => (
<Box key={`${category}_${idx}`}>
<Stack spacing={2}>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<StatusChip isOkay={result.ok} size="small" />
{result.label.url ? (
<Link href={result.label.url} target="_blank" rel="noopener noreferrer">
<ServerComponentText text={result.label.text} />
</Link>
) : (
<ServerComponentText text={result.label.text} />
)}
</Box>
{result.reason && <Typography color="text.secondary" dangerouslySetInnerHTML={{ __html: result.reason }}/>}
{(!result.ok && result.help) && (
<Link href={result.help} target="_blank" rel="noopener noreferrer" sx={{ mt: 1 }}>
Learn more
</Link>
)}
</Stack>
</Box>
))}
</Stack>
</Paper>
</Box>
))}
</Stack>
</Stack>
);
};
export default ServerStatusPage;

View File

@@ -314,6 +314,7 @@ export const QuarantineMediaButton = (props: ButtonProps) => {
export const ViewMediaButton = ({ mxcURL, label, uploadName, mimetype }) => {
const translate = useTranslate();
const [loading, setLoading] = useState(false);
const notify = useNotify();
const isImage = mimetype && mimetype.startsWith("image/");
const openFileInNewTab = (blobURL: string) => {
@@ -340,6 +341,8 @@ export const ViewMediaButton = ({ mxcURL, label, uploadName, mimetype }) => {
const handleFile = async (preview: boolean) => {
setLoading(true);
const response = await fetchAuthenticatedMedia(mxcURL, "original");
if (response.ok) {
const blob = await response.blob();
const blobURL = URL.createObjectURL(blob);
if (preview) {
@@ -347,6 +350,17 @@ export const ViewMediaButton = ({ mxcURL, label, uploadName, mimetype }) => {
} else {
downloadFile(blobURL);
}
} else {
const body = await response.json();
notify("resources.room_media.action.error", {
messageArgs: {
errcode: body.errcode,
errstatus: response.status,
error: body.error,
},
type: "error",
});
}
setLoading(false);
};
@@ -391,8 +405,16 @@ export const MediaIDField = ({ source }) => {
return null;
}
const uploadName = decodeURIComponent(get(record, "upload_name")?.toString());
const mxcURL = `mxc://${homeserver}/${mediaID}`;
let uploadName = mediaID;
if (get(record, "upload_name")) {
uploadName = decodeURIComponent(get(record, "upload_name")?.toString());
}
let mxcURL = mediaID;
if (!mediaID.startsWith(`mxc://${homeserver}`)) {
// this is user's media, where mediaID doesn't have the mxc://home_server/ prefix as it has in the rooms
mxcURL = `mxc://${homeserver}/${mediaID}`;
}
return <ViewMediaButton mxcURL={mxcURL} label={mediaID} uploadName={uploadName} mimetype={record.media_type}/>;
};

View File

@@ -64,6 +64,7 @@ const de: SynapseTranslationMessages = {
members: "Mitglieder",
detail: "Details",
permission: "Berechtigungen",
media: "Medien",
},
},
reports: { tabs: { basic: "Allgemein", detail: "Details" } },
@@ -159,11 +160,13 @@ const de: SynapseTranslationMessages = {
is_guest: "Gast",
admin: "Server Administrator",
locked: "Gesperrt",
suspended: "Suspendiert",
deactivated: "Deaktiviert",
erased: "Gelöscht",
guests: "Zeige Gäste",
show_deactivated: "Zeige deaktivierte Benutzer",
show_locked: "Zeige gesperrte Benutzer",
show_suspended: "Zeige suspendierte Benutzer",
user_id: "Suche Benutzer",
displayname: "Anzeigename",
password: "Passwort",
@@ -181,6 +184,7 @@ const de: SynapseTranslationMessages = {
password: "Durch die Änderung des Passworts wird der Benutzer von allen Sitzungen abgemeldet.",
create_password: "Generiere ein starkes und sicheres Passwort mit dem Button unten.",
deactivate: "Sie müssen ein Passwort angeben, um ein Konto wieder zu aktivieren.",
suspend: "Ein gesperrter Benutzer kann sich nicht mehr anmelden und wird in den schreibgeschützten Modus versetzt.",
erase: "DSGVO konformes Löschen der Benutzerdaten.",
erase_text: "Das bedeutet, dass die von dem/den Benutzer(n) gesendeten Nachrichten für alle, die zum Zeitpunkt des Sendens im Raum waren, sichtbar bleiben, aber für Benutzer, die dem Raum später beitreten, nicht sichtbar sind.",
erase_admin_error: "Das Löschen des eigenen Benutzers ist nicht erlaubt.",
@@ -429,6 +433,18 @@ const de: SynapseTranslationMessages = {
sender: "Absender",
},
},
room_media: {
name: "Medien",
fields: {
media_id: "Medien ID",
},
helper: {
info: "Dies ist eine Liste der Medien, die in den Raum hochgeladen wurden. Es ist nicht möglich, Medien zu löschen, die in externen Medien-Repositories hochgeladen wurden.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
room_directory: {
name: "Raumverzeichnis",
fields: {

View File

@@ -38,6 +38,7 @@ const en: SynapseTranslationMessages = {
members: "Members",
detail: "Details",
permission: "Permissions",
media: "Media",
}
},
reports: { tabs: { basic: "Basic", detail: "Details" } },
@@ -132,11 +133,13 @@ const en: SynapseTranslationMessages = {
is_guest: "Guest",
admin: "Server Administrator",
locked: "Locked",
suspended: "Suspended",
deactivated: "Deactivated",
erased: "Erased",
guests: "Show guests",
show_deactivated: "Show deactivated users",
show_locked: "Show locked users",
show_suspended: "Show suspended users",
user_id: "Search user",
displayname: "Displayname",
password: "Password",
@@ -154,6 +157,7 @@ const en: SynapseTranslationMessages = {
password: "Changing password will log user out of all sessions.",
create_password: "Generate a strong and secure password using the button below.",
deactivate: "You must provide a password to re-activate an account.",
suspend: "Suspending user means they are put into a read-only mode.",
erase: "Mark the user as GDPR-erased",
erase_text: "This means messages sent by the user(-s) will still be visible by anyone who was in the room when these messages were sent, but hidden from users joining the room afterward.",
erase_admin_error: "Deleting own user is not allowed.",
@@ -401,6 +405,18 @@ const en: SynapseTranslationMessages = {
sender: "Sender",
},
},
room_media: {
name: "Media",
fields: {
media_id: "Media ID",
},
helper: {
info: "This is a list of media that has been uploaded to the room. It is not possible to delete media that has been uploaded to external media repositories.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}",
},
},
room_directory: {
name: "Room directory",
fields: {

View File

@@ -32,6 +32,7 @@ const fa: SynapseTranslationMessages = {
members: "اعضا",
detail: "جزئیات",
permission: "مجوزها",
media: "رسانه ها",
},
},
reports: { tabs: { basic: "اصلی", detail: "جزئیات" } },
@@ -126,9 +127,12 @@ const fa: SynapseTranslationMessages = {
is_guest: "مهمان",
admin: "مدیر سرور",
deactivated: "غیرفعال",
locked: "قفل شده",
suspended: "معلق",
guests: "نمایش مهمانان",
show_deactivated: "نمایش کاربران غیرفعال شده",
show_locked: "نمایش کاربران قفل شده",
show_suspended: "نمایش کاربران معلق",
user_id: "جستجوی کاربر",
displayname: "نام نمایشی",
password: "رمز عبور",
@@ -146,6 +150,7 @@ const fa: SynapseTranslationMessages = {
password: "با تغییر رمز عبور کاربر از تمام دستگاه ها خارج می شود.",
create_password: "رمز عبور قوی و امنی را با استفاده از دکمه زیر ایجاد کنید.",
deactivate: "برای فعالسازی مجدد حساب باید رمز عبور وارد کنید.",
suspend: "کاربران معلق نمی توانند وارد شوند و پیام های آنها به دیگران نمایش داده نمی شود.",
erase: "کاربر را به عنوان GDPR پاک شده علامت گذاری کنید",
erase_text: "وهذا يعني أن الرسائل المرسلة من قبل المستخدم (المستخدمين) ستظل مرئية من قبل أي شخص كان في الغرفة عند إرسال هذه الرسائل، ولكنها مخفية عن المستخدمين الذين ينضمون إلى الغرفة بعد ذلك.",
erase_admin_error: "حذف المستخدم الخاص غير مسموح به.",
@@ -380,6 +385,18 @@ const fa: SynapseTranslationMessages = {
sender: "فرستنده",
},
},
room_media: {
name: "رسانه ها",
fields: {
media_id: "شناسه رسانه",
},
helper: {
info: "این یک لیست از رسانه ها است که در اتاق بارگذاری شده است. نمی توان رسانه ها را حذف کرد که در اتاق های خارجی بارگذاری شده اند.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
room_directory: {
name: "راهنمای اتاق",
fields: {

View File

@@ -32,6 +32,7 @@ const fr: SynapseTranslationMessages = {
members: "Membres",
detail: "Détails",
permission: "Permissions",
media: "Médias",
},
},
reports: { tabs: { basic: "Informations de base", detail: "Détails" } },
@@ -129,10 +130,12 @@ const fr: SynapseTranslationMessages = {
is_guest: "Visiteur",
admin: "Administrateur du serveur",
locked: "Verrouillé",
suspended: "Suspendu",
deactivated: "Désactivé",
guests: "Afficher les visiteurs",
show_deactivated: "Afficher les utilisateurs désactivés",
show_locked: "Afficher les utilisateurs verrouillés",
show_suspended: "Afficher les utilisateurs suspendus",
user_id: "Rechercher un utilisateur",
displayname: "Nom d'affichage",
password: "Mot de passe",
@@ -149,6 +152,7 @@ const fr: SynapseTranslationMessages = {
password: "Changer le mot de passe déconnectera l'utilisateur de toutes les sessions.",
create_password: "Générer un mot de passe fort et sécurisé en utilisant le bouton ci-dessous.",
deactivate: "Vous devrez fournir un mot de passe pour réactiver le compte.",
suspend: "L'utilisateur sera suspendu jusqu'à ce que vous le réactiviez.",
erase: "Marquer l'utilisateur comme effacé conformément au RGPD",
erase_text: "Cela signifie que les messages envoyés par le(s) utilisateur(s) seront toujours visibles par toute personne qui se trouvait dans la salle au moment où ces messages ont été envoyés, mais qu'ils seront cachés aux utilisateurs qui rejoindront la salle par la suite.",
erase_admin_error: "La suppression de son propre utilisateur n'est pas autorisée.",
@@ -383,6 +387,18 @@ const fr: SynapseTranslationMessages = {
sender: "Expéditeur",
},
},
room_media: {
name: "Médias",
fields: {
media_id: "Identifiant du média",
},
helper: {
info: "Cette liste contient les médias qui ont été téléchargés dans le salon. Il n'est pas possible de supprimer les médias qui ont été téléversés dans des dépôts de médias externes.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
room_directory: {
name: "Répertoire des salons",
fields: {

16
src/i18n/index.d.ts vendored
View File

@@ -31,6 +31,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
members: string;
detail: string;
permission: string;
media: string;
};
};
reports: { tabs: { basic: string; detail: string } };
@@ -124,11 +125,13 @@ interface SynapseTranslationMessages extends TranslationMessages {
is_guest: string;
admin: string;
locked?: string; // TODO: fa, zh
suspended: string;
deactivated: string;
erased?: string; // TODO: fa, fr, it, zh
guests: string;
show_deactivated: string;
show_locked?: string; // TODO: de, fa, fr, it, zh
show_suspended: string;
user_id: string;
displayname: string;
password: string;
@@ -146,6 +149,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
password: string;
create_password: string;
deactivate: string;
suspend: string;
erase: string;
erase_text: string;
erase_admin_error: string;
@@ -393,6 +397,18 @@ interface SynapseTranslationMessages extends TranslationMessages {
sender: string;
};
};
room_media?: {
name: string;
fields: {
media_id: string;
};
helper: {
info: string;
};
action: {
error: string;
};
};
room_directory?: {
name: string;
fields: {

View File

@@ -32,6 +32,7 @@ const it: SynapseTranslationMessages = {
members: "Membro",
detail: "Dettagli",
permission: "Permessi",
media: "Media",
},
},
reports: { tabs: { basic: "Semplice", detail: "Dettagli" } },
@@ -126,10 +127,12 @@ const it: SynapseTranslationMessages = {
is_guest: "Ospite",
admin: "Amministratore",
locked: "Bloccato",
suspended: "Sospeso",
deactivated: "Disattivato",
guests: "Mostra gli ospiti",
show_deactivated: "Mostra gli utenti disattivati",
show_locked: "Mostra gli utenti bloccati",
show_suspended: "Mostra gli utenti sospesi",
user_id: "Cerca utente",
displayname: "Nickname",
password: "Password",
@@ -147,6 +150,7 @@ const it: SynapseTranslationMessages = {
password: "Cambiando la password l'utente verrà disconnesso da tutte le sessioni attive.",
create_password: "Genera una password forte e sicura utilizzando il pulsante sottostante.",
deactivate: "Devi fornire una password per riattivare l'account.",
suspend: "Sospendi l'utente",
erase: "Constrassegna l'utente come cancellato dal GDPR",
erase_text: "Ciò significa che i messaggi inviati dall'utente (o dagli utenti) saranno ancora visibili da chiunque si trovasse nella stanza al momento dell'invio, ma saranno nascosti agli utenti che si uniranno alla stanza in seguito.",
erase_admin_error: "Non è consentito eliminare il proprio utente.",
@@ -376,6 +380,18 @@ const it: SynapseTranslationMessages = {
sender: "Mittente",
},
},
room_media: {
name: "Media",
fields: {
media_id: "ID Media",
},
helper: {
info: "Questo è un elenco dei media caricati nella stanza. Non è possibile eliminare i media caricati su repository esterni.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
room_directory: {
name: "Elenco delle stanze",
fields: {

View File

@@ -59,6 +59,7 @@ const ru: SynapseTranslationMessages = {
members: "Участники",
detail: "Подробности",
permission: "Права доступа",
media: "Медиа",
},
},
reports: { tabs: { basic: "Основные", detail: "Подробности" } },
@@ -162,11 +163,13 @@ const ru: SynapseTranslationMessages = {
is_guest: "Гость",
admin: "Администратор сервера",
locked: "Заблокирован",
suspended: "Приостановлен",
deactivated: "Деактивирован",
erased: "Удалён",
guests: "Показывать гостей",
show_deactivated: "Показывать деактивированных",
show_locked: "Показывать заблокированных",
show_suspended: "Показывать приостановленных",
user_id: "Поиск пользователя",
displayname: "Отображаемое имя",
password: "Пароль",
@@ -184,6 +187,7 @@ const ru: SynapseTranslationMessages = {
password: "Смена пароля завершит все сессии пользователя.",
create_password: "Сгенерировать надёжный и безопасный пароль, используя кнопку ниже.",
deactivate: "Вы должны предоставить пароль для реактивации учётной записи.",
suspend: "Приостановка учётной записи означает, что пользователь не сможет войти в свою учётную запись, пока она не будет снова активирована.",
erase: "Пометить пользователя как удалённого в соответствии с GDPR",
erase_text: "Это означает, что сообщения, отправленные пользователем (-ами), будут по-прежнему видны всем, кто находился в комнате в момент их отправки, но будут скрыты от пользователей, присоединившихся к комнате после этого.",
erase_admin_error: "Удаление собственного пользователя запрещено.",
@@ -434,6 +438,18 @@ const ru: SynapseTranslationMessages = {
sender: "Отправитель",
},
},
room_media: {
name: "Медиа",
fields: {
media_id: "ID медиа",
},
helper: {
info: "Это список медиа, которые были загружены в комнату. Невозможно удалить медиа, которые были загружены в внешние медиа-репозитории.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
room_directory: {
name: "Каталог комнат",
fields: {

View File

@@ -60,6 +60,7 @@ const zh: SynapseTranslationMessages = {
members: "成员",
detail: "细节",
permission: "权限",
media: "媒体",
},
},
reports: { tabs: { basic: "基本", detail: "细节" } },
@@ -153,10 +154,13 @@ const zh: SynapseTranslationMessages = {
name: "用户名",
is_guest: "访客",
admin: "服务器管理员",
locked: "锁定",
deactivated: "被禁用",
suspended: "被锁定",
guests: "显示访客",
show_deactivated: "显示被禁用的账户",
show_locked: "显示被锁定的账户",
show_suspended: "显示被锁定的账户",
user_id: "搜索用户",
displayname: "显示名字",
password: "密码",
@@ -172,6 +176,7 @@ const zh: SynapseTranslationMessages = {
password: "更改密码会使用户注销所有会话。",
create_password: "使用下面的按钮生成一个强大和安全的密码。",
deactivate: "您必须提供一串密码来激活账户。",
suspend: "您必须提供一串密码来暂停账户。",
erase: "将用户标记为根据 GDPR 的要求抹除了",
erase_text: "这意味着用户发送的信息对于发送信息时在房间内的任何人来说都是可见的,但对于之后加入房间的用户来说则是隐藏的。",
erase_admin_error: "不允许删除自己的用户",
@@ -344,6 +349,18 @@ const zh: SynapseTranslationMessages = {
media_length: "媒体文件长度",
},
},
room_media: {
name: "媒体",
fields: {
media_id: "媒体ID",
},
helper: {
info: "这是上传到房间的媒体列表。无法删除上传到外部媒体存储库的媒体。",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
},
};
export default zh;

View File

@@ -2,7 +2,8 @@ import React from "react";
import { createRoot } from "react-dom/client";
import {App, AppContext } from "./App";
import { App } from "./App";
import { AppContext } from "./Context";
import { FetchConfig, GetConfig } from "./utils/config";
await FetchConfig();

View File

@@ -4,7 +4,7 @@ import { render, screen } from "@testing-library/react";
import { AdminContext } from "react-admin";
import LoginPage from "./LoginPage";
import { AppContext } from "../App";
import { AppContext } from "../Context";
import englishMessages from "../i18n/en";
const i18nProvider = polyglotI18nProvider(() => englishMessages, "en", [{ locale: "en", name: "English" }]);
@@ -33,7 +33,9 @@ describe("LoginForm", () => {
it("renders with single restricted homeserver", () => {
render(
<AppContext.Provider value={{ restrictBaseUrl: "https://matrix.example.com" }}>
<AppContext.Provider
value={{ restrictBaseUrl: "https://matrix.example.com", asManagedUsers: [], menu: [] }}
>
<AdminContext i18nProvider={i18nProvider}>
<LoginPage />
</AdminContext>
@@ -56,6 +58,8 @@ describe("LoginForm", () => {
<AppContext.Provider
value={{
restrictBaseUrl: ["https://matrix.example.com", "https://matrix.example.org"],
asManagedUsers: [],
menu: [],
}}
>
<AdminContext i18nProvider={i18nProvider}>

View File

@@ -16,7 +16,7 @@ import {
} from "react-admin";
import { useFormContext } from "react-hook-form";
import LoginFormBox from "../components/LoginFormBox";
import { useAppContext } from "../App";
import { useAppContext } from "../Context";
import {
getServerVersion,
getSupportedFeatures,
@@ -171,11 +171,24 @@ const LoginPage = () => {
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const hostname = window.location.hostname;
const username = params.get("username");
const password = params.get("password");
const accessToken = params.get("accessToken");
let serverURL = params.get("server");
if (username) {
form.setValue("username", username);
}
if (hostname === "localhost" || hostname === "127.0.0.1") {
if (password) {
form.setValue("password", password);
}
if (accessToken) {
setLoginMethod("accessToken");
form.setValue("accessToken", accessToken);
}
}
if (serverURL) {
const isFullUrl = serverURL.match(/^(http|https):\/\//);
if (!isFullUrl) {

View File

@@ -123,6 +123,7 @@ export const DestinationList = (props: ListProps) => {
filters={destinationFilters}
pagination={<DestinationPagination />}
sort={{ field: "destination", order: "ASC" }}
perPage={50}
>
<DatagridConfigurable rowClick={id => `${id}/show/rooms`} bulkActionButtons={false}>
<FunctionField source="destination" render={destinationFieldRender} label="resources.destinations.fields.destination" />

View File

@@ -38,7 +38,7 @@ export const RegistrationTokenList = (props: ListProps) => (
filters={registrationTokenFilters}
filterDefaultValues={{ valid: true }}
pagination={false}
perPage={500}
perPage={50}
>
<DatagridConfigurable rowClick="edit">
<TextField source="token" sortable={false} label="resources.registration_tokens.fields.token"/>

View File

@@ -90,7 +90,7 @@ const ReportShowActions = () => {
};
export const ReportList = (props: ListProps) => (
<List {...props} pagination={<ReportPagination />} sort={{ field: "received_ts", order: "DESC" }}>
<List {...props} pagination={<ReportPagination />} perPage={50} sort={{ field: "received_ts", order: "DESC" }}>
<DatagridConfigurable rowClick="show" bulkActionButtons={false}>
<TextField source="id" sortable={false} label="resources.reports.fields.id" />
<DateField source="received_ts" showTime options={DATE_FORMAT} sortable={true} label="resources.reports.fields.received_ts" />

View File

@@ -135,7 +135,7 @@ const RoomDirectoryListActions = () => (
);
export const RoomDirectoryList = () => (
<List pagination={<RoomDirectoryPagination />} perPage={100} actions={<RoomDirectoryListActions />}>
<List pagination={<RoomDirectoryPagination />} perPage={50} actions={<RoomDirectoryListActions />}>
<DatagridConfigurable
rowClick={id => "/rooms/" + id + "/show"}
bulkActionButtons={<RoomDirectoryBulkUnpublishButton />}

View File

@@ -7,12 +7,12 @@ import PageviewIcon from "@mui/icons-material/Pageview";
import ViewListIcon from "@mui/icons-material/ViewList";
import RoomIcon from "@mui/icons-material/ViewList";
import VisibilityIcon from "@mui/icons-material/Visibility";
import PermMediaIcon from "@mui/icons-material/PermMedia";
import Box from "@mui/material/Box";
import { useTheme } from "@mui/material/styles";
import {
BooleanField,
DateField,
EditButton,
WrapperField,
Datagrid,
DatagridConfigurable,
@@ -38,6 +38,7 @@ import {
useTranslate,
useListContext,
useNotify,
DeleteButton,
} from "react-admin";
import TextField from "@mui/material/TextField";
@@ -49,6 +50,7 @@ import {
} from "./room_directory";
import { DATE_FORMAT } from "../utils/date";
import DeleteRoomButton from "../components/DeleteRoomButton";
import { MediaIDField } from "../components/media";
import AvatarField from "../components/AvatarField";
import { Room } from "../synapse/dataProvider";
import { useMutation } from "@tanstack/react-query";
@@ -58,6 +60,8 @@ import { useState } from "react";
import Button from "@mui/material/Button";
import PersonIcon from '@mui/icons-material/Person';
import Typography from "@mui/material/Typography";
import Alert from "@mui/material/Alert";
const RoomPagination = () => <Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />;
const RoomTitle = () => {
@@ -231,6 +235,16 @@ export const RoomShow = (props: ShowProps) => {
</ReferenceManyField>
</Tab>
<Tab label="synapseadmin.rooms.tabs.media" icon={<PermMediaIcon />} path="media">
<Alert severity="warning">{translate("resources.room_media.helper.info")}</Alert>
<ReferenceManyField reference="room_media" target="room_id" label={false}>
<Datagrid sx={{ width: "100%" }} bulkActionButtons={false}>
<MediaIDField source="media_id" />
<DeleteButton mutationMode="pessimistic" redirect={false} />
</Datagrid>
</ReferenceManyField>
</Tab>
<Tab label="synapseadmin.rooms.tabs.permission" icon={<VisibilityIcon />} path="permission">
<BooleanField source="federatable" />
<BooleanField source="public" />
@@ -352,6 +366,7 @@ export const RoomList = (props: ListProps) => {
sort={{ field: "name", order: "ASC" }}
filters={roomFilters}
actions={<RoomListActions />}
perPage={50}
>
<DatagridConfigurable
rowClick="show"

View File

@@ -37,6 +37,7 @@ export const UserMediaStatsList = (props: ListProps) => (
filters={userMediaStatsFilters}
pagination={<UserMediaStatsPagination />}
sort={{ field: "media_length", order: "DESC" }}
perPage={50}
>
<DatagridConfigurable rowClick={id => "/users/" + id + "/media"} bulkActionButtons={false}>
<TextField source="user_id" label="resources.users.fields.id" />

View File

@@ -111,6 +111,7 @@ const userFilters = [
<BooleanInput source="guests" alwaysOn />,
<BooleanInput label="resources.users.fields.show_deactivated" source="deactivated" alwaysOn />,
<BooleanInput label="resources.users.fields.show_locked" source="locked" alwaysOn />,
<BooleanInput label="resources.users.fields.show_suspended" source="suspended" alwaysOn />,
];
const UserPreventSelfDelete: React.FC<{ children: React.ReactNode; ownUserIsSelected: boolean; asManagedUserIsSelected: boolean }> = props => {
@@ -162,10 +163,11 @@ export const UserList = (props: ListProps) => (
<List
{...props}
filters={userFilters}
filterDefaultValues={{ guests: true, deactivated: false, locked: false }}
filterDefaultValues={{ guests: true, deactivated: false, locked: false, suspended: false }}
sort={{ field: "name", order: "ASC" }}
actions={<UserListActions />}
pagination={<UserPagination />}
perPage={50}
>
<DatagridConfigurable
rowClick={(id: Identifier, resource: string) => `/${resource}/${id}`}
@@ -178,6 +180,7 @@ export const UserList = (props: ListProps) => (
<BooleanField source="admin" label="resources.users.fields.admin" />
<BooleanField source="deactivated" label="resources.users.fields.deactivated" />
<BooleanField source="locked" label="resources.users.fields.locked" />
<BooleanField source="suspended" label="resources.users.fields.suspended" />
<BooleanField source="erased" sortable={false} label="resources.users.fields.erased" />
<DateField source="creation_ts" label="resources.users.fields.creation_ts_ms" showTime options={DATE_FORMAT} />
</DatagridConfigurable>
@@ -188,7 +191,7 @@ export const UserList = (props: ListProps) => (
// here only local part of user_id
// maxLength = 255 - "@" - ":" - storage.getItem("home_server").length
// storage.getItem("home_server").length is not valid here
const validateUser = [required(), maxLength(253), regex(/^[a-z0-9._=\-/]+$/, "synapseadmin.users.invalid_user_id")];
const validateUser = [required(), maxLength(253), regex(/^[a-z0-9._=\-\+/]+$/, "synapseadmin.users.invalid_user_id")];
const validateAddress = [required(), maxLength(255)];
@@ -432,6 +435,7 @@ export const UserEdit = (props: EditProps) => {
<BooleanInput source="admin" />
<UserBooleanInput source="locked" />
<UserBooleanInput source="deactivated" helperText="resources.users.helper.deactivate" />
<UserBooleanInput source="suspended" helperText="resources.users.helper.suspend" />
<BooleanInput source="erased" disabled />
<DateField source="creation_ts_ms" showTime options={DATE_FORMAT} />
<TextField source="consent_version" />

View File

@@ -138,6 +138,7 @@ export interface User {
external_ids: ExternalId[];
user_type?: string;
locked: boolean;
suspended?: boolean;
}
interface Device {
@@ -265,6 +266,31 @@ export interface UsernameAvailabilityResult {
errcode?: string;
}
export interface ServerStatusComponent {
ok: boolean;
category: string;
reason: string;
url: string;
help: string;
label: {
url: string;
icon: string;
text: string;
}
}
export interface ServerStatusResponse {
success: boolean;
ok: boolean;
host: string;
results: ServerStatusComponent[];
}
export interface ServerProcessResponse {
locked_at?: string;
command?: string;
}
export interface SynapseDataProvider extends DataProvider {
deleteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>;
uploadMedia: (params: UploadMediaParams) => Promise<UploadMediaResult>;
@@ -273,6 +299,8 @@ export interface SynapseDataProvider extends DataProvider {
setRateLimits: (id: Identifier, rateLimits: RateLimitsModel) => Promise<void>;
checkUsernameAvailability: (username: string) => Promise<UsernameAvailabilityResult>;
makeRoomAdmin: (room_id: string, user_id: string) => Promise<{ success: boolean; error?: string; errcode?: string }>;
getServerRunningProcess: (etkeAdminUrl: string) => Promise<ServerProcessResponse>;
getServerStatus: (etkeAdminUrl: string) => Promise<ServerStatusResponse>;
}
const resourceMap = {
@@ -357,6 +385,20 @@ const resourceMap = {
data: "members",
total: json => json.total,
},
room_media: {
map: (mediaId: string) => ({
id: mediaId.replace("mxc://" + localStorage.getItem("home_server") + "/", ""),
media_id: mediaId.replace("mxc://" + localStorage.getItem("home_server") + "/", ""),
}),
reference: (id: Identifier) => ({
endpoint: `/_synapse/admin/v1/room/${id}/media`,
}),
total: json => json.total,
data: "local",
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem("home_server")}/${params.id}`,
}),
},
room_state: {
map: (rs: RoomState) => ({
...rs,
@@ -548,7 +590,7 @@ function getSearchOrder(order: "ASC" | "DESC") {
const baseDataProvider: SynapseDataProvider = {
getList: async (resource, params) => {
console.log("getList " + resource);
const { user_id, name, guests, deactivated, locked, search_term, destination, valid } = params.filter;
const { user_id, name, guests, deactivated, locked, suspended, search_term, destination, valid } = params.filter;
const { page, perPage } = params.pagination as PaginationPayload;
const { field, order } = params.sort as SortPayload;
const from = (page - 1) * perPage;
@@ -562,6 +604,7 @@ const baseDataProvider: SynapseDataProvider = {
guests: guests,
deactivated: deactivated,
locked: locked,
suspended: suspended,
valid: valid,
order_by: field,
dir: getSearchOrder(order),
@@ -880,6 +923,60 @@ const baseDataProvider: SynapseDataProvider = {
}
throw error;
}
},
getServerRunningProcess: async (runningProcessUrl: string): Promise<ServerProcessResponse> => {
const locked_at = "";
const command = "";
try {
const response = await fetch(`${runningProcessUrl}/lock`, {
headers: {
"Authorization": `Bearer ${localStorage.getItem("access_token")}`
}
});
if (!response.ok) {
console.error(`Error getting server running process: ${response.status} ${response.statusText}`);
return { locked_at, command };
}
const status = response.status;
if (status === 200) {
const json = await response.json();
return json as { locked_at: string; command: string };
}
if (status === 204) {
return { locked_at, command };
}
} catch (error) {
console.error("Error getting server running process", error);
}
return { locked_at, command };
},
getServerStatus: async (serverStatusUrl: string): Promise<ServerStatusResponse> => {
try {
const response = await fetch(`${serverStatusUrl}/status`, {
headers: {
"Authorization": `Bearer ${localStorage.getItem("access_token")}`
}
});
if (!response.ok) {
console.error(`Error getting server status: ${response.status} ${response.statusText}`);
return { success: false, ok: false, host: "", results: [] };
}
const status = response.status;
if (status === 200) {
const json = await response.json();
const result = { success: true, ...json } as ServerStatusResponse;
return result;
}
} catch (error) {
console.error("Error getting server status", error);
}
return { success: false, ok: false, host: "", results: [] };
}
};

View File

@@ -2,6 +2,7 @@ export interface Config {
restrictBaseUrl: string | string[];
asManagedUsers: RegExp[];
menu: MenuItem[];
etkeccAdmin?: string;
}
export interface MenuItem {
@@ -17,6 +18,7 @@ let config: Config = {
restrictBaseUrl: "",
asManagedUsers: [],
menu: [],
etkeccAdmin: ""
};
export const FetchConfig = async () => {
@@ -49,6 +51,8 @@ export const FetchConfig = async () => {
}
// load config from context
// we deliberately processing each key separately to avoid overwriting the whole config, loosing some keys, and messing
// with typescript types
export const LoadConfig = (context: any) => {
if (context?.restrictBaseUrl) {
config.restrictBaseUrl = context.restrictBaseUrl as string | string[];
@@ -65,6 +69,10 @@ export const LoadConfig = (context: any) => {
if (menu.length > 0) {
config.menu = menu;
}
if (context?.etkeccAdmin) {
config.etkeccAdmin = context.etkeccAdmin;
}
}
// get config

View File

@@ -41,7 +41,6 @@ export function returnMXID(input: string | Identifier): string {
const homeserver = localStorage.getItem("home_server");
// Check if the input already looks like a valid MXID (i.e., starts with "@" and contains ":")
const mxidPattern = /^@[^@:]+:[^@:]+$/;
if (isMXID(input)) {
return input as string; // Already a valid MXID
}

1072
yarn.lock

File diff suppressed because it is too large Load Diff