mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 18:32:25 -06:00 
			
		
		
		
	[feature] Add HTTP header permission section to frontend (#2893)
* [feature] Add HTTP header filter section to frontend * tweak naming a bit
This commit is contained in:
		
					parent
					
						
							
								35b1c54bde
							
						
					
				
			
			
				commit
				
					
						6171dcbe51
					
				
			
		
					 27 changed files with 986 additions and 68 deletions
				
			
		| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
/*
 | 
			
		||||
	GoToSocial
 | 
			
		||||
	Copyright (C) GoToSocial Authors admin@gotosocial.org
 | 
			
		||||
	SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
	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/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { usePostHeaderAllowMutation, usePostHeaderBlockMutation } from "../../../lib/query/admin/http-header-permissions";
 | 
			
		||||
import { useTextInput } from "../../../lib/form";
 | 
			
		||||
import useFormSubmit from "../../../lib/form/submit";
 | 
			
		||||
import { TextInput } from "../../../components/form/inputs";
 | 
			
		||||
import MutationButton from "../../../components/form/mutation-button";
 | 
			
		||||
import { PermType } from "../../../lib/types/perm";
 | 
			
		||||
 | 
			
		||||
export default function HeaderPermCreateForm({ permType }: { permType: PermType }) {
 | 
			
		||||
	const form = {
 | 
			
		||||
		header: useTextInput("header", {
 | 
			
		||||
			validator: (val: string) => {
 | 
			
		||||
				// Technically invalid but avoid
 | 
			
		||||
				// showing red outline when user
 | 
			
		||||
				// hasn't entered anything yet.
 | 
			
		||||
				if (val.length === 0) {
 | 
			
		||||
					return "";
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Only requirement is that header
 | 
			
		||||
				// must be less than 1024 chars.
 | 
			
		||||
				if (val.length > 1024) {
 | 
			
		||||
					return "header must be less than 1024 characters";
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return "";
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
		regex: useTextInput("regex", {
 | 
			
		||||
			validator: (val: string) => {
 | 
			
		||||
				// Technically invalid but avoid
 | 
			
		||||
				// showing red outline when user
 | 
			
		||||
				// hasn't entered anything yet.
 | 
			
		||||
				if (val.length === 0) {
 | 
			
		||||
					return "";
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Ensure regex compiles.
 | 
			
		||||
				try {
 | 
			
		||||
					new RegExp(val);
 | 
			
		||||
				} catch (e) {
 | 
			
		||||
					return e;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return "";
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Use appropriate mutation for given permType.
 | 
			
		||||
	const [ postAllowTrigger, postAllowResult ] = usePostHeaderAllowMutation();
 | 
			
		||||
	const [ postBlockTrigger, postBlockResult ] = usePostHeaderBlockMutation();
 | 
			
		||||
 | 
			
		||||
	let mutationTrigger;
 | 
			
		||||
	let mutationResult;
 | 
			
		||||
 | 
			
		||||
	if (permType === "block") {
 | 
			
		||||
		mutationTrigger = postBlockTrigger;
 | 
			
		||||
		mutationResult = postBlockResult;
 | 
			
		||||
	} else {
 | 
			
		||||
		mutationTrigger = postAllowTrigger;
 | 
			
		||||
		mutationResult = postAllowResult;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const [formSubmit, result] = useFormSubmit(
 | 
			
		||||
		form,
 | 
			
		||||
		[mutationTrigger, mutationResult],
 | 
			
		||||
		{
 | 
			
		||||
			changedOnly: false,
 | 
			
		||||
			onFinish: ({ _data }) => {
 | 
			
		||||
				form.header.reset();
 | 
			
		||||
				form.regex.reset();
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<form onSubmit={formSubmit}>
 | 
			
		||||
			<h2>Create new HTTP header {permType}</h2>
 | 
			
		||||
			<TextInput
 | 
			
		||||
				field={form.header}
 | 
			
		||||
				label={
 | 
			
		||||
					<>
 | 
			
		||||
						HTTP Header Name 
 | 
			
		||||
						 <a
 | 
			
		||||
							href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers"
 | 
			
		||||
							target="_blank"
 | 
			
		||||
							className="docslink"
 | 
			
		||||
							rel="noreferrer"
 | 
			
		||||
						>
 | 
			
		||||
							Learn more about HTTP request headers (opens in a new tab)
 | 
			
		||||
						</a>
 | 
			
		||||
					</>
 | 
			
		||||
				}
 | 
			
		||||
				placeholder={"User-Agent"}
 | 
			
		||||
			/>
 | 
			
		||||
			<TextInput
 | 
			
		||||
				field={form.regex}
 | 
			
		||||
				label={
 | 
			
		||||
					<>
 | 
			
		||||
						HTTP Header Value Regex 
 | 
			
		||||
						<a
 | 
			
		||||
							href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions"
 | 
			
		||||
							target="_blank"
 | 
			
		||||
							className="docslink"
 | 
			
		||||
							rel="noreferrer"
 | 
			
		||||
						>
 | 
			
		||||
							Learn more about regular expressions (opens in a new tab)
 | 
			
		||||
						</a>
 | 
			
		||||
					</>
 | 
			
		||||
				}
 | 
			
		||||
				placeholder={"^.*Some-User-Agent.*$"}
 | 
			
		||||
				{...{className: "monospace"}}
 | 
			
		||||
			/>
 | 
			
		||||
			<MutationButton
 | 
			
		||||
				label="Save"
 | 
			
		||||
				result={result}
 | 
			
		||||
				disabled={
 | 
			
		||||
					(!form.header.value || !form.regex.value) ||
 | 
			
		||||
					(!form.header.valid || !form.regex.valid)
 | 
			
		||||
				}
 | 
			
		||||
			/>
 | 
			
		||||
		</form>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,246 @@
 | 
			
		|||
/*
 | 
			
		||||
	GoToSocial
 | 
			
		||||
	Copyright (C) GoToSocial Authors admin@gotosocial.org
 | 
			
		||||
	SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
	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/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import React, { useEffect, useMemo } from "react";
 | 
			
		||||
import { useLocation, useParams } from "wouter";
 | 
			
		||||
import { PermType } from "../../../lib/types/perm";
 | 
			
		||||
import { useDeleteHeaderAllowMutation, useDeleteHeaderBlockMutation, useGetHeaderAllowQuery, useGetHeaderBlockQuery } from "../../../lib/query/admin/http-header-permissions";
 | 
			
		||||
import { HeaderPermission } from "../../../lib/types/http-header-permissions";
 | 
			
		||||
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
 | 
			
		||||
import { SerializedError } from "@reduxjs/toolkit";
 | 
			
		||||
import Loading from "../../../components/loading";
 | 
			
		||||
import { Error } from "../../../components/error";
 | 
			
		||||
import { useLazyGetAccountQuery } from "../../../lib/query/admin";
 | 
			
		||||
import Username from "../../../components/username";
 | 
			
		||||
import { useBaseUrl } from "../../../lib/navigation/util";
 | 
			
		||||
import BackButton from "../../../components/back-button";
 | 
			
		||||
import MutationButton from "../../../components/form/mutation-button";
 | 
			
		||||
 | 
			
		||||
const testString = `/* To test this properly, set "flavor" to "Golang", as that's the language GoToSocial uses for regular expressions */
 | 
			
		||||
 | 
			
		||||
/* Amazon crawler User-Agent example */
 | 
			
		||||
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML\\, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)
 | 
			
		||||
 | 
			
		||||
