firewall Frontend Improvements

This commit is contained in:
Domingo Dirutigliano
2023-09-24 17:05:55 +02:00
parent 4247dc181d
commit 594e6b4e00
10 changed files with 389 additions and 26 deletions

View File

@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@emotion/react": "^11.11.0",
"@hello-pangea/dnd": "^16.3.0",
"@mantine/core": "^6.0.13",
"@mantine/form": "^6.0.13",
"@mantine/hooks": "^6.0.13",
@@ -21,6 +22,7 @@
"@types/react": "^18.0.12",
"@types/react-dom": "^18.0.5",
"buffer": "^6.0.3",
"clsx": "^2.0.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-icons": "^4.4.0",

View File

@@ -50,7 +50,7 @@ export type ServerResponseListed = {
}
export const rulesQueryKey = ["firewall","rules"]
export const firewallRulesQuery = () => useQuery({queryKey:rulesQueryKey, queryFn:firewall.rules})
export const firewallRulesQuery = () => useQuery({queryKey:rulesQueryKey, queryFn:firewall.rules, refetchInterval: false})
export const firewall = {
rules: async() => {
@@ -72,7 +72,7 @@ export const firewall = {
const { status } = await postapi(`firewall/rule/${rule_id}/rename`,{ name }) as ServerResponse;
return status === "ok"?undefined:status
},
servicesadd: async (data:RuleAddForm) => {
ruleset: async (data:RuleAddForm) => {
return await postapi("firewall/rules/set", data) as ServerResponseListed;
}
}

View File

@@ -97,8 +97,8 @@ export function getapiobject(){
export function HomeRedirector(){
const section = sessionStorage.getItem("home_section")
const path = section?`/${section}`:`/nfregex`
return <Navigate to={path} />
const path = section?`/${section}`:`/firewall`
return <Navigate to={path} replace/>
}
export function fireUpdateRequest(){ //TODO: change me: specify what to update

View File

@@ -0,0 +1,24 @@
.item {
display: flex;
align-items: center;
border-radius: var(--mantine-radius-md);
border: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
padding: var(--mantine-spacing-sm) var(--mantine-spacing-xl);
padding-left: calc(var(--mantine-spacing-xl) - var(--mantine-spacing-md));
background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-5));
margin-bottom: var(--mantine-spacing-sm);
}
.itemDragging {
box-shadow: var(--mantine-shadow-sm);
}
.dragHandle {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-1));
padding-left: var(--mantine-spacing-md);
padding-right: var(--mantine-spacing-md);
}

View File

@@ -1,42 +1,261 @@
import { ActionIcon, Badge, LoadingOverlay, Space, Title, Tooltip } from "@mantine/core"
import { ActionIcon, Badge, LoadingOverlay, NativeSelect, SegmentedControl, Space, Switch, Title, Tooltip } from "@mantine/core"
import { useEffect, useState } from "react";
import { BsPlusLg } from "react-icons/bs"
import { firewall, firewallRulesQuery } from "../../components/Firewall/utils";
import { errorNotify, getErrorMessage } from "../../js/utils";
import { rem, Text } from '@mantine/core';
import { ActionType, Rule, firewall, firewallRulesQuery } from "../../components/Firewall/utils";
import cx from 'clsx'
import { errorNotify, getErrorMessage, okNotify } from "../../js/utils";
import { useListState } from '@mantine/hooks';
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
import { TbGripVertical, TbReload } from "react-icons/tb";
import classes from './DndListHandle.module.scss';
import { useQueryClient } from "@tanstack/react-query";
import { TiTick } from "react-icons/ti";
import YesNoModal from "../../components/YesNoModal";
/*
{
"rules": [
{
"active": true,
"name": "R1",
"proto": "tcp",
"ip_src": "0.0.0.0/0",
"ip_dst": "0.0.0.0/0",
"port_src_from": 1,
"port_dst_from": 3030,
"port_src_to": 65535,
"port_dst_to": 3030,
"action": "reject",
"mode": "I"
},
{
"active": true,
"name": "R2",
"proto": "tcp",
"ip_src": "0.0.0.0/0",
"ip_dst": "0.0.0.0/0",
"port_src_from": 1,
"port_dst_from": 3030,
"port_src_to": 65535,
"port_dst_to": 3030,
"action": "drop",
"mode": "O"
},
{
"active": false,
"name": "R3",
"proto": "udp",
"ip_src": "192.168.0.1/24",
"ip_dst": "0.0.0.0/0",
"port_src_from": 1,
"port_dst_from": 2020,
"port_src_to": 65535,
"port_dst_to": 2020,
"action": "drop",
"mode": "I"
},
{
"active": true,
"name": "R4",
"proto": "any",
"ip_src": "::/0",
"ip_dst": "fdfd::ffff:123/64",
"port_src_from": 1,
"port_dst_from": 1,
"port_src_to": 1,
"port_dst_to": 1,
"action": "accept",
"mode": "I"
}
],
"policy": "accept"
}
*/
export const Firewall = () => {
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
const [tooltipApplyOpened, setTooltipApplyOpened] = useState(false);
const [open, setOpen] = useState(false);
const [currentPolicy, setCurrentPolicy] = useState<ActionType>(ActionType.ACCEPT)
const queryClient = useQueryClient()
const rules = firewallRulesQuery()
const [state, handlers] = useListState<Rule>([]);
const [enableFwModal, setEnableFwModal] = useState(false)
const [applyChangeModal, setApplyChangeModal] = useState(false)
useEffect(()=> {
if(rules.isError)
errorNotify("Firewall Update failed!", getErrorMessage(rules.error))
},[rules.isError])
useEffect(()=> {
if(!rules.isLoading && rules.isSuccess)
handlers.setState(JSON.parse(JSON.stringify(rules.data?.rules??[])))
},[rules.isSuccess, rules.isLoading])
const fwEnabled = rules.data?.enabled??false
const valuesChanged = JSON.stringify(rules.data?.rules) != JSON.stringify(state) || rules.data?.policy != currentPolicy
const enableFirewall = () => {
if (valuesChanged){
applyChangesRaw().then(()=>enableFirewallRaw())
}else{
enableFirewallRaw()
}
}
const enableFirewallRaw = () => {
return firewall.enable()
.then(()=>okNotify("Firewall enabled", "The firewall has been enabled"))
.catch((e)=>errorNotify("Firewall enable failed!", getErrorMessage(e)))
}
const disableFirewallRaw = () => {
return firewall.disable()
.then(()=>okNotify("Firewall disabled", "The firewall has been disabled"))
.catch((e)=>errorNotify("Firewall disable failed!", getErrorMessage(e)))
}
const switchState = () => {
if (fwEnabled)
disableFirewallRaw()
else
if ([ActionType.REJECT, ActionType.DROP].includes(currentPolicy) || valuesChanged){
setEnableFwModal(true)
}else{
enableFirewall()
}
}
const applyChanges = () => {
if (fwEnabled && rules.data?.policy == ActionType.ACCEPT && [ActionType.REJECT, ActionType.DROP].includes(currentPolicy)){
setApplyChangeModal(true)
}else{
applyChangesRaw()
}
}
const applyChangesRaw = () => {
return firewall.ruleset({rules:state, policy:currentPolicy})
.then(()=>okNotify("Firewall rules applied", "The firewall rules has been applied"))
.catch((e)=>errorNotify("Firewall rules apply failed!", getErrorMessage(e)))
}
const items = state.map((item, index) => (
<Draggable key={index} index={index} draggableId={index.toString()}>
{(provided, snapshot) => (
<div
className={cx(classes.item, { [classes.itemDragging]: snapshot.isDragging })}
ref={provided.innerRef}
{...provided.draggableProps}
>
<div {...provided.dragHandleProps} className={classes.dragHandle}>
<TbGripVertical style={{ width: rem(18), height: rem(18) }} />
</div>
<Space w="sm" />
<Switch
defaultChecked
label="I agree to sell my privacy"
/>
<div>
<Text>{item.name}</Text>
<Text c="dimmed" size="sm">
{JSON.stringify(item)}
</Text>
</div>
</div>
)}
</Draggable>
));
return <>
<Space h="sm" />
<LoadingOverlay visible={rules.isLoading} />
<div className='center-flex'>
<Title order={4}>Firewall Rules</Title>
<div className='flex-spacer' />
<Title order={3}>Firewall Rules</Title>
<div className='flex-spacer' />
Enabled: <Space w="sm" /> <Switch checked={fwEnabled} onChange={switchState} />
<Space w="sm" />
Policy:
<Space w="xs" />
<SegmentedControl
data={[
{
value: ActionType.ACCEPT,
label: 'Accept',
},
{
value: ActionType.REJECT,
label: 'Reject',
},
{
value: ActionType.DROP,
label: 'Drop',
}
]}
value={currentPolicy}
onChange={(value)=>setCurrentPolicy(value as ActionType)}
/>
<Space w="xs" />
<Badge size="sm" color="green" variant="filled">Rules: {rules.isLoading?0:rules.data?.rules.length}</Badge>
<Space w="xs" />
<Badge size="sm" color="yellow" variant="filled">Policy: {rules.isLoading?"unknown":rules.data?.policy}</Badge>
<Space w="xs" />
<Badge size="sm" color="violet" variant="filled">Enabled: {rules.isLoading?"?":(rules.data?.enabled?"🟢":"🔴")}</Badge>
<Space w="xs" />
<Tooltip label="Add a new rule" position='bottom' color="blue" opened={tooltipAddOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
</Tooltip>
<Space w="xs" />
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["firewall"])} size="lg" radius="md" variant="filled"
loading={rules.isFetching}
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip>
<Space w="xs" />
<Tooltip label="Apply" position='bottom' color="grape" opened={tooltipApplyOpened}>
<ActionIcon color="grape" onClick={applyChanges} size="lg" radius="md" variant="filled"
onFocus={() => setTooltipApplyOpened(false)} onBlur={() => setTooltipApplyOpened(false)}
onMouseEnter={() => setTooltipApplyOpened(true)} onMouseLeave={() => setTooltipApplyOpened(false)}
disabled={!valuesChanged}
><TiTick size={22} /></ActionIcon>
</Tooltip>
</div>
<LoadingOverlay visible={rules.isLoading} />
<Space h="xl" />
<DragDropContext
onDragEnd={({ destination, source }) =>
handlers.reorder({ from: source.index, to: destination?.index || 0 })
}
>
<Droppable droppableId="dnd-list" direction="vertical">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{items}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<YesNoModal
title='Are you sure to apply the changes to the firewall?'
description={`If there is a malconfiguration you can lose the access to your server! ⚠️`}
onClose={()=>setEnableFwModal(false) }
action={enableFirewall}
opened={enableFwModal}
/>
<YesNoModal
title='Are you sure to apply the changes to the firewall?'
description={`If there is a malconfiguration you can lose the access to your server! ⚠️`}
onClose={()=>setApplyChangeModal(false) }
action={applyChangesRaw}
opened={applyChangeModal}
/>
</>
}

View File

@@ -7,6 +7,8 @@ import { nfregexServiceQuery } from '../../components/NFRegex/utils';
import { errorNotify, getErrorMessage } from '../../js/utils';
import AddNewService from '../../components/NFRegex/AddNewService';
import AddNewRegex from '../../components/AddNewRegex';
import { useQueryClient } from '@tanstack/react-query';
import { TbReload } from 'react-icons/tb';
function NFRegex({ children }: { children: any }) {
@@ -14,16 +16,16 @@ function NFRegex({ children }: { children: any }) {
const navigator = useNavigate()
const [open, setOpen] = useState(false);
const {srv} = useParams()
const queryClient = useQueryClient()
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
const [tooltipAddServOpened, setTooltipAddServOpened] = useState(false);
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
const services = nfregexServiceQuery()
useEffect(()=> {
if(services.isError){
if(services.isError)
errorNotify("NFRegex Update failed!", getErrorMessage(services.error))
}
},[services.isError])
const closeModal = () => {setOpen(false);}
@@ -51,6 +53,13 @@ function NFRegex({ children }: { children: any }) {
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
</Tooltip>
}
<Space w="xs" />
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["nfregex"])} size="lg" radius="md" variant="filled"
loading={services.isFetching}
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip>
</div>
<div id="service-list" className="center-flex-row">
{srv?null:<>

View File

@@ -5,6 +5,8 @@ import ServiceRow from '../../components/PortHijack/ServiceRow';
import { porthijackServiceQuery } from '../../components/PortHijack/utils';
import { errorNotify, getErrorMessage } from '../../js/utils';
import AddNewService from '../../components/PortHijack/AddNewService';
import { useQueryClient } from '@tanstack/react-query';
import { TbReload } from 'react-icons/tb';
function PortHijack() {
@@ -12,14 +14,15 @@ function PortHijack() {
const [open, setOpen] = useState(false);
const [tooltipAddServOpened, setTooltipAddServOpened] = useState(false);
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
const queryClient = useQueryClient()
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
const services = porthijackServiceQuery()
useEffect(()=>{
if(services.isError){
if(services.isError)
errorNotify("Porthijack Update failed!", getErrorMessage(services.error))
}
},[services.isError])
const closeModal = () => {setOpen(false);}
@@ -36,6 +39,13 @@ function PortHijack() {
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
</Tooltip>
<Space w="xs" />
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["porthijack"])} size="lg" radius="md" variant="filled"
loading={services.isFetching}
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip>
</div>
<div id="service-list" className="center-flex-row">
<LoadingOverlay visible={services.isLoading} />
@@ -52,7 +62,6 @@ function PortHijack() {
</>}
<AddNewService opened={open} onClose={closeModal} />
</div>
<AddNewService opened={open} onClose={closeModal} />
</>
}

View File

@@ -7,6 +7,8 @@ import { regexproxyServiceQuery } from '../../components/RegexProxy/utils';
import { errorNotify, getErrorMessage } from '../../js/utils';
import AddNewService from '../../components/RegexProxy/AddNewService';
import AddNewRegex from '../../components/AddNewRegex';
import { useQueryClient } from '@tanstack/react-query';
import { TbReload } from 'react-icons/tb';
function RegexProxy({ children }: { children: any }) {
@@ -16,6 +18,8 @@ function RegexProxy({ children }: { children: any }) {
const {srv} = useParams()
const [tooltipAddServOpened, setTooltipAddServOpened] = useState(false);
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
const queryClient = useQueryClient()
const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false);
const services = regexproxyServiceQuery()
@@ -50,6 +54,13 @@ function RegexProxy({ children }: { children: any }) {
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size={18} /></ActionIcon>
</Tooltip>
}
<Space w="xs" />
<Tooltip label="Refresh" position='bottom' color="indigo" opened={tooltipRefreshOpened}>
<ActionIcon color="indigo" onClick={()=>queryClient.invalidateQueries(["regexproxy"])} size="lg" radius="md" variant="filled"
loading={services.isFetching}
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip>
</div>
<div id="service-list" className="center-flex-row">
{srv?null:<>

View File

@@ -188,6 +188,13 @@
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.12.1", "@babel/runtime@^7.22.5":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
@@ -448,6 +455,19 @@
aria-hidden "^1.1.3"
tabbable "^6.0.1"
"@hello-pangea/dnd@^16.3.0":
version "16.3.0"
resolved "https://registry.yarnpkg.com/@hello-pangea/dnd/-/dnd-16.3.0.tgz#3776212f812df4e8e69c42831ec8ab7ff3a087d6"
integrity sha512-RYQ/K8shtJoyNPvFWz0gfXIK7HF3P3mL9UZFGMuHB0ljRSXVgMjVFI/FxcZmakMzw6tO7NflWLriwTNBow/4vw==
dependencies:
"@babel/runtime" "^7.22.5"
css-box-model "^1.2.1"
memoize-one "^6.0.0"
raf-schd "^4.0.3"
react-redux "^8.1.1"
redux "^4.2.1"
use-memo-one "^1.1.3"
"@jest/expect-utils@^29.5.0":
version "29.5.0"
resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036"
@@ -873,6 +893,14 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
"@types/hoist-non-react-statics@^3.3.1":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#dc1e9ded53375d37603c479cc12c693b0878aa2a"
integrity sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44"
@@ -956,6 +984,11 @@
dependencies:
"@types/jest" "*"
"@types/use-sync-external-store@^0.0.3":
version "0.0.3"
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
@@ -1166,6 +1199,11 @@ clsx@1.1.1:
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
clsx@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@@ -1223,6 +1261,13 @@ cosmiconfig@^8.1.3:
parse-json "^5.0.0"
path-type "^4.0.0"
css-box-model@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
dependencies:
tiny-invariant "^1.0.6"
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
@@ -1556,7 +1601,7 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hoist-non-react-statics@^3.3.1:
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -1900,6 +1945,11 @@ lz-string@^1.5.0:
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
memoize-one@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
@@ -2044,6 +2094,11 @@ prop-types@^15.6.2:
object-assign "^4.1.1"
react-is "^16.13.1"
raf-schd@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
react-dom@^18.1.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@@ -2072,6 +2127,18 @@ react-is@^18.0.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-redux@^8.1.1:
version "8.1.2"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.2.tgz#9076bbc6b60f746659ad6d51cb05de9c5e1e9188"
integrity sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==
dependencies:
"@babel/runtime" "^7.12.1"
"@types/hoist-non-react-statics" "^3.3.1"
"@types/use-sync-external-store" "^0.0.3"
hoist-non-react-statics "^3.3.2"
react-is "^18.0.0"
use-sync-external-store "^1.0.0"
react-refresh@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
@@ -2161,11 +2228,23 @@ redent@^3.0.0:
indent-string "^4.0.0"
strip-indent "^3.0.0"
redux@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
dependencies:
"@babel/runtime" "^7.9.2"
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
regexp.prototype.flags@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb"
@@ -2326,6 +2405,11 @@ tabbable@^6.0.1:
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
tiny-invariant@^1.0.6:
version "1.3.1"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -2385,6 +2469,11 @@ use-latest@^1.2.1:
dependencies:
use-isomorphic-layout-effect "^1.1.1"
use-memo-one@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
use-sidecar@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"
@@ -2393,7 +2482,7 @@ use-sidecar@^1.1.2:
detect-node-es "^1.1.0"
tslib "^2.0.0"
use-sync-external-store@^1.2.0:
use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==