You've already forked nginx-proxy-manager
							
							
				mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-11-04 04:11:42 +03:00 
			
		
		
		
	Permissions polish for restricted users
This commit is contained in:
		@@ -1,8 +1,9 @@
 | 
				
			|||||||
import type { Table as ReactTable } from "@tanstack/react-table";
 | 
					import type { Table as ReactTable } from "@tanstack/react-table";
 | 
				
			||||||
import cn from "classnames";
 | 
					import cn from "classnames";
 | 
				
			||||||
import type { ReactNode } from "react";
 | 
					import type { ReactNode } from "react";
 | 
				
			||||||
import { Button } from "src/components";
 | 
					import { Button, HasPermission } from "src/components";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
 | 
					import { type ADMIN, MANAGE, type Permission, type Section } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	tableInstance: ReactTable<any>;
 | 
						tableInstance: ReactTable<any>;
 | 
				
			||||||
@@ -12,8 +13,20 @@ interface Props {
 | 
				
			|||||||
	objects: string;
 | 
						objects: string;
 | 
				
			||||||
	color?: string;
 | 
						color?: string;
 | 
				
			||||||
	customAddBtn?: ReactNode;
 | 
						customAddBtn?: ReactNode;
 | 
				
			||||||
 | 
						permissionSection?: Section | typeof ADMIN;
 | 
				
			||||||
 | 
						permission?: Permission;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function EmptyData({ tableInstance, onNew, isFiltered, object, objects, color = "primary", customAddBtn }: Props) {
 | 
					function EmptyData({
 | 
				
			||||||
 | 
						tableInstance,
 | 
				
			||||||
 | 
						onNew,
 | 
				
			||||||
 | 
						isFiltered,
 | 
				
			||||||
 | 
						object,
 | 
				
			||||||
 | 
						objects,
 | 
				
			||||||
 | 
						color = "primary",
 | 
				
			||||||
 | 
						customAddBtn,
 | 
				
			||||||
 | 
						permissionSection,
 | 
				
			||||||
 | 
						permission,
 | 
				
			||||||
 | 
					}: Props) {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<tr>
 | 
							<tr>
 | 
				
			||||||
			<td colSpan={tableInstance.getVisibleFlatColumns().length}>
 | 
								<td colSpan={tableInstance.getVisibleFlatColumns().length}>
 | 
				
			||||||
@@ -27,6 +40,7 @@ function EmptyData({ tableInstance, onNew, isFiltered, object, objects, color =
 | 
				
			|||||||
							<h2>
 | 
												<h2>
 | 
				
			||||||
								<T id="object.empty" tData={{ objects }} />
 | 
													<T id="object.empty" tData={{ objects }} />
 | 
				
			||||||
							</h2>
 | 
												</h2>
 | 
				
			||||||
 | 
												<HasPermission section={permissionSection} permission={permission || MANAGE} hideError>
 | 
				
			||||||
								<p className="text-muted">
 | 
													<p className="text-muted">
 | 
				
			||||||
									<T id="empty-subtitle" />
 | 
														<T id="empty-subtitle" />
 | 
				
			||||||
								</p>
 | 
													</p>
 | 
				
			||||||
@@ -37,6 +51,7 @@ function EmptyData({ tableInstance, onNew, isFiltered, object, objects, color =
 | 
				
			|||||||
										<T id="object.add" tData={{ object }} />
 | 
															<T id="object.add" tData={{ object }} />
 | 
				
			||||||
									</Button>
 | 
														</Button>
 | 
				
			||||||
								)}
 | 
													)}
 | 
				
			||||||
 | 
												</HasPermission>
 | 
				
			||||||
						</>
 | 
											</>
 | 
				
			||||||
					)}
 | 
										)}
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,25 +3,29 @@ import Alert from "react-bootstrap/Alert";
 | 
				
			|||||||
import { Loading, LoadingPage } from "src/components";
 | 
					import { Loading, LoadingPage } from "src/components";
 | 
				
			||||||