/* Some other test strings */
 | 
			
		||||
Some Test Value
 | 
			
		||||
Another Test Value`;
 | 
			
		||||
 | 
			
		||||
export default function HeaderPermDetail() {
 | 
			
		||||
	let params = useParams();
 | 
			
		||||
	if (params.permType !== "blocks" && params.permType !== "allows") {
 | 
			
		||||
		throw "unrecognized perm type " + params.permType;
 | 
			
		||||
	}
 | 
			
		||||
	const permType = useMemo(() => {
 | 
			
		||||
		return params.permType?.slice(0, -1) as PermType;
 | 
			
		||||
	}, [params]);
 | 
			
		||||
 | 
			
		||||
	let permID = params.permId as string | undefined;
 | 
			
		||||
	if (!permID) {
 | 
			
		||||
		throw "no perm ID";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (permType === "block") {
 | 
			
		||||
		return <BlockDetail id={permID} />;
 | 
			
		||||
	} else {
 | 
			
		||||
		return <AllowDetail id={permID} />;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function BlockDetail({ id }: { id: string }) {
 | 
			
		||||
	return (
 | 
			
		||||
		<PermDeets
 | 
			
		||||
			permType={"Block"}
 | 
			
		||||
			{...useGetHeaderBlockQuery(id)}
 | 
			
		||||
		/>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AllowDetail({ id }: { id: string }) {
 | 
			
		||||
	return (
 | 
			
		||||
		<PermDeets
 | 
			
		||||
			permType={"Allow"}
 | 
			
		||||
			{...useGetHeaderAllowQuery(id)}
 | 
			
		||||
		/>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface PermDeetsProps {
 | 
			
		||||
	permType: string;
 | 
			
		||||
	data?: HeaderPermission;
 | 
			
		||||
	isLoading: boolean;
 | 
			
		||||
	isFetching: boolean;
 | 
			
		||||
	isError: boolean;
 | 
			
		||||
	error?: FetchBaseQueryError | SerializedError;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function PermDeets({
 | 
			
		||||
	permType,
 | 
			
		||||
	data: perm,
 | 
			
		||||
	isLoading: isLoadingPerm,
 | 
			
		||||
	isFetching: isFetchingPerm,
 | 
			
		||||
	isError: isErrorPerm,
 | 
			
		||||
	error: errorPerm,
 | 
			
		||||
}: PermDeetsProps) {
 | 
			
		||||
	const [ location ] = useLocation();
 | 
			
		||||
	const baseUrl = useBaseUrl();
 | 
			
		||||
	
 | 
			
		||||
	// Once we've loaded the perm, trigger
 | 
			
		||||
	// getting the account that created it.
 | 
			
		||||
	const [ getAccount, getAccountRes ] = useLazyGetAccountQuery();
 | 
			
		||||
	useEffect(() => {
 | 
			
		||||
		if (!perm) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		getAccount(perm.created_by, true);
 | 
			
		||||
	}, [getAccount, perm]);
 | 
			
		||||
 | 
			
		||||
	// Load the createdByAccount if possible,
 | 
			
		||||
	// returning a username lozenge with
 | 
			
		||||
	// a link to the account.
 | 
			
		||||
	const createdByAccount = useMemo(() => {
 | 
			
		||||
		const {
 | 
			
		||||
			data: account,
 | 
			
		||||
			isLoading: isLoadingAccount,
 | 
			
		||||
			isFetching: isFetchingAccount,
 | 
			
		||||
			isError: isErrorAccount,
 | 
			
		||||
		} = getAccountRes;
 | 
			
		||||
		
 | 
			
		||||
		// Wait for query to finish, returning
 | 
			
		||||
		// loading spinner in the meantime.
 | 
			
		||||
		if (isLoadingAccount || isFetchingAccount || !perm) {
 | 
			
		||||
			return <Loading />;
 | 
			
		||||
		} else if (isErrorAccount || account === undefined) {
 | 
			
		||||
			// Fall back to account ID.
 | 
			
		||||
			return perm?.created_by;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return (
 | 
			
		||||
			<Username
 | 
			
		||||
				account={account}
 | 
			
		||||
				linkTo={`~/settings/moderation/accounts/${account.id}`}
 | 
			
		||||
				backLocation={`~${baseUrl}${location}`}
 | 
			
		||||
			/>
 | 
			
		||||
		);
 | 
			
		||||
	}, [getAccountRes, perm, baseUrl, location]);
 | 
			
		||||
 | 
			
		||||
	// Now wait til the perm itself is loaded.
 | 
			
		||||
	if (isLoadingPerm || isFetchingPerm) {
 | 
			
		||||
		return <Loading />;
 | 
			
		||||
	} else if (isErrorPerm) {
 | 
			
		||||
		return <Error error={errorPerm} />;
 | 
			
		||||
	} else if (perm === undefined) {
 | 
			
		||||
		throw "perm undefined";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const created = new Date(perm.created_at).toDateString();	
 | 
			
		||||
	
 | 
			
		||||
	// Create parameters to link to regex101
 | 
			
		||||
	// with this regular expression prepopulated.
 | 
			
		||||
	const testParams = new URLSearchParams();
 | 
			
		||||
	testParams.set("regex", perm.regex);
 | 
			
		||||
	testParams.set("flags", "g");
 | 
			
		||||
	testParams.set("testString", testString);
 | 
			
		||||
	const regexLink = `https://regex101.com/?${testParams.toString()}`;	
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<div className="http-header-permission-details">
 | 
			
		||||
			<h1><BackButton to={`~${baseUrl}/${permType.toLowerCase()}s`} /> HTTP Header {permType} Detail</h1>
 | 
			
		||||
			<dl className="info-list">
 | 
			
		||||
				<div className="info-list-entry">
 | 
			
		||||
					<dt>ID</dt>
 | 
			
		||||
					<dd>{perm.id}</dd>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div className="info-list-entry">
 | 
			
		||||
					<dt>Created</dt>
 | 
			
		||||
					<dd><time dateTime={perm.created_at}>{created}</time></dd>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div className="info-list-entry">
 | 
			
		||||
					<dt>Created By</dt>
 | 
			
		||||
					<dd>{createdByAccount}</dd>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div className="info-list-entry">
 | 
			
		||||
					<dt>Header Name</dt>
 | 
			
		||||
					<dd>{perm.header}</dd>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div className="info-list-entry">
 | 
			
		||||
					<dt>Header Value Regex</dt>
 | 
			
		||||
					<dd className="monospace">{perm.regex}</dd>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div className="info-list-entry">
 | 
			
		||||
					<dt>Test This Regex</dt>
 | 
			
		||||
					<dd>
 | 
			
		||||
						<a
 | 
			
		||||
							href={regexLink}
 | 
			
		||||
							target="_blank"
 | 
			
		||||
							rel="noreferrer"
 | 
			
		||||
						>
 | 
			
		||||
							<i className="fa fa-fw fa-external-link" aria-hidden="true"></i> Link to Regex101 (opens in a new tab)
 | 
			
		||||
						</a>
 | 
			
		||||
					</dd>
 | 
			
		||||
				</div>
 | 
			
		||||
			</dl>
 | 
			
		||||
			{ permType === "Block"
 | 
			
		||||
				? <DeleteBlock id={perm.id} />
 | 
			
		||||
				: <DeleteAllow id={perm.id} />
 | 
			
		||||
			}
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function DeleteBlock({ id }: { id: string }) {
 | 
			
		||||
	const [ _location, setLocation ] = useLocation();
 | 
			
		||||
	const baseUrl = useBaseUrl();
 | 
			
		||||
	const [ removeTrigger, removeResult ] = useDeleteHeaderBlockMutation();
 | 
			
		||||
	
 | 
			
		||||
	return (
 | 
			
		||||
		<MutationButton
 | 
			
		||||
			type="button"
 | 
			
		||||
			onClick={() => {
 | 
			
		||||
				removeTrigger(id);
 | 
			
		||||
				setLocation(`~${baseUrl}/blocks`);
 | 
			
		||||
			}}
 | 
			
		||||
			label="Remove this block"
 | 
			
		||||
			result={removeResult}
 | 
			
		||||
			className="button danger"
 | 
			
		||||
			showError={false}
 | 
			
		||||
			disabled={false}
 | 
			
		||||
		/>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function DeleteAllow({ id }: { id: string }) {
 | 
			
		||||
	const [ _location, setLocation ] = useLocation();
 | 
			
		||||
	const baseUrl = useBaseUrl();
 | 
			
		||||
	const [ removeTrigger, removeResult ] = useDeleteHeaderAllowMutation();
 | 
			
		||||
	
 | 
			
		||||
	return (
 | 
			
		||||
		<MutationButton
 | 
			
		||||
			type="button"
 | 
			
		||||
			onClick={() => {
 | 
			
		||||
				removeTrigger(id);
 | 
			
		||||
				setLocation(`~${baseUrl}/allows`);
 | 
			
		||||
			}}
 | 
			
		||||
			label="Remove this allow"
 | 
			
		||||
			result={removeResult}
 | 
			
		||||
			className="button danger"
 | 
			
		||||
			showError={false}
 | 
			
		||||
			disabled={false}
 | 
			
		||||
		/>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,169 @@
 | 
			
		|||
/*
 | 
			
		||||
	GoToSocial
 | 
			
		||||
	Copyright (C) GoToSocial Authors admin@gotosocial.org
 | 
			
		||||
	SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
	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/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import React, { useMemo } from "react";
 | 
			
		||||
import { useGetHeaderAllowsQuery, useGetHeaderBlocksQuery } from "../../../lib/query/admin/http-header-permissions";
 | 
			
		||||
import { NoArg } from "../../../lib/types/query";
 | 
			
		||||
import { PageableList } from "../../../components/pageable-list";
 | 
			
		||||
import { HeaderPermission } from "../../../lib/types/http-header-permissions";
 | 
			
		||||
import { useLocation, useParams } from "wouter";
 | 
			
		||||
import { PermType } from "../../../lib/types/perm";
 | 
			
		||||
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
 | 
			
		||||
import { SerializedError } from "@reduxjs/toolkit";
 | 
			
		||||
import HeaderPermCreateForm from "./create";
 | 
			
		||||
 | 
			
		||||
export default function HeaderPermsOverview() {
 | 
			
		||||
	const [ location, setLocation ] = useLocation();
 | 
			
		||||
	
 | 
			
		||||
	// Parse perm type from routing params.
 | 
			
		||||
	let params = useParams();
 | 
			
		||||
	if (params.permType !== "blocks" && params.permType !== "allows") {
 | 
			
		||||
		throw "unrecognized perm type " + params.permType;
 | 
			
		||||
	}
 | 
			
		||||
	const permType = useMemo(() => {
 | 
			
		||||
		return params.permType?.slice(0, -1) as PermType;
 | 
			
		||||
	}, [params]);
 | 
			
		||||
 | 
			
		||||
	// Uppercase first letter of given permType.
 | 
			
		||||
	const permTypeUpper = useMemo(() => {
 | 
			
		||||
		return permType.charAt(0).toUpperCase() + permType.slice(1); 
 | 
			
		||||
	}, [permType]);
 | 
			
		||||
	
 | 
			
		||||
	// Fetch desired perms, skipping
 | 
			
		||||
	// the ones we don't want.
 | 
			
		||||
	const {
 | 
			
		||||
		data: blocks,
 | 
			
		||||
		isLoading: isLoadingBlocks,
 | 
			
		||||
		isFetching: isFetchingBlocks,
 | 
			
		||||
		isSuccess: isSuccessBlocks,
 | 
			
		||||
		isError: isErrorBlocks,
 | 
			
		||||
		error: errorBlocks
 | 
			
		||||
	} = useGetHeaderBlocksQuery(NoArg, { skip: permType !== "block" });
 | 
			
		||||
 | 
			
		||||
	const {
 | 
			
		||||
		data: allows,
 | 
			
		||||
		isLoading: isLoadingAllows,
 | 
			
		||||
		isFetching: isFetchingAllows,
 | 
			
		||||
		isSuccess: isSuccessAllows,
 | 
			
		||||
		isError: isErrorAllows,
 | 
			
		||||
		error: errorAllows
 | 
			
		||||
	} = useGetHeaderAllowsQuery(NoArg, { skip: permType !== "allow" });
 | 
			
		||||
 | 
			
		||||
	const itemToEntry = (perm: HeaderPermission) => {
 | 
			
		||||
		return (
 | 
			
		||||
			<dl
 | 
			
		||||
				key={perm.id}
 | 
			
		||||
				className="entry spanlink"
 | 
			
		||||
				onClick={() => {
 | 
			
		||||
					// When clicking on a header perm,
 | 
			
		||||
					// go to the detail view for perm.
 | 
			
		||||
					setLocation(`/${permType}s/${perm.id}`, {
 | 
			
		||||
						// Store the back location in
 | 
			
		||||
						// history so the detail view
 | 
			
		||||
						// can use it to return here.
 | 
			
		||||
						state: { backLocation: location }
 | 
			
		||||
					});
 | 
			
		||||
				}}
 | 
			
		||||
				role="link"
 | 
			
		||||
				tabIndex={0}
 | 
			
		||||
			>
 | 
			
		||||
				<dt>{perm.header}</dt>
 | 
			
		||||
				<dd>{perm.regex}</dd>
 | 
			
		||||
			</dl>
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const emptyMessage = (
 | 
			
		||||
		<div className="info">
 | 
			
		||||
			<i className="fa fa-fw fa-info-circle" aria-hidden="true"></i>
 | 
			
		||||
			<b>
 | 
			
		||||
				No HTTP header {permType}s exist yet.
 | 
			
		||||
				You can create one using the form below.
 | 
			
		||||
			</b>
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	let isLoading: boolean;
 | 
			
		||||
	let isFetching: boolean;
 | 
			
		||||
	let isSuccess: boolean; 
 | 
			
		||||
	let isError: boolean;
 | 
			
		||||
	let error: FetchBaseQueryError | SerializedError | undefined;
 | 
			
		||||
	let items: HeaderPermission[] | undefined;
 | 
			
		||||
 | 
			
		||||
	if (permType === "block") {
 | 
			
		||||
		isLoading = isLoadingBlocks;
 | 
			
		||||
		isFetching = isFetchingBlocks;
 | 
			
		||||
		isSuccess = isSuccessBlocks;
 | 
			
		||||
		isError = isErrorBlocks;
 | 
			
		||||
		error = errorBlocks;
 | 
			
		||||
		items = blocks;
 | 
			
		||||
	} else {
 | 
			
		||||
		isLoading = isLoadingAllows;
 | 
			
		||||
		isFetching = isFetchingAllows;
 | 
			
		||||
		isSuccess = isSuccessAllows;
 | 
			
		||||
		isError = isErrorAllows;
 | 
			
		||||
		error = errorAllows;
 | 
			
		||||
		items = allows;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<div className="http-header-permissions">
 | 
			
		||||
			<div className="form-section-docs">
 | 
			
		||||
				<h1>HTTP Header {permTypeUpper}s</h1>
 | 
			
		||||
				<p>
 | 
			
		||||
					On this page, you can view, create, and remove HTTP header {permType} entries,
 | 
			
		||||
					<br/>
 | 
			
		||||
					Blocks and allows have different effects depending on the value you've set
 | 
			
		||||
					for <code>advanced-header-filter-mode</code> in your instance configuration.
 | 
			
		||||
					<br/>
 | 
			
		||||
					{ permType === "block" && <>
 | 
			
		||||
						<strong>
 | 
			
		||||
							When running in <code>block</code> mode, be very careful when creating
 | 
			
		||||
							your value regexes, as a too-broad match can cause your instance to
 | 
			
		||||
							deny all requests, locking you out of this settings panel.
 | 
			
		||||
						</strong>
 | 
			
		||||
						<br/>
 | 
			
		||||
						If you do this by accident, you can fix it by stopping your instance,
 | 
			
		||||
						changing <code>advanced-header-filter-mode</code> to an empty string
 | 
			
		||||
						(disabled), starting your instance again, and removing the block.
 | 
			
		||||
					</> }
 | 
			
		||||
				</p>
 | 
			
		||||
				<a
 | 
			
		||||
					href="https://docs.gotosocial.org/en/latest/admin/request_filtering_modes/"
 | 
			
		||||
					target="_blank"
 | 
			
		||||
					className="docslink"
 | 
			
		||||
					rel="noreferrer"
 | 
			
		||||
				>
 | 
			
		||||
					Learn more about HTTP request filtering (opens in a new tab)
 | 
			
		||||
				</a>
 | 
			
		||||
			</div>
 | 
			
		||||
			<PageableList
 | 
			
		||||
				isLoading={isLoading}
 | 
			
		||||
				isFetching={isFetching}
 | 
			
		||||
				isSuccess={isSuccess}
 | 
			
		||||
				isError={isError}
 | 
			
		||||
				error={error}
 | 
			
		||||
				items={items}
 | 
			
		||||
				itemToEntry={itemToEntry}
 | 
			
		||||
				emptyMessage={emptyMessage}
 | 
			
		||||
			/>
 | 
			
		||||
			<HeaderPermCreateForm permType={permType} />
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +36,10 @@ import { useHasPermission } from "../../lib/navigation/util";
 | 
			
		|||
 * - /settings/admin/actions
 | 
			
		||||
 * - /settings/admin/actions/media
 | 
			
		||||
 * - /settings/admin/actions/keys
 | 
			
		||||
 * - /settings/admin/http-header-permissions/blocks
 | 
			
		||||
 * - /settings/admin/http-header-permissions/blocks/:blockId\
 | 
			
		||||
 * - /settings/admin/http-header-permissions/allows
 | 
			
		||||
 * - /settings/admin/http-header-permissions/allows/:allowId
 | 
			
		||||
 */
 | 
			
		||||
export default function AdminMenu() {	
 | 
			
		||||
	const permissions = ["admin"];
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +58,7 @@ export default function AdminMenu() {
 | 
			
		|||
			<AdminInstanceMenu />
 | 
			
		||||
			<AdminEmojisMenu />
 | 
			
		||||
			<AdminActionsMenu />
 | 
			
		||||
			<AdminHTTPHeaderPermissionsMenu />
 | 
			
		||||
		</MenuItem>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -127,3 +132,25 @@ function AdminEmojisMenu() {
 | 
			
		|||
		</MenuItem>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AdminHTTPHeaderPermissionsMenu() {
 | 
			
		||||
	return (
 | 
			
		||||
		<MenuItem
 | 
			
		||||
			name="HTTP Header Permissions"
 | 
			
		||||
			itemUrl="http-header-permissions"
 | 
			
		||||
			defaultChild="blocks"
 | 
			
		||||
			icon="fa-hubzilla"
 | 
			
		||||
		>
 | 
			
		||||
			<MenuItem
 | 
			
		||||
				name="Blocks"
 | 
			
		||||
				itemUrl="blocks"
 | 
			
		||||
				icon="fa-close"
 | 
			
		||||
			/>
 | 
			
		||||
			<MenuItem
 | 
			
		||||
				name="Allows"
 | 
			
		||||
				itemUrl="allows"
 | 
			
		||||
				icon="fa-check"
 | 
			
		||||
			/>
 | 
			
		||||
		</MenuItem>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,15 +29,17 @@ import Keys from "./actions/keys";
 | 
			
		|||
import EmojiOverview from "./emoji/local/overview";
 | 
			
		||||
import EmojiDetail from "./emoji/local/detail";
 | 
			
		||||
import RemoteEmoji from "./emoji/remote";
 | 
			
		||||
import HeaderPermsOverview from "./http-header-permissions/overview";
 | 
			
		||||
import HeaderPermDetail from "./http-header-permissions/detail";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	EXPORTED COMPONENTS
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * - /settings/instance/settings
 | 
			
		||||
 * - /settings/instance/rules
 | 
			
		||||
 * - /settings/instance/rules/:ruleId
 | 
			
		||||
 * - /settings/admin/instance/settings
 | 
			
		||||
 * - /settings/admin/instance/rules
 | 
			
		||||
 * - /settings/admin/instance/rules/:ruleId
 | 
			
		||||
 * - /settings/admin/emojis
 | 
			
		||||
 * - /settings/admin/emojis/local
 | 
			
		||||
 * - /settings/admin/emojis/local/:emojiId
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +47,10 @@ import RemoteEmoji from "./emoji/remote";
 | 
			
		|||
 * - /settings/admin/actions
 | 
			
		||||
 * - /settings/admin/actions/media
 | 
			
		||||
 * - /settings/admin/actions/keys
 | 
			
		||||
 * - /settings/admin/http-header-permissions/allows
 | 
			
		||||
 * - /settings/admin/http-header-permissions/allows/:allowId
 | 
			
		||||
 * - /settings/admin/http-header-permissions/blocks
 | 
			
		||||
 * - /settings/admin/http-header-permissions/blocks/:blockId
 | 
			
		||||
 */
 | 
			
		||||
export default function AdminRouter() {
 | 
			
		||||
	const parentUrl = useBaseUrl();
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +63,7 @@ export default function AdminRouter() {
 | 
			
		|||
				<AdminInstanceRouter />
 | 
			
		||||
				<AdminEmojisRouter />
 | 
			
		||||
				<AdminActionsRouter />
 | 
			
		||||
				<AdminHTTPHeaderPermissionsRouter />
 | 
			
		||||
			</Router>
 | 
			
		||||
		</BaseUrlContext.Provider>
 | 
			
		||||
	);
 | 
			
		||||
| 
						 | 
				
			
			@ -125,9 +132,9 @@ function AdminActionsRouter() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * - /settings/instance/settings
 | 
			
		||||
 * - /settings/instance/rules
 | 
			
		||||
 * - /settings/instance/rules/:ruleId
 | 
			
		||||
 * - /settings/admin/instance/settings
 | 
			
		||||
 * - /settings/admin/instance/rules
 | 
			
		||||
 * - /settings/admin/instance/rules/:ruleId
 | 
			
		||||
 */
 | 
			
		||||
function AdminInstanceRouter() {
 | 
			
		||||
	const parentUrl = useBaseUrl();
 | 
			
		||||
| 
						 | 
				
			
			@ -149,3 +156,29 @@ function AdminInstanceRouter() {
 | 
			
		|||
		</BaseUrlContext.Provider>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * - /settings/admin/http-header-permissions/blocks
 | 
			
		||||
 * - /settings/admin/http-header-permissions/blocks/:blockId
 | 
			
		||||
 * - /settings/admin/http-header-permissions/allows
 | 
			
		||||
 * - /settings/admin/http-header-permissions/allows/:allowId
 | 
			
		||||
 */
 | 
			
		||||
function AdminHTTPHeaderPermissionsRouter() {
 | 
			
		||||
	const parentUrl = useBaseUrl();
 | 
			
		||||
	const thisBase = "/http-header-permissions";
 | 
			
		||||
	const absBase = parentUrl + thisBase;
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<BaseUrlContext.Provider value={absBase}>
 | 
			
		||||
			<Router base={thisBase}>
 | 
			
		||||
				<ErrorBoundary>
 | 
			
		||||
					<Switch>
 | 
			
		||||
						<Route path="/:permType" component={HeaderPermsOverview} />
 | 
			
		||||
						<Route path="/:permType/:permId" component={HeaderPermDetail} />
 | 
			
		||||
						<Route><Redirect to="/blocks" /></Route>
 | 
			
		||||
					</Switch>
 | 
			
		||||
				</ErrorBoundary>
 | 
			
		||||
			</Router>
 | 
			
		||||
		</BaseUrlContext.Provider>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue