Compare commits

127 Commits

Author SHA1 Message Date
Aine
8680dbc268 wrap MakeAdmin button with ReferenceField in user's rooms table, fixes #321 2025-01-30 18:35:35 +02:00
Aine
ea2b84c5dc Merge pull request #320 from etkecc/broken-upgrade
Broken upgrade (now fixed)
2025-01-30 09:12:07 +00:00
Borislav Pantaleev
45c7027d3c Fix tests 2025-01-30 09:43:46 +02:00
Aine
62017d4f4e Merge branch 'main' into broken-upgrade 2025-01-28 11:51:26 +02:00
Aine
0d7dcdc284 revisit traefik labels in readme, fixes #303 2025-01-28 11:46:19 +02:00
Aine
0eb3b77bc5 fix translations; fix tests 2025-01-27 13:09:37 +02:00
dependabot[bot]
e2fba4bbdd Bump @types/node from 22.10.7 to 22.10.10 (#304)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.10.7 to 22.10.10.
- [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-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-27 12:49:05 +02:00
dependabot[bot]
6425a6bfc4 Bump vite from 6.0.9 to 6.0.11 (#307)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.9 to 6.0.11.
- [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.11/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  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>
2025-01-27 12:48:52 +02:00
dependabot[bot]
42925e8a7c Bump eslint from 9.18.0 to 9.19.0 (#311)
Bumps [eslint](https://github.com/eslint/eslint) from 9.18.0 to 9.19.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.18.0...v9.19.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>
2025-01-27 12:48:32 +02:00
dependabot[bot]
75e89fe628 Bump ra-language-french from 5.4.4 to 5.5.2 (#305)
Bumps [ra-language-french](https://github.com/marmelab/react-admin) from 5.4.4 to 5.5.2.
- [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.4...v5.5.2)

---
updated-dependencies:
- dependency-name: ra-language-french
  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>
2025-01-27 12:30:36 +02:00
dependabot[bot]
f9c806d292 Bump @testing-library/user-event from 14.6.0 to 14.6.1 (#308)
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 14.6.0 to 14.6.1.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v14.6...v14.6.1)

---
updated-dependencies:
- dependency-name: "@testing-library/user-event"
  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>
2025-01-27 12:30:11 +02:00
dependabot[bot]
3f5022d515 Bump typescript-eslint from 8.20.0 to 8.21.0 (#312)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.20.0 to 8.21.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.21.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>
2025-01-27 12:29:51 +02:00
Aine
0748f98d47 revisit traefik labels in readme, fixes #303 2025-01-27 12:29:34 +02:00
dependabot[bot]
3c8fd351a1 Bump @eslint/js from 9.18.0 to 9.19.0 (#313)
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.18.0 to 9.19.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/commits/v9.19.0/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  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>
2025-01-27 12:29:19 +02:00
dependabot[bot]
40e6d80c35 Bump react-admin from 5.4.4 to 5.5.2 (#317)
Bumps [react-admin](https://github.com/marmelab/react-admin) from 5.4.4 to 5.5.2.
- [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.4...v5.5.2)

---
updated-dependencies:
- dependency-name: react-admin
  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>
2025-01-27 12:06:48 +02:00
dependabot[bot]
243cc40da4 Bump vite from 6.0.7 to 6.0.9 in the npm_and_yarn group (#302)
Bumps the npm_and_yarn group with 1 update: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `vite` from 6.0.7 to 6.0.9
- [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.9/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-22 06:37:53 +00:00
dependabot[bot]
3bcc51d12c Bump react-admin from 5.4.3 to 5.4.4 (#280)
Bumps [react-admin](https://github.com/marmelab/react-admin) from 5.4.3 to 5.4.4.
- [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.3...v5.4.4)

---
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>
2025-01-20 11:58:51 +02:00
dependabot[bot]
2afd7d6737 Bump react-router-dom from 6.28.1 to 6.28.2 (#282)
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.28.1 to 6.28.2.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.28.2/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  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>
2025-01-20 11:48:36 +02:00
dependabot[bot]
2357d63120 Bump @testing-library/user-event from 14.5.2 to 14.6.0 (#285)
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 14.5.2 to 14.6.0.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v14.5.2...v14.6)

---
updated-dependencies:
- dependency-name: "@testing-library/user-event"
  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>
2025-01-20 11:48:23 +02:00
dependabot[bot]
e28d07ebd3 Bump eslint-plugin-prettier from 5.2.1 to 5.2.3 (#281)
Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.2.1 to 5.2.3.
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.2.1...v5.2.3)

---
updated-dependencies:
- dependency-name: eslint-plugin-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>
2025-01-20 11:46:26 +02:00
dependabot[bot]
33f960579c Bump ra-language-english from 5.4.3 to 5.4.4 (#283)
Bumps [ra-language-english](https://github.com/marmelab/react-admin) from 5.4.3 to 5.4.4.
- [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.3...v5.4.4)

---
updated-dependencies:
- dependency-name: ra-language-english
  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>
2025-01-20 11:46:05 +02:00
dependabot[bot]
6e14bd7959 Bump ra-language-french from 5.4.3 to 5.4.4 (#284)
Bumps [ra-language-french](https://github.com/marmelab/react-admin) from 5.4.3 to 5.4.4.
- [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.3...v5.4.4)

---
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>
2025-01-20 11:45:52 +02:00
dependabot[bot]
bdbc0df95b Bump react-router from 6.28.1 to 6.28.2 (#286)
Bumps [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router) from 6.28.1 to 6.28.2.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@6.28.2/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  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>
2025-01-20 11:45:29 +02:00
dependabot[bot]
5e10d94e5f Bump @testing-library/react from 16.1.0 to 16.2.0 (#287)
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 16.1.0 to 16.2.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.1.0...v16.2.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>
2025-01-20 11:45:15 +02:00
dependabot[bot]
a934942bf6 Bump @tanstack/react-query from 5.62.15 to 5.64.2 (#290)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.62.15 to 5.64.2.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.64.2/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>
2025-01-20 11:44:58 +02:00
dependabot[bot]
c440e88806 Bump typescript from 5.7.2 to 5.7.3 (#292)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.7.2 to 5.7.3.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.7.2...v5.7.3)

---
updated-dependencies:
- dependency-name: typescript
  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>
2025-01-20 11:44:42 +02:00
dependabot[bot]
45b7ec005b Bump papaparse from 5.4.1 to 5.5.1 (#293)
Bumps [papaparse](https://github.com/mholt/PapaParse) from 5.4.1 to 5.5.1.
- [Release notes](https://github.com/mholt/PapaParse/releases)
- [Commits](https://github.com/mholt/PapaParse/commits)

---
updated-dependencies:
- dependency-name: papaparse
  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>
2025-01-20 11:44:28 +02:00
dependabot[bot]
c748523dbc Bump @types/node from 22.10.5 to 22.10.7 (#294)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.10.5 to 22.10.7.
- [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-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-20 11:43:45 +02:00
dependabot[bot]
34eea8dff4 Bump eslint-config-prettier from 9.1.0 to 10.0.1 (#295)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 9.1.0 to 10.0.1.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v9.1.0...v10.0.1)

---
updated-dependencies:
- dependency-name: eslint-config-prettier
  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>
2025-01-20 11:43:28 +02:00
dependabot[bot]
87408c0e6d Bump @mui/material from 6.3.1 to 6.4.0 (#296)
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 6.3.1 to 6.4.0.
- [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.4.0/packages/mui-material)

---
updated-dependencies:
- dependency-name: "@mui/material"
  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>
2025-01-20 11:43:02 +02:00
dependabot[bot]
5ad787075c Bump ra-i18n-polyglot from 5.4.3 to 5.4.4 (#298)
Bumps [ra-i18n-polyglot](https://github.com/marmelab/react-admin) from 5.4.3 to 5.4.4.
- [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.3...v5.4.4)

---
updated-dependencies:
- dependency-name: ra-i18n-polyglot
  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>
2025-01-20 11:42:49 +02:00
dependabot[bot]
01ae5a411f Bump eslint from 9.17.0 to 9.18.0 (#297)
Bumps [eslint](https://github.com/eslint/eslint) from 9.17.0 to 9.18.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.17.0...v9.18.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>
2025-01-20 10:46:18 +02:00
dependabot[bot]
cde60a2aba Bump @mui/utils from 5.16.13 to 5.16.14 (#299)
Bumps [@mui/utils](https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils) from 5.16.13 to 5.16.14.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/v5.16.14/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v5.16.14/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>
2025-01-20 10:45:56 +02:00
dependabot[bot]
3f5808c67b Bump ra-core from 5.4.3 to 5.4.4 (#300)
Bumps [ra-core](https://github.com/marmelab/react-admin) from 5.4.3 to 5.4.4.
- [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.3...v5.4.4)

---
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>
2025-01-20 10:45:44 +02:00
dependabot[bot]
2c697b40dd Bump typescript-eslint from 8.19.0 to 8.20.0 (#301)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.19.0 to 8.20.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.20.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>
2025-01-20 10:45:12 +02:00
Aine
9453490bca update docker instructions, fixes #278 2025-01-18 09:21:58 +02:00
Aine
0baf6ad94d try a different approach to #277 2025-01-17 10:38:40 +02:00
Aine
df911c9e97 safer decodeURIComponent(), fixes #277 2025-01-17 09:42:21 +02:00
Borislav Pantaleev
2a5b59002e Add Users' Account Data tab (#276)
* Add Account Data tab in User edit

* update readme
2025-01-16 11:00:59 +02:00
Aine
44d801a2f5 do not clear hash on replacing history 2025-01-14 14:12:39 +02:00
Hugo Renard
5ed5a88225 fix: extract loginToken using URLSearchParams (#275)
It's more robust than a regex and also works with extras queries.

Signed-off-by: Hugo Renard <hugo.renard@protonmail.com>
2025-01-14 14:01:33 +02:00
Aine
1ca029fe94 add element web to the dev stack 2025-01-13 11:02:31 +02:00
Aine
ddf3298b41 Respect base url when loading config.json (#274)
* Respect base url when loading config.json

* update readme
2025-01-09 22:36:45 +02:00
dependabot[bot]
e66c321ef9 Bump @types/node from 22.10.2 to 22.10.5 (#272)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.10.2 to 22.10.5.
- [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-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-06 23:55:38 +00:00
dependabot[bot]
c215aa2f25 Bump @mui/icons-material from 6.2.1 to 6.3.1 (#273)
Bumps [@mui/icons-material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material) from 6.2.1 to 6.3.1.
- [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.3.1/packages/mui-icons-material)

---
updated-dependencies:
- dependency-name: "@mui/icons-material"
  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>
2025-01-06 23:39:20 +00:00
dependabot[bot]
e1afcd2678 Bump @types/lodash from 4.17.13 to 4.17.14 (#271)
Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.13 to 4.17.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  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>
2025-01-06 23:38:58 +00:00
dependabot[bot]
acd0d6d848 Bump typescript-eslint from 8.18.1 to 8.19.0 (#268)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.18.1 to 8.19.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.19.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>
2025-01-04 21:03:36 +02:00
dependabot[bot]
9491c3ab14 Bump @tanstack/react-query from 5.62.8 to 5.62.15 (#266)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.62.8 to 5.62.15.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/HEAD/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>
2025-01-04 20:55:44 +02:00
dependabot[bot]
2e52d92fce Bump @mui/material from 6.2.1 to 6.3.1 (#267)
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 6.2.1 to 6.3.1.
- [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.3.1/packages/mui-material)

---
updated-dependencies:
- dependency-name: "@mui/material"
  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>
2025-01-04 20:55:32 +02:00
dependabot[bot]
a360e38b7f Bump @typescript-eslint/parser from 8.18.1 to 8.19.0 (#269)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.18.1 to 8.19.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.19.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  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>
2025-01-04 20:55:15 +02:00
dependabot[bot]
b72ace5adc Bump vite from 6.0.5 to 6.0.7 (#270)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.5 to 6.0.7.
- [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.7/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  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>
2025-01-04 20:55:02 +02:00
dependabot[bot]
c56bd76c73 Bump @typescript-eslint/eslint-plugin from 8.18.1 to 8.18.2 (#263)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.18.1 to 8.18.2.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.2/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  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>
2025-01-04 20:52:20 +02:00
dependabot[bot]
28e4ea0e76 Bump @mui/icons-material from 6.2.0 to 6.2.1 (#264)
Bumps [@mui/icons-material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material) from 6.2.0 to 6.2.1.
- [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.2.1/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>
2025-01-04 20:52:08 +02:00
dependabot[bot]
b9de3044be Bump @mui/utils from 5.16.12 to 5.16.13 (#265)
Bumps [@mui/utils](https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils) from 5.16.12 to 5.16.13.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/v5.16.13/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v5.16.13/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>
2025-01-04 20:51:57 +02:00
dependabot[bot]
7df67b99a6 Bump typescript-eslint from 8.18.0 to 8.18.1 (#251)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.18.0 to 8.18.1.
- [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.18.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  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-23 09:00:59 +00:00
dependabot[bot]
f17a245839 Bump @mui/utils from 5.16.8 to 5.16.12 (#254)
Bumps [@mui/utils](https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils) from 5.16.8 to 5.16.12.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/v5.16.12/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v5.16.12/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-23 08:45:19 +00:00
dependabot[bot]
1e1b1b0e80 Bump react-router-dom from 6.28.0 to 6.28.1 (#253)
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.28.0 to 6.28.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.28.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  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-23 08:45:06 +00:00
dependabot[bot]
0742c64c61 Bump @typescript-eslint/eslint-plugin from 8.18.0 to 8.18.1 (#248)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.18.0 to 8.18.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  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-23 08:44:44 +00:00
dependabot[bot]
992b3cd266 Bump ra-language-french from 5.4.2 to 5.4.3 (#256)
Bumps [ra-language-french](https://github.com/marmelab/react-admin) from 5.4.2 to 5.4.3.
- [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.2...v5.4.3)

---
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-23 08:29:39 +00:00
dependabot[bot]
f9a95882e9 Bump @tanstack/react-query from 5.62.7 to 5.62.8 (#249)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.62.7 to 5.62.8.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.62.8/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-23 08:29:03 +00:00
dependabot[bot]
7ca78f9fc5 Bump @typescript-eslint/parser from 8.18.0 to 8.18.1 (#247)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.18.0 to 8.18.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  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-23 08:28:38 +00:00
dependabot[bot]
95950dae86 Bump react-router from 6.28.0 to 6.28.1 (#246)
Bumps [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router) from 6.28.0 to 6.28.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/react-router@6.28.1/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@6.28.1/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  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-23 08:28:20 +00:00
dependabot[bot]
8e83204b34 Bump react-hook-form from 7.54.1 to 7.54.2 (#245)
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.54.1 to 7.54.2.
- [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.54.1...v7.54.2)

---
updated-dependencies:
- dependency-name: react-hook-form
  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-23 08:28:04 +00:00
dependabot[bot]
328c6b499d Bump vite from 6.0.3 to 6.0.5 (#244)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.3 to 6.0.5.
- [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.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  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-23 08:12:18 +00:00
dependabot[bot]
af708f5d13 Bump @mui/material from 6.2.0 to 6.2.1 (#242)
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 6.2.0 to 6.2.1.
- [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.2.1/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-23 08:11:57 +00:00
dependabot[bot]
7629e4b090 Bump react-admin from 5.4.2 to 5.4.3 (#243)
Bumps [react-admin](https://github.com/marmelab/react-admin) from 5.4.2 to 5.4.3.
- [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.2...v5.4.3)

---
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-23 08:11:30 +00:00
Borislav Pantaleev
c596d38d7a Add notifications badge and page (#240)
* WIP on server notifications

* WIP: Add server notifications page and removal of notifications

* improve design

* fix missing notifications case; add tooltop

* Fix api response

* fix tests

* add docs; update readme
2024-12-19 11:24:42 +02:00
Aine
c643bdcfce correctly handle MXIDs with not just-domain server names; update testdata config to include that case by default; fixes #239 2024-12-16 12:53:42 +02:00
Aine
630286a781 use the same protocol for .well-known lookup as in homeserver url from the login form, fixes #238 2024-12-16 12:44:00 +02:00
Aine
71e90c12a7 enable media bulk delete button, fixes #216 2024-12-16 11:51:12 +02:00
Aine
db6594f11f Merge pull request #237 from etkecc/purge-remote-media
Add "Purge Remote Media" button
2024-12-16 09:31:01 +00:00
Aine
7844987fe0 update readme 2024-12-16 11:30:48 +02:00
Aine
9f2293f9c4 Add "Purge Remote Media" button 2024-12-16 11:25:49 +02:00
Aine
fd4bf7ce5c Merge pull request #221 from etkecc/dependabot/npm_and_yarn/typescript-eslint-8.18.0
Bump typescript-eslint from 8.17.0 to 8.18.0
2024-12-16 08:50:35 +00:00
Aine
0dc969ce2c Merge pull request #236 from etkecc/dependabot/npm_and_yarn/mui/icons-material-6.2.0
Bump @mui/icons-material from 6.1.9 to 6.2.0
2024-12-16 08:50:25 +00:00
dependabot[bot]
f5455ef667 Bump @mui/icons-material from 6.1.9 to 6.2.0
Bumps [@mui/icons-material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material) from 6.1.9 to 6.2.0.
- [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.2.0/packages/mui-icons-material)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:36:21 +00:00
dependabot[bot]
edb8028162 Bump typescript-eslint from 8.17.0 to 8.18.0
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.17.0 to 8.18.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.18.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>
2024-12-16 08:35:55 +00:00
Aine
f415d637e9 Merge pull request #220 from etkecc/dependabot/npm_and_yarn/eslint-9.17.0
Bump eslint from 9.16.0 to 9.17.0
2024-12-16 08:34:50 +00:00
Aine
2c9103c769 Merge pull request #223 from etkecc/dependabot/npm_and_yarn/types/node-22.10.2
Bump @types/node from 22.10.1 to 22.10.2
2024-12-16 08:34:30 +00:00
Aine
1c6f91d3a9 Merge pull request #224 from etkecc/dependabot/npm_and_yarn/mui/material-6.2.0
Bump @mui/material from 6.1.10 to 6.2.0
2024-12-16 08:34:16 +00:00
Aine
05826e2ae1 Merge pull request #225 from etkecc/dependabot/npm_and_yarn/emotion/react-11.14.0
Bump @emotion/react from 11.13.5 to 11.14.0
2024-12-16 08:34:03 +00:00
Aine
f6ebd71ac2 Merge pull request #227 from etkecc/dependabot/npm_and_yarn/typescript-eslint/parser-8.18.0
Bump @typescript-eslint/parser from 8.17.0 to 8.18.0
2024-12-16 08:33:50 +00:00
dependabot[bot]
49d0157c81 Bump @emotion/react from 11.13.5 to 11.14.0
Bumps [@emotion/react](https://github.com/emotion-js/emotion) from 11.13.5 to 11.14.0.
- [Release notes](https://github.com/emotion-js/emotion/releases)
- [Changelog](https://github.com/emotion-js/emotion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/emotion-js/emotion/compare/@emotion/react@11.13.5...@emotion/react@11.14.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:32:54 +00:00
dependabot[bot]
ccfc5e4478 Bump @mui/material from 6.1.10 to 6.2.0
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 6.1.10 to 6.2.0.
- [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.2.0/packages/mui-material)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:32:46 +00:00
dependabot[bot]
66953dba3d Bump @types/node from 22.10.1 to 22.10.2
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.10.1 to 22.10.2.
- [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-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:31:49 +00:00
dependabot[bot]
f4df8c070c Bump eslint from 9.16.0 to 9.17.0
Bumps [eslint](https://github.com/eslint/eslint) from 9.16.0 to 9.17.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.16.0...v9.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:31:33 +00:00
dependabot[bot]
c3fa4daddb Bump @typescript-eslint/parser from 8.17.0 to 8.18.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.17.0 to 8.18.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.0/packages/parser)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:31:33 +00:00
Aine
b6782c64f7 Merge pull request #235 from etkecc/dependabot/npm_and_yarn/npm_and_yarn-af93afb32e
Bump nanoid from 3.3.7 to 3.3.8 in the npm_and_yarn group
2024-12-16 08:30:20 +00:00
Aine
8a65621b35 Merge pull request #228 from etkecc/dependabot/npm_and_yarn/ra-language-french-5.4.2
Bump ra-language-french from 5.4.1 to 5.4.2
2024-12-16 08:30:05 +00:00
Aine
b8b0d46070 Merge pull request #229 from etkecc/dependabot/npm_and_yarn/tanstack/react-query-5.62.7
Bump @tanstack/react-query from 5.62.3 to 5.62.7
2024-12-16 08:29:53 +00:00
dependabot[bot]
2f73970e47 Bump nanoid from 3.3.7 to 3.3.8 in the npm_and_yarn group
Bumps the npm_and_yarn group with 1 update: [nanoid](https://github.com/ai/nanoid).


Updates `nanoid` from 3.3.7 to 3.3.8
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:29:50 +00:00
Aine
ceacddf9c6 Merge pull request #231 from etkecc/dependabot/npm_and_yarn/emotion/styled-11.14.0
Bump @emotion/styled from 11.13.5 to 11.14.0
2024-12-16 08:29:39 +00:00
Aine
4e89ad5f56 Merge pull request #234 from etkecc/dependabot/npm_and_yarn/eslint/js-9.17.0
Bump @eslint/js from 9.16.0 to 9.17.0
2024-12-16 08:29:27 +00:00
Aine
1d4b4f97da Merge pull request #232 from etkecc/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-8.18.0
Bump @typescript-eslint/eslint-plugin from 8.17.0 to 8.18.0
2024-12-16 08:29:16 +00:00
Aine
ac36a93203 Merge pull request #230 from etkecc/dependabot/npm_and_yarn/react-hook-form-7.54.1
Bump react-hook-form from 7.54.0 to 7.54.1
2024-12-16 08:29:03 +00:00
Aine
1dd0b242e2 Merge pull request #233 from etkecc/dependabot/npm_and_yarn/react-admin-5.4.2
Bump react-admin from 5.4.1 to 5.4.2
2024-12-16 08:28:46 +00:00
dependabot[bot]
608d9fa9d8 Bump @eslint/js from 9.16.0 to 9.17.0
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.16.0 to 9.17.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/commits/v9.17.0/packages/js)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:09:20 +00:00
dependabot[bot]
f5841c6a30 Bump react-admin from 5.4.1 to 5.4.2
Bumps [react-admin](https://github.com/marmelab/react-admin) from 5.4.1 to 5.4.2.
- [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.1...v5.4.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:09:11 +00:00
dependabot[bot]
266b13ce0b Bump @typescript-eslint/eslint-plugin from 8.17.0 to 8.18.0
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.17.0 to 8.18.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.0/packages/eslint-plugin)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:09:02 +00:00
dependabot[bot]
1be9171b93 Bump @emotion/styled from 11.13.5 to 11.14.0
Bumps [@emotion/styled](https://github.com/emotion-js/emotion) from 11.13.5 to 11.14.0.
- [Release notes](https://github.com/emotion-js/emotion/releases)
- [Changelog](https://github.com/emotion-js/emotion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/emotion-js/emotion/compare/@emotion/styled@11.13.5...@emotion/styled@11.14.0)

---
updated-dependencies:
- dependency-name: "@emotion/styled"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:08:48 +00:00
dependabot[bot]
037c2acee2 Bump react-hook-form from 7.54.0 to 7.54.1
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.54.0 to 7.54.1.
- [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.54.0...v7.54.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:08:29 +00:00
dependabot[bot]
38a0c9a9fc Bump @tanstack/react-query from 5.62.3 to 5.62.7
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.62.3 to 5.62.7.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.62.7/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>
2024-12-16 08:08:20 +00:00
dependabot[bot]
8703cff7bf Bump ra-language-french from 5.4.1 to 5.4.2
Bumps [ra-language-french](https://github.com/marmelab/react-admin) from 5.4.1 to 5.4.2.
- [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.1...v5.4.2)

---
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>
2024-12-16 08:08:11 +00:00
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
43 changed files with 2979 additions and 1876 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
/testdata

11
Dockerfile.build Normal file
View File

@@ -0,0 +1,11 @@
FROM node:lts AS builder
ARG BASE_PATH=./
WORKDIR /src
COPY . /src
RUN yarn config set enableTelemetry 0 && \
yarn install --immutable --network-timeout=300000 && \
yarn build --base=$BASE_PATH
FROM ghcr.io/static-web-server/static-web-server:2
ENV SERVER_ROOT=/app
COPY --from=builder /src/dist /app

View File

@@ -21,6 +21,7 @@
* [Fork differences](#fork-differences) * [Fork differences](#fork-differences)
* [Changes](#changes) * [Changes](#changes)
* [exclusive for etke.cc customers](#exclusive-for-etkecc-customers)
* [Development](#development) * [Development](#development)
* [Support](#support) * [Support](#support)
* [Configuration](#configuration) * [Configuration](#configuration)
@@ -58,6 +59,8 @@ The full list is described below in the [Changes](#changes) section.
### Changes ### Changes
_the list will be updated as new changes are added_
The following changes are already implemented: The following changes are already implemented:
* 🛑 [Prevent admins from deleting themselves](https://github.com/etkecc/synapse-admin/pull/1) * 🛑 [Prevent admins from deleting themselves](https://github.com/etkecc/synapse-admin/pull/1)
@@ -97,21 +100,31 @@ The following changes are already implemented:
* 🛑 [Prevent accidental user overwrites](https://github.com/etkecc/synapse-admin/pull/139) * 🛑 [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) * 🔍 [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 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) * 🖼️ [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) * 🔄 [Allow prefilling any fields on the login form via GET params](https://github.com/etkecc/synapse-admin/pull/181)
* _(for [etke.cc](https://etke.cc) customers only)_ [Server Status indicator and page](https://github.com/etkecc/synapse-admin/pull/182) * 🎞️ [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)
* 🗑️ [Add "Purge Remote Media" button](https://github.com/etkecc/synapse-admin/pull/237)
* [Respect base url (`BASE_PATH` / `vite build --base`) when loading `config.json`](https://github.com/etkecc/synapse-admin/pull/274)
* [Add Users' Account Data tab](https://github.com/etkecc/synapse-admin/pull/276)
#### exclusive for [etke.cc](https://etke.cc) customers
_the list will be updated as new changes are added_ 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)
* 📬 [Server Notifications indicator and page](https://github.com/etkecc/synapse-admin/pull/240)
### Development ### Development
`just run-dev` to start the development stack (depending on your system speed, you may want to re-run this command if `just run-dev` to start the development stack (depending on your system speed, you may want to re-run this command if
user creation fails) user creation fails)
This command initializes the development environment (local Synapse server and Postgres DB), This command initializes the development environment (local Synapse server, Element Web client app, and Postgres DB),
and launches the app in a dev mode at `http://localhost:5173` and launches the app in a dev mode at `http://localhost:5173`
After that open [http://localhost:5173](http://localhost:5173?username=admin&password=admin&server=http://localhost:8008) in your browser, After that open [http://localhost:5173](http://localhost:5173?username=admin&password=admin&server=http://localhost:8008) in your browser,
@@ -121,6 +134,8 @@ login using the following credentials:
* Password: admin * Password: admin
* Homeserver URL: http://localhost:8008 * Homeserver URL: http://localhost:8008
Element Web runs on http://localhost:8080
### Support ### Support
If you have any questions or need help, feel free to join the [community room](https://matrix.to/#/#synapse-admin:etke.cc) or create an issue on GitHub. If you have any questions or need help, feel free to join the [community room](https://matrix.to/#/#synapse-admin:etke.cc) or create an issue on GitHub.
@@ -246,6 +261,7 @@ You have three options:
hostname: synapse-admin hostname: synapse-admin
build: build:
context: https://github.com/etkecc/synapse-admin.git context: https://github.com/etkecc/synapse-admin.git
dockerfile: Dockerfile.build
args: args:
- BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 - BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
# - NODE_OPTIONS="--max_old_space_size=1024" # - NODE_OPTIONS="--max_old_space_size=1024"
@@ -274,7 +290,7 @@ Example for Traefik:
```yml ```yml
services: services:
traefik: traefik:
image: traefik:mimolette image: traefik:v3
restart: unless-stopped restart: unless-stopped
ports: ports:
- 80:80 - 80:80
@@ -287,11 +303,12 @@ services:
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.synapse-admin.rule=Host(`example.com`)&&PathPrefix(`/admin`)" - "traefik.http.routers.admin.rule=Host(`example.com`) && PathPrefix(`/admin`)"
- "traefik.http.routers.synapse-admin.middlewares=admin,admin_path" - "traefik.http.services.admin.loadbalancer.server.port=80"
- "traefik.http.middlewares.admin.redirectregex.regex=^(.*)/admin/?" - "traefik.http.middlewares.admin-slashless-redirect.redirectregex.regex=(/admin)$$"
- "traefik.http.middlewares.admin.redirectregex.replacement=$${1}/admin/" - "traefik.http.middlewares.admin-slashless-redirect.redirectregex.replacement=$${1}/"
- "traefik.http.middlewares.admin_path.stripprefix.prefixes=/admin" - "traefik.http.middlewares.admin-strip-prefix.stripprefix.prefixes=/admin"
- "traefik.http.routers.admin.middlewares=admin-slashless-redirect,admin-strip-prefix"
``` ```
## Development ## Development

View File

@@ -1,6 +1,6 @@
services: services:
synapse: synapse:
image: ghcr.io/element-hq/synapse:latest image: ghcr.io/element-hq/synapse:develop
entrypoint: python entrypoint: python
command: "-m synapse.app.homeserver -c /config/homeserver.yaml" command: "-m synapse.app.homeserver -c /config/homeserver.yaml"
ports: ports:
@@ -18,3 +18,17 @@ services:
POSTGRES_PASSWORD: synapse POSTGRES_PASSWORD: synapse
POSTGRES_DB: synapse POSTGRES_DB: synapse
POSTGRES_INITDB_ARGS: "--lc-collate C --lc-ctype C --encoding UTF8" POSTGRES_INITDB_ARGS: "--lc-collate C --lc-ctype C --encoding UTF8"
element:
image: docker.io/vectorim/element-web:latest
depends_on:
synapse:
condition: service_healthy
restart: true
ports:
- "8080:8080"
volumes:
- ./testdata/element/nginx.conf:/etc/nginx/nginx.conf:ro
- /dev/null:/etc/nginx/conf.d/default.conf:ro
- ./testdata/element/config.json:/app/config.json:ro

View File

@@ -5,6 +5,7 @@ services:
image: ghcr.io/etkecc/synapse-admin:latest image: ghcr.io/etkecc/synapse-admin:latest
# build: # build:
# context: . # context: .
# dockerfile: Dockerfile.build
# to use the docker-compose as standalone without a local repo clone, # to use the docker-compose as standalone without a local repo clone,
# replace the context definition with this: # replace the context definition with this:

View File

@@ -2,12 +2,30 @@ import type { JestConfigWithTsJest } from "ts-jest";
const config: JestConfigWithTsJest = { const config: JestConfigWithTsJest = {
preset: "ts-jest", preset: "ts-jest",
testEnvironment: "jsdom", testEnvironment: "jest-fixed-jsdom",
collectCoverage: true, collectCoverage: true,
coveragePathIgnorePatterns: ["node_modules", "dist"], coveragePathIgnorePatterns: ["node_modules", "dist"],
coverageDirectory: "<rootDir>/coverage/", coverageDirectory: "<rootDir>/coverage/",
coverageReporters: ["html", "text", "text-summary", "cobertura"], coverageReporters: ["html", "text", "text-summary", "cobertura"],
extensionsToTreatAsEsm: [".ts", ".tsx"], extensionsToTreatAsEsm: [".ts", ".tsx"],
setupFilesAfterEnv: ["<rootDir>/src/jest.setup.ts"], setupFilesAfterEnv: ["<rootDir>/src/jest.setup.ts"],
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
diagnostics: {
ignoreCodes: [1343]
},
astTransformers: {
before: [
{
path: 'ts-jest-mock-import-meta',
options: { metaObjectReplacement: { env: { BASE_URL: "/" } } }
}
]
}
}
]
}
}; };
export default config; export default config;

View File

@@ -16,6 +16,8 @@ run-dev:
@docker-compose -f docker-compose-dev.yml up -d postgres @docker-compose -f docker-compose-dev.yml up -d postgres
@echo "Starting Synapse..." @echo "Starting Synapse..."
@docker-compose -f docker-compose-dev.yml up -d synapse @docker-compose -f docker-compose-dev.yml up -d synapse
@echo "Starting Element Web..."
@docker-compose -f docker-compose-dev.yml up -d element
@echo "Ensure admin user is registered..." @echo "Ensure admin user is registered..."
@docker-compose -f docker-compose-dev.yml exec synapse register_new_matrix_user --admin -u admin -p admin -c /config/homeserver.yaml http://localhost:8008 || true @docker-compose -f docker-compose-dev.yml exec synapse register_new_matrix_user --admin -u admin -p admin -c /config/homeserver.yaml http://localhost:8008 || true
@echo "Starting the app..." @echo "Starting the app..."

View File

@@ -11,64 +11,64 @@
"url": "https://github.com/etkecc/synapse-admin" "url": "https://github.com/etkecc/synapse-admin"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.13.0", "@eslint/js": "^9.19.0",
"@testing-library/dom": "^10.0.0", "@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.0", "@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.5.2", "@testing-library/user-event": "^14.6.1",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/lodash": "^4.17.13", "@types/lodash": "^4.17.14",
"@types/node": "^22.10.1", "@types/node": "^22.10.10",
"@types/papaparse": "^5.3.15", "@types/papaparse": "^5.3.15",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@typescript-eslint/eslint-plugin": "^8.14.0", "@typescript-eslint/eslint-plugin": "^8.18.2",
"@typescript-eslint/parser": "^8.15.0", "@typescript-eslint/parser": "^8.19.0",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.16.0", "eslint": "^9.19.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^10.0.1",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-unused-imports": "^4.1.4", "eslint-plugin-unused-imports": "^4.1.4",
"eslint-plugin-yaml": "^1.0.3", "eslint-plugin-yaml": "^1.0.3",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3", "jest-fetch-mock": "^3.0.3",
"prettier": "^3.4.1", "prettier": "^3.4.2",
"react-test-renderer": "^18.3.1", "react-test-renderer": "^18.3.1",
"ts-jest": "^29.2.5", "ts-jest": "^29.2.5",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.7.2", "typescript": "^5.7.3",
"typescript-eslint": "^8.16.0", "typescript-eslint": "^8.21.0",
"vite": "^5.4.11", "vite": "^6.0.11",
"vite-plugin-version-mark": "^0.1.3" "vite-plugin-version-mark": "^0.1.4"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.13.5", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.13.5", "@emotion/styled": "^11.14.0",
"@haleos/ra-language-german": "^1.0.0", "@haleos/ra-language-german": "^1.0.0",
"@haxqer/ra-language-chinese": "^4.16.2", "@haxqer/ra-language-chinese": "^4.16.2",
"@mui/icons-material": "^6.1.8", "@mui/icons-material": "^6.3.1",
"@mui/material": "^6.1.9", "@mui/material": "^6.4.0",
"@mui/utils": "^5.16.8", "@mui/utils": "^5.16.14",
"@tanstack/react-query": "^5.62.0", "@tanstack/react-query": "^5.64.2",
"history": "^5.3.0", "history": "^5.3.0",
"jest-fixed-jsdom": "^0.0.9",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"papaparse": "^5.4.1", "papaparse": "^5.5.1",
"ra-core": "^5.3.4", "ra-core": "^5.4.4",
"ra-i18n-polyglot": "^5.3.4", "ra-i18n-polyglot": "^5.4.4",
"ra-language-english": "^5.3.4", "ra-language-english": "^5.4.4",
"ra-language-farsi": "^5.0.0", "ra-language-farsi": "^5.1.0",
"ra-language-french": "^5.4.0", "ra-language-french": "^5.5.2",
"ra-language-italian": "^3.13.1", "ra-language-italian": "^3.13.1",
"ra-language-russian": "^4.14.2", "ra-language-russian": "^4.14.2",
"react": "^18.3.1", "react": "^18.3.1",
"react-admin": "^5.4.0", "react-admin": "^5.5.2",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.53.2", "react-hook-form": "^7.54.2",
"react-is": "^18.3.1", "react-is": "^18.3.1",
"react-router": "^6.26.2", "ts-jest-mock-import-meta": "^1.2.1"
"react-router-dom": "^6.28.0"
}, },
"scripts": { "scripts": {
"start": "vite serve", "start": "vite serve",

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -1,12 +1,32 @@
import { render, screen } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
import { BrowserRouter } from "react-router-dom";
import fetchMock from "jest-fetch-mock"; import fetchMock from "jest-fetch-mock";
fetchMock.enableMocks(); fetchMock.enableMocks();
jest.mock("./synapse/authProvider", () => ({
__esModule: true,
default: {
logout: jest.fn().mockResolvedValue(undefined),
},
}));
import App from "./App"; import App from "./App";
describe("App", () => { describe("App", () => {
beforeEach(() => {
// Reset all mocks before each test
fetchMock.resetMocks();
// Mock any fetch call to return empty JSON immediately
fetchMock.mockResponseOnce(JSON.stringify({}));
});
it("renders", async () => { it("renders", async () => {
render(<App />); render(
<BrowserRouter>
<App />
</BrowserRouter>
);
await screen.findAllByText("Welcome to Synapse Admin"); await screen.findAllByText("Welcome to Synapse Admin");
}); });
}); });

View File

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

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

View File

@@ -0,0 +1,65 @@
import { useDataProvider, useRecordContext, useTranslate } from "react-admin";
import { useEffect, useState } from "react";
import { Typography, Box, Stack, Accordion, AccordionSummary, AccordionDetails } from "@mui/material";
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
import { SynapseDataProvider } from "../synapse/dataProvider";
const UserAccountData = () => {
const dataProvider = useDataProvider() as SynapseDataProvider;
const record = useRecordContext();
const translate = useTranslate();
const [globalAccountData, setGlobalAccountData] = useState({});
const [roomsAccountData, setRoomsAccountData] = useState({});
if (!record) {
return null;
}
useEffect(() => {
const fetchAccountData = async () => {
const accountData = await dataProvider.getAccountData(record.id);
setGlobalAccountData(accountData.account_data.global);
setRoomsAccountData(accountData.account_data.rooms);
};
fetchAccountData();
}, []);
if (Object.keys(globalAccountData).length === 0 && Object.keys(roomsAccountData).length === 0) {
return <Typography variant="body2">{translate('ra.navigation.no_results', {
resource: 'Account Data',
_: 'No results found.',
})}</Typography>;
}
return <>
<Stack
direction="column"
spacing={2}
width="100%"
>
<Typography variant="h6">{translate('resources.users.account_data.title')}</Typography>
<Typography variant="body1">
<Box>
<Accordion>
<AccordionSummary expandIcon={<ArrowDownwardIcon />}>
<Typography variant="h6">{translate('resources.users.account_data.global')}</Typography>
</AccordionSummary>
<AccordionDetails>
<Box sx={{ whiteSpace: "pre-wrap" }}>{JSON.stringify(globalAccountData, null, 4)}</Box>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary expandIcon={<ArrowDownwardIcon />}>
<Typography variant="h6">{translate('resources.users.account_data.rooms')}</Typography>
</AccordionSummary>
<AccordionDetails>
<Box sx={{ whiteSpace: "pre-wrap" }}>{JSON.stringify(roomsAccountData, null, 4)}</Box>
</AccordionDetails>
</Accordion>
</Box>
</Typography>
</Stack>
</>
}
export default UserAccountData;

View File

@@ -28,3 +28,15 @@ Server Status page. This page contains the following information:
* Overall server status (up/updating/has issues) * Overall server status (up/updating/has issues)
* Details about the currently running command (if any) * 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 * Details about the server's components statuses (up/down with error details and suggested actions) by categories
### Server Notifications icon
![Server Notifications icon](../../../screenshots/etke.cc/server-notifications/badge.webp)
In the application bar the new notifications icon is displayed that shows the number of unread (not removed) notifications
### Server Notifications page
![Server Notifications Page](../../../screenshots/etke.cc/server-notifications/page.webp)
When you click on a notification from the [Server Notifications icon](#server-notifications-icon)'s list in the application bar, you will be redirected to the Server Notifications page. This page contains the full text of all the notifications you have about your server.

View File

@@ -0,0 +1,184 @@
import { Badge, useTheme, Button, Paper, Popper, ClickAwayListener, Box, List, ListItem, ListItemText, Typography, ListSubheader, IconButton, Divider, Tooltip } from "@mui/material";
import NotificationsIcon from '@mui/icons-material/Notifications';
import DeleteIcon from "@mui/icons-material/Delete";
import { useDataProvider, useStore } from "react-admin";
import { useNavigate } from "react-router";
import { Fragment, useEffect, useState } from "react";
import { useAppContext } from "../../Context";
import { ServerNotificationsResponse } from "../../synapse/dataProvider";
const SERVER_NOTIFICATIONS_INTERVAL_TIME = 300000;
const useServerNotifications = () => {
const [serverNotifications, setServerNotifications] = useStore<ServerNotificationsResponse>("serverNotifications", { notifications: [], success: false });
const { etkeccAdmin } = useAppContext();
const dataProvider = useDataProvider();
const { notifications, success } = serverNotifications;
const fetchNotifications = async () => {
const notificationsResponse: ServerNotificationsResponse = await dataProvider.getServerNotifications(etkeccAdmin);
setServerNotifications({
...notificationsResponse,
notifications: notificationsResponse.notifications,
success: notificationsResponse.success
});
};
const deleteServerNotifications = async () => {
const deleteResponse = await dataProvider.deleteServerNotifications(etkeccAdmin);
if (deleteResponse.success) {
await fetchNotifications();
}
};
useEffect(() => {
let serverNotificationsInterval: NodeJS.Timeout;
if (etkeccAdmin) {
fetchNotifications();
setTimeout(() => {
// start the interval after the SERVER_NOTIFICATIONS_INTERVAL_TIME to avoid too many requests
serverNotificationsInterval = setInterval(fetchNotifications, SERVER_NOTIFICATIONS_INTERVAL_TIME);
}, SERVER_NOTIFICATIONS_INTERVAL_TIME);
}
return () => {
if (serverNotificationsInterval) {
clearInterval(serverNotificationsInterval);
}
}
}, [etkeccAdmin]);
return { success, notifications, deleteServerNotifications };
};
export const ServerNotificationsBadge = () => {
const navigate = useNavigate();
const { success, notifications, deleteServerNotifications } = useServerNotifications();
const theme = useTheme();
// Modify menu state to work with Popper
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleSeeAllNotifications = () => {
handleClose();
navigate("/server_notifications");
};
const handleClearAllNotifications = async () => {
deleteServerNotifications()
handleClose();
};
if (!success) {
return null;
}
return (
<Box>
<IconButton onClick={handleOpen} sx={{ color: theme.palette.common.white }}>
<Tooltip title={notifications && notifications.length > 0 ? `${notifications.length} new notifications` : `No notifications yet`}>
{notifications && notifications.length > 0 && (
<Badge badgeContent={notifications.length} color="error">
<NotificationsIcon />
</Badge>
) || <NotificationsIcon />}
</Tooltip>
</IconButton>
<Popper
open={open}
anchorEl={anchorEl}
placement="bottom-end"
style={{ zIndex: 1300 }}
>
<ClickAwayListener onClickAway={handleClose}>
<Paper
elevation={3}
sx={{
p: 1,
maxHeight: "350px",
overflowY: "auto",
minWidth: "300px",
maxWidth: {
xs: "100vw", // Full width on mobile
sm: "400px" // Fixed width on desktop
}
}}
>
{(!notifications || notifications.length === 0) ? (
<Typography sx={{ p: 1 }} variant="body2">No new notifications</Typography>
) : (
<List sx={{ p: 0 }} dense={true}>
<ListSubheader
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
fontWeight: "bold",
backgroundColor: "inherit",
}}
>
<Typography variant="h6">Notifications</Typography>
<Box sx={{ cursor: "pointer", color: theme.palette.primary.main }} onClick={() => handleSeeAllNotifications()}>See all notifications</Box>
</ListSubheader>
<Divider />
{notifications.map((notification, index) => {
return (<Fragment key={notification.event_id ? notification.event_id : index }>
<ListItem
onClick={() => handleSeeAllNotifications()}
sx={{
"&:hover": {
backgroundColor: "action.hover",
cursor: "pointer"
}
}}
>
<ListItemText
primary={
<Typography
variant="body2"
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}}
dangerouslySetInnerHTML={{ __html: notification.output.split("\n")[0] }}
/>
}
/>
</ListItem>
<Divider />
</Fragment>
)})}
<ListItem>
<Button
key="clear-all-notifications"
onClick={() => handleClearAllNotifications()}
size="small"
color="error"
sx={{
pl: 0,
pt: 1,
verticalAlign: "middle"
}}
>
<DeleteIcon fontSize="small" sx={{ mr: 1 }} />
Clear all
</Button>
</ListItem>
</List>
)}
</Paper>
</ClickAwayListener>
</Popper>
</Box>
);
};

View File

@@ -0,0 +1,58 @@
import { Box, Typography, Paper, Button } from "@mui/material"
import { Stack } from "@mui/material"
import { useStore } from "react-admin"
import dataProvider, { ServerNotificationsResponse } from "../../synapse/dataProvider"
import { useAppContext } from "../../Context";
import DeleteIcon from "@mui/icons-material/Delete";
const DisplayTime = ({ date }: { date: string }) => {
const dateFromDateString = new Date(date);
return <>{dateFromDateString.toLocaleString()}</>;
};
const ServerNotificationsPage = () => {
const { etkeccAdmin } = useAppContext();
const [serverNotifications, setServerNotifications] = useStore<ServerNotificationsResponse>("serverNotifications", {
notifications: [],
success: false,
});
const notifications = serverNotifications.notifications;
return (
<Stack spacing={3} mt={3}>
<Stack spacing={1} direction="row" alignItems="center">
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", gap: 1 }}>
<Typography variant="h4">Server Notifications</Typography>
<Button variant="text" color="error" onClick={async () => {
await dataProvider.deleteServerNotifications(etkeccAdmin);
setServerNotifications({
notifications: [],
success: true,
});
}}>
<DeleteIcon fontSize="small" sx={{ mr: 1 }} /> Clear
</Button>
</Box>
</Stack>
{notifications.length === 0 ? (
<Paper sx={{ p: 2 }}>
<Typography>No new notifications.</Typography>
</Paper>
) : (
notifications.map((notification, index) => (
<Paper key={notification.event_id ? notification.event_id : index} sx={{ p: 2 }}>
<Stack spacing={1}>
<Typography variant="subtitle1" fontWeight="bold" color="text.secondary">
<DisplayTime date={notification.sent_at} />
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: notification.output }} />
</Stack>
</Paper>
))
)}
</Stack>
);
};
export default ServerNotificationsPage;

View File

@@ -1,6 +1,6 @@
import { Avatar, Badge, Theme, Tooltip } from "@mui/material"; import { Avatar, Badge, Theme, Tooltip } from "@mui/material";
import { useEffect } from "react"; import { useEffect } from "react";
import { useAppContext } from "../../App"; import { useAppContext } from "../../Context";
import { Button, useDataProvider, useStore } from "react-admin"; import { Button, useDataProvider, useStore } from "react-admin";
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
import MonitorHeartIcon from '@mui/icons-material/MonitorHeart'; import MonitorHeartIcon from '@mui/icons-material/MonitorHeart';

View File

@@ -35,6 +35,7 @@ import { useMutation } from "@tanstack/react-query";
import { dateParser } from "../utils/date"; import { dateParser } from "../utils/date";
import { DeleteMediaParams, SynapseDataProvider } from "../synapse/dataProvider"; import { DeleteMediaParams, SynapseDataProvider } from "../synapse/dataProvider";
import { fetchAuthenticatedMedia } from "../utils/fetchMedia"; import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
import decodeURLComponent from "../utils/decodeURLComponent";
const DeleteMediaDialog = ({ open, onClose, onSubmit }) => { const DeleteMediaDialog = ({ open, onClose, onSubmit }) => {
const translate = useTranslate(); const translate = useTranslate();
@@ -124,6 +125,80 @@ export const DeleteMediaButton = (props: ButtonProps) => {
); );
}; };
const PurgeRemoteMediaDialog = ({ open, onClose, onSubmit }) => {
const translate = useTranslate();
const PurgeRemoteMediaToolbar = (props: ToolbarProps) => (
<Toolbar {...props}>
<SaveButton label="purge_remote_media.action.send" icon={<DeleteSweepIcon />} />
<Button label="ra.action.cancel" onClick={onClose}>
<IconCancel />
</Button>
</Toolbar>
);
return (
<Dialog open={open} onClose={onClose}>
<DialogTitle>{translate("purge_remote_media.action.send")}</DialogTitle>
<DialogContent>
<DialogContentText>{translate("purge_remote_media.helper.send")}</DialogContentText>
<SimpleForm toolbar={<PurgeRemoteMediaToolbar />} onSubmit={onSubmit}>
<DateTimeInput
source="before_ts"
label="purge_remote_media.fields.before_ts"
defaultValue={0}
parse={dateParser}
/>
</SimpleForm>
</DialogContent>
</Dialog>
);
};
export const PurgeRemoteMediaButton = (props: ButtonProps) => {
const theme = useTheme();
const [open, setOpen] = useState(false);
const notify = useNotify();
const dataProvider = useDataProvider<SynapseDataProvider>();
const { mutate: purgeRemoteMedia, isPending } = useMutation({
mutationFn: (values: DeleteMediaParams) => dataProvider.purgeRemoteMedia(values),
onSuccess: () => {
notify("purge_remote_media.action.send_success");
closeDialog();
},
onError: () => {
notify("purge_remote_media.action.send_failure", {
type: "error",
});
},
});
const openDialog = () => setOpen(true);
const closeDialog = () => setOpen(false);
return (
<>
<Button
{...props}
label="purge_remote_media.action.send"
onClick={openDialog}
disabled={isPending}
sx={{
"&:hover": {
// Reset on mouse devices
"@media (hover: none)": {
backgroundColor: "transparent",
},
},
}}
>
<DeleteSweepIcon />
</Button>
<PurgeRemoteMediaDialog open={open} onClose={closeDialog} onSubmit={purgeRemoteMedia} />
</>
);
};
export const ProtectMediaButton = (props: ButtonProps) => { export const ProtectMediaButton = (props: ButtonProps) => {
const record = useRecordContext(); const record = useRecordContext();
const translate = useTranslate(); const translate = useTranslate();
@@ -314,6 +389,7 @@ export const QuarantineMediaButton = (props: ButtonProps) => {
export const ViewMediaButton = ({ mxcURL, label, uploadName, mimetype }) => { export const ViewMediaButton = ({ mxcURL, label, uploadName, mimetype }) => {
const translate = useTranslate(); const translate = useTranslate();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const notify = useNotify();
const isImage = mimetype && mimetype.startsWith("image/"); const isImage = mimetype && mimetype.startsWith("image/");
const openFileInNewTab = (blobURL: string) => { const openFileInNewTab = (blobURL: string) => {
@@ -340,12 +416,25 @@ export const ViewMediaButton = ({ mxcURL, label, uploadName, mimetype }) => {
const handleFile = async (preview: boolean) => { const handleFile = async (preview: boolean) => {
setLoading(true); setLoading(true);
const response = await fetchAuthenticatedMedia(mxcURL, "original"); const response = await fetchAuthenticatedMedia(mxcURL, "original");
const blob = await response.blob();
const blobURL = URL.createObjectURL(blob); if (response.ok) {
if (preview) { const blob = await response.blob();
openFileInNewTab(blobURL); const blobURL = URL.createObjectURL(blob);
if (preview) {
openFileInNewTab(blobURL);
} else {
downloadFile(blobURL);
}
} else { } else {
downloadFile(blobURL); 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); setLoading(false);
}; };
@@ -391,8 +480,16 @@ export const MediaIDField = ({ source }) => {
return null; return null;
} }
const uploadName = decodeURIComponent(get(record, "upload_name")?.toString()); let uploadName = mediaID;
const mxcURL = `mxc://${homeserver}/${mediaID}`; if (get(record, "upload_name")) {
uploadName = decodeURLComponent(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}/>; return <ViewMediaButton mxcURL={mxcURL} label={mediaID} uploadName={uploadName} mimetype={record.media_type}/>;
}; };
@@ -408,7 +505,10 @@ export const ReportMediaContent = ({ source }) => {
return null; return null;
} }
const uploadName = decodeURIComponent(get(record, "event_json.content.body")?.toString()); let uploadName = "";
if (get(record, "event_json.content.body")) {
uploadName = decodeURLComponent(get(record, "event_json.content.body")?.toString());
}
return <ViewMediaButton mxcURL={mxcURL} label={mxcURL} uploadName={uploadName} mimetype={record.media_type}/>; return <ViewMediaButton mxcURL={mxcURL} label={mxcURL} uploadName={uploadName} mimetype={record.media_type}/>;
}; };

View File

@@ -15,6 +15,7 @@ const fixedGermanMessages = {
action: { action: {
...formalGermanMessages.ra.action, ...formalGermanMessages.ra.action,
update_application: "Anwendung aktualisieren", update_application: "Anwendung aktualisieren",
select_all_button: "Alle auswählen",
}, },
page: { page: {
...formalGermanMessages.ra.page, ...formalGermanMessages.ra.page,
@@ -28,6 +29,7 @@ const fixedGermanMessages = {
"Sie haben nicht die erforderlichen Berechtigungen um auf diese Seite zuzugreifen.", "Sie haben nicht die erforderlichen Berechtigungen um auf diese Seite zuzugreifen.",
authentication_error: authentication_error:
"Der Authentifizierungsserver hat einen Fehler zurückgegeben und Ihre Anmeldedaten konnten nicht überprüft werden.", "Der Authentifizierungsserver hat einen Fehler zurückgegeben und Ihre Anmeldedaten konnten nicht überprüft werden.",
select_all_limit_reached: "Es gibt zu viele Elemente, um sie alle auszuwählen. Es wurden nur die ersten %{max} Elemente ausgewählt.",
}, },
}, },
} }
@@ -55,7 +57,7 @@ const de: SynapseTranslationMessages = {
}, },
users: { users: {
invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.", invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.",
tabs: { sso: "SSO", experimental: "Experimentell", limits: "Rate Limits" }, tabs: { sso: "SSO", experimental: "Experimentell", limits: "Rate Limits", account_data: "Kontodaten" },
}, },
rooms: { rooms: {
details: "Raumdetails", details: "Raumdetails",
@@ -64,6 +66,7 @@ const de: SynapseTranslationMessages = {
members: "Mitglieder", members: "Mitglieder",
detail: "Details", detail: "Details",
permission: "Berechtigungen", permission: "Berechtigungen",
media: "Medien",
}, },
}, },
reports: { tabs: { basic: "Allgemein", detail: "Details" } }, reports: { tabs: { basic: "Allgemein", detail: "Details" } },
@@ -146,6 +149,20 @@ const de: SynapseTranslationMessages = {
send: "Diese API löscht die lokalen Medien von der Festplatte des eigenen Servers. Dies umfasst alle lokalen Miniaturbilder und Kopien von Medien. Diese API wirkt sich nicht auf Medien aus, die sich in externen Medien-Repositories befinden.", send: "Diese API löscht die lokalen Medien von der Festplatte des eigenen Servers. Dies umfasst alle lokalen Miniaturbilder und Kopien von Medien. Diese API wirkt sich nicht auf Medien aus, die sich in externen Medien-Repositories befinden.",
}, },
}, },
purge_remote_media: {
name: "Externe Medien",
fields: {
before_ts: "letzter Zugriff vor",
},
action: {
send: "Externe Medien löschen",
send_success: "Die Anfrage zum Löschen externer Medien wurde gesendet.",
send_failure: "Bei der Anfrage zum Löschen externer Medien ist ein Fehler aufgetreten.",
},
helper: {
send: "Diese API löscht den externen Medien-Cache von der Festplatte Ihres eigenen Servers. Dazu gehören alle lokalen Thumbnails und Kopien heruntergeladener Medien. Diese API beeinflusst nicht die Medien, die in das eigene Medienarchiv des Servers hochgeladen wurden.",
},
},
resources: { resources: {
users: { users: {
name: "Benutzer", name: "Benutzer",
@@ -159,11 +176,13 @@ const de: SynapseTranslationMessages = {
is_guest: "Gast", is_guest: "Gast",
admin: "Server Administrator", admin: "Server Administrator",
locked: "Gesperrt", locked: "Gesperrt",
suspended: "Suspendiert",
deactivated: "Deaktiviert", deactivated: "Deaktiviert",
erased: "Gelöscht", erased: "Gelöscht",
guests: "Zeige Gäste", guests: "Zeige Gäste",
show_deactivated: "Zeige deaktivierte Benutzer", show_deactivated: "Zeige deaktivierte Benutzer",
show_locked: "Zeige gesperrte Benutzer", show_locked: "Zeige gesperrte Benutzer",
show_suspended: "Zeige suspendierte Benutzer",
user_id: "Suche Benutzer", user_id: "Suche Benutzer",
displayname: "Anzeigename", displayname: "Anzeigename",
password: "Passwort", password: "Passwort",
@@ -181,6 +200,7 @@ const de: SynapseTranslationMessages = {
password: "Durch die Änderung des Passworts wird der Benutzer von allen Sitzungen abgemeldet.", 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.", 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.", 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: "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_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.", erase_admin_error: "Das Löschen des eigenen Benutzers ist nicht erlaubt.",
@@ -211,6 +231,11 @@ const de: SynapseTranslationMessages = {
messages_per_second_text: "Die Anzahl der Aktionen, die in einer Sekunde durchgeführt werden können.", messages_per_second_text: "Die Anzahl der Aktionen, die in einer Sekunde durchgeführt werden können.",
burst_count: "Burst-Anzahl", burst_count: "Burst-Anzahl",
burst_count_text: "Die Anzahl der Aktionen, die vor der Begrenzung durchgeführt werden können.", burst_count_text: "Die Anzahl der Aktionen, die vor der Begrenzung durchgeführt werden können.",
},
account_data: {
title: "Kontodaten",
global: "Globale",
rooms: "Räume",
} }
}, },
rooms: { rooms: {
@@ -429,6 +454,18 @@ const de: SynapseTranslationMessages = {
sender: "Absender", 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: { room_directory: {
name: "Raumverzeichnis", name: "Raumverzeichnis",
fields: { fields: {

View File

@@ -29,6 +29,7 @@ const en: SynapseTranslationMessages = {
sso: "SSO", sso: "SSO",
experimental: "Experimental", experimental: "Experimental",
limits: "Rate Limits", limits: "Rate Limits",
account_data: "Account Data",
}, },
}, },
rooms: { rooms: {
@@ -38,6 +39,7 @@ const en: SynapseTranslationMessages = {
members: "Members", members: "Members",
detail: "Details", detail: "Details",
permission: "Permissions", permission: "Permissions",
media: "Media",
} }
}, },
reports: { tabs: { basic: "Basic", detail: "Details" } }, reports: { tabs: { basic: "Basic", detail: "Details" } },
@@ -119,6 +121,20 @@ const en: SynapseTranslationMessages = {
send: "This API deletes the local media from the disk of your own server. This includes any local thumbnails and copies of media downloaded. This API will not affect media that has been uploaded to external media repositories.", send: "This API deletes the local media from the disk of your own server. This includes any local thumbnails and copies of media downloaded. This API will not affect media that has been uploaded to external media repositories.",
}, },
}, },
purge_remote_media: {
name: "Remote Media",
fields: {
before_ts: "last access before",
},
action: {
send: "Purge remote media",
send_success: "Purge remote media request has been sent.",
send_failure: "An error has occurred with the purge remote media request.",
},
helper: {
send: "This API purges the remote media cache from the disk of your own server. This includes any local thumbnails and copies of media downloaded. This API will not affect media that has been uploaded to the server's own media repository.",
},
},
resources: { resources: {
users: { users: {
name: "User |||| Users", name: "User |||| Users",
@@ -132,11 +148,13 @@ const en: SynapseTranslationMessages = {
is_guest: "Guest", is_guest: "Guest",
admin: "Server Administrator", admin: "Server Administrator",
locked: "Locked", locked: "Locked",
suspended: "Suspended",
deactivated: "Deactivated", deactivated: "Deactivated",
erased: "Erased", erased: "Erased",
guests: "Show guests", guests: "Show guests",
show_deactivated: "Show deactivated users", show_deactivated: "Show deactivated users",
show_locked: "Show locked users", show_locked: "Show locked users",
show_suspended: "Show suspended users",
user_id: "Search user", user_id: "Search user",
displayname: "Displayname", displayname: "Displayname",
password: "Password", password: "Password",
@@ -154,6 +172,7 @@ const en: SynapseTranslationMessages = {
password: "Changing password will log user out of all sessions.", password: "Changing password will log user out of all sessions.",
create_password: "Generate a strong and secure password using the button below.", create_password: "Generate a strong and secure password using the button below.",
deactivate: "You must provide a password to re-activate an account.", 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: "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_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.", erase_admin_error: "Deleting own user is not allowed.",
@@ -184,6 +203,11 @@ const en: SynapseTranslationMessages = {
messages_per_second_text: "The number of actions that can be performed in a second.", messages_per_second_text: "The number of actions that can be performed in a second.",
burst_count: "Burst count", burst_count: "Burst count",
burst_count_text: "How many actions that can be performed before being limited.", burst_count_text: "How many actions that can be performed before being limited.",
},
account_data: {
title: "Account Data",
global: "Global",
rooms: "Rooms",
} }
}, },
rooms: { rooms: {
@@ -401,6 +425,18 @@ const en: SynapseTranslationMessages = {
sender: "Sender", 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: { room_directory: {
name: "Room directory", name: "Room directory",
fields: { fields: {

View File

@@ -24,7 +24,7 @@ const fa: SynapseTranslationMessages = {
}, },
users: { users: {
invalid_user_id: "بخش محلی یک شناسه کاربری ماتریکس بدون سرور خانگی.", invalid_user_id: "بخش محلی یک شناسه کاربری ماتریکس بدون سرور خانگی.",
tabs: { sso: "SSO", experimental: "تجربی", limits: "محدودیت ها" }, tabs: { sso: "SSO", experimental: "تجربی", limits: "محدودیت ها", account_data: "داده های کاربر" },
}, },
rooms: { rooms: {
tabs: { tabs: {
@@ -32,6 +32,7 @@ const fa: SynapseTranslationMessages = {
members: "اعضا", members: "اعضا",
detail: "جزئیات", detail: "جزئیات",
permission: "مجوزها", permission: "مجوزها",
media: "رسانه ها",
}, },
}, },
reports: { tabs: { basic: "اصلی", detail: "جزئیات" } }, reports: { tabs: { basic: "اصلی", detail: "جزئیات" } },
@@ -113,6 +114,20 @@ const fa: SynapseTranslationMessages = {
send: "این API رسانه های محلی را از دیسک سرور خود حذف می کند. این شامل هر تصویر کوچک محلی و کپی از رسانه دانلود شده است. این API بر رسانه‌هایی که در مخازن رسانه خارجی آپلود شده‌اند تأثیری نخواهد گذاشت.", send: "این API رسانه های محلی را از دیسک سرور خود حذف می کند. این شامل هر تصویر کوچک محلی و کپی از رسانه دانلود شده است. این API بر رسانه‌هایی که در مخازن رسانه خارجی آپلود شده‌اند تأثیری نخواهد گذاشت.",
}, },
}, },
purge_remote_media: {
name: "رسانه‌های از راه دور",
fields: {
before_ts: "آخرین دسترسی قبل از",
},
action: {
send: "پاک کردن رسانه‌های از راه دور",
send_success: "درخواست پاک کردن رسانه‌های از راه دور ارسال شد.",
send_failure: "درخواست برای پاک کردن رسانه‌های از راه دور با خطا مواجه شد.",
},
helper: {
send: "این API کش رسانه‌های از راه دور را از دیسک سرور شما پاک می‌کند. این شامل هر گونه بندانگشتی محلی و نسخه‌های رسانه‌های دانلود شده می‌شود. این API بر رسانه‌های آپلود شده به مخزن رسانه سرور تأثیری نخواهد داشت.",
},
},
resources: { resources: {
users: { users: {
name: "کاربر |||| کاربران", name: "کاربر |||| کاربران",
@@ -126,9 +141,12 @@ const fa: SynapseTranslationMessages = {
is_guest: "مهمان", is_guest: "مهمان",
admin: "مدیر سرور", admin: "مدیر سرور",
deactivated: "غیرفعال", deactivated: "غیرفعال",
locked: "قفل شده",
suspended: "معلق",
guests: "نمایش مهمانان", guests: "نمایش مهمانان",
show_deactivated: "نمایش کاربران غیرفعال شده", show_deactivated: "نمایش کاربران غیرفعال شده",
show_locked: "نمایش کاربران قفل شده", show_locked: "نمایش کاربران قفل شده",
show_suspended: "نمایش کاربران معلق",
user_id: "جستجوی کاربر", user_id: "جستجوی کاربر",
displayname: "نام نمایشی", displayname: "نام نمایشی",
password: "رمز عبور", password: "رمز عبور",
@@ -146,6 +164,7 @@ const fa: SynapseTranslationMessages = {
password: "با تغییر رمز عبور کاربر از تمام دستگاه ها خارج می شود.", password: "با تغییر رمز عبور کاربر از تمام دستگاه ها خارج می شود.",
create_password: "رمز عبور قوی و امنی را با استفاده از دکمه زیر ایجاد کنید.", create_password: "رمز عبور قوی و امنی را با استفاده از دکمه زیر ایجاد کنید.",
deactivate: "برای فعالسازی مجدد حساب باید رمز عبور وارد کنید.", deactivate: "برای فعالسازی مجدد حساب باید رمز عبور وارد کنید.",
suspend: "کاربران معلق نمی توانند وارد شوند و پیام های آنها به دیگران نمایش داده نمی شود.",
erase: "کاربر را به عنوان GDPR پاک شده علامت گذاری کنید", erase: "کاربر را به عنوان GDPR پاک شده علامت گذاری کنید",
erase_text: "وهذا يعني أن الرسائل المرسلة من قبل المستخدم (المستخدمين) ستظل مرئية من قبل أي شخص كان في الغرفة عند إرسال هذه الرسائل، ولكنها مخفية عن المستخدمين الذين ينضمون إلى الغرفة بعد ذلك.", erase_text: "وهذا يعني أن الرسائل المرسلة من قبل المستخدم (المستخدمين) ستظل مرئية من قبل أي شخص كان في الغرفة عند إرسال هذه الرسائل، ولكنها مخفية عن المستخدمين الذين ينضمون إلى الغرفة بعد ذلك.",
erase_admin_error: "حذف المستخدم الخاص غير مسموح به.", erase_admin_error: "حذف المستخدم الخاص غير مسموح به.",
@@ -176,6 +195,11 @@ const fa: SynapseTranslationMessages = {
messages_per_second_text: "تعداد عملیاتی که می تواند در یک ثانیه انجام شود.", messages_per_second_text: "تعداد عملیاتی که می تواند در یک ثانیه انجام شود.",
burst_count: "تعداد پیچیدگی", burst_count: "تعداد پیچیدگی",
burst_count_text: "تعداد عملیاتی که می تواند قبل از محدودیت انجام شود.", burst_count_text: "تعداد عملیاتی که می تواند قبل از محدودیت انجام شود.",
},
account_data: {
title: "داده های کاربر",
global: "عمومی",
rooms: "اتاق ها",
} }
}, },
rooms: { rooms: {
@@ -380,6 +404,18 @@ const fa: SynapseTranslationMessages = {
sender: "فرستنده", sender: "فرستنده",
}, },
}, },
room_media: {
name: "رسانه ها",
fields: {
media_id: "شناسه رسانه",
},
helper: {
info: "این یک لیست از رسانه ها است که در اتاق بارگذاری شده است. نمی توان رسانه ها را حذف کرد که در اتاق های خارجی بارگذاری شده اند.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
room_directory: { room_directory: {
name: "راهنمای اتاق", name: "راهنمای اتاق",
fields: { fields: {

View File

@@ -24,7 +24,7 @@ const fr: SynapseTranslationMessages = {
}, },
users: { users: {
invalid_user_id: "Partie locale d'un identifiant utilisateur Matrix sans le nom du serveur daccueil.", invalid_user_id: "Partie locale d'un identifiant utilisateur Matrix sans le nom du serveur daccueil.",
tabs: { sso: "Authentification unique", experimental: "Expérimental", limits: "Limites" }, tabs: { sso: "Authentification unique", experimental: "Expérimental", limits: "Limites", account_data: "Données du compte" },
}, },
rooms: { rooms: {
tabs: { tabs: {
@@ -32,6 +32,7 @@ const fr: SynapseTranslationMessages = {
members: "Membres", members: "Membres",
detail: "Détails", detail: "Détails",
permission: "Permissions", permission: "Permissions",
media: "Médias",
}, },
}, },
reports: { tabs: { basic: "Informations de base", detail: "Détails" } }, reports: { tabs: { basic: "Informations de base", detail: "Détails" } },
@@ -116,6 +117,20 @@ const fr: SynapseTranslationMessages = {
send: "Cette API supprime les médias locaux du disque de votre propre serveur. Cela inclut toutes les vignettes locales et les copies des médias téléchargés. Cette API n'affectera pas les médias qui ont été téléversés dans des dépôts de médias externes.", send: "Cette API supprime les médias locaux du disque de votre propre serveur. Cela inclut toutes les vignettes locales et les copies des médias téléchargés. Cette API n'affectera pas les médias qui ont été téléversés dans des dépôts de médias externes.",
}, },
}, },
purge_remote_media: {
name: "Médias distants",
fields: {
before_ts: "dernier accès avant",
},
action: {
send: "Purger les médias distants",
send_success: "La demande de purge des médias distants a été envoyée.",
send_failure: "Une erreur est survenue lors de la demande de purge des médias distants.",
},
helper: {
send: "Cette API purge le cache des médias distants du disque de votre propre serveur. Cela inclut toutes les vignettes locales et les copies des médias téléchargés. Cette API n'affectera pas les médias qui ont été téléchargés dans le dépôt de médias du serveur.",
},
},
resources: { resources: {
users: { users: {
name: "Utilisateur |||| Utilisateurs", name: "Utilisateur |||| Utilisateurs",
@@ -129,10 +144,12 @@ const fr: SynapseTranslationMessages = {
is_guest: "Visiteur", is_guest: "Visiteur",
admin: "Administrateur du serveur", admin: "Administrateur du serveur",
locked: "Verrouillé", locked: "Verrouillé",
suspended: "Suspendu",
deactivated: "Désactivé", deactivated: "Désactivé",
guests: "Afficher les visiteurs", guests: "Afficher les visiteurs",
show_deactivated: "Afficher les utilisateurs désactivés", show_deactivated: "Afficher les utilisateurs désactivés",
show_locked: "Afficher les utilisateurs verrouillés", show_locked: "Afficher les utilisateurs verrouillés",
show_suspended: "Afficher les utilisateurs suspendus",
user_id: "Rechercher un utilisateur", user_id: "Rechercher un utilisateur",
displayname: "Nom d'affichage", displayname: "Nom d'affichage",
password: "Mot de passe", password: "Mot de passe",
@@ -149,6 +166,7 @@ const fr: SynapseTranslationMessages = {
password: "Changer le mot de passe déconnectera l'utilisateur de toutes les sessions.", 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.", 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.", 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: "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_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.", erase_admin_error: "La suppression de son propre utilisateur n'est pas autorisée.",
@@ -179,6 +197,11 @@ const fr: SynapseTranslationMessages = {
messages_per_second_text: "Le nombre d'actions que l'utilisateur peut effectuer par seconde.", messages_per_second_text: "Le nombre d'actions que l'utilisateur peut effectuer par seconde.",
burst_count: "Compteur de pics", burst_count: "Compteur de pics",
burst_count_text: "Le nombre d'actions que l'utilisateur peut effectuer avant d'être limité.", burst_count_text: "Le nombre d'actions que l'utilisateur peut effectuer avant d'être limité.",
},
account_data: {
title: "Données du compte",
global: "Globales",
rooms: "Salons",
} }
}, },
rooms: { rooms: {
@@ -383,6 +406,18 @@ const fr: SynapseTranslationMessages = {
sender: "Expéditeur", 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: { room_directory: {
name: "Répertoire des salons", name: "Répertoire des salons",
fields: { fields: {

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

@@ -22,7 +22,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
}; };
users: { users: {
invalid_user_id: string; invalid_user_id: string;
tabs: { sso: string; experimental: string; limits: string; }; tabs: { sso: string; experimental: string; limits: string; account_data: string; };
}; };
rooms: { rooms: {
details?: string; // TODO: fa, fr, it, zh details?: string; // TODO: fa, fr, it, zh
@@ -31,6 +31,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
members: string; members: string;
detail: string; detail: string;
permission: string; permission: string;
media: string;
}; };
}; };
reports: { tabs: { basic: string; detail: string } }; reports: { tabs: { basic: string; detail: string } };
@@ -111,6 +112,20 @@ interface SynapseTranslationMessages extends TranslationMessages {
send: string; send: string;
}; };
}; };
purge_remote_media: {
name: string;
fields: {
before_ts: string;
};
action: {
send: string;
send_success: string;
send_failure: string;
};
helper: {
send: string;
};
};
resources: { resources: {
users: { users: {
name: string; name: string;
@@ -124,11 +139,13 @@ interface SynapseTranslationMessages extends TranslationMessages {
is_guest: string; is_guest: string;
admin: string; admin: string;
locked?: string; // TODO: fa, zh locked?: string; // TODO: fa, zh
suspended: string;
deactivated: string; deactivated: string;
erased?: string; // TODO: fa, fr, it, zh erased?: string; // TODO: fa, fr, it, zh
guests: string; guests: string;
show_deactivated: string; show_deactivated: string;
show_locked?: string; // TODO: de, fa, fr, it, zh show_locked?: string; // TODO: de, fa, fr, it, zh
show_suspended: string;
user_id: string; user_id: string;
displayname: string; displayname: string;
password: string; password: string;
@@ -146,6 +163,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
password: string; password: string;
create_password: string; create_password: string;
deactivate: string; deactivate: string;
suspend: string;
erase: string; erase: string;
erase_text: string; erase_text: string;
erase_admin_error: string; erase_admin_error: string;
@@ -177,6 +195,11 @@ interface SynapseTranslationMessages extends TranslationMessages {
burst_count: string; burst_count: string;
burst_count_text: string; burst_count_text: string;
}; };
account_data: {
title: string;
global: string;
rooms: string;
}
}; };
rooms: { rooms: {
name: string; name: string;
@@ -393,6 +416,18 @@ interface SynapseTranslationMessages extends TranslationMessages {
sender: string; sender: string;
}; };
}; };
room_media?: {
name: string;
fields: {
media_id: string;
};
helper: {
info: string;
};
action: {
error: string;
};
};
room_directory?: { room_directory?: {
name: string; name: string;
fields: { fields: {

View File

@@ -24,7 +24,7 @@ const it: SynapseTranslationMessages = {
}, },
users: { users: {
invalid_user_id: "ID utente non valido su questo homeserver.", invalid_user_id: "ID utente non valido su questo homeserver.",
tabs: { sso: "SSO", experimental: "Sperimentale", limits: "Limiti" }, tabs: { sso: "SSO", experimental: "Sperimentale", limits: "Limiti", account_data: "Dati del profilo" },
}, },
rooms: { rooms: {
tabs: { tabs: {
@@ -32,6 +32,7 @@ const it: SynapseTranslationMessages = {
members: "Membro", members: "Membro",
detail: "Dettagli", detail: "Dettagli",
permission: "Permessi", permission: "Permessi",
media: "Media",
}, },
}, },
reports: { tabs: { basic: "Semplice", detail: "Dettagli" } }, reports: { tabs: { basic: "Semplice", detail: "Dettagli" } },
@@ -113,6 +114,20 @@ const it: SynapseTranslationMessages = {
send: "Questa API cancella i media locali dal disco del tuo server. Questo include anche ogni miniatura e copia del media scaricato. Questa API non inciderà sui media che sono stati caricati nei repository esterni.", send: "Questa API cancella i media locali dal disco del tuo server. Questo include anche ogni miniatura e copia del media scaricato. Questa API non inciderà sui media che sono stati caricati nei repository esterni.",
}, },
}, },
purge_remote_media: {
name: "Media Remoti",
fields: {
before_ts: "ultimo accesso prima di",
},
action: {
send: "Elimina media remoti",
send_success: "La richiesta per eliminare i media remoti è stata inviata.",
send_failure: "Si è verificato un errore con la richiesta di eliminazione dei media remoti.",
},
helper: {
send: "Questa API elimina la cache dei media remoti dal disco del tuo server. Questo include qualsiasi miniatura locale e copie di media scaricati. Questa API non influirà sui media che sono stati caricati nel repository multimediale del server.",
},
},
resources: { resources: {
users: { users: {
name: "Utente |||| Utenti", name: "Utente |||| Utenti",
@@ -126,10 +141,12 @@ const it: SynapseTranslationMessages = {
is_guest: "Ospite", is_guest: "Ospite",
admin: "Amministratore", admin: "Amministratore",
locked: "Bloccato", locked: "Bloccato",
suspended: "Sospeso",
deactivated: "Disattivato", deactivated: "Disattivato",
guests: "Mostra gli ospiti", guests: "Mostra gli ospiti",
show_deactivated: "Mostra gli utenti disattivati", show_deactivated: "Mostra gli utenti disattivati",
show_locked: "Mostra gli utenti bloccati", show_locked: "Mostra gli utenti bloccati",
show_suspended: "Mostra gli utenti sospesi",
user_id: "Cerca utente", user_id: "Cerca utente",
displayname: "Nickname", displayname: "Nickname",
password: "Password", password: "Password",
@@ -147,6 +164,7 @@ const it: SynapseTranslationMessages = {
password: "Cambiando la password l'utente verrà disconnesso da tutte le sessioni attive.", 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.", create_password: "Genera una password forte e sicura utilizzando il pulsante sottostante.",
deactivate: "Devi fornire una password per riattivare l'account.", deactivate: "Devi fornire una password per riattivare l'account.",
suspend: "Sospendi l'utente",
erase: "Constrassegna l'utente come cancellato dal GDPR", 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_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.", erase_admin_error: "Non è consentito eliminare il proprio utente.",
@@ -177,6 +195,11 @@ const it: SynapseTranslationMessages = {
messages_per_second_text: "Il numero di azioni che l'utente può eseguire al secondo.", messages_per_second_text: "Il numero di azioni che l'utente può eseguire al secondo.",
burst_count: "Burst-conteggio", burst_count: "Burst-conteggio",
burst_count_text: "Il numero di azioni che l'utente può eseguire prima di essere limitato.", burst_count_text: "Il numero di azioni che l'utente può eseguire prima di essere limitato.",
},
account_data: {
title: "Dati del profilo",
global: "Globale",
rooms: "Stanza",
} }
}, },
rooms: { rooms: {
@@ -376,6 +399,18 @@ const it: SynapseTranslationMessages = {
sender: "Mittente", 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: { room_directory: {
name: "Elenco delle stanze", name: "Elenco delle stanze",
fields: { fields: {

View File

@@ -11,6 +11,10 @@ const fixedRussianMessages = {
no_filtered_results: "Нет результатов", no_filtered_results: "Нет результатов",
clear_filters: "Все фильтры сбросить", clear_filters: "Все фильтры сбросить",
}, },
action: {
...russianMessages.ra.action,
select_all_button: "Выбрать все",
},
page: { page: {
...russianMessages.ra.page, ...russianMessages.ra.page,
empty: "Пусто", empty: "Пусто",
@@ -23,6 +27,7 @@ const fixedRussianMessages = {
"У вас нет прав доступа к этой странице.", "У вас нет прав доступа к этой странице.",
authentication_error: authentication_error:
"Сервер аутентификации вернул ошибку и не смог проверить ваши учетные данные.", "Сервер аутентификации вернул ошибку и не смог проверить ваши учетные данные.",
select_all_limit_reached: "Слишком много элементов для выбора. Были выбраны только первые %{max} элементов.",
}, },
}, },
} }
@@ -50,7 +55,7 @@ const ru: SynapseTranslationMessages = {
}, },
users: { users: {
invalid_user_id: "Локальная часть ID пользователя Matrix без адреса домашнего сервера.", invalid_user_id: "Локальная часть ID пользователя Matrix без адреса домашнего сервера.",
tabs: { sso: "SSO", experimental: "Экспериментальные", limits: "Ограничения" }, tabs: { sso: "SSO", experimental: "Экспериментальные", limits: "Ограничения", account_data: "Данные пользователя" },
}, },
rooms: { rooms: {
details: "Данные комнаты", details: "Данные комнаты",
@@ -59,6 +64,7 @@ const ru: SynapseTranslationMessages = {
members: "Участники", members: "Участники",
detail: "Подробности", detail: "Подробности",
permission: "Права доступа", permission: "Права доступа",
media: "Медиа",
}, },
}, },
reports: { tabs: { basic: "Основные", detail: "Подробности" } }, reports: { tabs: { basic: "Основные", detail: "Подробности" } },
@@ -149,6 +155,20 @@ const ru: SynapseTranslationMessages = {
Данный API не затрагивает файлы, загруженные во внешние хранилища.", Данный API не затрагивает файлы, загруженные во внешние хранилища.",
}, },
}, },
purge_remote_media: {
name: "Внешние медиа",
fields: {
before_ts: "последний доступ до",
},
action: {
send: "Очистить внешние медиа",
send_success: "Запрос на очистку внешних медиа был отправлен.",
send_failure: "Произошла ошибка при запросе очистки внешних медиа.",
},
helper: {
send: "Этот API очищает кэш внешних медиа с диска вашего сервера. Это включает любые локальные миниатюры и копии загруженных медиа. Этот API не повлияет на медиа, которые были загружены в собственное медиа-хранилище сервера.",
},
},
resources: { resources: {
users: { users: {
name: "Пользователь |||| Пользователи", name: "Пользователь |||| Пользователи",
@@ -162,11 +182,13 @@ const ru: SynapseTranslationMessages = {
is_guest: "Гость", is_guest: "Гость",
admin: "Администратор сервера", admin: "Администратор сервера",
locked: "Заблокирован", locked: "Заблокирован",
suspended: "Приостановлен",
deactivated: "Деактивирован", deactivated: "Деактивирован",
erased: "Удалён", erased: "Удалён",
guests: "Показывать гостей", guests: "Показывать гостей",
show_deactivated: "Показывать деактивированных", show_deactivated: "Показывать деактивированных",
show_locked: "Показывать заблокированных", show_locked: "Показывать заблокированных",
show_suspended: "Показывать приостановленных",
user_id: "Поиск пользователя", user_id: "Поиск пользователя",
displayname: "Отображаемое имя", displayname: "Отображаемое имя",
password: "Пароль", password: "Пароль",
@@ -184,6 +206,7 @@ const ru: SynapseTranslationMessages = {
password: "Смена пароля завершит все сессии пользователя.", password: "Смена пароля завершит все сессии пользователя.",
create_password: "Сгенерировать надёжный и безопасный пароль, используя кнопку ниже.", create_password: "Сгенерировать надёжный и безопасный пароль, используя кнопку ниже.",
deactivate: "Вы должны предоставить пароль для реактивации учётной записи.", deactivate: "Вы должны предоставить пароль для реактивации учётной записи.",
suspend: "Приостановка учётной записи означает, что пользователь не сможет войти в свою учётную запись, пока она не будет снова активирована.",
erase: "Пометить пользователя как удалённого в соответствии с GDPR", erase: "Пометить пользователя как удалённого в соответствии с GDPR",
erase_text: "Это означает, что сообщения, отправленные пользователем (-ами), будут по-прежнему видны всем, кто находился в комнате в момент их отправки, но будут скрыты от пользователей, присоединившихся к комнате после этого.", erase_text: "Это означает, что сообщения, отправленные пользователем (-ами), будут по-прежнему видны всем, кто находился в комнате в момент их отправки, но будут скрыты от пользователей, присоединившихся к комнате после этого.",
erase_admin_error: "Удаление собственного пользователя запрещено.", erase_admin_error: "Удаление собственного пользователя запрещено.",
@@ -214,6 +237,11 @@ const ru: SynapseTranslationMessages = {
messages_per_second_text: "Количество действий, которые могут быть выполнены в секунду.", messages_per_second_text: "Количество действий, которые могут быть выполнены в секунду.",
burst_count: "Burst-счётчик", burst_count: "Burst-счётчик",
burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.", burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.",
},
account_data: {
title: "Данные пользователя",
global: "Глобальные",
rooms: "Комнаты",
} }
}, },
rooms: { rooms: {
@@ -434,6 +462,18 @@ const ru: SynapseTranslationMessages = {
sender: "Отправитель", sender: "Отправитель",
}, },
}, },
room_media: {
name: "Медиа",
fields: {
media_id: "ID медиа",
},
helper: {
info: "Это список медиа, которые были загружены в комнату. Невозможно удалить медиа, которые были загружены в внешние медиа-репозитории.",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
room_directory: { room_directory: {
name: "Каталог комнат", name: "Каталог комнат",
fields: { fields: {

View File

@@ -13,7 +13,8 @@ const fixedChineseMessages = {
}, },
action: { action: {
...chineseMessages.ra.action, ...chineseMessages.ra.action,
update_application: "Anwendung aktualisieren", update_application: "更新应用",
select_all_button: "全部选择",
}, },
page: { page: {
...chineseMessages.ra.page, ...chineseMessages.ra.page,
@@ -26,6 +27,7 @@ const fixedChineseMessages = {
"您没有访问此页面的权限。", "您没有访问此页面的权限。",
authentication_error: authentication_error:
"身份验证服务器返回错误,无法验证您的凭据。", "身份验证服务器返回错误,无法验证您的凭据。",
select_all_limit_reached: "选择的元素太多。只选择了前 %{max} 个元素。",
}, },
}, },
} }
@@ -52,7 +54,7 @@ const zh: SynapseTranslationMessages = {
}, },
users: { users: {
invalid_user_id: "必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver", invalid_user_id: "必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver",
tabs: { sso: "SSO", experimental: "实验性", limits: "限制" }, tabs: { sso: "SSO", experimental: "实验性", limits: "限制", account_data: "账户数据" },
}, },
rooms: { rooms: {
tabs: { tabs: {
@@ -60,6 +62,7 @@ const zh: SynapseTranslationMessages = {
members: "成员", members: "成员",
detail: "细节", detail: "细节",
permission: "权限", permission: "权限",
media: "媒体",
}, },
}, },
reports: { tabs: { basic: "基本", detail: "细节" } }, reports: { tabs: { basic: "基本", detail: "细节" } },
@@ -141,6 +144,20 @@ const zh: SynapseTranslationMessages = {
send: "这个API会删除您硬盘上的本地媒体。包含了任何的本地缓存和下载的媒体备份。这个API不会影响上传到外部媒体存储库上的媒体文件。", send: "这个API会删除您硬盘上的本地媒体。包含了任何的本地缓存和下载的媒体备份。这个API不会影响上传到外部媒体存储库上的媒体文件。",
}, },
}, },
purge_remote_media: {
name: "远程媒体",
fields: {
before_ts: "最后访问于之前",
},
action: {
send: "清除远程媒体",
send_success: "远程媒体清除请求已发送。",
send_failure: "发生错误,远程媒体清除请求未成功。",
},
helper: {
send: "此API清除您服务器磁盘上的远程媒体缓存。这包括任何本地缩略图和下载的媒体副本。此API不会影响已经上传到服务器媒体存储库的媒体。",
},
},
resources: { resources: {
users: { users: {
name: "用户", name: "用户",
@@ -153,10 +170,13 @@ const zh: SynapseTranslationMessages = {
name: "用户名", name: "用户名",
is_guest: "访客", is_guest: "访客",
admin: "服务器管理员", admin: "服务器管理员",
locked: "锁定",
deactivated: "被禁用", deactivated: "被禁用",
suspended: "被锁定",
guests: "显示访客", guests: "显示访客",
show_deactivated: "显示被禁用的账户", show_deactivated: "显示被禁用的账户",
show_locked: "显示被锁定的账户", show_locked: "显示被锁定的账户",
show_suspended: "显示被锁定的账户",
user_id: "搜索用户", user_id: "搜索用户",
displayname: "显示名字", displayname: "显示名字",
password: "密码", password: "密码",
@@ -172,6 +192,7 @@ const zh: SynapseTranslationMessages = {
password: "更改密码会使用户注销所有会话。", password: "更改密码会使用户注销所有会话。",
create_password: "使用下面的按钮生成一个强大和安全的密码。", create_password: "使用下面的按钮生成一个强大和安全的密码。",
deactivate: "您必须提供一串密码来激活账户。", deactivate: "您必须提供一串密码来激活账户。",
suspend: "您必须提供一串密码来暂停账户。",
erase: "将用户标记为根据 GDPR 的要求抹除了", erase: "将用户标记为根据 GDPR 的要求抹除了",
erase_text: "这意味着用户发送的信息对于发送信息时在房间内的任何人来说都是可见的,但对于之后加入房间的用户来说则是隐藏的。", erase_text: "这意味着用户发送的信息对于发送信息时在房间内的任何人来说都是可见的,但对于之后加入房间的用户来说则是隐藏的。",
erase_admin_error: "不允许删除自己的用户", erase_admin_error: "不允许删除自己的用户",
@@ -202,6 +223,11 @@ const zh: SynapseTranslationMessages = {
messages_per_second_text: "每秒可以执行的操作数。", messages_per_second_text: "每秒可以执行的操作数。",
burst_count: "Burst-计数", burst_count: "Burst-计数",
burst_count_text: "在限制之前可以执行的操作数。", burst_count_text: "在限制之前可以执行的操作数。",
},
account_data: {
title: "账户数据",
global: "全局",
rooms: "房间",
} }
}, },
rooms: { rooms: {
@@ -344,6 +370,18 @@ const zh: SynapseTranslationMessages = {
media_length: "媒体文件长度", media_length: "媒体文件长度",
}, },
}, },
room_media: {
name: "媒体",
fields: {
media_id: "媒体ID",
},
helper: {
info: "这是上传到房间的媒体列表。无法删除上传到外部媒体存储库的媒体。",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}"
}
},
}, },
}; };
export default zh; export default zh;

View File

@@ -2,7 +2,8 @@ import React from "react";
import { createRoot } from "react-dom/client"; 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"; import { FetchConfig, GetConfig } from "./utils/config";
await FetchConfig(); await FetchConfig();

View File

@@ -2,9 +2,10 @@ import polyglotI18nProvider from "ra-i18n-polyglot";
import { render, screen } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
import { AdminContext } from "react-admin"; import { AdminContext } from "react-admin";
import { BrowserRouter } from "react-router-dom";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import { AppContext } from "../App"; import { AppContext } from "../Context";
import englishMessages from "../i18n/en"; import englishMessages from "../i18n/en";
const i18nProvider = polyglotI18nProvider(() => englishMessages, "en", [{ locale: "en", name: "English" }]); const i18nProvider = polyglotI18nProvider(() => englishMessages, "en", [{ locale: "en", name: "English" }]);
@@ -14,9 +15,11 @@ describe("LoginForm", () => {
it("renders with no restriction to homeserver", async () => { it("renders with no restriction to homeserver", async () => {
await act(async () => { await act(async () => {
render( render(
<AdminContext i18nProvider={i18nProvider}> <BrowserRouter>
<LoginPage /> <AdminContext i18nProvider={i18nProvider}>
</AdminContext> <LoginPage />
</AdminContext>
</BrowserRouter>
); );
}); });
@@ -33,11 +36,15 @@ describe("LoginForm", () => {
it("renders with single restricted homeserver", () => { it("renders with single restricted homeserver", () => {
render( render(
<AppContext.Provider value={{ restrictBaseUrl: "https://matrix.example.com" }}> <BrowserRouter>
<AppContext.Provider
value={{ restrictBaseUrl: "https://matrix.example.com", asManagedUsers: [], menu: [] }}
>
<AdminContext i18nProvider={i18nProvider}> <AdminContext i18nProvider={i18nProvider}>
<LoginPage /> <LoginPage />
</AdminContext> </AdminContext>
</AppContext.Provider> </AppContext.Provider>
</BrowserRouter>
); );
screen.getByText(englishMessages.synapseadmin.auth.welcome); screen.getByText(englishMessages.synapseadmin.auth.welcome);
@@ -54,12 +61,16 @@ describe("LoginForm", () => {
it("renders with multiple restricted homeservers", async () => { it("renders with multiple restricted homeservers", async () => {
render( render(
<AppContext.Provider <AppContext.Provider
value={{ value={{
restrictBaseUrl: ["https://matrix.example.com", "https://matrix.example.org"], restrictBaseUrl: ["https://matrix.example.com", "https://matrix.example.org"],
asManagedUsers: [],
menu: [],
}} }}
> >
<AdminContext i18nProvider={i18nProvider}> <AdminContext i18nProvider={i18nProvider}>
<LoginPage /> <BrowserRouter>
<LoginPage />
</BrowserRouter>
</AdminContext> </AdminContext>
</AppContext.Provider> </AppContext.Provider>
); );

View File

@@ -16,7 +16,7 @@ import {
} from "react-admin"; } from "react-admin";
import { useFormContext } from "react-hook-form"; import { useFormContext } from "react-hook-form";
import LoginFormBox from "../components/LoginFormBox"; import LoginFormBox from "../components/LoginFormBox";
import { useAppContext } from "../App"; import { useAppContext } from "../Context";
import { import {
getServerVersion, getServerVersion,
getSupportedFeatures, getSupportedFeatures,
@@ -47,7 +47,7 @@ const LoginPage = () => {
const translate = useTranslate(); const translate = useTranslate();
const base_url = allowSingleBaseUrl ? restrictBaseUrl : localStorage.getItem("base_url"); const base_url = allowSingleBaseUrl ? restrictBaseUrl : localStorage.getItem("base_url");
const [ssoBaseUrl, setSSOBaseUrl] = useState(""); const [ssoBaseUrl, setSSOBaseUrl] = useState("");
const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href); const loginToken = new URLSearchParams(window.location.search).get("loginToken")
const [loginMethod, setLoginMethod] = useState<LoginMethod>("credentials"); const [loginMethod, setLoginMethod] = useState<LoginMethod>("credentials");
useEffect(() => { useEffect(() => {
@@ -55,10 +55,11 @@ const LoginPage = () => {
return; return;
} }
const ssoToken = loginToken[1]; console.log("SSO token is", loginToken);
console.log("SSO token is", ssoToken);
// Prevent further requests // Prevent further requests
window.history.replaceState({}, "", window.location.href.replace(loginToken[0], "#").split("#")[0]); const previousUrl = new URL(window.location.toString())
previousUrl.searchParams.delete("loginToken")
window.history.replaceState({}, "", previousUrl.toString());
const baseUrl = localStorage.getItem("sso_base_url"); const baseUrl = localStorage.getItem("sso_base_url");
localStorage.removeItem("sso_base_url"); localStorage.removeItem("sso_base_url");
if (baseUrl) { if (baseUrl) {
@@ -66,10 +67,10 @@ const LoginPage = () => {
base_url: baseUrl, base_url: baseUrl,
username: null, username: null,
password: null, password: null,
loginToken: ssoToken, loginToken,
}; };
console.log("Base URL is:", baseUrl); console.log("Base URL is:", baseUrl);
console.log("SSO Token is:", ssoToken); console.log("SSO Token is:", loginToken);
console.log("Let's try token login..."); console.log("Let's try token login...");
login(auth).catch(error => { login(auth).catch(error => {
alert( alert(

View File

@@ -7,12 +7,12 @@ import PageviewIcon from "@mui/icons-material/Pageview";
import ViewListIcon from "@mui/icons-material/ViewList"; import ViewListIcon from "@mui/icons-material/ViewList";
import RoomIcon from "@mui/icons-material/ViewList"; import RoomIcon from "@mui/icons-material/ViewList";
import VisibilityIcon from "@mui/icons-material/Visibility"; import VisibilityIcon from "@mui/icons-material/Visibility";
import PermMediaIcon from "@mui/icons-material/PermMedia";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { useTheme } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles";
import { import {
BooleanField, BooleanField,
DateField, DateField,
EditButton,
WrapperField, WrapperField,
Datagrid, Datagrid,
DatagridConfigurable, DatagridConfigurable,
@@ -38,6 +38,7 @@ import {
useTranslate, useTranslate,
useListContext, useListContext,
useNotify, useNotify,
DeleteButton,
} from "react-admin"; } from "react-admin";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
@@ -49,6 +50,7 @@ import {
} from "./room_directory"; } from "./room_directory";
import { DATE_FORMAT } from "../utils/date"; import { DATE_FORMAT } from "../utils/date";
import DeleteRoomButton from "../components/DeleteRoomButton"; import DeleteRoomButton from "../components/DeleteRoomButton";
import { MediaIDField } from "../components/media";
import AvatarField from "../components/AvatarField"; import AvatarField from "../components/AvatarField";
import { Room } from "../synapse/dataProvider"; import { Room } from "../synapse/dataProvider";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
@@ -58,6 +60,8 @@ import { useState } from "react";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import PersonIcon from '@mui/icons-material/Person'; import PersonIcon from '@mui/icons-material/Person';
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Alert from "@mui/material/Alert";
const RoomPagination = () => <Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />; const RoomPagination = () => <Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />;
const RoomTitle = () => { const RoomTitle = () => {
@@ -231,6 +235,16 @@ export const RoomShow = (props: ShowProps) => {
</ReferenceManyField> </ReferenceManyField>
</Tab> </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"> <Tab label="synapseadmin.rooms.tabs.permission" icon={<VisibilityIcon />} path="permission">
<BooleanField source="federatable" /> <BooleanField source="federatable" />
<BooleanField source="public" /> <BooleanField source="public" />

View File

@@ -14,13 +14,14 @@ import {
useListContext, useListContext,
} from "react-admin"; } from "react-admin";
import { DeleteMediaButton } from "../components/media"; import { DeleteMediaButton, PurgeRemoteMediaButton } from "../components/media";
const ListActions = () => { const ListActions = () => {
const { isLoading, total } = useListContext(); const { isLoading, total } = useListContext();
return ( return (
<TopToolbar> <TopToolbar>
<DeleteMediaButton /> <DeleteMediaButton />
<PurgeRemoteMediaButton />
<ExportButton disabled={isLoading || total === 0} /> <ExportButton disabled={isLoading || total === 0} />
</TopToolbar> </TopToolbar>
); );

View File

@@ -10,6 +10,7 @@ import SettingsInputComponentIcon from "@mui/icons-material/SettingsInputCompone
import ScienceIcon from "@mui/icons-material/Science"; import ScienceIcon from "@mui/icons-material/Science";
import LockClockIcon from '@mui/icons-material/LockClock'; import LockClockIcon from '@mui/icons-material/LockClock';
import ViewListIcon from "@mui/icons-material/ViewList"; import ViewListIcon from "@mui/icons-material/ViewList";
import DocumentScannerIcon from "@mui/icons-material/DocumentScanner";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Alert, Typography } from "@mui/material"; import { Alert, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles";
@@ -50,6 +51,7 @@ import {
SaveButton, SaveButton,
CreateButton, CreateButton,
ExportButton, ExportButton,
BulkDeleteButton,
TopToolbar, TopToolbar,
Toolbar, Toolbar,
NumberField, NumberField,
@@ -80,6 +82,8 @@ import ExperimentalFeaturesList from "../components/ExperimentalFeatures";
import UserRateLimits from "../components/UserRateLimits"; import UserRateLimits from "../components/UserRateLimits";
import { User, UsernameAvailabilityResult } from "../synapse/dataProvider"; import { User, UsernameAvailabilityResult } from "../synapse/dataProvider";
import { MakeAdminBtn } from "./rooms"; import { MakeAdminBtn } from "./rooms";
import UserAccountData from "../components/UserAccountData";
import decodeURLComponent from "../utils/decodeURLComponent";
const choices_medium = [ const choices_medium = [
{ id: "email", name: "resources.users.email" }, { id: "email", name: "resources.users.email" },
@@ -111,6 +115,7 @@ const userFilters = [
<BooleanInput source="guests" alwaysOn />, <BooleanInput source="guests" alwaysOn />,
<BooleanInput label="resources.users.fields.show_deactivated" source="deactivated" 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_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 => { const UserPreventSelfDelete: React.FC<{ children: React.ReactNode; ownUserIsSelected: boolean; asManagedUserIsSelected: boolean }> = props => {
@@ -162,7 +167,7 @@ export const UserList = (props: ListProps) => (
<List <List
{...props} {...props}
filters={userFilters} filters={userFilters}
filterDefaultValues={{ guests: true, deactivated: false, locked: false }} filterDefaultValues={{ guests: true, deactivated: false, locked: false, suspended: false }}
sort={{ field: "name", order: "ASC" }} sort={{ field: "name", order: "ASC" }}
actions={<UserListActions />} actions={<UserListActions />}
pagination={<UserPagination />} pagination={<UserPagination />}
@@ -179,6 +184,7 @@ export const UserList = (props: ListProps) => (
<BooleanField source="admin" label="resources.users.fields.admin" /> <BooleanField source="admin" label="resources.users.fields.admin" />
<BooleanField source="deactivated" label="resources.users.fields.deactivated" /> <BooleanField source="deactivated" label="resources.users.fields.deactivated" />
<BooleanField source="locked" label="resources.users.fields.locked" /> <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" /> <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} /> <DateField source="creation_ts" label="resources.users.fields.creation_ts_ms" showTime options={DATE_FORMAT} />
</DatagridConfigurable> </DatagridConfigurable>
@@ -189,7 +195,7 @@ export const UserList = (props: ListProps) => (
// here only local part of user_id // here only local part of user_id
// maxLength = 255 - "@" - ":" - storage.getItem("home_server").length // maxLength = 255 - "@" - ":" - storage.getItem("home_server").length
// storage.getItem("home_server").length is not valid here // 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)]; const validateAddress = [required(), maxLength(255)];
@@ -433,6 +439,7 @@ export const UserEdit = (props: EditProps) => {
<BooleanInput source="admin" /> <BooleanInput source="admin" />
<UserBooleanInput source="locked" /> <UserBooleanInput source="locked" />
<UserBooleanInput source="deactivated" helperText="resources.users.helper.deactivate" /> <UserBooleanInput source="deactivated" helperText="resources.users.helper.deactivate" />
<UserBooleanInput source="suspended" helperText="resources.users.helper.suspend" />
<BooleanInput source="erased" disabled /> <BooleanInput source="erased" disabled />
<DateField source="creation_ts_ms" showTime options={DATE_FORMAT} /> <DateField source="creation_ts_ms" showTime options={DATE_FORMAT} />
<TextField source="consent_version" /> <TextField source="consent_version" />
@@ -493,13 +500,13 @@ export const UserEdit = (props: EditProps) => {
perPage={50} perPage={50}
sort={{ field: "created_ts", order: "DESC" }} sort={{ field: "created_ts", order: "DESC" }}
> >
<Datagrid sx={{ width: "100%" }}> <Datagrid sx={{ width: "100%" }} bulkActionButtons={<BulkDeleteButton/>}>
<MediaIDField source="media_id" /> <MediaIDField source="media_id" />
<DateField source="created_ts" showTime options={DATE_FORMAT} /> <DateField source="created_ts" showTime options={DATE_FORMAT} />
<DateField source="last_access_ts" showTime options={DATE_FORMAT} /> <DateField source="last_access_ts" showTime options={DATE_FORMAT} />
<NumberField source="media_length" /> <NumberField source="media_length" />
<TextField source="media_type" sx={{ display: "block", width: 200, wordBreak: "break-word" }} /> <TextField source="media_type" sx={{ display: "block", width: 200, wordBreak: "break-word" }} />
<FunctionField source="upload_name" render={record => decodeURIComponent(record.upload_name)} /> <FunctionField source="upload_name" render={record => record.upload_name ? decodeURLComponent(record.upload_name) : ""} />
<TextField source="quarantined_by" /> <TextField source="quarantined_by" />
<QuarantineMediaButton label="resources.quarantine_media.action.name" /> <QuarantineMediaButton label="resources.quarantine_media.action.name" />
<ProtectMediaButton label="resources.users_media.fields.safe_from_quarantine" /> <ProtectMediaButton label="resources.users_media.fields.safe_from_quarantine" />
@@ -521,7 +528,9 @@ export const UserEdit = (props: EditProps) => {
<ReferenceField reference="rooms" source="id" label="resources.rooms.fields.joined_members" link={false} sortable={false}> <ReferenceField reference="rooms" source="id" label="resources.rooms.fields.joined_members" link={false} sortable={false}>
<TextField source="joined_members" sortable={false} /> <TextField source="joined_members" sortable={false} />
</ReferenceField> </ReferenceField>
<ReferenceField reference="rooms" source="id" label={false} link={false} sortable={false}>
<MakeAdminBtn /> <MakeAdminBtn />
</ReferenceField>
</Datagrid> </Datagrid>
</ReferenceManyField> </ReferenceManyField>
</FormTab> </FormTab>
@@ -552,6 +561,10 @@ export const UserEdit = (props: EditProps) => {
<FormTab label="synapseadmin.users.tabs.limits" icon={<LockClockIcon />} path="limits"> <FormTab label="synapseadmin.users.tabs.limits" icon={<LockClockIcon />} path="limits">
<UserRateLimits /> <UserRateLimits />
</FormTab> </FormTab>
<FormTab label="synapseadmin.users.tabs.account_data" icon={<DocumentScannerIcon />} path="accountdata">
<UserAccountData />
</FormTab>
</TabbedForm> </TabbedForm>
</Edit> </Edit>
); );

View File

@@ -3,6 +3,7 @@ import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
import { MatrixError, displayError } from "../utils/error"; import { MatrixError, displayError } from "../utils/error";
import { fetchAuthenticatedMedia } from "../utils/fetchMedia"; import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
import { FetchConfig, ClearConfig } from "../utils/config"; import { FetchConfig, ClearConfig } from "../utils/config";
import decodeURLComponent from "../utils/decodeURLComponent";
const authProvider: AuthProvider = { const authProvider: AuthProvider = {
// called when the user attempts to log in // called when the user attempts to log in
@@ -57,7 +58,7 @@ const authProvider: AuthProvider = {
base_url = base_url.replace(/\/+$/g, ""); base_url = base_url.replace(/\/+$/g, "");
localStorage.setItem("base_url", base_url); localStorage.setItem("base_url", base_url);
const decoded_base_url = window.decodeURIComponent(base_url); const decoded_base_url = decodeURLComponent(base_url);
let login_api_url = decoded_base_url + (accessToken ? "/_matrix/client/v3/account/whoami" : "/_matrix/client/v3/login"); let login_api_url = decoded_base_url + (accessToken ? "/_matrix/client/v3/account/whoami" : "/_matrix/client/v3/login");
let response; let response;
@@ -80,11 +81,7 @@ const authProvider: AuthProvider = {
localStorage.setItem("access_token", accessToken ? accessToken : json.access_token); localStorage.setItem("access_token", accessToken ? accessToken : json.access_token);
localStorage.setItem("device_id", json.device_id); localStorage.setItem("device_id", json.device_id);
localStorage.setItem("login_type", accessToken ? "accessToken" : "credentials"); localStorage.setItem("login_type", accessToken ? "accessToken" : "credentials");
await FetchConfig();
// when doing access token auth, config is not fetched, so we need to do it here
if (accessToken) {
await FetchConfig();
}
return Promise.resolve({redirectTo: "/"}); return Promise.resolve({redirectTo: "/"});
} catch(err) { } catch(err) {

View File

@@ -138,6 +138,7 @@ export interface User {
external_ids: ExternalId[]; external_ids: ExternalId[];
user_type?: string; user_type?: string;
locked: boolean; locked: boolean;
suspended?: boolean;
} }
interface Device { interface Device {
@@ -259,6 +260,17 @@ export interface RateLimitsModel {
burst_count?: number; burst_count?: number;
} }
export interface AccountDataModel {
account_data: {
global: {
[key: string]: object;
},
rooms: {
[key: string]: object;
};
}
}
export interface UsernameAvailabilityResult { export interface UsernameAvailabilityResult {
available?: boolean; available?: boolean;
error?: string; error?: string;
@@ -290,16 +302,31 @@ export interface ServerProcessResponse {
command?: string; command?: string;
} }
export interface ServerNotification {
event_id: string;
output: string;
sent_at: string;
}
export interface ServerNotificationsResponse {
success: boolean;
notifications: ServerNotification[];
}
export interface SynapseDataProvider extends DataProvider { export interface SynapseDataProvider extends DataProvider {
deleteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>; deleteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>;
purgeRemoteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>;
uploadMedia: (params: UploadMediaParams) => Promise<UploadMediaResult>; uploadMedia: (params: UploadMediaParams) => Promise<UploadMediaResult>;
updateFeatures: (id: Identifier, features: ExperimentalFeaturesModel) => Promise<void>; updateFeatures: (id: Identifier, features: ExperimentalFeaturesModel) => Promise<void>;
getRateLimits: (id: Identifier) => Promise<RateLimitsModel>; getRateLimits: (id: Identifier) => Promise<RateLimitsModel>;
setRateLimits: (id: Identifier, rateLimits: RateLimitsModel) => Promise<void>; setRateLimits: (id: Identifier, rateLimits: RateLimitsModel) => Promise<void>;
getAccountData: (id: Identifier) => Promise<AccountDataModel>;
checkUsernameAvailability: (username: string) => Promise<UsernameAvailabilityResult>; checkUsernameAvailability: (username: string) => Promise<UsernameAvailabilityResult>;
makeRoomAdmin: (room_id: string, user_id: string) => Promise<{ success: boolean; error?: string; errcode?: string }>; makeRoomAdmin: (room_id: string, user_id: string) => Promise<{ success: boolean; error?: string; errcode?: string }>;
getServerRunningProcess: (etkeAdminUrl: string) => Promise<ServerProcessResponse>; getServerRunningProcess: (etkeAdminUrl: string) => Promise<ServerProcessResponse>;
getServerStatus: (etkeAdminUrl: string) => Promise<ServerStatusResponse>; getServerStatus: (etkeAdminUrl: string) => Promise<ServerStatusResponse>;
getServerNotifications: (etkeAdminUrl: string) => Promise<ServerNotificationsResponse>;
deleteServerNotifications: (etkeAdminUrl: string) => Promise<{ success: boolean }>;
} }
const resourceMap = { const resourceMap = {
@@ -384,6 +411,20 @@ const resourceMap = {
data: "members", data: "members",
total: json => json.total, 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: { room_state: {
map: (rs: RoomState) => ({ map: (rs: RoomState) => ({
...rs, ...rs,
@@ -575,7 +616,7 @@ function getSearchOrder(order: "ASC" | "DESC") {
const baseDataProvider: SynapseDataProvider = { const baseDataProvider: SynapseDataProvider = {
getList: async (resource, params) => { getList: async (resource, params) => {
console.log("getList " + resource); 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 { page, perPage } = params.pagination as PaginationPayload;
const { field, order } = params.sort as SortPayload; const { field, order } = params.sort as SortPayload;
const from = (page - 1) * perPage; const from = (page - 1) * perPage;
@@ -589,6 +630,7 @@ const baseDataProvider: SynapseDataProvider = {
guests: guests, guests: guests,
deactivated: deactivated, deactivated: deactivated,
locked: locked, locked: locked,
suspended: suspended,
valid: valid, valid: valid,
order_by: field, order_by: field,
dir: getSearchOrder(order), dir: getSearchOrder(order),
@@ -833,6 +875,23 @@ const baseDataProvider: SynapseDataProvider = {
return json as DeleteMediaResult; return json as DeleteMediaResult;
}, },
/**
* Purge remote media by date
*
* @link https://element-hq.github.io/synapse/latest/admin_api/media_admin_api.html#purge-remote-media-api
*
* @param before_ts Unix timestamp in milliseconds. Files that were last used before this timestamp will be deleted. It is the timestamp of last access, not the timestamp when the file was created.
* @returns
*/
purgeRemoteMedia: async ({ before_ts }) => {
const endpoint = `/_synapse/admin/v1/purge_media_cache?before_ts=${before_ts}`;
const base_url = localStorage.getItem("base_url");
const endpoint_url = base_url + endpoint;
const { json } = await jsonClient(endpoint_url, { method: "POST" });
return json as DeleteMediaResult;
},
uploadMedia: async ({ file, filename, content_type }: UploadMediaParams) => { uploadMedia: async ({ file, filename, content_type }: UploadMediaParams) => {
const base_url = localStorage.getItem("base_url"); const base_url = localStorage.getItem("base_url");
const uploadMediaURL = `${base_url}/_matrix/media/v3/upload`; const uploadMediaURL = `${base_url}/_matrix/media/v3/upload`;
@@ -864,6 +923,12 @@ const baseDataProvider: SynapseDataProvider = {
const { json } = await jsonClient(endpoint_url); const { json } = await jsonClient(endpoint_url);
return json as RateLimitsModel; return json as RateLimitsModel;
}, },
getAccountData: async (id: Identifier) => {
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/accountdata`;
const { json } = await jsonClient(endpoint_url);
return json as AccountDataModel;
},
setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => { setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => {
const filtered = Object.entries(rateLimits). const filtered = Object.entries(rateLimits).
filter(([key, value]) => value !== null && value !== undefined). filter(([key, value]) => value !== null && value !== undefined).
@@ -961,6 +1026,60 @@ const baseDataProvider: SynapseDataProvider = {
} }
return { success: false, ok: false, host: "", results: [] }; return { success: false, ok: false, host: "", results: [] };
},
getServerNotifications: async (serverNotificationsUrl: string): Promise<ServerNotificationsResponse> => {
try {
const response = await fetch(`${serverNotificationsUrl}/notifications`, {
headers: {
"Authorization": `Bearer ${localStorage.getItem("access_token")}`
}
});
if (!response.ok) {
console.error(`Error getting server notifications: ${response.status} ${response.statusText}`);
return { success: false, notifications: [] };
}
const status = response.status;
if (status === 204) {
return { success: true, notifications: [] };
}
if (status === 200) {
const json = await response.json();
const result = { success: true, notifications: json } as ServerNotificationsResponse;
return result;
}
return { success: true, notifications: [] };
} catch (error) {
console.error("Error getting server notifications", error);
}
return { success: false, notifications: [] };
},
deleteServerNotifications: async (serverNotificationsUrl: string) => {
try {
const response = await fetch(`${serverNotificationsUrl}/notifications`, {
headers: {
"Authorization": `Bearer ${localStorage.getItem("access_token")}`
},
method: "DELETE"
});
if (!response.ok) {
console.error(`Error deleting server notifications: ${response.status} ${response.statusText}`);
return { success: false };
}
const status = response.status;
if (status === 204) {
const result = { success: true }
return result;
}
} catch (error) {
console.error("Error deleting server notifications", error);
}
return { success: false };
} }
}; };

View File

@@ -22,32 +22,43 @@ let config: Config = {
}; };
export const FetchConfig = async () => { export const FetchConfig = async () => {
// load config.json and honor vite base url (import.meta.env.BASE_URL)
// if that url doesn't have a trailing slash - add it
let configJSONUrl = "config.json"
if (import.meta.env.BASE_URL) {
configJSONUrl = `${import.meta.env.BASE_URL.replace(/\/?$/, '/')}config.json`;
}
try { try {
const resp = await fetch("config.json"); const resp = await fetch(configJSONUrl);
const configJSON = await resp.json(); const configJSON = await resp.json();
console.log("Loaded config.json", configJSON); console.log("Loaded", configJSONUrl, configJSON);
LoadConfig(configJSON); LoadConfig(configJSON);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
let protocol = "https";
const baseURL = localStorage.getItem("base_url");
if (baseURL && baseURL.startsWith("http://")) {
protocol = "http";
}
// if home_server is set, try to load https://home_server/.well-known/matrix/client // if home_server is set, try to load https://home_server/.well-known/matrix/client
const homeserver = localStorage.getItem("home_server"); const homeserver = localStorage.getItem("home_server");
if (homeserver) { if (homeserver) {
try { try {
const resp = await fetch(`https://${homeserver}/.well-known/matrix/client`); const resp = await fetch(`${protocol}://${homeserver}/.well-known/matrix/client`);
const configWK = await resp.json(); const configWK = await resp.json();
if (!configWK[WellKnownKey]) { if (!configWK[WellKnownKey]) {
console.log(`Loaded https://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK); console.log(`Loaded ${protocol}://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK);
} else { } else {
console.log(`Loaded https://${homeserver}.well-known/matrix/client`, configWK); console.log(`Loaded ${protocol}://${homeserver}.well-known/matrix/client`, configWK);
LoadConfig(configWK[WellKnownKey]); LoadConfig(configWK[WellKnownKey]);
} }
} catch (e) { } catch (e) {
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e); console.log(`${protocol}://${homeserver}/.well-known/matrix/client not found, skipping`, e);
} }
} }
} }
// load config from context // load config from context

View File

@@ -0,0 +1,15 @@
/**
* Decode a URI component, and if it fails, return the original string.
* @param str The string to decode.
* @returns The decoded string, or the original string if decoding fails.
* @example decodeURIComponent("Hello%20World") // "Hello World"
*/
const decodeURLComponent = (str: any): any => {
try {
return decodeURIComponent(str);
} catch (e) {
return str;
}
}
export default decodeURLComponent;

View File

@@ -16,7 +16,11 @@ export const isMXID = (id: string | Identifier): boolean => mxidPattern.test(id
* @returns Whether the user is managed by an application service * @returns Whether the user is managed by an application service
*/ */
export const isASManaged = (id: string | Identifier): boolean => { export const isASManaged = (id: string | Identifier): boolean => {
return GetConfig().asManagedUsers.some(regex => regex.test(id as string)); const managedUsers = GetConfig().asManagedUsers;
if (!managedUsers) {
return false;
}
return managedUsers.some(regex => regex.test(id as string));
}; };
/** /**
@@ -38,15 +42,20 @@ export function generateRandomMXID(): string {
* @returns full MXID as string * @returns full MXID as string
*/ */
export function returnMXID(input: string | Identifier): string { export function returnMXID(input: string | Identifier): string {
const homeserver = localStorage.getItem("home_server"); const inputStr = input as string;
const homeserver = localStorage.getItem("home_server") || "";
// when homeserver is not (just) a domain name, but a domain:port or even an IPv6 address
if (homeserver != "" && inputStr.endsWith(homeserver) && inputStr.startsWith("@")) {
return inputStr; // Already a valid MXID
}
// Check if the input already looks like a valid MXID (i.e., starts with "@" and contains ":") // Check if the input already looks like a valid MXID (i.e., starts with "@" and contains ":")
const mxidPattern = /^@[^@:]+:[^@:]+$/;
if (isMXID(input)) { if (isMXID(input)) {
return input as string; // Already a valid MXID return inputStr; // Already a valid MXID
} }
// If input is not a valid MXID, assume it's a localpart and construct the MXID // If input is not a valid MXID, assume it's a localpart and construct the MXID
const localpart = typeof input === 'string' && input.startsWith('@') ? input.slice(1) : input; const localpart = typeof input === 'string' && inputStr.startsWith('@') ? inputStr.slice(1) : inputStr;
return `@${localpart}:${homeserver}`; return `@${localpart}:${homeserver}`;
} }

9
testdata/element/config.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"default_hs_url": "http://localhost:8008",
"default_is_url": "https://vector.im",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"enableLabs": true
}

34
testdata/element/nginx.conf vendored Normal file
View File

@@ -0,0 +1,34 @@
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
client_body_temp_path /tmp/client_body_temp;
proxy_temp_path /tmp/proxy_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

View File

@@ -78,7 +78,7 @@ password_config:
pid_file: /homeserver.pid pid_file: /homeserver.pid
presence: presence:
enabled: true enabled: true
public_baseurl: http://synapse:8008/ public_baseurl: http://localhost:8008/
push: push:
include_content: true include_content: true
rc_admin_redaction: rc_admin_redaction:
@@ -149,7 +149,7 @@ saml2_config:
sp_config: null sp_config: null
user_mapping_provider: user_mapping_provider:
config: null config: null
server_name: synapse server_name: synapse:8008
signing_key_path: /config/synapse.signing.key signing_key_path: /config/synapse.signing.key
spam_checker: [] spam_checker: []
sso: null sso: null

3585
yarn.lock

File diff suppressed because it is too large Load Diff