import { useUser } from "src/hooks";
 | 
					import { useUser } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
 | 
					import { type ADMIN, hasPermission, type Permission, type Section } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	permission: string;
 | 
						section?: Section | typeof ADMIN;
 | 
				
			||||||
	type: "manage" | "view";
 | 
						permission: Permission;
 | 
				
			||||||
	hideError?: boolean;
 | 
						hideError?: boolean;
 | 
				
			||||||
	children?: ReactNode;
 | 
						children?: ReactNode;
 | 
				
			||||||
	pageLoading?: boolean;
 | 
						pageLoading?: boolean;
 | 
				
			||||||
	loadingNoLogo?: boolean;
 | 
						loadingNoLogo?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function HasPermission({
 | 
					function HasPermission({
 | 
				
			||||||
 | 
						section,
 | 
				
			||||||
	permission,
 | 
						permission,
 | 
				
			||||||
	type,
 | 
					 | 
				
			||||||
	children,
 | 
						children,
 | 
				
			||||||
	hideError = false,
 | 
						hideError = false,
 | 
				
			||||||
	pageLoading = false,
 | 
						pageLoading = false,
 | 
				
			||||||
	loadingNoLogo = false,
 | 
						loadingNoLogo = false,
 | 
				
			||||||
}: Props) {
 | 
					}: Props) {
 | 
				
			||||||
	const { data, isLoading } = useUser("me");
 | 
						const { data, isLoading } = useUser("me");
 | 
				
			||||||
	const perms = data?.permissions;
 | 
					
 | 
				
			||||||
 | 
						if (!section) {
 | 
				
			||||||
 | 
							return <>{children}</>;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (isLoading) {
 | 
						if (isLoading) {
 | 
				
			||||||
		if (hideError) {
 | 
							if (hideError) {
 | 
				
			||||||
@@ -33,33 +37,7 @@ function HasPermission({
 | 
				
			|||||||
		return <Loading noLogo={loadingNoLogo} />;
 | 
							return <Loading noLogo={loadingNoLogo} />;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let allowed = permission === "";
 | 
						const allowed = hasPermission(section, permission, data?.permissions, data?.roles);
 | 
				
			||||||
	const acceptable = ["manage", type];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (permission) {
 | 
					 | 
				
			||||||
		case "admin":
 | 
					 | 
				
			||||||
			allowed = data?.roles?.includes("admin") || false;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case "proxyHosts":
 | 
					 | 
				
			||||||
			allowed = acceptable.indexOf(perms?.proxyHosts || "") !== -1;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case "redirectionHosts":
 | 
					 | 
				
			||||||
			allowed = acceptable.indexOf(perms?.redirectionHosts || "") !== -1;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case "deadHosts":
 | 
					 | 
				
			||||||
			allowed = acceptable.indexOf(perms?.deadHosts || "") !== -1;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case "streams":
 | 
					 | 
				
			||||||
			allowed = acceptable.indexOf(perms?.streams || "") !== -1;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case "accessLists":
 | 
					 | 
				
			||||||
			allowed = acceptable.indexOf(perms?.accessLists || "") !== -1;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case "certificates":
 | 
					 | 
				
			||||||
			allowed = acceptable.indexOf(perms?.certificates || "") !== -1;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (allowed) {
 | 
						if (allowed) {
 | 
				
			||||||
		return <>{children}</>;
 | 
							return <>{children}</>;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,14 +11,26 @@ import cn from "classnames";
 | 
				
			|||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { HasPermission, NavLink } from "src/components";
 | 
					import { HasPermission, NavLink } from "src/components";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
						ACCESS_LISTS,
 | 
				
			||||||
 | 
						ADMIN,
 | 
				
			||||||
 | 
						CERTIFICATES,
 | 
				
			||||||
 | 
						DEAD_HOSTS,
 | 
				
			||||||
 | 
						type MANAGE,
 | 
				
			||||||
 | 
						PROXY_HOSTS,
 | 
				
			||||||
 | 
						REDIRECTION_HOSTS,
 | 
				
			||||||
 | 
						type Section,
 | 
				
			||||||
 | 
						STREAMS,
 | 
				
			||||||
 | 
						VIEW,
 | 
				
			||||||
 | 
					} from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface MenuItem {
 | 
					interface MenuItem {
 | 
				
			||||||
	label: string;
 | 
						label: string;
 | 
				
			||||||
	icon?: React.ElementType;
 | 
						icon?: React.ElementType;
 | 
				
			||||||
	to?: string;
 | 
						to?: string;
 | 
				
			||||||
	items?: MenuItem[];
 | 
						items?: MenuItem[];
 | 
				
			||||||
	permission?: string;
 | 
						permissionSection?: Section | typeof ADMIN;
 | 
				
			||||||
	permissionType?: "view" | "manage";
 | 
						permission?: typeof VIEW | typeof MANAGE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const menuItems: MenuItem[] = [
 | 
					const menuItems: MenuItem[] = [
 | 
				
			||||||
@@ -34,26 +46,26 @@ const menuItems: MenuItem[] = [
 | 
				
			|||||||
			{
 | 
								{
 | 
				
			||||||
				to: "/nginx/proxy",
 | 
									to: "/nginx/proxy",
 | 
				
			||||||
				label: "proxy-hosts",
 | 
									label: "proxy-hosts",
 | 
				
			||||||
				permission: "proxyHosts",
 | 
									permissionSection: PROXY_HOSTS,
 | 
				
			||||||
				permissionType: "view",
 | 
									permission: VIEW,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				to: "/nginx/redirection",
 | 
									to: "/nginx/redirection",
 | 
				
			||||||
				label: "redirection-hosts",
 | 
									label: "redirection-hosts",
 | 
				
			||||||
				permission: "redirectionHosts",
 | 
									permissionSection: REDIRECTION_HOSTS,
 | 
				
			||||||
				permissionType: "view",
 | 
									permission: VIEW,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				to: "/nginx/stream",
 | 
									to: "/nginx/stream",
 | 
				
			||||||
				label: "streams",
 | 
									label: "streams",
 | 
				
			||||||
				permission: "streams",
 | 
									permissionSection: STREAMS,
 | 
				
			||||||
				permissionType: "view",
 | 
									permission: VIEW,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				to: "/nginx/404",
 | 
									to: "/nginx/404",
 | 
				
			||||||
				label: "dead-hosts",
 | 
									label: "dead-hosts",
 | 
				
			||||||
				permission: "deadHosts",
 | 
									permissionSection: DEAD_HOSTS,
 | 
				
			||||||
				permissionType: "view",
 | 
									permission: VIEW,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		],
 | 
							],
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
@@ -61,33 +73,33 @@ const menuItems: MenuItem[] = [
 | 
				
			|||||||
		to: "/access",
 | 
							to: "/access",
 | 
				
			||||||
		icon: IconLock,
 | 
							icon: IconLock,
 | 
				
			||||||
		label: "access-lists",
 | 
							label: "access-lists",
 | 
				
			||||||
		permission: "accessLists",
 | 
							permissionSection: ACCESS_LISTS,
 | 
				
			||||||
		permissionType: "view",
 | 
							permission: VIEW,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		to: "/certificates",
 | 
							to: "/certificates",
 | 
				
			||||||
		icon: IconShield,
 | 
							icon: IconShield,
 | 
				
			||||||
		label: "certificates",
 | 
							label: "certificates",
 | 
				
			||||||
		permission: "certificates",
 | 
							permissionSection: CERTIFICATES,
 | 
				
			||||||
		permissionType: "view",
 | 
							permission: VIEW,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		to: "/users",
 | 
							to: "/users",
 | 
				
			||||||
		icon: IconUser,
 | 
							icon: IconUser,
 | 
				
			||||||
		label: "users",
 | 
							label: "users",
 | 
				
			||||||
		permission: "admin",
 | 
							permissionSection: ADMIN,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		to: "/audit-log",
 | 
							to: "/audit-log",
 | 
				
			||||||
		icon: IconBook,
 | 
							icon: IconBook,
 | 
				
			||||||
		label: "auditlogs",
 | 
							label: "auditlogs",
 | 
				
			||||||
		permission: "admin",
 | 
							permissionSection: ADMIN,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		to: "/settings",
 | 
							to: "/settings",
 | 
				
			||||||
		icon: IconSettings,
 | 
							icon: IconSettings,
 | 
				
			||||||
		label: "settings",
 | 
							label: "settings",
 | 
				
			||||||
		permission: "admin",
 | 
							permissionSection: ADMIN,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -99,8 +111,8 @@ const getMenuItem = (item: MenuItem, onClick?: () => void) => {
 | 
				
			|||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission
 | 
							<HasPermission
 | 
				
			||||||
			key={`item-${item.label}`}
 | 
								key={`item-${item.label}`}
 | 
				
			||||||
			permission={item.permission || ""}
 | 
								section={item.permissionSection}
 | 
				
			||||||
			type={item.permissionType || "view"}
 | 
								permission={item.permission || VIEW}
 | 
				
			||||||
			hideError
 | 
								hideError
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<li className="nav-item">
 | 
								<li className="nav-item">
 | 
				
			||||||
@@ -122,8 +134,8 @@ const getMenuDropown = (item: MenuItem, onClick?: () => void) => {
 | 
				
			|||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission
 | 
							<HasPermission
 | 
				
			||||||
			key={`item-${item.label}`}
 | 
								key={`item-${item.label}`}
 | 
				
			||||||
			permission={item.permission || ""}
 | 
								section={item.permissionSection}
 | 
				
			||||||
			type={item.permissionType || "view"}
 | 
								permission={item.permission || VIEW}
 | 
				
			||||||
			hideError
 | 
								hideError
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<li className={cns}>
 | 
								<li className={cns}>
 | 
				
			||||||
@@ -147,8 +159,8 @@ const getMenuDropown = (item: MenuItem, onClick?: () => void) => {
 | 
				
			|||||||
						return (
 | 
											return (
 | 
				
			||||||
							<HasPermission
 | 
												<HasPermission
 | 
				
			||||||
								key={`${idx}-${subitem.to}`}
 | 
													key={`${idx}-${subitem.to}`}
 | 
				
			||||||
								permission={subitem.permission || ""}
 | 
													section={subitem.permissionSection}
 | 
				
			||||||
								type={subitem.permissionType || "view"}
 | 
													permission={subitem.permission || VIEW}
 | 
				
			||||||
								hideError
 | 
													hideError
 | 
				
			||||||
							>
 | 
												>
 | 
				
			||||||
								<NavLink to={subitem.to} isDropdownItem onClick={onClick}>
 | 
													<NavLink to={subitem.to} isDropdownItem onClick={onClick}>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,11 +47,41 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// given the field and clicked permission, intelligently set the value, and
 | 
				
			||||||
 | 
						// other values that depends on it.
 | 
				
			||||||
 | 
						const handleChange = (form: any, field: any, perm: string) => {
 | 
				
			||||||
 | 
							if (field.name === "proxyHosts" && perm !== "hidden" && form.values.accessLists === "hidden") {
 | 
				
			||||||
 | 
								form.setFieldValue("accessLists", "view");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// certs are required for proxy and redirection hosts, and streams
 | 
				
			||||||
 | 
							if (
 | 
				
			||||||
 | 
								["proxyHosts", "redirectionHosts", "deadHosts", "streams"].includes(field.name) &&
 | 
				
			||||||
 | 
								perm !== "hidden" &&
 | 
				
			||||||
 | 
								form.values.certificates === "hidden"
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
 | 
								form.setFieldValue("certificates", "view");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							form.setFieldValue(field.name, perm);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const getPermissionButtons = (field: any, form: any) => {
 | 
						const getPermissionButtons = (field: any, form: any) => {
 | 
				
			||||||
		const isManage = field.value === "manage";
 | 
							const isManage = field.value === "manage";
 | 
				
			||||||
		const isView = field.value === "view";
 | 
							const isView = field.value === "view";
 | 
				
			||||||
		const isHidden = field.value === "hidden";
 | 
							const isHidden = field.value === "hidden";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let hiddenDisabled = false;
 | 
				
			||||||
 | 
							if (field.name === "accessLists") {
 | 
				
			||||||
 | 
								hiddenDisabled = form.values.proxyHosts !== "hidden";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (field.name === "certificates") {
 | 
				
			||||||
 | 
								hiddenDisabled =
 | 
				
			||||||
 | 
									form.values.proxyHosts !== "hidden" ||
 | 
				
			||||||
 | 
									form.values.redirectionHosts !== "hidden" ||
 | 
				
			||||||
 | 
									form.values.deadHosts !== "hidden" ||
 | 
				
			||||||
 | 
									form.values.streams !== "hidden";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return (
 | 
							return (
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
				<div className="btn-group w-100" role="group">
 | 
									<div className="btn-group w-100" role="group">
 | 
				
			||||||
@@ -63,7 +93,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			|||||||
						autoComplete="off"
 | 
											autoComplete="off"
 | 
				
			||||||
						value="manage"
 | 
											value="manage"
 | 
				
			||||||
						checked={field.value === "manage"}
 | 
											checked={field.value === "manage"}
 | 
				
			||||||
						onChange={() => form.setFieldValue(field.name, "manage")}
 | 
											onChange={() => handleChange(form, field, "manage")}
 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
					<label htmlFor={`${field.name}-manage`} className={getClasses(isManage)}>
 | 
										<label htmlFor={`${field.name}-manage`} className={getClasses(isManage)}>
 | 
				
			||||||
						<T id="permissions.manage" />
 | 
											<T id="permissions.manage" />
 | 
				
			||||||
@@ -76,7 +106,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			|||||||
						autoComplete="off"
 | 
											autoComplete="off"
 | 
				
			||||||
						value="view"
 | 
											value="view"
 | 
				
			||||||
						checked={field.value === "view"}
 | 
											checked={field.value === "view"}
 | 
				
			||||||
						onChange={() => form.setFieldValue(field.name, "view")}
 | 
											onChange={() => handleChange(form, field, "view")}
 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
					<label htmlFor={`${field.name}-view`} className={getClasses(isView)}>
 | 
										<label htmlFor={`${field.name}-view`} className={getClasses(isView)}>
 | 
				
			||||||
						<T id="permissions.view" />
 | 
											<T id="permissions.view" />
 | 
				
			||||||
@@ -89,7 +119,8 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			|||||||
						autoComplete="off"
 | 
											autoComplete="off"
 | 
				
			||||||
						value="hidden"
 | 
											value="hidden"
 | 
				
			||||||
						checked={field.value === "hidden"}
 | 
											checked={field.value === "hidden"}
 | 
				
			||||||
						onChange={() => form.setFieldValue(field.name, "hidden")}
 | 
											disabled={hiddenDisabled}
 | 
				
			||||||
 | 
											onChange={() => handleChange(form, field, "hidden")}
 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
					<label htmlFor={`${field.name}-hidden`} className={getClasses(isHidden)}>
 | 
										<label htmlFor={`${field.name}-hidden`} className={getClasses(isHidden)}>
 | 
				
			||||||
						<T id="permissions.hidden" />
 | 
											<T id="permissions.hidden" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,14 +9,16 @@ import {
 | 
				
			|||||||
	AccessField,
 | 
						AccessField,
 | 
				
			||||||
	Button,
 | 
						Button,
 | 
				
			||||||
	DomainNamesField,
 | 
						DomainNamesField,
 | 
				
			||||||
 | 
						HasPermission,
 | 
				
			||||||
	Loading,
 | 
						Loading,
 | 
				
			||||||
	LocationsFields,
 | 
						LocationsFields,
 | 
				
			||||||
	NginxConfigField,
 | 
						NginxConfigField,
 | 
				
			||||||
	SSLCertificateField,
 | 
						SSLCertificateField,
 | 
				
			||||||
	SSLOptionsFields,
 | 
						SSLOptionsFields,
 | 
				
			||||||
} from "src/components";
 | 
					} from "src/components";
 | 
				
			||||||
import { useProxyHost, useSetProxyHost } from "src/hooks";
 | 
					import { useProxyHost, useSetProxyHost, useUser } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
 | 
					import { MANAGE, PROXY_HOSTS } from "src/modules/Permissions";
 | 
				
			||||||
import { validateNumber, validateString } from "src/modules/Validations";
 | 
					import { validateNumber, validateString } from "src/modules/Validations";
 | 
				
			||||||
import { showObjectSuccess } from "src/notifications";
 | 
					import { showObjectSuccess } from "src/notifications";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,6 +30,7 @@ interface Props extends InnerModalProps {
 | 
				
			|||||||
	id: number | "new";
 | 
						id: number | "new";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
					const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			||||||
 | 
						const { data: currentUser, isLoading: userIsLoading, error: userError } = useUser("me");
 | 
				
			||||||
	const { data, isLoading, error } = useProxyHost(id);
 | 
						const { data, isLoading, error } = useProxyHost(id);
 | 
				
			||||||
	const { mutate: setProxyHost } = useSetProxyHost();
 | 
						const { mutate: setProxyHost } = useSetProxyHost();
 | 
				
			||||||
	const [errorMsg, setErrorMsg] = useState<ReactNode | null>(null);
 | 
						const [errorMsg, setErrorMsg] = useState<ReactNode | null>(null);
 | 
				
			||||||
@@ -58,13 +61,13 @@ const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<Modal show={visible} onHide={remove}>
 | 
							<Modal show={visible} onHide={remove}>
 | 
				
			||||||
			{!isLoading && error && (
 | 
								{!isLoading && (error || userError) && (
 | 
				
			||||||
				<Alert variant="danger" className="m-3">
 | 
									<Alert variant="danger" className="m-3">
 | 
				
			||||||
					{error?.message || "Unknown error"}
 | 
										{error?.message || userError?.message || "Unknown error"}
 | 
				
			||||||
				</Alert>
 | 
									</Alert>
 | 
				
			||||||
			)}
 | 
								)}
 | 
				
			||||||
			{isLoading && <Loading noLogo />}
 | 
								{isLoading || (userIsLoading && <Loading noLogo />)}
 | 
				
			||||||
			{!isLoading && data && (
 | 
								{!isLoading && !userIsLoading && data && currentUser && (
 | 
				
			||||||
				<Formik
 | 
									<Formik
 | 
				
			||||||
					initialValues={
 | 
										initialValues={
 | 
				
			||||||
						{
 | 
											{
 | 
				
			||||||
@@ -349,6 +352,7 @@ const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			|||||||
								<Button data-bs-dismiss="modal" onClick={remove} disabled={isSubmitting}>
 | 
													<Button data-bs-dismiss="modal" onClick={remove} disabled={isSubmitting}>
 | 
				
			||||||
									<T id="cancel" />
 | 
														<T id="cancel" />
 | 
				
			||||||
								</Button>
 | 
													</Button>
 | 
				
			||||||
 | 
													<HasPermission section={PROXY_HOSTS} permission={MANAGE} hideError>
 | 
				
			||||||
									<Button
 | 
														<Button
 | 
				
			||||||
										type="submit"
 | 
															type="submit"
 | 
				
			||||||
										actionType="primary"
 | 
															actionType="primary"
 | 
				
			||||||
@@ -359,6 +363,7 @@ const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
 | 
				
			|||||||
									>
 | 
														>
 | 
				
			||||||
										<T id="save" />
 | 
															<T id="save" />
 | 
				
			||||||
									</Button>
 | 
														</Button>
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</Modal.Footer>
 | 
												</Modal.Footer>
 | 
				
			||||||
						</Form>
 | 
											</Form>
 | 
				
			||||||
					)}
 | 
										)}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										49
									
								
								frontend/src/modules/Permissions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								frontend/src/modules/Permissions.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					import type { UserPermissions } from "src/api/backend";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ADMIN = "admin";
 | 
				
			||||||
 | 
					export const VISIBILITY = "visibility";
 | 
				
			||||||
 | 
					export const PROXY_HOSTS = "proxyHosts";
 | 
				
			||||||
 | 
					export const REDIRECTION_HOSTS = "redirectionHosts";
 | 
				
			||||||
 | 
					export const DEAD_HOSTS = "deadHosts";
 | 
				
			||||||
 | 
					export const STREAMS = "streams";
 | 
				
			||||||
 | 
					export const CERTIFICATES = "certificates";
 | 
				
			||||||
 | 
					export const ACCESS_LISTS = "accessLists";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MANAGE = "manage";
 | 
				
			||||||
 | 
					export const VIEW = "view";
 | 
				
			||||||
 | 
					export const HIDDEN = "hidden";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ALL = "all";
 | 
				
			||||||
 | 
					export const USER = "user";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Section =
 | 
				
			||||||
 | 
						| typeof ADMIN
 | 
				
			||||||
 | 
						| typeof VISIBILITY
 | 
				
			||||||
 | 
						| typeof PROXY_HOSTS
 | 
				
			||||||
 | 
						| typeof REDIRECTION_HOSTS
 | 
				
			||||||
 | 
						| typeof DEAD_HOSTS
 | 
				
			||||||
 | 
						| typeof STREAMS
 | 
				
			||||||
 | 
						| typeof CERTIFICATES
 | 
				
			||||||
 | 
						| typeof ACCESS_LISTS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Permission = typeof MANAGE | typeof VIEW;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const hasPermission = (
 | 
				
			||||||
 | 
						section: Section,
 | 
				
			||||||
 | 
						perm: Permission,
 | 
				
			||||||
 | 
						userPerms: UserPermissions | undefined,
 | 
				
			||||||
 | 
						roles: string[] | undefined,
 | 
				
			||||||
 | 
					): boolean => {
 | 
				
			||||||
 | 
						if (!userPerms) return false;
 | 
				
			||||||
 | 
						if (isAdmin(roles)) return true;
 | 
				
			||||||
 | 
						const acceptable = [MANAGE, perm];
 | 
				
			||||||
 | 
						// @ts-expect-error 7053
 | 
				
			||||||
 | 
						const v = typeof userPerms[section] !== "undefined" ? userPerms[section] : HIDDEN;
 | 
				
			||||||
 | 
						return acceptable.indexOf(v) !== -1;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isAdmin = (roles: string[] | undefined): boolean => {
 | 
				
			||||||
 | 
						return roles?.includes("admin") || false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { hasPermission, isAdmin };
 | 
				
			||||||
@@ -2,9 +2,10 @@ import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react";
 | 
				
			|||||||
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
 | 
					import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
 | 
				
			||||||
import { useMemo } from "react";
 | 
					import { useMemo } from "react";
 | 
				
			||||||
import type { AccessList } from "src/api/backend";
 | 
					import type { AccessList } from "src/api/backend";
 | 
				
			||||||
import { EmptyData, GravatarFormatter, ValueWithDateFormatter } from "src/components";
 | 
					import { EmptyData, GravatarFormatter, HasPermission, ValueWithDateFormatter } from "src/components";
 | 
				
			||||||
import { TableLayout } from "src/components/Table/TableLayout";
 | 
					import { TableLayout } from "src/components/Table/TableLayout";
 | 
				
			||||||
import { intl, T } from "src/locale";
 | 
					import { intl, T } from "src/locale";
 | 
				
			||||||
 | 
					import { ACCESS_LISTS, MANAGE } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	data: AccessList[];
 | 
						data: AccessList[];
 | 
				
			||||||
@@ -84,6 +85,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
 | 
				
			|||||||
									<IconEdit size={16} />
 | 
														<IconEdit size={16} />
 | 
				
			||||||
									<T id="action.edit" />
 | 
														<T id="action.edit" />
 | 
				
			||||||
								</a>
 | 
													</a>
 | 
				
			||||||
 | 
													<HasPermission section={ACCESS_LISTS} permission={MANAGE} hideError>
 | 
				
			||||||
									<div className="dropdown-divider" />
 | 
														<div className="dropdown-divider" />
 | 
				
			||||||
									<a
 | 
														<a
 | 
				
			||||||
										className="dropdown-item"
 | 
															className="dropdown-item"
 | 
				
			||||||
@@ -96,6 +98,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
 | 
				
			|||||||
										<IconTrash size={16} />
 | 
															<IconTrash size={16} />
 | 
				
			||||||
										<T id="action.delete" />
 | 
															<T id="action.delete" />
 | 
				
			||||||
									</a>
 | 
														</a>
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
@@ -130,6 +133,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
 | 
				
			|||||||
					onNew={onNew}
 | 
										onNew={onNew}
 | 
				
			||||||
					isFiltered={isFiltered}
 | 
										isFiltered={isFiltered}
 | 
				
			||||||
					color="cyan"
 | 
										color="cyan"
 | 
				
			||||||
 | 
										permissionSection={ACCESS_LISTS}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,10 +2,11 @@ import { IconHelp, IconSearch } from "@tabler/icons-react";
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import Alert from "react-bootstrap/Alert";
 | 
					import Alert from "react-bootstrap/Alert";
 | 
				
			||||||
import { deleteAccessList } from "src/api/backend";
 | 
					import { deleteAccessList } from "src/api/backend";
 | 
				
			||||||
import { Button, LoadingPage } from "src/components";
 | 
					import { Button, HasPermission, LoadingPage } from "src/components";
 | 
				
			||||||
import { useAccessLists } from "src/hooks";
 | 
					import { useAccessLists } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
import { showAccessListModal, showDeleteConfirmModal, showHelpModal } from "src/modals";
 | 
					import { showAccessListModal, showDeleteConfirmModal, showHelpModal } from "src/modals";
 | 
				
			||||||
 | 
					import { ACCESS_LISTS, MANAGE } from "src/modules/Permissions";
 | 
				
			||||||
import { showObjectSuccess } from "src/notifications";
 | 
					import { showObjectSuccess } from "src/notifications";
 | 
				
			||||||
import Table from "./Table";
 | 
					import Table from "./Table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,11 +68,17 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<Button size="sm" onClick={() => showHelpModal("AccessLists", "cyan")}>
 | 
													<Button size="sm" onClick={() => showHelpModal("AccessLists", "cyan")}>
 | 
				
			||||||
									<IconHelp size={20} />
 | 
														<IconHelp size={20} />
 | 
				
			||||||
								</Button>
 | 
													</Button>
 | 
				
			||||||
 | 
													<HasPermission section={ACCESS_LISTS} permission={MANAGE} hideError>
 | 
				
			||||||
									{data?.length ? (
 | 
														{data?.length ? (
 | 
				
			||||||
									<Button size="sm" className="btn-cyan" onClick={() => showAccessListModal("new")}>
 | 
															<Button
 | 
				
			||||||
 | 
																size="sm"
 | 
				
			||||||
 | 
																className="btn-cyan"
 | 
				
			||||||
 | 
																onClick={() => showAccessListModal("new")}
 | 
				
			||||||
 | 
															>
 | 
				
			||||||
											<T id="object.add" tData={{ object: "access-list" }} />
 | 
																<T id="object.add" tData={{ object: "access-list" }} />
 | 
				
			||||||
										</Button>
 | 
															</Button>
 | 
				
			||||||
									) : null}
 | 
														) : null}
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { ACCESS_LISTS, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Access = () => {
 | 
					const Access = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="accessLists" type="view" pageLoading loadingNoLogo>
 | 
							<HasPermission section={ACCESS_LISTS} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { ADMIN, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AuditLog = () => {
 | 
					const AuditLog = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="admin" type="manage" pageLoading loadingNoLogo>
 | 
							<HasPermission section={ADMIN} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,10 +8,12 @@ import {
 | 
				
			|||||||
	DomainsFormatter,
 | 
						DomainsFormatter,
 | 
				
			||||||
	EmptyData,
 | 
						EmptyData,
 | 
				
			||||||
	GravatarFormatter,
 | 
						GravatarFormatter,
 | 
				
			||||||
 | 
						HasPermission,
 | 
				
			||||||
} from "src/components";
 | 
					} from "src/components";
 | 
				
			||||||
import { TableLayout } from "src/components/Table/TableLayout";
 | 
					import { TableLayout } from "src/components/Table/TableLayout";
 | 
				
			||||||
import { intl, T } from "src/locale";
 | 
					import { intl, T } from "src/locale";
 | 
				
			||||||
import { showCustomCertificateModal, showDNSCertificateModal, showHTTPCertificateModal } from "src/modals";
 | 
					import { showCustomCertificateModal, showDNSCertificateModal, showHTTPCertificateModal } from "src/modals";
 | 
				
			||||||
 | 
					import { CERTIFICATES, MANAGE } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	data: Certificate[];
 | 
						data: Certificate[];
 | 
				
			||||||
@@ -125,6 +127,7 @@ export default function Table({ data, isFetching, onDelete, onRenew, onDownload,
 | 
				
			|||||||
									<IconRefresh size={16} />
 | 
														<IconRefresh size={16} />
 | 
				
			||||||
									<T id="action.renew" />
 | 
														<T id="action.renew" />
 | 
				
			||||||
								</a>
 | 
													</a>
 | 
				
			||||||
 | 
													<HasPermission section={CERTIFICATES} permission={MANAGE} hideError>
 | 
				
			||||||
									<a
 | 
														<a
 | 
				
			||||||
										className="dropdown-item"
 | 
															className="dropdown-item"
 | 
				
			||||||
										href="#"
 | 
															href="#"
 | 
				
			||||||
@@ -148,6 +151,7 @@ export default function Table({ data, isFetching, onDelete, onRenew, onDownload,
 | 
				
			|||||||
										<IconTrash size={16} />
 | 
															<IconTrash size={16} />
 | 
				
			||||||
										<T id="action.delete" />
 | 
															<T id="action.delete" />
 | 
				
			||||||
									</a>
 | 
														</a>
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
@@ -223,6 +227,7 @@ export default function Table({ data, isFetching, onDelete, onRenew, onDownload,
 | 
				
			|||||||
					isFiltered={isFiltered}
 | 
										isFiltered={isFiltered}
 | 
				
			||||||
					color="pink"
 | 
										color="pink"
 | 
				
			||||||
					customAddBtn={customAddBtn}
 | 
										customAddBtn={customAddBtn}
 | 
				
			||||||
 | 
										permissionSection={CERTIFICATES}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { IconHelp, IconSearch } from "@tabler/icons-react";
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import Alert from "react-bootstrap/Alert";
 | 
					import Alert from "react-bootstrap/Alert";
 | 
				
			||||||
import { deleteCertificate, downloadCertificate } from "src/api/backend";
 | 
					import { deleteCertificate, downloadCertificate } from "src/api/backend";
 | 
				
			||||||
import { Button, LoadingPage } from "src/components";
 | 
					import { Button, HasPermission, LoadingPage } from "src/components";
 | 
				
			||||||
import { useCertificates } from "src/hooks";
 | 
					import { useCertificates } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@@ -13,6 +13,7 @@ import {
 | 
				
			|||||||
	showHTTPCertificateModal,
 | 
						showHTTPCertificateModal,
 | 
				
			||||||
	showRenewCertificateModal,
 | 
						showRenewCertificateModal,
 | 
				
			||||||
} from "src/modals";
 | 
					} from "src/modals";
 | 
				
			||||||
 | 
					import { CERTIFICATES, MANAGE } from "src/modules/Permissions";
 | 
				
			||||||
import { showError, showObjectSuccess } from "src/notifications";
 | 
					import { showError, showObjectSuccess } from "src/notifications";
 | 
				
			||||||
import Table from "./Table";
 | 
					import Table from "./Table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,7 +71,6 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<T id="certificates" />
 | 
													<T id="certificates" />
 | 
				
			||||||
							</h2>
 | 
												</h2>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
						<div className="col-md-auto col-sm-12">
 | 
											<div className="col-md-auto col-sm-12">
 | 
				
			||||||
							<div className="ms-auto d-flex flex-wrap btn-list">
 | 
												<div className="ms-auto d-flex flex-wrap btn-list">
 | 
				
			||||||
								{data?.length ? (
 | 
													{data?.length ? (
 | 
				
			||||||
@@ -90,6 +90,7 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<Button size="sm" onClick={() => showHelpModal("Certificates", "pink")}>
 | 
													<Button size="sm" onClick={() => showHelpModal("Certificates", "pink")}>
 | 
				
			||||||
									<IconHelp size={20} />
 | 
														<IconHelp size={20} />
 | 
				
			||||||
								</Button>
 | 
													</Button>
 | 
				
			||||||
 | 
													<HasPermission section={CERTIFICATES} permission={MANAGE} hideError>
 | 
				
			||||||
									{data?.length ? (
 | 
														{data?.length ? (
 | 
				
			||||||
										<div className="dropdown">
 | 
															<div className="dropdown">
 | 
				
			||||||
											<button
 | 
																<button
 | 
				
			||||||
@@ -134,6 +135,7 @@ export default function TableWrapper() {
 | 
				
			|||||||
											</div>
 | 
																</div>
 | 
				
			||||||
										</div>
 | 
															</div>
 | 
				
			||||||
									) : null}
 | 
														) : null}
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { CERTIFICATES, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Certificates = () => {
 | 
					const Certificates = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="certificates" type="view" pageLoading loadingNoLogo>
 | 
							<HasPermission section={CERTIFICATES} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom";
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
import { useHostReport } from "src/hooks";
 | 
					import { useHostReport } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
 | 
					import { DEAD_HOSTS, PROXY_HOSTS, REDIRECTION_HOSTS, STREAMS, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Dashboard = () => {
 | 
					const Dashboard = () => {
 | 
				
			||||||
	const { data: hostReport } = useHostReport();
 | 
						const { data: hostReport } = useHostReport();
 | 
				
			||||||
@@ -16,7 +17,7 @@ const Dashboard = () => {
 | 
				
			|||||||
			<div className="row row-deck row-cards">
 | 
								<div className="row row-deck row-cards">
 | 
				
			||||||
				<div className="col-12 my-4">
 | 
									<div className="col-12 my-4">
 | 
				
			||||||
					<div className="row row-cards">
 | 
										<div className="row row-cards">
 | 
				
			||||||
						<HasPermission permission="proxyHosts" type="view" hideError>
 | 
											<HasPermission section={PROXY_HOSTS} permission={VIEW} hideError>
 | 
				
			||||||
							<div className="col-sm-6 col-lg-3">
 | 
												<div className="col-sm-6 col-lg-3">
 | 
				
			||||||
								<a
 | 
													<a
 | 
				
			||||||
									href="/nginx/proxy"
 | 
														href="/nginx/proxy"
 | 
				
			||||||
@@ -43,7 +44,7 @@ const Dashboard = () => {
 | 
				
			|||||||
								</a>
 | 
													</a>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</HasPermission>
 | 
											</HasPermission>
 | 
				
			||||||
						<HasPermission permission="redirectionHosts" type="view" hideError>
 | 
											<HasPermission section={REDIRECTION_HOSTS} permission={VIEW} hideError>
 | 
				
			||||||
							<div className="col-sm-6 col-lg-3">
 | 
												<div className="col-sm-6 col-lg-3">
 | 
				
			||||||
								<a
 | 
													<a
 | 
				
			||||||
									href="/nginx/redirection"
 | 
														href="/nginx/redirection"
 | 
				
			||||||
@@ -71,7 +72,7 @@ const Dashboard = () => {
 | 
				
			|||||||
								</a>
 | 
													</a>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</HasPermission>
 | 
											</HasPermission>
 | 
				
			||||||
						<HasPermission permission="streams" type="view" hideError>
 | 
											<HasPermission section={STREAMS} permission={VIEW} hideError>
 | 
				
			||||||
							<div className="col-sm-6 col-lg-3">
 | 
												<div className="col-sm-6 col-lg-3">
 | 
				
			||||||
								<a
 | 
													<a
 | 
				
			||||||
									href="/nginx/stream"
 | 
														href="/nginx/stream"
 | 
				
			||||||
@@ -96,7 +97,7 @@ const Dashboard = () => {
 | 
				
			|||||||
								</a>
 | 
													</a>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</HasPermission>
 | 
											</HasPermission>
 | 
				
			||||||
						<HasPermission permission="deadHosts" type="view" hideError>
 | 
											<HasPermission section={DEAD_HOSTS} permission={VIEW} hideError>
 | 
				
			||||||
							<div className="col-sm-6 col-lg-3">
 | 
												<div className="col-sm-6 col-lg-3">
 | 
				
			||||||
								<a
 | 
													<a
 | 
				
			||||||
									href="/nginx/404"
 | 
														href="/nginx/404"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,12 @@ import {
 | 
				
			|||||||
	DomainsFormatter,
 | 
						DomainsFormatter,
 | 
				
			||||||
	EmptyData,
 | 
						EmptyData,
 | 
				
			||||||
	GravatarFormatter,
 | 
						GravatarFormatter,
 | 
				
			||||||
 | 
						HasPermission,
 | 
				
			||||||
	TrueFalseFormatter,
 | 
						TrueFalseFormatter,
 | 
				
			||||||
} from "src/components";
 | 
					} from "src/components";
 | 
				
			||||||
import { TableLayout } from "src/components/Table/TableLayout";
 | 
					import { TableLayout } from "src/components/Table/TableLayout";
 | 
				
			||||||
import { intl, T } from "src/locale";
 | 
					import { intl, T } from "src/locale";
 | 
				
			||||||
 | 
					import { DEAD_HOSTS, MANAGE } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	data: DeadHost[];
 | 
						data: DeadHost[];
 | 
				
			||||||
@@ -89,6 +91,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
									<IconEdit size={16} />
 | 
														<IconEdit size={16} />
 | 
				
			||||||
									<T id="action.edit" />
 | 
														<T id="action.edit" />
 | 
				
			||||||
								</a>
 | 
													</a>
 | 
				
			||||||
 | 
													<HasPermission section={DEAD_HOSTS} permission={MANAGE} hideError>
 | 
				
			||||||
									<a
 | 
														<a
 | 
				
			||||||
										className="dropdown-item"
 | 
															className="dropdown-item"
 | 
				
			||||||
										href="#"
 | 
															href="#"
 | 
				
			||||||
@@ -112,6 +115,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
										<IconTrash size={16} />
 | 
															<IconTrash size={16} />
 | 
				
			||||||
										<T id="action.delete" />
 | 
															<T id="action.delete" />
 | 
				
			||||||
									</a>
 | 
														</a>
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
@@ -146,6 +150,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
					onNew={onNew}
 | 
										onNew={onNew}
 | 
				
			||||||
					isFiltered={isFiltered}
 | 
										isFiltered={isFiltered}
 | 
				
			||||||
					color="red"
 | 
										color="red"
 | 
				
			||||||
 | 
										permissionSection={DEAD_HOSTS}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,11 @@ import { useQueryClient } from "@tanstack/react-query";
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import Alert from "react-bootstrap/Alert";
 | 
					import Alert from "react-bootstrap/Alert";
 | 
				
			||||||
import { deleteDeadHost, toggleDeadHost } from "src/api/backend";
 | 
					import { deleteDeadHost, toggleDeadHost } from "src/api/backend";
 | 
				
			||||||
import { Button, LoadingPage } from "src/components";
 | 
					import { Button, HasPermission, LoadingPage } from "src/components";
 | 
				
			||||||
import { useDeadHosts } from "src/hooks";
 | 
					import { useDeadHosts } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
import { showDeadHostModal, showDeleteConfirmModal, showHelpModal } from "src/modals";
 | 
					import { showDeadHostModal, showDeleteConfirmModal, showHelpModal } from "src/modals";
 | 
				
			||||||
 | 
					import { DEAD_HOSTS, MANAGE } from "src/modules/Permissions";
 | 
				
			||||||
import { showObjectSuccess } from "src/notifications";
 | 
					import { showObjectSuccess } from "src/notifications";
 | 
				
			||||||
import Table from "./Table";
 | 
					import Table from "./Table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -76,11 +77,13 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<Button size="sm" onClick={() => showHelpModal("DeadHosts", "red")}>
 | 
													<Button size="sm" onClick={() => showHelpModal("DeadHosts", "red")}>
 | 
				
			||||||
									<IconHelp size={20} />
 | 
														<IconHelp size={20} />
 | 
				
			||||||
								</Button>
 | 
													</Button>
 | 
				
			||||||
 | 
													<HasPermission section={DEAD_HOSTS} permission={MANAGE} hideError>
 | 
				
			||||||
									{data?.length ? (
 | 
														{data?.length ? (
 | 
				
			||||||
										<Button size="sm" className="btn-red" onClick={() => showDeadHostModal("new")}>
 | 
															<Button size="sm" className="btn-red" onClick={() => showDeadHostModal("new")}>
 | 
				
			||||||
											<T id="object.add" tData={{ object: "dead-host" }} />
 | 
																<T id="object.add" tData={{ object: "dead-host" }} />
 | 
				
			||||||
										</Button>
 | 
															</Button>
 | 
				
			||||||
									) : null}
 | 
														) : null}
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { DEAD_HOSTS, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DeadHosts = () => {
 | 
					const DeadHosts = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="deadHosts" type="view" pageLoading loadingNoLogo>
 | 
							<HasPermission section={DEAD_HOSTS} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,10 +8,12 @@ import {
 | 
				
			|||||||
	DomainsFormatter,
 | 
						DomainsFormatter,
 | 
				
			||||||
	EmptyData,
 | 
						EmptyData,
 | 
				
			||||||
	GravatarFormatter,
 | 
						GravatarFormatter,
 | 
				
			||||||
 | 
						HasPermission,
 | 
				
			||||||
	TrueFalseFormatter,
 | 
						TrueFalseFormatter,
 | 
				
			||||||
} from "src/components";
 | 
					} from "src/components";
 | 
				
			||||||
import { TableLayout } from "src/components/Table/TableLayout";
 | 
					import { TableLayout } from "src/components/Table/TableLayout";
 | 
				
			||||||
import { intl, T } from "src/locale";
 | 
					import { intl, T } from "src/locale";
 | 
				
			||||||
 | 
					import { MANAGE, PROXY_HOSTS } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	data: ProxyHost[];
 | 
						data: ProxyHost[];
 | 
				
			||||||
@@ -105,6 +107,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
									<IconEdit size={16} />
 | 
														<IconEdit size={16} />
 | 
				
			||||||
									<T id="action.edit" />
 | 
														<T id="action.edit" />
 | 
				
			||||||
								</a>
 | 
													</a>
 | 
				
			||||||
 | 
													<HasPermission section={PROXY_HOSTS} permission={MANAGE} hideError>
 | 
				
			||||||
									<a
 | 
														<a
 | 
				
			||||||
										className="dropdown-item"
 | 
															className="dropdown-item"
 | 
				
			||||||
										href="#"
 | 
															href="#"
 | 
				
			||||||
@@ -128,6 +131,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
										<IconTrash size={16} />
 | 
															<IconTrash size={16} />
 | 
				
			||||||
										<T id="action.delete" />
 | 
															<T id="action.delete" />
 | 
				
			||||||
									</a>
 | 
														</a>
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
@@ -162,6 +166,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
					onNew={onNew}
 | 
										onNew={onNew}
 | 
				
			||||||
					isFiltered={isFiltered}
 | 
										isFiltered={isFiltered}
 | 
				
			||||||
					color="lime"
 | 
										color="lime"
 | 
				
			||||||
 | 
										permissionSection={PROXY_HOSTS}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,11 @@ import { useQueryClient } from "@tanstack/react-query";
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import Alert from "react-bootstrap/Alert";
 | 
					import Alert from "react-bootstrap/Alert";
 | 
				
			||||||
import { deleteProxyHost, toggleProxyHost } from "src/api/backend";
 | 
					import { deleteProxyHost, toggleProxyHost } from "src/api/backend";
 | 
				
			||||||
import { Button, LoadingPage } from "src/components";
 | 
					import { Button, HasPermission, LoadingPage } from "src/components";
 | 
				
			||||||
import { useProxyHosts } from "src/hooks";
 | 
					import { useProxyHosts } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
import { showDeleteConfirmModal, showHelpModal, showProxyHostModal } from "src/modals";
 | 
					import { showDeleteConfirmModal, showHelpModal, showProxyHostModal } from "src/modals";
 | 
				
			||||||
 | 
					import { MANAGE, PROXY_HOSTS } from "src/modules/Permissions";
 | 
				
			||||||
import { showObjectSuccess } from "src/notifications";
 | 
					import { showObjectSuccess } from "src/notifications";
 | 
				
			||||||
import Table from "./Table";
 | 
					import Table from "./Table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +60,6 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<T id="proxy-hosts" />
 | 
													<T id="proxy-hosts" />
 | 
				
			||||||
							</h2>
 | 
												</h2>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
						<div className="col-md-auto col-sm-12">
 | 
											<div className="col-md-auto col-sm-12">
 | 
				
			||||||
							<div className="ms-auto d-flex flex-wrap btn-list">
 | 
												<div className="ms-auto d-flex flex-wrap btn-list">
 | 
				
			||||||
								{data?.length ? (
 | 
													{data?.length ? (
 | 
				
			||||||
@@ -79,11 +79,17 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<Button size="sm" onClick={() => showHelpModal("ProxyHosts", "lime")}>
 | 
													<Button size="sm" onClick={() => showHelpModal("ProxyHosts", "lime")}>
 | 
				
			||||||
									<IconHelp size={20} />
 | 
														<IconHelp size={20} />
 | 
				
			||||||
								</Button>
 | 
													</Button>
 | 
				
			||||||
 | 
													<HasPermission section={PROXY_HOSTS} permission={MANAGE} hideError>
 | 
				
			||||||
									{data?.length ? (
 | 
														{data?.length ? (
 | 
				
			||||||
									<Button size="sm" className="btn-lime" onClick={() => showProxyHostModal("new")}>
 | 
															<Button
 | 
				
			||||||
 | 
																size="sm"
 | 
				
			||||||
 | 
																className="btn-lime"
 | 
				
			||||||
 | 
																onClick={() => showProxyHostModal("new")}
 | 
				
			||||||
 | 
															>
 | 
				
			||||||
											<T id="object.add" tData={{ object: "proxy-host" }} />
 | 
																<T id="object.add" tData={{ object: "proxy-host" }} />
 | 
				
			||||||
										</Button>
 | 
															</Button>
 | 
				
			||||||
									) : null}
 | 
														) : null}
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { PROXY_HOSTS, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ProxyHosts = () => {
 | 
					const ProxyHosts = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="proxyHosts" type="view" pageLoading loadingNoLogo>
 | 
							<HasPermission section={PROXY_HOSTS} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,12 @@ import {
 | 
				
			|||||||
	DomainsFormatter,
 | 
						DomainsFormatter,
 | 
				
			||||||
	EmptyData,
 | 
						EmptyData,
 | 
				
			||||||
	GravatarFormatter,
 | 
						GravatarFormatter,
 | 
				
			||||||
 | 
						HasPermission,
 | 
				
			||||||
	TrueFalseFormatter,
 | 
						TrueFalseFormatter,
 | 
				
			||||||
} from "src/components";
 | 
					} from "src/components";
 | 
				
			||||||
import { TableLayout } from "src/components/Table/TableLayout";
 | 
					import { TableLayout } from "src/components/Table/TableLayout";
 | 
				
			||||||
import { intl, T } from "src/locale";
 | 
					import { intl, T } from "src/locale";
 | 
				
			||||||
 | 
					import { MANAGE, REDIRECTION_HOSTS } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	data: RedirectionHost[];
 | 
						data: RedirectionHost[];
 | 
				
			||||||
@@ -110,6 +112,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
									<IconEdit size={16} />
 | 
														<IconEdit size={16} />
 | 
				
			||||||
									<T id="action.edit" />
 | 
														<T id="action.edit" />
 | 
				
			||||||
								</a>
 | 
													</a>
 | 
				
			||||||
 | 
													<HasPermission section={REDIRECTION_HOSTS} permission={MANAGE} hideError>
 | 
				
			||||||
									<a
 | 
														<a
 | 
				
			||||||
										className="dropdown-item"
 | 
															className="dropdown-item"
 | 
				
			||||||
										href="#"
 | 
															href="#"
 | 
				
			||||||
@@ -133,6 +136,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
										<IconTrash size={16} />
 | 
															<IconTrash size={16} />
 | 
				
			||||||
										<T id="action.delete" />
 | 
															<T id="action.delete" />
 | 
				
			||||||
									</a>
 | 
														</a>
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
@@ -167,6 +171,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
 | 
				
			|||||||
					onNew={onNew}
 | 
										onNew={onNew}
 | 
				
			||||||
					isFiltered={isFiltered}
 | 
										isFiltered={isFiltered}
 | 
				
			||||||
					color="yellow"
 | 
										color="yellow"
 | 
				
			||||||
 | 
										permissionSection={REDIRECTION_HOSTS}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,11 @@ import { useQueryClient } from "@tanstack/react-query";
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import Alert from "react-bootstrap/Alert";
 | 
					import Alert from "react-bootstrap/Alert";
 | 
				
			||||||
import { deleteRedirectionHost, toggleRedirectionHost } from "src/api/backend";
 | 
					import { deleteRedirectionHost, toggleRedirectionHost } from "src/api/backend";
 | 
				
			||||||
import { Button, LoadingPage } from "src/components";
 | 
					import { Button, HasPermission, LoadingPage } from "src/components";
 | 
				
			||||||
import { useRedirectionHosts } from "src/hooks";
 | 
					import { useRedirectionHosts } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
import { showDeleteConfirmModal, showHelpModal, showRedirectionHostModal } from "src/modals";
 | 
					import { showDeleteConfirmModal, showHelpModal, showRedirectionHostModal } from "src/modals";
 | 
				
			||||||
 | 
					import { MANAGE, REDIRECTION_HOSTS } from "src/modules/Permissions";
 | 
				
			||||||
import { showObjectSuccess } from "src/notifications";
 | 
					import { showObjectSuccess } from "src/notifications";
 | 
				
			||||||
import Table from "./Table";
 | 
					import Table from "./Table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +60,6 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<T id="redirection-hosts" />
 | 
													<T id="redirection-hosts" />
 | 
				
			||||||
							</h2>
 | 
												</h2>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
						<div className="col-md-auto col-sm-12">
 | 
											<div className="col-md-auto col-sm-12">
 | 
				
			||||||
							<div className="ms-auto d-flex flex-wrap btn-list">
 | 
												<div className="ms-auto d-flex flex-wrap btn-list">
 | 
				
			||||||
								{data?.length ? (
 | 
													{data?.length ? (
 | 
				
			||||||
@@ -79,6 +79,7 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<Button size="sm" onClick={() => showHelpModal("RedirectionHosts", "yellow")}>
 | 
													<Button size="sm" onClick={() => showHelpModal("RedirectionHosts", "yellow")}>
 | 
				
			||||||
									<IconHelp size={20} />
 | 
														<IconHelp size={20} />
 | 
				
			||||||
								</Button>
 | 
													</Button>
 | 
				
			||||||
 | 
													<HasPermission section={REDIRECTION_HOSTS} permission={MANAGE} hideError>
 | 
				
			||||||
									{data?.length ? (
 | 
														{data?.length ? (
 | 
				
			||||||
										<Button
 | 
															<Button
 | 
				
			||||||
											size="sm"
 | 
																size="sm"
 | 
				
			||||||
@@ -88,6 +89,7 @@ export default function TableWrapper() {
 | 
				
			|||||||
											<T id="object.add" tData={{ object: "redirection-host" }} />
 | 
																<T id="object.add" tData={{ object: "redirection-host" }} />
 | 
				
			||||||
										</Button>
 | 
															</Button>
 | 
				
			||||||
									) : null}
 | 
														) : null}
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { REDIRECTION_HOSTS, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const RedirectionHosts = () => {
 | 
					const RedirectionHosts = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="redirectionHosts" type="view" pageLoading loadingNoLogo>
 | 
							<HasPermission section={REDIRECTION_HOSTS} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,11 +6,13 @@ import {
 | 
				
			|||||||
	CertificateFormatter,
 | 
						CertificateFormatter,
 | 
				
			||||||
	EmptyData,
 | 
						EmptyData,
 | 
				
			||||||
	GravatarFormatter,
 | 
						GravatarFormatter,
 | 
				
			||||||
 | 
						HasPermission,
 | 
				
			||||||
	TrueFalseFormatter,
 | 
						TrueFalseFormatter,
 | 
				
			||||||
	ValueWithDateFormatter,
 | 
						ValueWithDateFormatter,
 | 
				
			||||||
} from "src/components";
 | 
					} from "src/components";
 | 
				
			||||||
import { TableLayout } from "src/components/Table/TableLayout";
 | 
					import { TableLayout } from "src/components/Table/TableLayout";
 | 
				
			||||||
import { intl, T } from "src/locale";
 | 
					import { intl, T } from "src/locale";
 | 
				
			||||||
 | 
					import { MANAGE, STREAMS } from "src/modules/Permissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
	data: Stream[];
 | 
						data: Stream[];
 | 
				
			||||||
@@ -118,6 +120,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
 | 
				
			|||||||
									<IconEdit size={16} />
 | 
														<IconEdit size={16} />
 | 
				
			||||||
									<T id="action.edit" />
 | 
														<T id="action.edit" />
 | 
				
			||||||
								</a>
 | 
													</a>
 | 
				
			||||||
 | 
													<HasPermission section={STREAMS} permission={MANAGE} hideError>
 | 
				
			||||||
									<a
 | 
														<a
 | 
				
			||||||
										className="dropdown-item"
 | 
															className="dropdown-item"
 | 
				
			||||||
										href="#"
 | 
															href="#"
 | 
				
			||||||
@@ -141,6 +144,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
 | 
				
			|||||||
										<IconTrash size={16} />
 | 
															<IconTrash size={16} />
 | 
				
			||||||
										<T id="action.delete" />
 | 
															<T id="action.delete" />
 | 
				
			||||||
									</a>
 | 
														</a>
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
@@ -175,6 +179,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
 | 
				
			|||||||
					onNew={onNew}
 | 
										onNew={onNew}
 | 
				
			||||||
					isFiltered={isFiltered}
 | 
										isFiltered={isFiltered}
 | 
				
			||||||
					color="blue"
 | 
										color="blue"
 | 
				
			||||||
 | 
										permissionSection={STREAMS}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,11 @@ import { useQueryClient } from "@tanstack/react-query";
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import Alert from "react-bootstrap/Alert";
 | 
					import Alert from "react-bootstrap/Alert";
 | 
				
			||||||
import { deleteStream, toggleStream } from "src/api/backend";
 | 
					import { deleteStream, toggleStream } from "src/api/backend";
 | 
				
			||||||
import { Button, LoadingPage } from "src/components";
 | 
					import { Button, HasPermission, LoadingPage } from "src/components";
 | 
				
			||||||
import { useStreams } from "src/hooks";
 | 
					import { useStreams } from "src/hooks";
 | 
				
			||||||
import { T } from "src/locale";
 | 
					import { T } from "src/locale";
 | 
				
			||||||
import { showDeleteConfirmModal, showHelpModal, showStreamModal } from "src/modals";
 | 
					import { showDeleteConfirmModal, showHelpModal, showStreamModal } from "src/modals";
 | 
				
			||||||
 | 
					import { MANAGE, STREAMS } from "src/modules/Permissions";
 | 
				
			||||||
import { showObjectSuccess } from "src/notifications";
 | 
					import { showObjectSuccess } from "src/notifications";
 | 
				
			||||||
import Table from "./Table";
 | 
					import Table from "./Table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,7 +62,6 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<T id="streams" />
 | 
													<T id="streams" />
 | 
				
			||||||
							</h2>
 | 
												</h2>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
						<div className="col-md-auto col-sm-12">
 | 
											<div className="col-md-auto col-sm-12">
 | 
				
			||||||
							<div className="ms-auto d-flex flex-wrap btn-list">
 | 
												<div className="ms-auto d-flex flex-wrap btn-list">
 | 
				
			||||||
								{data?.length ? (
 | 
													{data?.length ? (
 | 
				
			||||||
@@ -81,11 +81,13 @@ export default function TableWrapper() {
 | 
				
			|||||||
								<Button size="sm" onClick={() => showHelpModal("Streams", "blue")}>
 | 
													<Button size="sm" onClick={() => showHelpModal("Streams", "blue")}>
 | 
				
			||||||
									<IconHelp size={20} />
 | 
														<IconHelp size={20} />
 | 
				
			||||||
								</Button>
 | 
													</Button>
 | 
				
			||||||
 | 
													<HasPermission section={STREAMS} permission={MANAGE} hideError>
 | 
				
			||||||
									{data?.length ? (
 | 
														{data?.length ? (
 | 
				
			||||||
										<Button size="sm" className="btn-blue" onClick={() => showStreamModal("new")}>
 | 
															<Button size="sm" className="btn-blue" onClick={() => showStreamModal("new")}>
 | 
				
			||||||
											<T id="object.add" tData={{ object: "stream" }} />
 | 
																<T id="object.add" tData={{ object: "stream" }} />
 | 
				
			||||||
										</Button>
 | 
															</Button>
 | 
				
			||||||
									) : null}
 | 
														) : null}
 | 
				
			||||||
 | 
													</HasPermission>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { STREAMS, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Streams = () => {
 | 
					const Streams = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="streams" type="view" pageLoading loadingNoLogo>
 | 
							<HasPermission section={STREAMS} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { ADMIN, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import Layout from "./Layout";
 | 
					import Layout from "./Layout";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Settings = () => {
 | 
					const Settings = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="admin" type="manage" pageLoading loadingNoLogo>
 | 
							<HasPermission section={ADMIN} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<Layout />
 | 
								<Layout />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { HasPermission } from "src/components";
 | 
					import { HasPermission } from "src/components";
 | 
				
			||||||
 | 
					import { ADMIN, VIEW } from "src/modules/Permissions";
 | 
				
			||||||
import TableWrapper from "./TableWrapper";
 | 
					import TableWrapper from "./TableWrapper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Users = () => {
 | 
					const Users = () => {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<HasPermission permission="admin" type="manage" pageLoading loadingNoLogo>
 | 
							<HasPermission section={ADMIN} permission={VIEW} pageLoading loadingNoLogo>
 | 
				
			||||||
			<TableWrapper />
 | 
								<TableWrapper />
 | 
				
			||||||
		</HasPermission>
 | 
							</HasPermission>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user