diff options
Diffstat (limited to 'web/source/settings-panel/admin')
-rw-r--r-- | web/source/settings-panel/admin/actions.js | 61 | ||||
-rw-r--r-- | web/source/settings-panel/admin/emoji.js | 212 | ||||
-rw-r--r-- | web/source/settings-panel/admin/federation.js | 382 | ||||
-rw-r--r-- | web/source/settings-panel/admin/settings.js | 110 |
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'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">< 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 |