summaryrefslogtreecommitdiff
path: root/web/source/settings-panel/admin
diff options
context:
space:
mode:
Diffstat (limited to 'web/source/settings-panel/admin')
-rw-r--r--web/source/settings-panel/admin/actions.js61
-rw-r--r--web/source/settings-panel/admin/emoji.js212
-rw-r--r--web/source/settings-panel/admin/federation.js382
-rw-r--r--web/source/settings-panel/admin/settings.js110
4 files changed, 0 insertions, 765 deletions
diff --git a/web/source/settings-panel/admin/actions.js b/web/source/settings-panel/admin/actions.js
deleted file mode 100644
index d4980d021..000000000
--- a/web/source/settings-panel/admin/actions.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Submit = require("../components/submit");
-
-const api = require("../lib/api");
-const submit = require("../lib/submit");
-
-module.exports = function AdminActionPanel() {
- const dispatch = Redux.useDispatch();
-
- const [days, setDays] = React.useState(30);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const removeMedia = submit(
- () => dispatch(api.admin.mediaCleanup(days)),
- {setStatus, setError}
- );
-
- return (
- <>
- <h1>Admin Actions</h1>
- <div>
- <h2>Media cleanup</h2>
- <p>
- Clean up remote media older than the specified number of days.
- If the remote instance is still online they will be refetched when needed.
- Also cleans up unused headers and avatars from the media cache.
- </p>
- <div>
- <label htmlFor="days">Days: </label>
- <input id="days" type="number" value={days} onChange={(e) => setDays(e.target.value)}/>
- </div>
- <Submit onClick={removeMedia} label="Remove media" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- </>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/admin/emoji.js b/web/source/settings-panel/admin/emoji.js
deleted file mode 100644
index 1ef4a54a3..000000000
--- a/web/source/settings-panel/admin/emoji.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Submit = require("../components/submit");
-const FakeToot = require("../components/fake-toot");
-const { formFields } = require("../components/form-fields");
-
-const api = require("../lib/api");
-const adminActions = require("../redux/reducers/admin").actions;
-const submit = require("../lib/submit");
-
-const base = "/settings/admin/custom-emoji";
-
-module.exports = function CustomEmoji() {
- return (
- <Switch>
- <Route path={`${base}/:emojiId`}>
- <EmojiDetailWrapped />
- </Route>
- <EmojiOverview />
- </Switch>
- );
-};
-
-function EmojiOverview() {
- const dispatch = Redux.useDispatch();
- const [loaded, setLoaded] = React.useState(false);
-
- const [errorMsg, setError] = React.useState("");
-
- React.useEffect(() => {
- if (!loaded) {
- Promise.try(() => {
- return dispatch(api.admin.fetchCustomEmoji());
- }).then(() => {
- setLoaded(true);
- }).catch((e) => {
- setLoaded(true);
- setError(e.message);
- });
- }
- }, []);
-
- if (!loaded) {
- return (
- <>
- <h1>Custom Emoji</h1>
- Loading...
- </>
- );
- }
-
- return (
- <>
- <h1>Custom Emoji</h1>
- <EmojiList/>
- <NewEmoji/>
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- </>
- );
-}
-
-const NewEmojiForm = formFields(adminActions.updateNewEmojiVal, (state) => state.admin.newEmoji);
-function NewEmoji() {
- const dispatch = Redux.useDispatch();
- const newEmojiForm = Redux.useSelector((state) => state.admin.newEmoji);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const uploadEmoji = submit(
- () => dispatch(api.admin.newEmoji()),
- {
- setStatus, setError,
- onSuccess: function() {
- URL.revokeObjectURL(newEmojiForm.image);
- return Promise.all([
- dispatch(adminActions.updateNewEmojiVal(["image", undefined])),
- dispatch(adminActions.updateNewEmojiVal(["imageFile", undefined])),
- dispatch(adminActions.updateNewEmojiVal(["shortcode", ""])),
- ]);
- }
- }
- );
-
- React.useEffect(() => {
- if (newEmojiForm.shortcode.length == 0) {
- if (newEmojiForm.imageFile != undefined) {
- let [name, ext] = newEmojiForm.imageFile.name.split(".");
- dispatch(adminActions.updateNewEmojiVal(["shortcode", name]));
- }
- }
- });
-
- let emojiOrShortcode = `:${newEmojiForm.shortcode}:`;
-
- if (newEmojiForm.image != undefined) {
- emojiOrShortcode = <img
- className="emoji"
- src={newEmojiForm.image}
- title={`:${newEmojiForm.shortcode}:`}
- alt={newEmojiForm.shortcode}
- />;
- }
-
- return (
- <div>
- <h2>Add new custom emoji</h2>
-
- <FakeToot>
- Look at this new custom emoji {emojiOrShortcode} isn&apos;t it cool?
- </FakeToot>
-
- <NewEmojiForm.File
- id="image"
- name="Image"
- fileType="image/png,image/gif"
- showSize={true}
- maxSize={50 * 1000}
- />
-
- <NewEmojiForm.TextInput
- id="shortcode"
- name="Shortcode (without : :), must be unique on the instance"
- placeHolder="blobcat"
- />
-
- <Submit onClick={uploadEmoji} label="Upload" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- );
-}
-
-function EmojiList() {
- const emoji = Redux.useSelector((state) => state.admin.emoji);
-
- return (
- <div>
- <h2>Overview</h2>
- <div className="list emoji-list">
- {Object.entries(emoji).map(([category, entries]) => {
- return <EmojiCategory key={category} category={category} entries={entries}/>;
- })}
- </div>
- </div>
- );
-}
-
-function EmojiCategory({category, entries}) {
- return (
- <div className="entry">
- <b>{category}</b>
- <div className="emoji-group">
- {entries.map((e) => {
- return (
- // <Link key={e.static_url} to={`${base}/${e.shortcode}`}>
- <Link key={e.static_url} to={`${base}`}>
- <a>
- <img src={e.static_url} alt={e.shortcode} title={`:${e.shortcode}:`}/>
- </a>
- </Link>
- );
- })}
- </div>
- </div>
- );
-}
-
-function EmojiDetailWrapped() {
- /* 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, {emojiId}] = useRoute(`${base}/:emojiId`);
-
- function alterEmoji([key, val]) {
- return adminActions.updateDomainBlockVal([emojiId, key, val]);
- }
-
- const fields = formFields(alterEmoji, (state) => state.admin.blockedInstances[emojiId]);
-
- return <EmojiDetail id={emojiId} Form={fields} />;
-}
-
-function EmojiDetail({id, Form}) {
- return (
- "Not implemented yet"
- );
-} \ No newline at end of file
diff --git a/web/source/settings-panel/admin/federation.js b/web/source/settings-panel/admin/federation.js
deleted file mode 100644
index 7afc3c699..000000000
--- a/web/source/settings-panel/admin/federation.js
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 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());
- });
- }
- }, []);
-
- if (!loadedBlockedInstances) {
- return (
- <div>
- <h1>Federation</h1>
- Loading...
- </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();
-
- 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">
- {Object.values(blockedInstances).filter((a) => a.domain.startsWith(filter)).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 BackButton() {
- return (
- <Link to={base}>
- <a className="button">&lt; back</a>
- </Link>
- );
-}
-
-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));
- }
- }, []);
-
- 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/> Federation settings for: {domain}</h1>
- {entry.new && "No stored block yet, you can add one below:"}
-
- <Form.TextArea
- id="public_comment"
- name="Public comment"
- />
-
- <Form.TextArea
- id="private_comment"
- name="Private comment"
- />
-
- <Form.Checkbox
- id="obfuscate"
- name="Obfuscate domain? "
- />
-
- <div className="messagebutton">
- <button type="submit" onClick={updateBlock}>{entry.new ? "Add block" : "Save block"}</button>
-
- {!entry.new &&
- <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
diff --git a/web/source/settings-panel/admin/settings.js b/web/source/settings-panel/admin/settings.js
deleted file mode 100644
index 845a1f924..000000000
--- a/web/source/settings-panel/admin/settings.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Submit = require("../components/submit");
-
-const api = require("../lib/api");
-const submit = require("../lib/submit");
-
-const adminActions = require("../redux/reducers/instances").actions;
-
-const {
- TextInput,
- TextArea,
- File
-} = require("../components/form-fields").formFields(adminActions.setAdminSettingsVal, (state) => state.instances.adminSettings);
-
-module.exports = function AdminSettings() {
- const dispatch = Redux.useDispatch();
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const updateSettings = submit(
- () => dispatch(api.admin.updateInstance()),
- {setStatus, setError}
- );
-
- return (
- <div>
- <h1>Instance Settings</h1>
- <TextInput
- id="title"
- name="Title"
- placeHolder="My GoToSocial instance"
- />
-
- <TextArea
- id="short_description"
- name="Short description"
- placeHolder="A small testing instance for the GoToSocial alpha."
- />
- <TextArea
- id="description"
- name="Full description"
- placeHolder="A small testing instance for the GoToSocial alpha."
- />
-
- <TextInput
- id="contact_account.username"
- name="Contact user (local account username)"
- placeHolder="admin"
- />
- <TextInput
- id="email"
- name="Contact email"
- placeHolder="admin@example.com"
- />
-
- <TextArea
- id="terms"
- name="Terms & Conditions"
- placeHolder=""
- />
-
- {/* <div className="file-upload">
- <h3>Instance avatar</h3>
- <div>
- <img className="preview avatar" src={instance.avatar} alt={instance.avatar ? `Avatar image for the instance` : "No instance avatar image set"} />
- <File
- id="avatar"
- fileType="image/*"
- />
- </div>
- </div>
-
- <div className="file-upload">
- <h3>Instance header</h3>
- <div>
- <img className="preview header" src={instance.header} alt={instance.header ? `Header image for the instance` : "No instance header image set"} />
- <File
- id="header"
- fileType="image/*"
- />
- </div>
- </div> */}
- <Submit onClick={updateSettings} label="Save" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- );
-}; \ No newline at end of file