summaryrefslogtreecommitdiff
path: root/web/source/settings/admin/federation.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/source/settings/admin/federation.js')
-rw-r--r--web/source/settings/admin/federation.js394
1 files changed, 0 insertions, 394 deletions
diff --git a/web/source/settings/admin/federation.js b/web/source/settings/admin/federation.js
deleted file mode 100644
index b7658f444..000000000
--- a/web/source/settings/admin/federation.js
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-"use strict";
-
-const Promise = require("bluebird");
-const React = require("react");
-const Redux = require("react-redux");
-const {Switch, Route, Link, Redirect, useRoute, useLocation} = require("wouter");
-const fileDownload = require("js-file-download");
-
-const { formFields } = require("../components/form-fields");
-
-const api = require("../lib/api");
-const adminActions = require("../redux/reducers/admin").actions;
-const submit = require("../lib/submit");
-const BackButton = require("../components/back-button");
-const Loading = require("../components/loading");
-const { matchSorter } = require("match-sorter");
-
-const base = "/settings/admin/federation";
-
-// const {
-// TextInput,
-// TextArea,
-// File
-// } = require("../components/form-fields").formFields(adminActions.setAdminSettingsVal, (state) => state.instances.adminSettings);
-
-module.exports = function AdminSettings() {
- const dispatch = Redux.useDispatch();
- // const instance = Redux.useSelector(state => state.instances.adminSettings);
- const loadedBlockedInstances = Redux.useSelector(state => state.admin.loadedBlockedInstances);
-
- React.useEffect(() => {
- if (!loadedBlockedInstances ) {
- Promise.try(() => {
- return dispatch(api.admin.fetchDomainBlocks());
- });
- }
- }, [dispatch, loadedBlockedInstances]);
-
- if (!loadedBlockedInstances) {
- return (
- <div>
- <h1>Federation</h1>
- <div>
- <Loading/>
- </div>
- </div>
- );
- }
-
- return (
- <Switch>
- <Route path={`${base}/:domain`}>
- <InstancePageWrapped />
- </Route>
- <InstanceOverview />
- </Switch>
- );
-};
-
-function InstanceOverview() {
- const [filter, setFilter] = React.useState("");
- const blockedInstances = Redux.useSelector(state => state.admin.blockedInstances);
- const [_location, setLocation] = useLocation();
-
- const filteredInstances = React.useMemo(() => {
- return matchSorter(Object.values(blockedInstances), filter, {keys: ["domain"]});
- }, [blockedInstances, filter]);
-
- function filterFormSubmit(e) {
- e.preventDefault();
- setLocation(`${base}/${filter}`);
- }
-
- return (
- <>
- <h1>Federation</h1>
- Here you can see an overview of blocked instances.
-
- <div className="instance-list">
- <h2>Blocked instances</h2>
- <form action={`${base}/view`} className="filter" role="search" onSubmit={filterFormSubmit}>
- <input name="domain" value={filter} onChange={(e) => setFilter(e.target.value)}/>
- <Link to={`${base}/${filter}`}><a className="button">Add block</a></Link>
- </form>
- <div className="list">
- {filteredInstances.map((entry) => {
- return (
- <Link key={entry.domain} to={`${base}/${entry.domain}`}>
- <a className="entry nounderline">
- <span id="domain">
- {entry.domain}
- </span>
- <span id="date">
- {new Date(entry.created_at).toLocaleString()}
- </span>
- </a>
- </Link>
- );
- })}
- </div>
- </div>
-
- <BulkBlocking/>
- </>
- );
-}
-
-const Bulk = formFields(adminActions.updateBulkBlockVal, (state) => state.admin.bulkBlock);
-function BulkBlocking() {
- const dispatch = Redux.useDispatch();
- const {bulkBlock, blockedInstances} = Redux.useSelector(state => state.admin);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- function importBlocks() {
- setStatus("Processing");
- setError("");
- return Promise.try(() => {
- return dispatch(api.admin.bulkDomainBlock());
- }).then(({success, invalidDomains}) => {
- return Promise.try(() => {
- return resetBulk();
- }).then(() => {
- dispatch(adminActions.updateBulkBlockVal(["list", invalidDomains.join("\n")]));
-
- let stat = "";
- if (success == 0) {
- return setError("No valid domains in import");
- } else if (success == 1) {
- stat = "Imported 1 domain";
- } else {
- stat = `Imported ${success} domains`;
- }
-
- if (invalidDomains.length > 0) {
- if (invalidDomains.length == 1) {
- stat += ", input contained 1 invalid domain.";
- } else {
- stat += `, input contained ${invalidDomains.length} invalid domains.`;
- }
- } else {
- stat += "!";
- }
-
- setStatus(stat);
- });
- }).catch((e) => {
- console.error(e);
- setError(e.message);
- setStatus("");
- });
- }
-
- function exportBlocks() {
- return Promise.try(() => {
- setStatus("Exporting");
- setError("");
- let asJSON = bulkBlock.exportType.startsWith("json");
- let _asCSV = bulkBlock.exportType.startsWith("csv");
-
- let exportList = Object.values(blockedInstances).map((entry) => {
- if (asJSON) {
- return {
- domain: entry.domain,
- public_comment: entry.public_comment
- };
- } else {
- return entry.domain;
- }
- });
-
- if (bulkBlock.exportType == "json") {
- return dispatch(adminActions.updateBulkBlockVal(["list", JSON.stringify(exportList)]));
- } else if (bulkBlock.exportType == "json-download") {
- return fileDownload(JSON.stringify(exportList), "block-export.json");
- } else if (bulkBlock.exportType == "plain") {
- return dispatch(adminActions.updateBulkBlockVal(["list", exportList.join("\n")]));
- }
- }).then(() => {
- setStatus("Exported!");
- }).catch((e) => {
- setError(e.message);
- setStatus("");
- });
- }
-
- function resetBulk(e) {
- if (e != undefined) {
- e.preventDefault();
- }
- return dispatch(adminActions.resetBulkBlockVal());
- }
-
- function disableInfoFields(props={}) {
- if (bulkBlock.list[0] == "[") {
- return {
- ...props,
- disabled: true,
- placeHolder: "Domain list is a JSON import, input disabled"
- };
- } else {
- return props;
- }
- }
-
- return (
- <div className="bulk">
- <h2>Import / Export <a onClick={resetBulk}>reset</a></h2>
- <Bulk.TextArea
- id="list"
- name="Domains, one per line"
- placeHolder={`google.com\nfacebook.com`}
- />
-
- <Bulk.TextArea
- id="public_comment"
- name="Public comment"
- inputProps={disableInfoFields({rows: 3})}
- />
-
- <Bulk.TextArea
- id="private_comment"
- name="Private comment"
- inputProps={disableInfoFields({rows: 3})}
- />
-
- <Bulk.Checkbox
- id="obfuscate"
- name="Obfuscate domains? "
- inputProps={disableInfoFields()}
- />
-
- <div className="hidden">
- <Bulk.File
- id="json"
- fileType="application/json"
- withPreview={false}
- />
- </div>
-
- <div className="messagebutton">
- <div>
- <button type="submit" onClick={importBlocks}>Import</button>
- </div>
-
- <div>
- <button type="submit" onClick={exportBlocks}>Export</button>
-
- <Bulk.Select id="exportType" name="Export type" options={
- <>
- <option value="plain">One per line in text field</option>
- <option value="json">JSON in text field</option>
- <option value="json-download">JSON file download</option>
- <option disabled value="csv">CSV in text field (glitch-soc)</option>
- <option disabled value="csv-download">CSV file download (glitch-soc)</option>
- </>
- }/>
- </div>
- <br/>
- <div>
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- {statusMsg.length > 0 &&
- <div className="accent">{statusMsg}</div>
- }
- </div>
- </div>
- </div>
- );
-}
-
-function InstancePageWrapped() {
- /* We wrap the component to generate formFields with a setter depending on the domain
- if formFields() is used inside the same component that is re-rendered with their state,
- inputs get re-created on every change, causing them to lose focus, and bad performance
- */
- let [_match, {domain}] = useRoute(`${base}/:domain`);
-
- if (domain == "view") { // from form field submission
- let realDomain = (new URL(document.location)).searchParams.get("domain");
- if (realDomain == undefined) {
- return <Redirect to={base}/>;
- } else {
- domain = realDomain;
- }
- }
-
- function alterDomain([key, val]) {
- return adminActions.updateDomainBlockVal([domain, key, val]);
- }
-
- const fields = formFields(alterDomain, (state) => state.admin.newInstanceBlocks[domain]);
-
- return <InstancePage domain={domain} Form={fields} />;
-}
-
-function InstancePage({domain, Form}) {
- const dispatch = Redux.useDispatch();
- const entry = Redux.useSelector(state => state.admin.newInstanceBlocks[domain]);
- const [_location, setLocation] = useLocation();
-
- React.useEffect(() => {
- if (entry == undefined) {
- dispatch(api.admin.getEditableDomainBlock(domain));
- }
- }, [dispatch, domain, entry]);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- if (entry == undefined) {
- return <Loading/>;
- }
-
- const updateBlock = submit(
- () => dispatch(api.admin.updateDomainBlock(domain)),
- {setStatus, setError}
- );
-
- const removeBlock = submit(
- () => dispatch(api.admin.removeDomainBlock(domain)),
- {setStatus, setError, startStatus: "Removing", successStatus: "Removed!", onSuccess: () => {
- setLocation(base);
- }}
- );
-
- return (
- <div>
- <h1><BackButton to={base}/> Federation settings for: {domain}</h1>
- {entry.new
- ? "No stored block yet, you can add one below:"
- : <b className="error">Editing domain blocks is not implemented yet, <a href="https://github.com/superseriousbusiness/gotosocial/issues/1198" target="_blank" rel="noopener noreferrer">check here for progress</a>.</b>
- }
-
- <Form.TextArea
- id="public_comment"
- name="Public comment"
- inputProps={{
- disabled: !entry.new
- }}
- />
-
- <Form.TextArea
- id="private_comment"
- name="Private comment"
- inputProps={{
- disabled: !entry.new
- }}
- />
-
- <Form.Checkbox
- id="obfuscate"
- name="Obfuscate domain? "
- inputProps={{
- disabled: !entry.new
- }}
- />
-
- <div className="messagebutton">
- {entry.new
- ? <button type="submit" onClick={updateBlock}>{entry.new ? "Add block" : "Save block"}</button>
- : <button className="danger" onClick={removeBlock}>Remove block</button>
- }
-
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- {statusMsg.length > 0 &&
- <div className="accent">{statusMsg}</div>
- }
- </div>
- </div>
- );
-} \ No newline at end of file