From d456e9687d5b201085871d8eee2ac7be99ad57f6 Mon Sep 17 00:00:00 2001 From: Francesco Aliprandi Date: Tue, 5 Mar 2024 22:38:58 +0100 Subject: [PATCH 1/5] feat(frontend): create `ServiceRegexesList` component --- .../src/components/ServiceRegexesList.tsx | 112 ++++++++++++++++++ web/frontend/src/pages/Service.tsx | 11 +- 2 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 web/frontend/src/components/ServiceRegexesList.tsx diff --git a/web/frontend/src/components/ServiceRegexesList.tsx b/web/frontend/src/components/ServiceRegexesList.tsx new file mode 100644 index 0000000..b2012c5 --- /dev/null +++ b/web/frontend/src/components/ServiceRegexesList.tsx @@ -0,0 +1,112 @@ +import { Button, Spinner, Tab, Tabs, Tooltip } from "@nextui-org/react" +import { FaPen, FaTrash, FaArrowLeft, FaArrowRight } from "react-icons/fa6" +import { useFetchSync } from "../hooks/useFetch" +import { CerberoRegexes } from "../types/cerbero" + +export type ServiceRegexesListProps = { + nfq: string +} + +export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { + const [ + response, + isLoading, + error + ] = useFetchSync(`/api/regexes/${nfq}`) + + if(isLoading) { + return ( +
+ +
+ ) + } + + if(error) { + return ( +
+ {error.status} ({error.statusText}) + {error.data.error} +
+ ) + } + + return ( + + + {response?.regexes.active.length === 0 ? +
+ No regexes here, cerbero went to bed... +
: +
    + {response?.regexes.active.map((regex, i) => { + return ( +
  • +
    +
    + + {regex} +
    +
    + + + + + + + + + +
    +
    +
  • + ) + })} +
} +
+ + {response?.regexes.inactive.length === 0 ? +
+ No regexes here, cerbero went to bed... +
: +
    + {response?.regexes.inactive.map((regex, i) => { + return ( +
  • +
    +
    + + {regex} +
    +
    + + + + + + + + + +
    +
    +
  • + ) + })} +
} +
+
+ ) +} diff --git a/web/frontend/src/pages/Service.tsx b/web/frontend/src/pages/Service.tsx index a74352b..36dea6c 100644 --- a/web/frontend/src/pages/Service.tsx +++ b/web/frontend/src/pages/Service.tsx @@ -1,6 +1,7 @@ -import { Chip } from "@nextui-org/react" +import { Card, CardBody, Chip } from "@nextui-org/react" import { useParams } from "react-router-dom" import Header from "../components/Header" +import ServiceRegexesList from "../components/ServiceRegexesList" import { useFetchSync } from "../hooks/useFetch" import Main from "../layouts/Main" import Page from "../layouts/Page" @@ -55,9 +56,11 @@ export default function Service() {
Regexes -
- Placeholder -
+ + + + +
From 0948d5b54aa422b691c0ae63550e18402ee8d978 Mon Sep 17 00:00:00 2001 From: Francesco Aliprandi Date: Wed, 6 Mar 2024 13:20:03 +0100 Subject: [PATCH 2/5] feat(components): add functionality for adding regexes --- .../src/components/ServiceRegexesList.tsx | 69 ++++++++++++++++--- web/frontend/src/hooks/useFetch.ts | 10 +-- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/web/frontend/src/components/ServiceRegexesList.tsx b/web/frontend/src/components/ServiceRegexesList.tsx index b2012c5..459f8c7 100644 --- a/web/frontend/src/components/ServiceRegexesList.tsx +++ b/web/frontend/src/components/ServiceRegexesList.tsx @@ -1,18 +1,58 @@ -import { Button, Spinner, Tab, Tabs, Tooltip } from "@nextui-org/react" -import { FaPen, FaTrash, FaArrowLeft, FaArrowRight } from "react-icons/fa6" -import { useFetchSync } from "../hooks/useFetch" +import { Button, Input, Spinner, Tab, Tabs, Tooltip } from "@nextui-org/react" +import { useEffect, useState } from "react" +import { FaArrowLeft, FaArrowRight, FaCircleCheck, FaPen, FaTrash } from "react-icons/fa6" +import { useFetch, useFetchSync } from "../hooks/useFetch" import { CerberoRegexes } from "../types/cerbero" export type ServiceRegexesListProps = { nfq: string } +type NewRegexResponse = { + regexes: { + active: string[] + inactive: string[] + } +} + export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { const [ response, + fetchRegexes, isLoading, error - ] = useFetchSync(`/api/regexes/${nfq}`) + ] = useFetch() + + const [newRegex, setNewRegex] = useState("") + + const [ + , + newRegexFetch, + isNewRegexLoading, + ] = useFetch() + + useEffect(() => { + fetchRegexes(`/api/regexes/${nfq}`) + }, []) + + async function addNewRegex() { + if(!newRegex) { + return + } + + await newRegexFetch(`/api/regexes/${nfq}`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + regexes: [newRegex] + }) + }) + + setNewRegex("") + fetchRegexes(`/api/regexes/${nfq}`) + } if(isLoading) { return ( @@ -33,10 +73,23 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { return ( - + +
+ setNewRegex(target.value)} + className="bg-transparent" + /> + +
{response?.regexes.active.length === 0 ?
- No regexes here, cerbero went to bed... + No regexes here, add one from the input field above!
:
    {response?.regexes.active.map((regex, i) => { @@ -45,7 +98,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) {
    - {regex} + {regex}
    @@ -73,7 +126,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { {response?.regexes.inactive.length === 0 ?
    - No regexes here, cerbero went to bed... + No regexes here, deactivate one from the `Active` tab.
    :
      {response?.regexes.inactive.map((regex, i) => { diff --git a/web/frontend/src/hooks/useFetch.ts b/web/frontend/src/hooks/useFetch.ts index 8b285d2..a0e1ae0 100644 --- a/web/frontend/src/hooks/useFetch.ts +++ b/web/frontend/src/hooks/useFetch.ts @@ -18,9 +18,9 @@ export type UseFetchError = { * 2. A `boolean` flag that states if the request is loading. * 3. In case of an error a `UseFetchError` object, otherwise `undefined`. */ -export function useFetch(url: string, init?: RequestInit): [ +export function useFetch(): [ T | undefined, - () => Promise, + (url: string, init?: RequestInit) => Promise, boolean, UseFetchError | undefined ] { @@ -28,7 +28,7 @@ export function useFetch(url: string, init?: RequestInit): [ const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState() - async function triggerFetch() { + async function triggerFetch(url: string, init?: RequestInit) { setIsLoading(true) const response = await fetch(url, init) @@ -76,10 +76,10 @@ export function useFetchSync(url: string, init?: RequestInit): [ triggerFetch, isLoading, error - ] = useFetch(url, init) + ] = useFetch() useEffect(() => { - void triggerFetch() + void triggerFetch(url, init) }, []) return [ From 0224d8b932243e1be7c344481e8e1f22c5a5d409 Mon Sep 17 00:00:00 2001 From: Francesco Aliprandi Date: Wed, 6 Mar 2024 13:52:01 +0100 Subject: [PATCH 3/5] feat(components): add functionality for deleting regexes --- .../src/components/ServiceRegexesList.tsx | 30 ++++++++++++------- web/frontend/src/utils/regexes.ts | 7 +++++ 2 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 web/frontend/src/utils/regexes.ts diff --git a/web/frontend/src/components/ServiceRegexesList.tsx b/web/frontend/src/components/ServiceRegexesList.tsx index 459f8c7..e55f57c 100644 --- a/web/frontend/src/components/ServiceRegexesList.tsx +++ b/web/frontend/src/components/ServiceRegexesList.tsx @@ -1,20 +1,14 @@ import { Button, Input, Spinner, Tab, Tabs, Tooltip } from "@nextui-org/react" import { useEffect, useState } from "react" -import { FaArrowLeft, FaArrowRight, FaCircleCheck, FaPen, FaTrash } from "react-icons/fa6" -import { useFetch, useFetchSync } from "../hooks/useFetch" +import { FaArrowLeft, FaArrowRight, FaPen, FaTrash } from "react-icons/fa6" +import { useFetch } from "../hooks/useFetch" import { CerberoRegexes } from "../types/cerbero" +import { hexEncodeRegex } from "../utils/regexes" export type ServiceRegexesListProps = { nfq: string } -type NewRegexResponse = { - regexes: { - active: string[] - inactive: string[] - } -} - export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { const [ response, @@ -29,7 +23,13 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { , newRegexFetch, isNewRegexLoading, - ] = useFetch() + ] = useFetch() + + const [ + , + deleteRegexFetch, + isDeleteRegexLoading, + ] = useFetch() useEffect(() => { fetchRegexes(`/api/regexes/${nfq}`) @@ -54,6 +54,14 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { fetchRegexes(`/api/regexes/${nfq}`) } + async function deleteRegex(reghex: string, state: "active" | "inactive") { + await deleteRegexFetch(`/api/regexes/${nfq}?reghex=${reghex}&state=${state}`, { + method: "DELETE" + }) + + fetchRegexes(`/api/regexes/${nfq}`) + } + if(isLoading) { return (
      @@ -112,7 +120,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { - diff --git a/web/frontend/src/utils/regexes.ts b/web/frontend/src/utils/regexes.ts new file mode 100644 index 0000000..efd3875 --- /dev/null +++ b/web/frontend/src/utils/regexes.ts @@ -0,0 +1,7 @@ +export function hexEncodeRegex(regex: string) { + const encoder = new TextEncoder(); + const encodedArray = encoder.encode(regex); + const encodedRegex = Array.from(encodedArray).map(byte => byte.toString(16).padStart(2, '0')).join(''); + + return encodedRegex +} From 7c1deb9757153477a943db643a3b9ef0f5eac6c2 Mon Sep 17 00:00:00 2001 From: Francesco Aliprandi Date: Wed, 6 Mar 2024 17:49:36 +0100 Subject: [PATCH 4/5] feat(components): add functionality to activate/deactivate regexes --- .../src/components/ServiceRegexesList.tsx | 81 +++++++++++-------- web/frontend/src/hooks/useFetch.ts | 33 +++++--- web/frontend/src/utils/regexes.ts | 8 +- 3 files changed, 72 insertions(+), 50 deletions(-) diff --git a/web/frontend/src/components/ServiceRegexesList.tsx b/web/frontend/src/components/ServiceRegexesList.tsx index e55f57c..2cfb8e5 100644 --- a/web/frontend/src/components/ServiceRegexesList.tsx +++ b/web/frontend/src/components/ServiceRegexesList.tsx @@ -1,9 +1,9 @@ import { Button, Input, Spinner, Tab, Tabs, Tooltip } from "@nextui-org/react" import { useEffect, useState } from "react" -import { FaArrowLeft, FaArrowRight, FaPen, FaTrash } from "react-icons/fa6" +import { FaArrowLeft, FaArrowRight, FaTrash } from "react-icons/fa6" import { useFetch } from "../hooks/useFetch" import { CerberoRegexes } from "../types/cerbero" -import { hexEncodeRegex } from "../utils/regexes" +import { hexEncode } from "../utils/regexes" export type ServiceRegexesListProps = { nfq: string @@ -11,10 +11,10 @@ export type ServiceRegexesListProps = { export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { const [ - response, - fetchRegexes, - isLoading, - error + regexesResponse, + regexesFetch, + isRegexesLoading, + regexesError ] = useFetch() const [newRegex, setNewRegex] = useState("") @@ -25,6 +25,12 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { isNewRegexLoading, ] = useFetch() + const [ + , + editRegexFetch, + isEditRegexFetchLoading + ] = useFetch() + const [ , deleteRegexFetch, @@ -32,7 +38,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { ] = useFetch() useEffect(() => { - fetchRegexes(`/api/regexes/${nfq}`) + regexesFetch(`/api/regexes/${nfq}`) }, []) async function addNewRegex() { @@ -51,18 +57,37 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { }) setNewRegex("") - fetchRegexes(`/api/regexes/${nfq}`) + regexesFetch(`/api/regexes/${nfq}`) + } + + async function editRegex(regex: string, currentState: "active" | "inactive", newRegex: string, newState: "active" | "inactive") { + const reghex = hexEncode(regex) + + await editRegexFetch(`/api/regexes/${nfq}?reghex=${reghex}&state=${currentState}`, { + method: "PUT", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + regex: newRegex, + state: newState + }) + }) + + regexesFetch(`/api/regexes/${nfq}`) } - async function deleteRegex(reghex: string, state: "active" | "inactive") { + async function deleteRegex(regex: string, state: "active" | "inactive") { + const reghex = hexEncode(regex) + await deleteRegexFetch(`/api/regexes/${nfq}?reghex=${reghex}&state=${state}`, { method: "DELETE" }) - fetchRegexes(`/api/regexes/${nfq}`) + regexesFetch(`/api/regexes/${nfq}`) } - if(isLoading) { + if(isRegexesLoading) { return (
      @@ -70,11 +95,11 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { ) } - if(error) { + if(regexesError) { return (
      - {error.status} ({error.statusText}) - {error.data.error} + {regexesError.status} ({regexesError.statusText}) + {regexesError.data.error}
      ) } @@ -95,12 +120,12 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { Add regex
      - {response?.regexes.active.length === 0 ? + {regexesResponse?.regexes.active.length === 0 ?
      No regexes here, add one from the input field above!
      :
        - {response?.regexes.active.map((regex, i) => { + {regexesResponse?.regexes.active.map((regex, i) => { return (
      • @@ -109,18 +134,13 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { {regex}
        - - - - - @@ -132,12 +152,12 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) {
      } - {response?.regexes.inactive.length === 0 ? + {regexesResponse?.regexes.inactive.length === 0 ?
      No regexes here, deactivate one from the `Active` tab.
      :
        - {response?.regexes.inactive.map((regex, i) => { + {regexesResponse?.regexes.inactive.map((regex, i) => { return (
      • @@ -146,18 +166,13 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { {regex}
        - - - - - - diff --git a/web/frontend/src/hooks/useFetch.ts b/web/frontend/src/hooks/useFetch.ts index a0e1ae0..d2331a3 100644 --- a/web/frontend/src/hooks/useFetch.ts +++ b/web/frontend/src/hooks/useFetch.ts @@ -26,26 +26,33 @@ export function useFetch(): [ ] { const [response, setResponse] = useState() const [isLoading, setIsLoading] = useState(false) + // TODO: set 400+ responses in response instead of error const [error, setError] = useState() async function triggerFetch(url: string, init?: RequestInit) { - setIsLoading(true) + try { + setIsLoading(true) - const response = await fetch(url, init) - const responseJson = await response.json() as T + const response = await fetch(url, init) + const responseJson = await response.json() as T - if(response.ok) { - setResponse(responseJson) + if(response.ok) { + setResponse(responseJson) + } + else { + setError({ + status: response.status, + statusText: response.statusText, + data: responseJson as UseFetchError["data"] + }) + } } - else { - setError({ - status: response.status, - statusText: response.statusText, - data: responseJson as UseFetchError["data"] - }) + catch(e) { + console.error(e) + } + finally { + setIsLoading(false) } - - setIsLoading(false) } return [ diff --git a/web/frontend/src/utils/regexes.ts b/web/frontend/src/utils/regexes.ts index efd3875..cfecfe3 100644 --- a/web/frontend/src/utils/regexes.ts +++ b/web/frontend/src/utils/regexes.ts @@ -1,7 +1,7 @@ -export function hexEncodeRegex(regex: string) { +export function hexEncode(s: string) { const encoder = new TextEncoder(); - const encodedArray = encoder.encode(regex); - const encodedRegex = Array.from(encodedArray).map(byte => byte.toString(16).padStart(2, '0')).join(''); + const encodedArray = encoder.encode(s); + const encodedS = Array.from(encodedArray).map(byte => byte.toString(16).padStart(2, '0')).join(''); - return encodedRegex + return encodedS } From 8cb83992d78714083b30eaba9f1beb4273a7bf07 Mon Sep 17 00:00:00 2001 From: Francesco Aliprandi Date: Wed, 6 Mar 2024 18:01:14 +0100 Subject: [PATCH 5/5] style(frontend): re-format following eslint rules --- .../src/components/ServiceRegexesList.tsx | 18 +++++++++--------- web/frontend/src/hooks/useFetch.ts | 2 +- web/frontend/src/utils/regexes.ts | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/web/frontend/src/components/ServiceRegexesList.tsx b/web/frontend/src/components/ServiceRegexesList.tsx index 2cfb8e5..2dfca60 100644 --- a/web/frontend/src/components/ServiceRegexesList.tsx +++ b/web/frontend/src/components/ServiceRegexesList.tsx @@ -38,7 +38,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { ] = useFetch() useEffect(() => { - regexesFetch(`/api/regexes/${nfq}`) + void regexesFetch(`/api/regexes/${nfq}`) }, []) async function addNewRegex() { @@ -57,7 +57,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { }) setNewRegex("") - regexesFetch(`/api/regexes/${nfq}`) + void regexesFetch(`/api/regexes/${nfq}`) } async function editRegex(regex: string, currentState: "active" | "inactive", newRegex: string, newState: "active" | "inactive") { @@ -74,7 +74,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { }) }) - regexesFetch(`/api/regexes/${nfq}`) + void regexesFetch(`/api/regexes/${nfq}`) } async function deleteRegex(regex: string, state: "active" | "inactive") { @@ -84,7 +84,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { method: "DELETE" }) - regexesFetch(`/api/regexes/${nfq}`) + void regexesFetch(`/api/regexes/${nfq}`) } if(isRegexesLoading) { @@ -116,7 +116,7 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) { onChange={({ target }) => setNewRegex(target.value)} className="bg-transparent" /> -
        @@ -135,12 +135,12 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) {
      - - @@ -167,12 +167,12 @@ export default function ServiceRegexesList({ nfq }: ServiceRegexesListProps) {
      - - diff --git a/web/frontend/src/hooks/useFetch.ts b/web/frontend/src/hooks/useFetch.ts index d2331a3..1f7edce 100644 --- a/web/frontend/src/hooks/useFetch.ts +++ b/web/frontend/src/hooks/useFetch.ts @@ -23,7 +23,7 @@ export function useFetch(): [ (url: string, init?: RequestInit) => Promise, boolean, UseFetchError | undefined -] { + ] { const [response, setResponse] = useState() const [isLoading, setIsLoading] = useState(false) // TODO: set 400+ responses in response instead of error diff --git a/web/frontend/src/utils/regexes.ts b/web/frontend/src/utils/regexes.ts index cfecfe3..04ba1b2 100644 --- a/web/frontend/src/utils/regexes.ts +++ b/web/frontend/src/utils/regexes.ts @@ -1,7 +1,7 @@ export function hexEncode(s: string) { - const encoder = new TextEncoder(); - const encodedArray = encoder.encode(s); - const encodedS = Array.from(encodedArray).map(byte => byte.toString(16).padStart(2, '0')).join(''); + const encoder = new TextEncoder() + const encodedArray = encoder.encode(s) + const encodedS = Array.from(encodedArray).map(byte => byte.toString(16).padStart(2, "0")).join("") return encodedS }