v4: an instance that takes no users of others still blocks instances its own users may not sign in to — with GERBEUR_FEDERATION_INCOMING=0, /manage hid its list of instances, though a blocked one still kept the instance's users from signing in there, and no admin could block another. The list now stays while federated sign-in goes either way: without incoming sign-ins it shows and adds only blocked instances, the trusted ones kept for when they're back, and says what blocking does there. Where both ways are on, its note now says that users here can't sign in to a blocked instance either.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -237,10 +237,11 @@ export const FEDERATION_ALLOW_PRIVATE: boolean =
|
|||||||
*
|
*
|
||||||
* - `incoming` (`GERBEUR_FEDERATION_INCOMING`): users of other instances sign
|
* - `incoming` (`GERBEUR_FEDERATION_INCOMING`): users of other instances sign
|
||||||
* in here. Off, the login and register pages offer no other instance,
|
* in here. Off, the login and register pages offer no other instance,
|
||||||
* `/manage` no list of instances to trust or block,
|
* `/manage` lists only the blocked ones, which this instance's users can't
|
||||||
* `federation-service.ts` refuses their sign-ins, and the accounts that came
|
* sign in to (and none with `outgoing` off too), `federation-service.ts`
|
||||||
* from one are signed out (`getSessionState`) until it is on again. Their
|
* refuses their sign-ins, and the accounts that came from one are signed
|
||||||
* homes' events are still applied, so that none is lost meanwhile.
|
* out (`getSessionState`) until it is on again. Their homes' events are
|
||||||
|
* still applied, so that none is lost meanwhile.
|
||||||
* - `outgoing` (`GERBEUR_FEDERATION_OUTGOING`): this instance's users sign in
|
* - `outgoing` (`GERBEUR_FEDERATION_OUTGOING`): this instance's users sign in
|
||||||
* to others. Off, `identity-provider-service.ts` lets no instance sign them
|
* to others. Off, `identity-provider-service.ts` lets no instance sign them
|
||||||
* in, and its metadata says so, which the others check before sending
|
* in, and its metadata says so, which the others check before sending
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ msgstr "Already dumped by {0} {1}"
|
|||||||
msgid "Already have an account? <0>Log in</0>"
|
msgid "Already have an account? <0>Log in</0>"
|
||||||
msgstr "Already have an account? <0>Log in</0>"
|
msgstr "Already have an account? <0>Log in</0>"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:149
|
#: src/components/federation/PeerManager.tsx:167
|
||||||
msgid "An instance's address, like music.example"
|
msgid "An instance's address, like music.example"
|
||||||
msgstr "An instance's address, like music.example"
|
msgstr "An instance's address, like music.example"
|
||||||
|
|
||||||
@@ -395,11 +395,12 @@ msgstr "Back to login"
|
|||||||
msgid "Back to the login page"
|
msgid "Back to the login page"
|
||||||
msgstr "Back to the login page"
|
msgstr "Back to the login page"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:163
|
#: src/components/federation/PeerManager.tsx:184
|
||||||
|
#: src/components/federation/PeerManager.tsx:190
|
||||||
msgid "Block"
|
msgid "Block"
|
||||||
msgstr "Block"
|
msgstr "Block"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:33
|
#: src/components/federation/PeerManager.tsx:39
|
||||||
msgid "Blocked"
|
msgid "Blocked"
|
||||||
msgstr "Blocked"
|
msgstr "Blocked"
|
||||||
|
|
||||||
@@ -453,7 +454,7 @@ msgid "Cancel search"
|
|||||||
msgstr "Cancel search"
|
msgstr "Cancel search"
|
||||||
|
|
||||||
#: src/components/category/CategorySelect.tsx:33
|
#: src/components/category/CategorySelect.tsx:33
|
||||||
#: src/pages/Manage.tsx:38
|
#: src/pages/Manage.tsx:42
|
||||||
msgid "Categories"
|
msgid "Categories"
|
||||||
msgstr "Categories"
|
msgstr "Categories"
|
||||||
|
|
||||||
@@ -502,7 +503,8 @@ msgstr "Chat ({unreadChatCount} unread)"
|
|||||||
msgid "Checking invite…"
|
msgid "Checking invite…"
|
||||||
msgstr "Checking invite…"
|
msgstr "Checking invite…"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:154
|
#: src/components/federation/PeerManager.tsx:175
|
||||||
|
#: src/components/federation/PeerManager.tsx:189
|
||||||
msgid "Checking…"
|
msgid "Checking…"
|
||||||
msgstr "Checking…"
|
msgstr "Checking…"
|
||||||
|
|
||||||
@@ -590,7 +592,7 @@ msgstr "Could not rename"
|
|||||||
msgid "Could not save"
|
msgid "Could not save"
|
||||||
msgstr "Could not save"
|
msgstr "Could not save"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:146
|
#: src/components/federation/PeerManager.tsx:164
|
||||||
msgid "Couldn't add that instance"
|
msgid "Couldn't add that instance"
|
||||||
msgstr "Couldn't add that instance"
|
msgstr "Couldn't add that instance"
|
||||||
|
|
||||||
@@ -885,7 +887,7 @@ msgid "Failed to post reply"
|
|||||||
msgstr "Failed to post reply"
|
msgstr "Failed to post reply"
|
||||||
|
|
||||||
#: src/components/federation/GrantList.tsx:98
|
#: src/components/federation/GrantList.tsx:98
|
||||||
#: src/components/federation/PeerManager.tsx:143
|
#: src/components/federation/PeerManager.tsx:158
|
||||||
#: src/components/playlist/PlaylistEditForm.tsx:185
|
#: src/components/playlist/PlaylistEditForm.tsx:185
|
||||||
#: src/components/profile/DescriptionEditor.tsx:99
|
#: src/components/profile/DescriptionEditor.tsx:99
|
||||||
#: src/components/profile/EmailEditor.tsx:86
|
#: src/components/profile/EmailEditor.tsx:86
|
||||||
@@ -1029,7 +1031,7 @@ msgstr "In the chat"
|
|||||||
msgid "Insert emoji"
|
msgid "Insert emoji"
|
||||||
msgstr "Insert emoji"
|
msgstr "Insert emoji"
|
||||||
|
|
||||||
#: src/pages/Manage.tsx:45
|
#: src/pages/Manage.tsx:49
|
||||||
msgid "Instances"
|
msgid "Instances"
|
||||||
msgstr "Instances"
|
msgstr "Instances"
|
||||||
|
|
||||||
@@ -1145,7 +1147,7 @@ msgstr "Loading profile…"
|
|||||||
#: src/components/category/CategoryManager.tsx:60
|
#: src/components/category/CategoryManager.tsx:60
|
||||||
#: src/components/chat/ChatModal.tsx:763
|
#: src/components/chat/ChatModal.tsx:763
|
||||||
#: src/components/editor/EmojiPickerPopup.tsx:125
|
#: src/components/editor/EmojiPickerPopup.tsx:125
|
||||||
#: src/components/federation/PeerManager.tsx:108
|
#: src/components/federation/PeerManager.tsx:120
|
||||||
#: src/components/playlist/PlaylistMembershipPanel.tsx:45
|
#: src/components/playlist/PlaylistMembershipPanel.tsx:45
|
||||||
#: src/components/shared/ListState.tsx:38
|
#: src/components/shared/ListState.tsx:38
|
||||||
#: src/components/shared/LoadMoreButton.tsx:32
|
#: src/components/shared/LoadMoreButton.tsx:32
|
||||||
@@ -1194,8 +1196,8 @@ msgid "Lowercase letters, numbers and single hyphens only"
|
|||||||
msgstr "Lowercase letters, numbers and single hyphens only"
|
msgstr "Lowercase letters, numbers and single hyphens only"
|
||||||
|
|
||||||
#: src/components/layout/UserMenu.tsx:114
|
#: src/components/layout/UserMenu.tsx:114
|
||||||
#: src/pages/Manage.tsx:29
|
#: src/pages/Manage.tsx:32
|
||||||
#: src/pages/Manage.tsx:36
|
#: src/pages/Manage.tsx:40
|
||||||
#: src/pages/UserPublicProfile.tsx:164
|
#: src/pages/UserPublicProfile.tsx:164
|
||||||
msgid "Manage"
|
msgid "Manage"
|
||||||
msgstr "Manage"
|
msgstr "Manage"
|
||||||
@@ -1538,8 +1540,8 @@ msgid "Related"
|
|||||||
msgstr "Related"
|
msgstr "Related"
|
||||||
|
|
||||||
#. placeholder {0}: peer.host
|
#. placeholder {0}: peer.host
|
||||||
#: src/components/federation/PeerManager.tsx:133
|
#: src/components/federation/PeerManager.tsx:148
|
||||||
#: src/components/federation/PeerManager.tsx:134
|
#: src/components/federation/PeerManager.tsx:149
|
||||||
msgid "Remove {0}"
|
msgid "Remove {0}"
|
||||||
msgstr "Remove {0}"
|
msgstr "Remove {0}"
|
||||||
|
|
||||||
@@ -1893,11 +1895,11 @@ msgstr "Top this week"
|
|||||||
msgid "Track {0}"
|
msgid "Track {0}"
|
||||||
msgstr "Track {0}"
|
msgstr "Track {0}"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:155
|
#: src/components/federation/PeerManager.tsx:176
|
||||||
msgid "Trust"
|
msgid "Trust"
|
||||||
msgstr "Trust"
|
msgstr "Trust"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:32
|
#: src/components/federation/PeerManager.tsx:38
|
||||||
msgid "Trusted"
|
msgid "Trusted"
|
||||||
msgstr "Trusted"
|
msgstr "Trusted"
|
||||||
|
|
||||||
@@ -2032,7 +2034,15 @@ msgstr "Username"
|
|||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr "Users"
|
msgstr "Users"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:169
|
#: src/components/federation/PeerManager.tsx:198
|
||||||
|
msgid "Users here can't sign in to a blocked instance."
|
||||||
|
msgstr "Users here can't sign in to a blocked instance."
|
||||||
|
|
||||||
|
#: src/components/federation/PeerManager.tsx:201
|
||||||
|
msgid "Users of a trusted instance sign in here with their account there. Those of a blocked one can't, and users here can't sign in to it. Users of any other instance need an invite."
|
||||||
|
msgstr "Users of a trusted instance sign in here with their account there. Those of a blocked one can't, and users here can't sign in to it. Users of any other instance need an invite."
|
||||||
|
|
||||||
|
#: src/components/federation/PeerManager.tsx:208
|
||||||
msgid "Users of a trusted instance sign in here with their account there. Those of a blocked one can't. Users of any other instance need an invite."
|
msgid "Users of a trusted instance sign in here with their account there. Those of a blocked one can't. Users of any other instance need an invite."
|
||||||
msgstr "Users of a trusted instance sign in here with their account there. Those of a blocked one can't. Users of any other instance need an invite."
|
msgstr "Users of a trusted instance sign in here with their account there. Those of a blocked one can't. Users of any other instance need an invite."
|
||||||
|
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ msgstr "Déjà recommandé par {0} {1}"
|
|||||||
msgid "Already have an account? <0>Log in</0>"
|
msgid "Already have an account? <0>Log in</0>"
|
||||||
msgstr "Vous avez déjà un compte ? <0>Se connecter</0>"
|
msgstr "Vous avez déjà un compte ? <0>Se connecter</0>"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:149
|
#: src/components/federation/PeerManager.tsx:167
|
||||||
msgid "An instance's address, like music.example"
|
msgid "An instance's address, like music.example"
|
||||||
msgstr "L'adresse d'une instance, comme music.example"
|
msgstr "L'adresse d'une instance, comme music.example"
|
||||||
|
|
||||||
@@ -395,11 +395,12 @@ msgstr "Retour à la connexion"
|
|||||||
msgid "Back to the login page"
|
msgid "Back to the login page"
|
||||||
msgstr "Retour à la page de connexion"
|
msgstr "Retour à la page de connexion"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:163
|
#: src/components/federation/PeerManager.tsx:184
|
||||||
|
#: src/components/federation/PeerManager.tsx:190
|
||||||
msgid "Block"
|
msgid "Block"
|
||||||
msgstr "Bloquer"
|
msgstr "Bloquer"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:33
|
#: src/components/federation/PeerManager.tsx:39
|
||||||
msgid "Blocked"
|
msgid "Blocked"
|
||||||
msgstr "Bloquée"
|
msgstr "Bloquée"
|
||||||
|
|
||||||
@@ -453,7 +454,7 @@ msgid "Cancel search"
|
|||||||
msgstr "Annuler la recherche"
|
msgstr "Annuler la recherche"
|
||||||
|
|
||||||
#: src/components/category/CategorySelect.tsx:33
|
#: src/components/category/CategorySelect.tsx:33
|
||||||
#: src/pages/Manage.tsx:38
|
#: src/pages/Manage.tsx:42
|
||||||
msgid "Categories"
|
msgid "Categories"
|
||||||
msgstr "Catégories"
|
msgstr "Catégories"
|
||||||
|
|
||||||
@@ -502,7 +503,8 @@ msgstr "{unreadChatCount} messages non lus"
|
|||||||
msgid "Checking invite…"
|
msgid "Checking invite…"
|
||||||
msgstr "Vérification de l'invitation…"
|
msgstr "Vérification de l'invitation…"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:154
|
#: src/components/federation/PeerManager.tsx:175
|
||||||
|
#: src/components/federation/PeerManager.tsx:189
|
||||||
msgid "Checking…"
|
msgid "Checking…"
|
||||||
msgstr "Vérification…"
|
msgstr "Vérification…"
|
||||||
|
|
||||||
@@ -590,7 +592,7 @@ msgstr "Renommage impossible"
|
|||||||
msgid "Could not save"
|
msgid "Could not save"
|
||||||
msgstr "Sauvegarde impossible"
|
msgstr "Sauvegarde impossible"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:146
|
#: src/components/federation/PeerManager.tsx:164
|
||||||
msgid "Couldn't add that instance"
|
msgid "Couldn't add that instance"
|
||||||
msgstr "Impossible d'ajouter cette instance"
|
msgstr "Impossible d'ajouter cette instance"
|
||||||
|
|
||||||
@@ -885,7 +887,7 @@ msgid "Failed to post reply"
|
|||||||
msgstr "Impossible de publier la réponse"
|
msgstr "Impossible de publier la réponse"
|
||||||
|
|
||||||
#: src/components/federation/GrantList.tsx:98
|
#: src/components/federation/GrantList.tsx:98
|
||||||
#: src/components/federation/PeerManager.tsx:143
|
#: src/components/federation/PeerManager.tsx:158
|
||||||
#: src/components/playlist/PlaylistEditForm.tsx:185
|
#: src/components/playlist/PlaylistEditForm.tsx:185
|
||||||
#: src/components/profile/DescriptionEditor.tsx:99
|
#: src/components/profile/DescriptionEditor.tsx:99
|
||||||
#: src/components/profile/EmailEditor.tsx:86
|
#: src/components/profile/EmailEditor.tsx:86
|
||||||
@@ -1029,7 +1031,7 @@ msgstr "Dans la tribune"
|
|||||||
msgid "Insert emoji"
|
msgid "Insert emoji"
|
||||||
msgstr "Insérer un emoji"
|
msgstr "Insérer un emoji"
|
||||||
|
|
||||||
#: src/pages/Manage.tsx:45
|
#: src/pages/Manage.tsx:49
|
||||||
msgid "Instances"
|
msgid "Instances"
|
||||||
msgstr "Instances"
|
msgstr "Instances"
|
||||||
|
|
||||||
@@ -1145,7 +1147,7 @@ msgstr "Chargement du profil…"
|
|||||||
#: src/components/category/CategoryManager.tsx:60
|
#: src/components/category/CategoryManager.tsx:60
|
||||||
#: src/components/chat/ChatModal.tsx:763
|
#: src/components/chat/ChatModal.tsx:763
|
||||||
#: src/components/editor/EmojiPickerPopup.tsx:125
|
#: src/components/editor/EmojiPickerPopup.tsx:125
|
||||||
#: src/components/federation/PeerManager.tsx:108
|
#: src/components/federation/PeerManager.tsx:120
|
||||||
#: src/components/playlist/PlaylistMembershipPanel.tsx:45
|
#: src/components/playlist/PlaylistMembershipPanel.tsx:45
|
||||||
#: src/components/shared/ListState.tsx:38
|
#: src/components/shared/ListState.tsx:38
|
||||||
#: src/components/shared/LoadMoreButton.tsx:32
|
#: src/components/shared/LoadMoreButton.tsx:32
|
||||||
@@ -1194,8 +1196,8 @@ msgid "Lowercase letters, numbers and single hyphens only"
|
|||||||
msgstr "Minuscules, chiffres et tirets simples seulement"
|
msgstr "Minuscules, chiffres et tirets simples seulement"
|
||||||
|
|
||||||
#: src/components/layout/UserMenu.tsx:114
|
#: src/components/layout/UserMenu.tsx:114
|
||||||
#: src/pages/Manage.tsx:29
|
#: src/pages/Manage.tsx:32
|
||||||
#: src/pages/Manage.tsx:36
|
#: src/pages/Manage.tsx:40
|
||||||
#: src/pages/UserPublicProfile.tsx:164
|
#: src/pages/UserPublicProfile.tsx:164
|
||||||
msgid "Manage"
|
msgid "Manage"
|
||||||
msgstr "Administration"
|
msgstr "Administration"
|
||||||
@@ -1538,8 +1540,8 @@ msgid "Related"
|
|||||||
msgstr "Connexe"
|
msgstr "Connexe"
|
||||||
|
|
||||||
#. placeholder {0}: peer.host
|
#. placeholder {0}: peer.host
|
||||||
#: src/components/federation/PeerManager.tsx:133
|
#: src/components/federation/PeerManager.tsx:148
|
||||||
#: src/components/federation/PeerManager.tsx:134
|
#: src/components/federation/PeerManager.tsx:149
|
||||||
msgid "Remove {0}"
|
msgid "Remove {0}"
|
||||||
msgstr "Retirer {0}"
|
msgstr "Retirer {0}"
|
||||||
|
|
||||||
@@ -1893,11 +1895,11 @@ msgstr "Les plus votées de la semaine"
|
|||||||
msgid "Track {0}"
|
msgid "Track {0}"
|
||||||
msgstr "Piste {0}"
|
msgstr "Piste {0}"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:155
|
#: src/components/federation/PeerManager.tsx:176
|
||||||
msgid "Trust"
|
msgid "Trust"
|
||||||
msgstr "Approuver"
|
msgstr "Approuver"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:32
|
#: src/components/federation/PeerManager.tsx:38
|
||||||
msgid "Trusted"
|
msgid "Trusted"
|
||||||
msgstr "Approuvée"
|
msgstr "Approuvée"
|
||||||
|
|
||||||
@@ -2032,7 +2034,15 @@ msgstr "Nom d'utilisateur"
|
|||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr "Utilisateurs"
|
msgstr "Utilisateurs"
|
||||||
|
|
||||||
#: src/components/federation/PeerManager.tsx:169
|
#: src/components/federation/PeerManager.tsx:198
|
||||||
|
msgid "Users here can't sign in to a blocked instance."
|
||||||
|
msgstr "Les utilisateurs d'ici ne peuvent pas se connecter à une instance bloquée."
|
||||||
|
|
||||||
|
#: src/components/federation/PeerManager.tsx:201
|
||||||
|
msgid "Users of a trusted instance sign in here with their account there. Those of a blocked one can't, and users here can't sign in to it. Users of any other instance need an invite."
|
||||||
|
msgstr "Les utilisateurs d'une instance approuvée se connectent ici avec leur compte de là-bas. Ceux d'une instance bloquée ne le peuvent pas, et les utilisateurs d'ici ne peuvent pas s'y connecter. Ceux de toute autre instance ont besoin d'une invitation."
|
||||||
|
|
||||||
|
#: src/components/federation/PeerManager.tsx:208
|
||||||
msgid "Users of a trusted instance sign in here with their account there. Those of a blocked one can't. Users of any other instance need an invite."
|
msgid "Users of a trusted instance sign in here with their account there. Those of a blocked one can't. Users of any other instance need an invite."
|
||||||
msgstr "Les utilisateurs d'une instance approuvée se connectent ici avec leur compte de là-bas. Ceux d'une instance bloquée ne le peuvent pas. Ceux de toute autre instance ont besoin d'une invitation."
|
msgstr "Les utilisateurs d'une instance approuvée se connectent ici avec leur compte de là-bas. Ceux d'une instance bloquée ne le peuvent pas. Ceux de toute autre instance ont besoin d'une invitation."
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Renders the list of the other gerbeur instances an admin trusts or blocks,
|
* Renders the list of the other gerbeur instances an admin trusts or blocks,
|
||||||
* on `/manage`, for those who hold `federation:manage`.
|
* on `/manage`, for those who hold `federation:manage`. Where users of other
|
||||||
|
* instances don't sign in, only blocking is left: it keeps this instance's
|
||||||
|
* users from signing in to one.
|
||||||
*
|
*
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
@@ -11,6 +13,10 @@ import { Trans } from "@lingui/react/macro";
|
|||||||
import { FormProvider } from "react-hook-form";
|
import { FormProvider } from "react-hook-form";
|
||||||
|
|
||||||
import { API_URL } from "../../config/api.ts";
|
import { API_URL } from "../../config/api.ts";
|
||||||
|
import {
|
||||||
|
FEDERATION_INCOMING,
|
||||||
|
FEDERATION_OUTGOING,
|
||||||
|
} from "../../config/instance.ts";
|
||||||
import type { Wire } from "../../../shared/api.ts";
|
import type { Wire } from "../../../shared/api.ts";
|
||||||
import type {
|
import type {
|
||||||
FederationPeer,
|
FederationPeer,
|
||||||
@@ -42,6 +48,8 @@ interface Values {
|
|||||||
* (`PUT /api/federation/peers`), and a button that takes it off the lists
|
* (`PUT /api/federation/peers`), and a button that takes it off the lists
|
||||||
* (`DELETE /api/federation/peers/:host`), then a field that adds one. The
|
* (`DELETE /api/federation/peers/:host`), then a field that adds one. The
|
||||||
* server checks that it answers as a gerbeur, and the permission again.
|
* server checks that it answers as a gerbeur, and the permission again.
|
||||||
|
* Without {@link FEDERATION_INCOMING} it lists and adds only blocked
|
||||||
|
* instances; the trusted ones are kept for when it is back.
|
||||||
*/
|
*/
|
||||||
export function PeerManager(): React.JSX.Element {
|
export function PeerManager(): React.JSX.Element {
|
||||||
const { authFetch } = useAuth();
|
const { authFetch } = useAuth();
|
||||||
@@ -101,6 +109,10 @@ export function PeerManager(): React.JSX.Element {
|
|||||||
form.reset();
|
form.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const shown = FEDERATION_INCOMING
|
||||||
|
? peers
|
||||||
|
: peers?.filter((peer) => peer.status === "blocked") ?? null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="peer-manager">
|
<div className="peer-manager">
|
||||||
{peers === null && !error && (
|
{peers === null && !error && (
|
||||||
@@ -108,9 +120,9 @@ export function PeerManager(): React.JSX.Element {
|
|||||||
<Trans>Loading…</Trans>
|
<Trans>Loading…</Trans>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{peers && peers.length > 0 && (
|
{shown && shown.length > 0 && (
|
||||||
<div className="profile-appearance-grid">
|
<div className="profile-appearance-grid">
|
||||||
{peers.map((peer) => (
|
{shown.map((peer) => (
|
||||||
<SettingRow
|
<SettingRow
|
||||||
key={peer.issuer}
|
key={peer.issuer}
|
||||||
label={
|
label={
|
||||||
@@ -121,11 +133,14 @@ export function PeerManager(): React.JSX.Element {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="peer-controls">
|
<span className="peer-controls">
|
||||||
<Segmented
|
{FEDERATION_INCOMING && (
|
||||||
options={STATUS_OPTIONS}
|
<Segmented
|
||||||
value={peer.status}
|
options={STATUS_OPTIONS}
|
||||||
onChange={(status) => change(() => save(peer.issuer, status))}
|
value={peer.status}
|
||||||
/>
|
onChange={(status) =>
|
||||||
|
change(() => save(peer.issuer, status))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn--ghost peer-remove"
|
className="btn--ghost peer-remove"
|
||||||
@@ -142,7 +157,10 @@ export function PeerManager(): React.JSX.Element {
|
|||||||
)}
|
)}
|
||||||
{error && <ErrorCard title={t`Failed to save`} message={error} />}
|
{error && <ErrorCard title={t`Failed to save`} message={error} />}
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<form className="form peer-add" onSubmit={add("trusted")}>
|
<form
|
||||||
|
className="form peer-add"
|
||||||
|
onSubmit={add(FEDERATION_INCOMING ? "trusted" : "blocked")}
|
||||||
|
>
|
||||||
<FormError title={t`Couldn't add that instance`} />
|
<FormError title={t`Couldn't add that instance`} />
|
||||||
<TextField<Values>
|
<TextField<Values>
|
||||||
name="instance"
|
name="instance"
|
||||||
@@ -151,26 +169,48 @@ export function PeerManager(): React.JSX.Element {
|
|||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
/>
|
/>
|
||||||
<div className="peer-add-actions">
|
<div className="peer-add-actions">
|
||||||
<SubmitButton pendingLabel={<Trans>Checking…</Trans>}>
|
{FEDERATION_INCOMING
|
||||||
<Trans>Trust</Trans>
|
? (
|
||||||
</SubmitButton>
|
<>
|
||||||
<button
|
<SubmitButton pendingLabel={<Trans>Checking…</Trans>}>
|
||||||
type="button"
|
<Trans>Trust</Trans>
|
||||||
className="btn-secondary"
|
</SubmitButton>
|
||||||
disabled={form.formState.isSubmitting}
|
<button
|
||||||
onClick={add("blocked")}
|
type="button"
|
||||||
>
|
className="btn-secondary"
|
||||||
<Trans>Block</Trans>
|
disabled={form.formState.isSubmitting}
|
||||||
</button>
|
onClick={add("blocked")}
|
||||||
|
>
|
||||||
|
<Trans>Block</Trans>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<SubmitButton pendingLabel={<Trans>Checking…</Trans>}>
|
||||||
|
<Trans>Block</Trans>
|
||||||
|
</SubmitButton>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
<p className="form-hint">
|
<p className="form-hint">
|
||||||
<Trans>
|
{!FEDERATION_INCOMING
|
||||||
Users of a trusted instance sign in here with their account there.
|
? <Trans>Users here can't sign in to a blocked instance.</Trans>
|
||||||
Those of a blocked one can't. Users of any other instance need an
|
: FEDERATION_OUTGOING
|
||||||
invite.
|
? (
|
||||||
</Trans>
|
<Trans>
|
||||||
|
Users of a trusted instance sign in here with their account there.
|
||||||
|
Those of a blocked one can't, and users here can't sign in to it.
|
||||||
|
Users of any other instance need an invite.
|
||||||
|
</Trans>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<Trans>
|
||||||
|
Users of a trusted instance sign in here with their account there.
|
||||||
|
Those of a blocked one can't. Users of any other instance need an
|
||||||
|
invite.
|
||||||
|
</Trans>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,20 +16,24 @@ import { PageHeader } from "../components/layout/PageHeader.tsx";
|
|||||||
import { PageGroup } from "../components/layout/PageGroup.tsx";
|
import { PageGroup } from "../components/layout/PageGroup.tsx";
|
||||||
import { CategoryManager } from "../components/category/CategoryManager.tsx";
|
import { CategoryManager } from "../components/category/CategoryManager.tsx";
|
||||||
import { PeerManager } from "../components/federation/PeerManager.tsx";
|
import { PeerManager } from "../components/federation/PeerManager.tsx";
|
||||||
import { FEDERATION_INCOMING } from "../config/instance.ts";
|
import {
|
||||||
|
FEDERATION_INCOMING,
|
||||||
|
FEDERATION_OUTGOING,
|
||||||
|
} from "../config/instance.ts";
|
||||||
import { NotFound } from "./NotFound.tsx";
|
import { NotFound } from "./NotFound.tsx";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the category manager and the instances the site trusts or blocks,
|
* Renders the category manager and the instances the site trusts or blocks,
|
||||||
* each to those who hold its permission, which the API checks again, the
|
* each to those who hold its permission, which the API checks again, the
|
||||||
* instances only while users of other instances may sign in here; without
|
* instances only while federated sign-in goes at least one way; without
|
||||||
* either, the page isn't found.
|
* either, the page isn't found.
|
||||||
*/
|
*/
|
||||||
export function Manage(): React.JSX.Element {
|
export function Manage(): React.JSX.Element {
|
||||||
useDocumentTitle(t`Manage`);
|
useDocumentTitle(t`Manage`);
|
||||||
const { user } = useRequiredAuth();
|
const { user } = useRequiredAuth();
|
||||||
const categories = can(user, "category:manage");
|
const categories = can(user, "category:manage");
|
||||||
const instances = FEDERATION_INCOMING && can(user, "federation:manage");
|
const instances = (FEDERATION_INCOMING || FEDERATION_OUTGOING) &&
|
||||||
|
can(user, "federation:manage");
|
||||||
if (!categories && !instances) return <NotFound />;
|
if (!categories && !instances) return <NotFound />;
|
||||||
return (
|
return (
|
||||||
<PageShell narrow>
|
<PageShell narrow>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, test } from "vitest";
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||||
import { screen, waitFor, within } from "@testing-library/react";
|
import { screen, waitFor, within } from "@testing-library/react";
|
||||||
import { userEvent } from "@testing-library/user-event";
|
import { userEvent } from "@testing-library/user-event";
|
||||||
import { Manage } from "../../pages/Manage.tsx";
|
import { Manage } from "../../pages/Manage.tsx";
|
||||||
@@ -6,6 +6,20 @@ import { calls, fail, route } from "../fetch.ts";
|
|||||||
import { makeCategory, makeSession, wire } from "../fixtures.ts";
|
import { makeCategory, makeSession, wire } from "../fixtures.ts";
|
||||||
import { renderApp } from "../render.tsx";
|
import { renderApp } from "../render.tsx";
|
||||||
|
|
||||||
|
const instance = vi.hoisted(() => ({
|
||||||
|
federationIncoming: true,
|
||||||
|
federationOutgoing: true,
|
||||||
|
}));
|
||||||
|
vi.mock("../../config/instance.ts", async (importOriginal) => ({
|
||||||
|
...await importOriginal<typeof import("../../config/instance.ts")>(),
|
||||||
|
get FEDERATION_INCOMING() {
|
||||||
|
return instance.federationIncoming;
|
||||||
|
},
|
||||||
|
get FEDERATION_OUTGOING() {
|
||||||
|
return instance.federationOutgoing;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const admin = makeSession({ id: "ann", username: "ann", role: "admin" });
|
const admin = makeSession({ id: "ann", username: "ann", role: "admin" });
|
||||||
|
|
||||||
function open(session = admin) {
|
function open(session = admin) {
|
||||||
@@ -16,6 +30,11 @@ function open(session = admin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("Manage", () => {
|
describe("Manage", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
instance.federationIncoming = true;
|
||||||
|
instance.federationOutgoing = true;
|
||||||
|
});
|
||||||
|
|
||||||
test("an admin manages categories", async () => {
|
test("an admin manages categories", async () => {
|
||||||
const music = makeCategory({ id: "c1", slug: "music", name: "Music" });
|
const music = makeCategory({ id: "c1", slug: "music", name: "Music" });
|
||||||
let list = [music];
|
let list = [music];
|
||||||
@@ -128,6 +147,52 @@ describe("Manage", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("where users of other instances don't sign in, an admin only blocks instances", async () => {
|
||||||
|
instance.federationIncoming = false;
|
||||||
|
route("GET /api/categories", () => []);
|
||||||
|
const peer = (name: string, host: string, status: string) => ({
|
||||||
|
issuer: `https://${host}`,
|
||||||
|
host,
|
||||||
|
name,
|
||||||
|
status,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
route("GET /api/federation/peers", () => [
|
||||||
|
peer("Films", "films.example", "trusted"),
|
||||||
|
peer("Spam", "spam.example", "blocked"),
|
||||||
|
]);
|
||||||
|
route(
|
||||||
|
"PUT /api/federation/peers",
|
||||||
|
() => peer("Music", "music.example", "blocked"),
|
||||||
|
);
|
||||||
|
const user = open();
|
||||||
|
expect(await screen.findByText("Spam")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Films")).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Trust" })).not
|
||||||
|
.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Blocked" })).not
|
||||||
|
.toBeInTheDocument();
|
||||||
|
await user.type(
|
||||||
|
screen.getByPlaceholderText("An instance's address, like music.example"),
|
||||||
|
"music.example{Enter}",
|
||||||
|
);
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(calls("PUT /api/federation/peers")[0]?.body).toEqual({
|
||||||
|
instance: "music.example",
|
||||||
|
status: "blocked",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("without federated sign-in either way, there are no instances to manage", async () => {
|
||||||
|
instance.federationIncoming = false;
|
||||||
|
instance.federationOutgoing = false;
|
||||||
|
route("GET /api/categories", () => []);
|
||||||
|
open();
|
||||||
|
expect(await screen.findByText("Categories")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Instances")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test("without category:manage, the page isn't found", async () => {
|
test("without category:manage, the page isn't found", async () => {
|
||||||
open(makeSession({ id: "mod", role: "moderator" }));
|
open(makeSession({ id: "mod", role: "moderator" }));
|
||||||
expect(await screen.findByText("This page does not exist."))
|
expect(await screen.findByText("This page does not exist."))
|
||||||
|
|||||||
Reference in New Issue
Block a user