From ca5d94dc84afae701a27102ed1ae45902e9a2558 Mon Sep 17 00:00:00 2001 From: nikolajovancevic Date: Tue, 30 Jul 2024 14:55:49 +0200 Subject: [PATCH 001/111] feat: added new selectors --- .../src/components/atoms/ShowMoreButton.tsx | 2 +- .../molecules/GovernanceActionsFilters.tsx | 2 +- .../molecules/GovernanceActionsSorting.tsx | 5 +++- .../molecules/UserProfileButton.tsx | 3 +++ .../components/organisms/Footer/Footer.tsx | 25 +++++++++++++++---- .../src/components/organisms/Hero/Hero.tsx | 9 +++++-- .../components/organisms/Hero/HeroActions.tsx | 2 ++ .../Modals/PreviewReasoningModal.tsx | 22 +++++++++++----- .../organisms/Modals/SignInModal.tsx | 14 +++++++++-- .../organisms/Modals/SignOutModal.tsx | 12 +++++++-- .../organisms/Modals/SignUpModal.tsx | 2 ++ .../src/components/organisms/NotFound.tsx | 4 +-- .../components/organisms/PageTitleTabs.tsx | 1 + .../organisms/TopNavigation/TopNav.tsx | 3 ++- 14 files changed, 83 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/atoms/ShowMoreButton.tsx b/frontend/src/components/atoms/ShowMoreButton.tsx index e6fa7bd4..7dfed1d2 100644 --- a/frontend/src/components/atoms/ShowMoreButton.tsx +++ b/frontend/src/components/atoms/ShowMoreButton.tsx @@ -15,7 +15,7 @@ export const ShowMoreButton = ({ isLoading, hasNextPage, callBack }: Props) => { ) : ( hasNextPage && ( - diff --git a/frontend/src/components/molecules/GovernanceActionsFilters.tsx b/frontend/src/components/molecules/GovernanceActionsFilters.tsx index 2d4b930a..4f8d19df 100644 --- a/frontend/src/components/molecules/GovernanceActionsFilters.tsx +++ b/frontend/src/components/molecules/GovernanceActionsFilters.tsx @@ -104,7 +104,7 @@ export const GovernanceActionsFilters = ({ /> } label={ - + {item.label} } diff --git a/frontend/src/components/molecules/GovernanceActionsSorting.tsx b/frontend/src/components/molecules/GovernanceActionsSorting.tsx index eca84d2e..3e0d758f 100644 --- a/frontend/src/components/molecules/GovernanceActionsSorting.tsx +++ b/frontend/src/components/molecules/GovernanceActionsSorting.tsx @@ -73,7 +73,10 @@ export const GovernanceActionsSorting = ({ key={item.key} value={item.key} control={} - label={item.label} + // todo: check this with kiki + label={ + {item.label} + } /> ))} diff --git a/frontend/src/components/molecules/UserProfileButton.tsx b/frontend/src/components/molecules/UserProfileButton.tsx index 70f2ad55..d7c9d9c9 100644 --- a/frontend/src/components/molecules/UserProfileButton.tsx +++ b/frontend/src/components/molecules/UserProfileButton.tsx @@ -65,6 +65,7 @@ export default function UserProfileButton({ /> } endIcon={} + data-testid="user-profile-menu-button" > {user?.name || "User"} @@ -92,6 +93,7 @@ export default function UserProfileButton({ variant="outlined" onClick={editProfile} startIcon={} + data-testid="edit-profile-button" > {t("Navigation.editProfile")} @@ -100,6 +102,7 @@ export default function UserProfileButton({ variant="outlined" onClick={signOut} startIcon={} + data-testid="sign-out-button" > {t("Navigation.signOut")} diff --git a/frontend/src/components/organisms/Footer/Footer.tsx b/frontend/src/components/organisms/Footer/Footer.tsx index c6d35fa1..c2878ce0 100644 --- a/frontend/src/components/organisms/Footer/Footer.tsx +++ b/frontend/src/components/organisms/Footer/Footer.tsx @@ -35,7 +35,11 @@ export const Footer = ({ > - + {t("copyright")} @@ -47,10 +51,18 @@ export const Footer = ({ gap={2} alignItems={{ xxs: "flex-start", md: "center" }} > - + {t("privacyPolicy")} - + {t("termsOfService")} @@ -70,7 +82,8 @@ export const Footer = ({ }); }} > - {t("signIn")} + // todo: check this with kiki + {t("signIn")} )} @@ -87,7 +100,9 @@ export const Footer = ({ - + diff --git a/frontend/src/components/organisms/Hero/Hero.tsx b/frontend/src/components/organisms/Hero/Hero.tsx index 770199a2..b2014f8c 100644 --- a/frontend/src/components/organisms/Hero/Hero.tsx +++ b/frontend/src/components/organisms/Hero/Hero.tsx @@ -24,7 +24,11 @@ export const Hero = ({ children }: HeroProps) => { sx={{ backgroundColor: customPalette.arcticWhite }} > - + {t("hero.headline")} { maxWidth: 630, my: { xxs: 2, md: 5 }, whiteSpace: "pre-line", - }} + }} + data-testid="hero-section-description-text" > {t("hero.description")} diff --git a/frontend/src/components/organisms/Hero/HeroActions.tsx b/frontend/src/components/organisms/Hero/HeroActions.tsx index 758ab677..f1618206 100644 --- a/frontend/src/components/organisms/Hero/HeroActions.tsx +++ b/frontend/src/components/organisms/Hero/HeroActions.tsx @@ -24,6 +24,7 @@ export function HeroActions({ role }: HeroActionsProps) { type: "signIn", }); }} + data-testid="admin-hero-sign-in-button" > {t("hero.signIn")} @@ -34,6 +35,7 @@ export function HeroActions({ role }: HeroActionsProps) { variant="outlined" size="large" startIcon={} + data-testid="admin-hero-const-comitee-portal-button" > {t("hero.constitutionalCommitteePortal")} diff --git a/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx b/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx index f63fc363..c831b64a 100644 --- a/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx +++ b/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx @@ -118,13 +118,17 @@ export const PreviewReasoningModal = () => { src={IMAGES.pastelAddMember} /> - {t("previewRationale.headline")} + // todo: check this with kiki + + {t("previewRationale.headline")} + {t("previewRationale.description")} @@ -151,6 +155,7 @@ export const PreviewReasoningModal = () => { description={reasoning.contents.content} link={reasoning.url} hash={reasoning.blake2b} + data-testid="asdf" /> )} @@ -162,7 +167,7 @@ export const PreviewReasoningModal = () => { {getShortenedGovActionId(govAction.tx_hash, isMobile ? 4 : 20)} - + {t("previewRationale.governanceActionCategory")} @@ -175,7 +180,11 @@ export const PreviewReasoningModal = () => { {t("previewRationale.voted")} - + @@ -189,7 +198,7 @@ export const PreviewReasoningModal = () => { tooltipParagraph={t( "previewRationale.tooltips.submissionDate.paragraphOne" )} - dataTestId="submit-date" + dataTestId="submit-date-row" /> )} {govAction.vote_submit_time && ( @@ -202,7 +211,7 @@ export const PreviewReasoningModal = () => { tooltipParagraph={t( "previewRationale.tooltips.submissionDate.vote.paragraphOne" )} - dataTestId="vote-submit-date" + dataTestId="vote-submit-date-row" /> )} {govAction.end_time && ( @@ -213,8 +222,8 @@ export const PreviewReasoningModal = () => { tooltipParagraph={t( "previewRationale.tooltips.expiryDate.paragraphTwo" )} - dataTestId="expiry-date" bgColor="rgba(247, 249, 251, 1)" + dataTestId="expiry-date-row" /> )} @@ -244,6 +253,7 @@ export const PreviewReasoningModal = () => { sx={{ width: "100%", }} + data-testid="rationale-modal-close-button" > {t("common.close")} diff --git a/frontend/src/components/organisms/Modals/SignInModal.tsx b/frontend/src/components/organisms/Modals/SignInModal.tsx index d220e7d7..26f20aa9 100644 --- a/frontend/src/components/organisms/Modals/SignInModal.tsx +++ b/frontend/src/components/organisms/Modals/SignInModal.tsx @@ -34,10 +34,19 @@ export const SignInModal = () => { return ( - {t("signIn.headline")} + + // todo: check this with kiki + + {t("signIn.headline")} + +
- + {t("signIn.description")} @@ -47,6 +56,7 @@ export const SignInModal = () => { control={control} type="email" {...register("email", { required: "Email is required" })} + data-testid="sign-in-modal-input-field" /> diff --git a/frontend/src/components/organisms/Modals/SignOutModal.tsx b/frontend/src/components/organisms/Modals/SignOutModal.tsx index 3e02fc86..8bd6f430 100644 --- a/frontend/src/components/organisms/Modals/SignOutModal.tsx +++ b/frontend/src/components/organisms/Modals/SignOutModal.tsx @@ -25,10 +25,18 @@ export const SignOutModal = () => { return ( - {t("signOut.headline")} + + + {t("signOut.headline")} + + - + {t("signOut.description")} diff --git a/frontend/src/components/organisms/Modals/SignUpModal.tsx b/frontend/src/components/organisms/Modals/SignUpModal.tsx index 21f0bcfe..8b2856c5 100644 --- a/frontend/src/components/organisms/Modals/SignUpModal.tsx +++ b/frontend/src/components/organisms/Modals/SignUpModal.tsx @@ -164,6 +164,8 @@ export const SignUpModal = () => { ) : ( - diff --git a/frontend/src/components/organisms/Modals/AddMember.tsx b/frontend/src/components/organisms/Modals/AddMember.tsx index 89afebf4..1910dbea 100644 --- a/frontend/src/components/organisms/Modals/AddMember.tsx +++ b/frontend/src/components/organisms/Modals/AddMember.tsx @@ -89,7 +89,7 @@ export const AddMemberModal = () => { /> )} - +
diff --git a/frontend/src/components/organisms/Modals/UploadConstitution.tsx b/frontend/src/components/organisms/Modals/UploadConstitution.tsx index efcc2e83..48009abc 100644 --- a/frontend/src/components/organisms/Modals/UploadConstitution.tsx +++ b/frontend/src/components/organisms/Modals/UploadConstitution.tsx @@ -66,7 +66,10 @@ export const UploadConstitution = () => { > {t("uploadConstitution.upload")} - +
From dc47fa00390900b2fa0d26ca1fc13b5aa05c74d3 Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 12 Aug 2024 14:05:37 +0200 Subject: [PATCH 008/111] feat: AdminTopNav Search component selector id --- frontend/src/components/molecules/Search.tsx | 9 +++++++-- .../components/organisms/TopNavigation/AdminTopNav.tsx | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/molecules/Search.tsx b/frontend/src/components/molecules/Search.tsx index 91eec640..b8d99c3c 100644 --- a/frontend/src/components/molecules/Search.tsx +++ b/frontend/src/components/molecules/Search.tsx @@ -9,9 +9,14 @@ import { useEffect, useState } from "react"; interface Props { setSearchText: (value) => void; delay?: number; + dataTestId?: string; } -export const Search = ({ setSearchText, delay = 500 }: Props) => { +export const Search = ({ + setSearchText, + delay = 500, + dataTestId = "search-input", +}: Props) => { const [searchInput, setSearchInput] = useState(""); const debouncedSearchInput = useDebounce(searchInput, delay); @@ -21,7 +26,7 @@ export const Search = ({ setSearchText, delay = 500 }: Props) => { return ( setSearchInput(e.target.value)} placeholder="Search..." value={searchInput} diff --git a/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx b/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx index bdace67c..e78e4df2 100644 --- a/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx +++ b/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx @@ -50,7 +50,10 @@ export const AdminTopNav = () => { width="100%" > - + { {!!userSession && ( <> - + {getNavItems()} From 4822824255cbdb7f2b55ca776d6d0983682460d7 Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 12 Aug 2024 14:08:34 +0200 Subject: [PATCH 009/111] feat: Admin signout & modal selector ids --- frontend/src/components/organisms/Footer/AdminFooter.tsx | 1 + frontend/src/components/organisms/Modals/SignOutModal.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/organisms/Footer/AdminFooter.tsx b/frontend/src/components/organisms/Footer/AdminFooter.tsx index de7e812b..96e6ca18 100644 --- a/frontend/src/components/organisms/Footer/AdminFooter.tsx +++ b/frontend/src/components/organisms/Footer/AdminFooter.tsx @@ -39,6 +39,7 @@ export const AdminFooter = () => { } variant="outlined" size="medium" + data-testid="admin-signout-button" > {t("signOut")} diff --git a/frontend/src/components/organisms/Modals/SignOutModal.tsx b/frontend/src/components/organisms/Modals/SignOutModal.tsx index 8bd6f430..0cf1773f 100644 --- a/frontend/src/components/organisms/Modals/SignOutModal.tsx +++ b/frontend/src/components/organisms/Modals/SignOutModal.tsx @@ -26,7 +26,7 @@ export const SignOutModal = () => { return ( - + {t("signOut.headline")} @@ -39,7 +39,7 @@ export const SignOutModal = () => { > {t("signOut.description")}
- + From c4d2bbac5924eae43f4d9bf66ea2ecc82233c1c0 Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 12 Aug 2024 14:11:49 +0200 Subject: [PATCH 010/111] feat: AdminTopNav buttons selector ids --- .../organisms/TopNavigation/AdminTopNav.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx b/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx index e78e4df2..a873c1e0 100644 --- a/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx +++ b/frontend/src/components/organisms/TopNavigation/AdminTopNav.tsx @@ -66,6 +66,7 @@ export const AdminTopNav = () => { variant="outlined" href={PATHS.constitution} component={Link} + data-testid="admin-top-nav-see-constitution-button" > {t("seeConstituton")} @@ -73,7 +74,12 @@ export const AdminTopNav = () => { permissions={userSession?.permissions} requiredPermission="manage_cc_members" > - @@ -81,7 +87,12 @@ export const AdminTopNav = () => { permissions={userSession?.permissions} requiredPermission="add_constitution_version" > - From 941e4c860eb1433920fd3ef1fdcdae717938f54c Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 12 Aug 2024 14:28:34 +0200 Subject: [PATCH 011/111] feat: Navigation links selector ids --- frontend/src/components/atoms/Button.tsx | 2 ++ frontend/src/components/atoms/types.ts | 1 + frontend/src/components/molecules/UserProfileButton.tsx | 2 +- frontend/src/components/organisms/TopNavigation/TopNav.tsx | 4 ++-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/atoms/Button.tsx b/frontend/src/components/atoms/Button.tsx index ea877d75..67f6ef2f 100644 --- a/frontend/src/components/atoms/Button.tsx +++ b/frontend/src/components/atoms/Button.tsx @@ -7,6 +7,7 @@ export const Button = ({ size = "large", variant = "contained", sx, + dataTestId, ...props }: ButtonProps) => { const buttonHeight = { @@ -24,6 +25,7 @@ export const Button = ({ ...sx, }} variant={variant} + data-testid={dataTestId} {...props} > {props.children} diff --git a/frontend/src/components/atoms/types.ts b/frontend/src/components/atoms/types.ts index 2ed9a110..3f14181e 100644 --- a/frontend/src/components/atoms/types.ts +++ b/frontend/src/components/atoms/types.ts @@ -10,6 +10,7 @@ import { ButtonProps as MUIButtonProps } from "@mui/material/Button"; export type ButtonProps = Omit & { size?: "small" | "medium" | "large" | "extraLarge"; + dataTestId?: string; }; export type TypographyProps = Pick< diff --git a/frontend/src/components/molecules/UserProfileButton.tsx b/frontend/src/components/molecules/UserProfileButton.tsx index d7c9d9c9..f9fd64cf 100644 --- a/frontend/src/components/molecules/UserProfileButton.tsx +++ b/frontend/src/components/molecules/UserProfileButton.tsx @@ -65,7 +65,7 @@ export default function UserProfileButton({ /> } endIcon={} - data-testid="user-profile-menu-button" + dataTestId="user-profile-menu-button" > {user?.name || "User"} diff --git a/frontend/src/components/organisms/TopNavigation/TopNav.tsx b/frontend/src/components/organisms/TopNavigation/TopNav.tsx index fdb49854..e63dfb06 100644 --- a/frontend/src/components/organisms/TopNavigation/TopNav.tsx +++ b/frontend/src/components/organisms/TopNavigation/TopNav.tsx @@ -33,8 +33,7 @@ export const TopNav = () => { items.map((navItem) => ( @@ -52,6 +51,7 @@ export const TopNav = () => { variant="outlined" href={PATHS.admin.dashboard} component={NextLink} + data-testid="top-nav-admin-dashboard-button" > {t("adminDashboard")} From d54372be06245ad96ad109224e48d5c85b69f55b Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 12 Aug 2024 14:35:01 +0200 Subject: [PATCH 012/111] Feat: Footer selector ids --- frontend/src/components/organisms/Footer/Footer.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/organisms/Footer/Footer.tsx b/frontend/src/components/organisms/Footer/Footer.tsx index 5db99c27..7e52e2ed 100644 --- a/frontend/src/components/organisms/Footer/Footer.tsx +++ b/frontend/src/components/organisms/Footer/Footer.tsx @@ -85,14 +85,14 @@ export const Footer = ({ sx={{ cursor: "pointer", ml: { xxs: 0, md: 1 } }} fontWeight={500} variant="caption" + data-testid="footer-sign-in-button" onClick={() => { openModal({ type: "signIn", }); }} > - // todo: check this with kiki - {t("signIn")} + {t("signIn")}
)} @@ -102,6 +102,7 @@ export const Footer = ({ href={EXTERNAL_LINKS.guides} startIcon={} variant="text" + data-testid="footer-guides-button" > {t("guides")} From b9788049cd3adb755e7af89a8d9e8f486ef01df2 Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 12 Aug 2024 15:21:00 +0200 Subject: [PATCH 013/111] feat: Constitution page selector ids --- frontend/src/components/atoms/CopyButton.tsx | 11 +++++++++-- frontend/src/components/molecules/Card.tsx | 3 +++ .../organisms/Constitution/Constitution.tsx | 6 +++++- .../organisms/Constitution/MDXComponents.tsx | 8 +++++++- .../src/components/organisms/Hero/HeroActions.tsx | 6 +++++- .../organisms/Modals/CompareConstitutionModal.tsx | 6 +++++- 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/atoms/CopyButton.tsx b/frontend/src/components/atoms/CopyButton.tsx index c020ce1d..d9da8988 100644 --- a/frontend/src/components/atoms/CopyButton.tsx +++ b/frontend/src/components/atoms/CopyButton.tsx @@ -11,9 +11,16 @@ interface Props { text: string; variant?: string; size?: number; + dataTestId?: string; } -export const CopyButton = ({ isChecked, text, variant, size = 24 }: Props) => { +export const CopyButton = ({ + isChecked, + text, + variant, + size = 24, + dataTestId = "copy-button", +}: Props) => { const { addSuccessAlert } = useSnackbar(); const t = useTranslations("Snackbar"); @@ -31,7 +38,7 @@ export const CopyButton = ({ isChecked, text, variant, size = 24 }: Props) => { return ( copy { navigator.clipboard.writeText(text); diff --git a/frontend/src/components/molecules/Card.tsx b/frontend/src/components/molecules/Card.tsx index f13818a9..443e7cbd 100644 --- a/frontend/src/components/molecules/Card.tsx +++ b/frontend/src/components/molecules/Card.tsx @@ -16,6 +16,7 @@ type CardProps = PropsWithChildren & { label?: string; sx?: SxProps; variant?: "default" | "error" | "primary" | "success" | "warning"; + dataTestId?: string; }; export const Card = ({ @@ -25,6 +26,7 @@ export const Card = ({ elevation = 4, label, sx, + dataTestId = "card", }: CardProps) => { const colors = COLORS[variant]; @@ -40,6 +42,7 @@ export const Card = ({ position: "relative", ...sx, }} + data-testid={dataTestId} > {label && ( setIsOpen(!isOpen)} top={{ xxs: 75, md: 90 }} left={0} + dataTestId="constitution-nav-drawer" > - + diff --git a/frontend/src/components/organisms/Constitution/MDXComponents.tsx b/frontend/src/components/organisms/Constitution/MDXComponents.tsx index 481308f8..932f716a 100644 --- a/frontend/src/components/organisms/Constitution/MDXComponents.tsx +++ b/frontend/src/components/organisms/Constitution/MDXComponents.tsx @@ -123,15 +123,18 @@ export const NavDrawer = ({ isOpen, left = 0, top = { xxs: 75, md: 90 }, + dataTestId, }: { children: ReactNode; onClick: () => void; isOpen: boolean; left: number; top: { xxs: number; md: number }; + dataTestId?: string; }) => { return ( ( - + ( size="medium" onClick={onClick} variant="outlined" + data-testid="compare-button" > {buttonLabel} diff --git a/frontend/src/components/organisms/Hero/HeroActions.tsx b/frontend/src/components/organisms/Hero/HeroActions.tsx index d34ed4a5..37d0a1fd 100644 --- a/frontend/src/components/organisms/Hero/HeroActions.tsx +++ b/frontend/src/components/organisms/Hero/HeroActions.tsx @@ -45,7 +45,11 @@ export function HeroActions({ role }: HeroActionsProps) { ) : ( - diff --git a/frontend/src/components/organisms/Modals/CompareConstitutionModal.tsx b/frontend/src/components/organisms/Modals/CompareConstitutionModal.tsx index 41e893b3..7dfb1586 100644 --- a/frontend/src/components/organisms/Modals/CompareConstitutionModal.tsx +++ b/frontend/src/components/organisms/Modals/CompareConstitutionModal.tsx @@ -111,7 +111,11 @@ export const CompareConstitutionModal = () => { ) : ( )} - From e61a861ecb7909c70ffa569c5b527b16cc1d6329 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 13 Aug 2024 12:30:37 +0200 Subject: [PATCH 014/111] feat: Latest updates and votes selector ids --- frontend/src/components/atoms/VotePill.tsx | 1 + .../src/components/molecules/CopyCard.tsx | 4 +++- .../src/components/molecules/CopyPill.tsx | 4 +++- .../src/components/molecules/Reasoning.tsx | 7 +++++- .../organisms/Modals/GovActionModal.tsx | 24 +++++++++++++++---- .../Modals/PreviewReasoningModal.tsx | 7 ++++-- .../organisms/VotesTable/VotesTableRow.tsx | 11 ++++++--- 7 files changed, 46 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/atoms/VotePill.tsx b/frontend/src/components/atoms/VotePill.tsx index 3ce29312..72b22e74 100644 --- a/frontend/src/components/atoms/VotePill.tsx +++ b/frontend/src/components/atoms/VotePill.tsx @@ -33,6 +33,7 @@ export const VotePill = ({ fontSize={12} fontWeight={400} lineHeight="16px" + data-testid="ga-vote-text" > {vote} diff --git a/frontend/src/components/molecules/CopyCard.tsx b/frontend/src/components/molecules/CopyCard.tsx index 2af7d12d..359a989a 100644 --- a/frontend/src/components/molecules/CopyCard.tsx +++ b/frontend/src/components/molecules/CopyCard.tsx @@ -30,7 +30,9 @@ export const CopyCard = ({ {title} - {copyText} + + {copyText} + diff --git a/frontend/src/components/molecules/CopyPill.tsx b/frontend/src/components/molecules/CopyPill.tsx index 92941fc5..58ee4102 100644 --- a/frontend/src/components/molecules/CopyPill.tsx +++ b/frontend/src/components/molecules/CopyPill.tsx @@ -23,7 +23,9 @@ export const CopyPill = ({ copyText, copyValue, iconSize = 14, sx }: Props) => { sx={sx} > - {copyText} + + {copyText} + ); }; diff --git a/frontend/src/components/molecules/Reasoning.tsx b/frontend/src/components/molecules/Reasoning.tsx index 47b04f15..6a9e396a 100644 --- a/frontend/src/components/molecules/Reasoning.tsx +++ b/frontend/src/components/molecules/Reasoning.tsx @@ -14,13 +14,18 @@ export const Reasoning = ({ title, description, link, hash }: ReasoningI) => { return ( - + {title} {description} diff --git a/frontend/src/components/organisms/Modals/GovActionModal.tsx b/frontend/src/components/organisms/Modals/GovActionModal.tsx index f58e67f0..cdd44a75 100644 --- a/frontend/src/components/organisms/Modals/GovActionModal.tsx +++ b/frontend/src/components/organisms/Modals/GovActionModal.tsx @@ -67,7 +67,9 @@ export const GovActionModal = () => { src={IMAGES.pastelSignIn} /> - {govAction.title} + + {govAction.title} + { fontWeight={400} sx={{ pb: 0 }} color={customPalette.textGray} + data-testid="governance-action-modal-abstract-text" > {govAction?.abstract} @@ -89,7 +92,10 @@ export const GovActionModal = () => { {t("govActionModal.govActionCategory")} - + {getProposalTypeLabel(govAction.type)} @@ -98,7 +104,10 @@ export const GovActionModal = () => { {t("govActionModal.gaStatus")} - + {govAction.status} @@ -120,6 +129,7 @@ export const GovActionModal = () => { fontWeight={600} sx={{ flexWrap: "nowrap" }} variant="caption" + data-testid="governance-action-modal-submit-time" > {formatDisplayDate(govAction.submit_time)} @@ -141,13 +151,19 @@ export const GovActionModal = () => { fontWeight={600} sx={{ flexWrap: "nowrap" }} variant="caption" + data-testid="governance-action-modal-end-time" > {formatDisplayDate(govAction.end_time)} )} - diff --git a/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx b/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx index 6036af5c..e6e5dec3 100644 --- a/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx +++ b/frontend/src/components/organisms/Modals/PreviewReasoningModal.tsx @@ -119,7 +119,6 @@ export const PreviewReasoningModal = () => { src={IMAGES.pastelAddMember} /> - // todo: check this with kiki {t("previewRationale.headline")} @@ -176,7 +175,10 @@ export const PreviewReasoningModal = () => { {t("previewRationale.governanceActionCategory")} - + {getProposalTypeLabel(govAction.type)} @@ -247,6 +249,7 @@ export const PreviewReasoningModal = () => { width: "100%", marginBottom: 1.5, }} + data-testid="rationale-modal-action-button" > {actionTitle} diff --git a/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx b/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx index ef14f5c4..19b8ab79 100644 --- a/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx +++ b/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx @@ -56,7 +56,7 @@ export const VotesTableRow = ({ opacity: disabled && 0.5, }} > - + {t("govActionCategoryShort")} - + {getProposalTypeLabel(gov_action_proposal_type)} @@ -175,7 +179,7 @@ export const VotesTableRow = ({ > {t("rationale")} - + {reasoning_comment ? truncateText(reasoning_comment, 100) : t("notAvailable")} @@ -195,6 +199,7 @@ export const VotesTableRow = ({ sx={{ whiteSpace: "nowrap" }} onClick={() => onActionClick(votes)} variant="outlined" + data-testid="ga-show-more-button" > {actionTitle} From 7f057c0de1134f4a3550ea3ea066bf691b25b52c Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 13 Aug 2024 12:57:37 +0200 Subject: [PATCH 015/111] feat: GA table selector ids --- .../src/components/atoms/GovActionStatusPill.tsx | 1 + .../GovActionTable/GovActionTableRow.tsx | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/atoms/GovActionStatusPill.tsx b/frontend/src/components/atoms/GovActionStatusPill.tsx index d5edf738..707295c9 100644 --- a/frontend/src/components/atoms/GovActionStatusPill.tsx +++ b/frontend/src/components/atoms/GovActionStatusPill.tsx @@ -41,6 +41,7 @@ export const GovActionStatusPill = ({ fontSize={12} fontWeight={400} lineHeight="16px" + data-testid="ga-table-vote-status-text" > {status} diff --git a/frontend/src/components/organisms/GovActionTable/GovActionTableRow.tsx b/frontend/src/components/organisms/GovActionTable/GovActionTableRow.tsx index 83e2b035..5c78e369 100644 --- a/frontend/src/components/organisms/GovActionTable/GovActionTableRow.tsx +++ b/frontend/src/components/organisms/GovActionTable/GovActionTableRow.tsx @@ -141,6 +141,7 @@ export const GovActionTableRow = ({ govActions }: Props) => { { /> } > - {title ? truncateText(title, 15) : t("notAvailable")} + + {title ? truncateText(title, 15) : t("notAvailable")} + @@ -172,7 +175,10 @@ export const GovActionTableRow = ({ govActions }: Props) => { > {t("govActionCategoryShort")} - + {truncateText(getProposalTypeLabel(type), 20)} @@ -210,7 +216,10 @@ export const GovActionTableRow = ({ govActions }: Props) => { > {t("gaStatus")} - + {status} @@ -254,6 +263,7 @@ export const GovActionTableRow = ({ govActions }: Props) => { : openUpdateReasoningModal() } variant="outlined" + data-testid="ga-table-rationale-button" > {canAddReasoning ? t("addRationale") : t("updateRationale")} From 2b5bc6c62738144a4048f83eae931848b2c1aea7 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 12:44:59 +0200 Subject: [PATCH 016/111] fix dockle scan on ci --- .github/workflows/merge.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index d136b7ce..aca72f3e 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -132,13 +132,6 @@ jobs: NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY }} NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY={{ secrets.DEV_NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY }} - - name: Login to GHCR - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Scan Docker image with Dockle id: dockle run: | @@ -156,6 +149,15 @@ jobs: run: | docker push ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} + + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Deploy with Qovery if: github.ref == 'refs/heads/develop' env: From 39a1732fa919d2c8fae6365b79abd2dafc2ee204 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 12:46:35 +0200 Subject: [PATCH 017/111] fix registry login --- .github/workflows/merge.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index aca72f3e..d03dd48d 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -145,10 +145,6 @@ jobs: echo "outcome=success" >> $GITHUB_OUTPUT - - name: Push Docker image to GHCR - run: | - docker push ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} - - name: Login to GHCR uses: docker/login-action@v2 @@ -158,6 +154,11 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} + - name: Push Docker image to GHCR + run: | + docker push ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} + + - name: Deploy with Qovery if: github.ref == 'refs/heads/develop' env: From cf7123c8daca0dfb8ca66e3f5e7bc028d6727dcf Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 13:05:54 +0200 Subject: [PATCH 018/111] debug imgs --- .github/workflows/merge.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index d03dd48d..80db51f5 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -139,7 +139,14 @@ jobs: tar zxvf dockle_0.4.14_Linux-64bit.tar.gz sudo mv dockle /usr/local/bin - dockle --exit-level fatal --format json --output ${{ matrix.workdir }}/dockle_scan_output.json ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} + docker images + + echo "-------" + echo "grepping" + echo "--------" + docker images | grep '${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }}' + + dockle --exit-level fatal --format json --output ${{ matrix.workdir }}/dockle_scan_output.json '${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }}' echo " dockle exited w/ $?" cat ${{ matrix.workdir }}/dockle_scan_output.json From 6bee09c2187204a37902c01fbaf71690a8a662fa Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 13:30:18 +0200 Subject: [PATCH 019/111] scan tar imgs --- .github/workflows/merge.yaml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 80db51f5..7b6232e8 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -124,13 +124,14 @@ jobs: context: ${{ matrix.workdir }} file: ${{ matrix.dockerfile }} tags: ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} - load: true + load: false cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache + outputs: type=oci,dest=/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar build-args: | NEXT_PUBLIC_API_URL=${{ secrets.DEV_NEXT_PUBLIC_API_URL }} NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY }} - NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY={{ secrets.DEV_NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY }} + NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY }} - name: Scan Docker image with Dockle id: dockle @@ -139,14 +140,7 @@ jobs: tar zxvf dockle_0.4.14_Linux-64bit.tar.gz sudo mv dockle /usr/local/bin - docker images - - echo "-------" - echo "grepping" - echo "--------" - docker images | grep '${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }}' - - dockle --exit-level fatal --format json --output ${{ matrix.workdir }}/dockle_scan_output.json '${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }}' + dockle --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json echo " dockle exited w/ $?" cat ${{ matrix.workdir }}/dockle_scan_output.json @@ -163,6 +157,7 @@ jobs: - name: Push Docker image to GHCR run: | + docker load -i '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' docker push ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} From 0c1460c0f028557a051bc9820d28c9e616796f4e Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 13:35:51 +0200 Subject: [PATCH 020/111] test --- .github/workflows/merge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 7b6232e8..a2b3f55c 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -127,7 +127,7 @@ jobs: load: false cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache - outputs: type=oci,dest=/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar + outputs: type=tar,dest=/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar build-args: | NEXT_PUBLIC_API_URL=${{ secrets.DEV_NEXT_PUBLIC_API_URL }} NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY }} From dd6318bb1bc7744265ad1319c79fae7c33536ba7 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 13:36:29 +0200 Subject: [PATCH 021/111] add tar cleanup --- .github/workflows/merge.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index a2b3f55c..a3cbbe66 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -158,6 +158,7 @@ jobs: - name: Push Docker image to GHCR run: | docker load -i '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' + rm -rf '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' docker push ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} From a3b8dae2fba5267ce4e97c2fd28a68f145df7057 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 13:45:03 +0200 Subject: [PATCH 022/111] test --- .github/workflows/merge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index a3cbbe66..38d19cf1 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -127,7 +127,7 @@ jobs: load: false cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache - outputs: type=tar,dest=/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar + outputs: type=docker,dest=/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar build-args: | NEXT_PUBLIC_API_URL=${{ secrets.DEV_NEXT_PUBLIC_API_URL }} NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY }} From 2298f2f2c6ad566d6c6a54647582962e17057f26 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Mon, 26 Aug 2024 14:15:18 +0200 Subject: [PATCH 023/111] pr workflow fix --- .github/workflows/pr.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 15eeb42e..80cf29bb 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -111,9 +111,14 @@ jobs: context: ${{ matrix.workdir }} file: ${{ matrix.dockerfile }} tags: ${{ steps.image_lowercase.outputs.lowercase }}:${{ github.sha }} - load: true + load: false cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache + outputs: type=docker,dest=/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar + build-args: | + NEXT_PUBLIC_API_URL=${{ secrets.DEV_NEXT_PUBLIC_API_URL }} + NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY }} + NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY=${{ secrets.DEV_NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY }} - name: Scan Docker image with Dockle id: dockle @@ -122,7 +127,8 @@ jobs: tar zxvf dockle_0.4.14_Linux-64bit.tar.gz sudo mv dockle /usr/local/bin - dockle --exit-level fatal --format json --output ${{ matrix.workdir }}/dockle_scan_output.json ${{ steps.image_lowercase.outputs.lowercase }}:${{ github.sha }} + dockle --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json + rm -rf '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' echo " dockle exited w/ $?" cat ${{ matrix.workdir }}/dockle_scan_output.json From ef1b6142a65cc5ba4f082f96f919fe518b23c0e2 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Tue, 27 Aug 2024 12:01:30 +0200 Subject: [PATCH 024/111] declutter dockle --- .github/workflows/merge.yaml | 7 +++---- .github/workflows/pr.yaml | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 38d19cf1..dc2b4813 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -136,12 +136,11 @@ jobs: - name: Scan Docker image with Dockle id: dockle run: | - wget https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz - tar zxvf dockle_0.4.14_Linux-64bit.tar.gz + wget -q https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz + tar zxf dockle_0.4.14_Linux-64bit.tar.gz sudo mv dockle /usr/local/bin - dockle --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json - echo " dockle exited w/ $?" + dockle --exit-code 1 --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json cat ${{ matrix.workdir }}/dockle_scan_output.json echo "outcome=success" >> $GITHUB_OUTPUT diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 80cf29bb..bde0f6d7 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -123,13 +123,12 @@ jobs: - name: Scan Docker image with Dockle id: dockle run: | - wget https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz - tar zxvf dockle_0.4.14_Linux-64bit.tar.gz + wget -q https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz + tar zxf dockle_0.4.14_Linux-64bit.tar.gz sudo mv dockle /usr/local/bin - dockle --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json + dockle --exit-code 1 --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json rm -rf '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' - echo " dockle exited w/ $?" cat ${{ matrix.workdir }}/dockle_scan_output.json echo "outcome=success" >> $GITHUB_OUTPUT From 458e3a9b2d5b6a95eec9dc32d445f6e071b3d4a9 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Tue, 27 Aug 2024 13:27:46 +0200 Subject: [PATCH 025/111] add comment --- .github/workflows/merge.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index dc2b4813..79932af3 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -160,6 +160,14 @@ jobs: rm -rf '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' docker push ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} + - name: Add tag as a PR comment + uses: ubie-oss/comment-to-merged-pr-action@v0.3.3 + id: comment-to-merged-pr + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + message: |- + This PR is in the tag: ${{ env.TAG }} + - name: Deploy with Qovery if: github.ref == 'refs/heads/develop' From 9b68718fa92944ff6a9e0679f71990d645268715 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Tue, 27 Aug 2024 13:40:41 +0200 Subject: [PATCH 026/111] add microservice in pr comment --- .github/workflows/merge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 79932af3..5acf610c 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -166,7 +166,7 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} message: |- - This PR is in the tag: ${{ env.TAG }} + This PR is in the tag: ${{ env.TAG }} , for ${{ matrix.name }} service - name: Deploy with Qovery From 24877fd6271a6924c89d2ea1eba5b4f72a04ac84 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Tue, 27 Aug 2024 13:53:27 +0200 Subject: [PATCH 027/111] test w/ individual service failing --- .github/workflows/merge.yaml | 1 + .github/workflows/pr.yaml | 1 + frontend/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 5acf610c..83750fb1 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -19,6 +19,7 @@ env: jobs: check-build-deploy: strategy: + fail-fast: false matrix: include: - workdir: ./frontend diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index bde0f6d7..da4b11ec 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -15,6 +15,7 @@ permissions: jobs: static-checks: strategy: + fail-fast: false matrix: include: - workdir: ./frontend diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 95ab907e..5af22187 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine AS base +FROM node:20-apline AS base # Install dependencies only when needed FROM base AS deps From dfcb047565b0d8dbf1ee7af4fd31c2efb3f40948 Mon Sep 17 00:00:00 2001 From: Baja-KS Date: Tue, 27 Aug 2024 13:59:10 +0200 Subject: [PATCH 028/111] fix dockerfile --- frontend/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 5af22187..95ab907e 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-apline AS base +FROM node:20-alpine AS base # Install dependencies only when needed FROM base AS deps From 6e1477e41e5c58140d7383c39e5aeada70174cd9 Mon Sep 17 00:00:00 2001 From: Vojimirovich Date: Tue, 3 Sep 2024 18:19:13 +0200 Subject: [PATCH 029/111] fix: vote table row responsiveness --- .../components/atoms/OutlinedLightButton.tsx | 12 +- .../src/components/molecules/TableDivider.tsx | 6 +- .../organisms/VotesTable/VotesTableRow.tsx | 288 +++++++++--------- 3 files changed, 157 insertions(+), 149 deletions(-) diff --git a/frontend/src/components/atoms/OutlinedLightButton.tsx b/frontend/src/components/atoms/OutlinedLightButton.tsx index e36dc721..74d929fe 100644 --- a/frontend/src/components/atoms/OutlinedLightButton.tsx +++ b/frontend/src/components/atoms/OutlinedLightButton.tsx @@ -1,8 +1,7 @@ "use client"; -import React from "react"; -import { Button, SxProps, Theme } from "@mui/material"; import { customPalette } from "@consts"; +import { Button, SxProps, Theme } from "@mui/material"; import { ButtonProps } from "@mui/material/Button"; interface Props extends ButtonProps { @@ -23,10 +22,11 @@ export const OutlinedLightButton = ({ color: customPalette.textBlack, fontWeight: 400, fontSize: 12, - whiteSpace: "nowrap", + lineHeight: "18px", + whiteSpace: "normal;", "&": { - pointerEvents: nonInteractive ? "none" : "all", - }, + pointerEvents: nonInteractive ? "none" : "all" + } }; return ( @@ -36,7 +36,7 @@ export const OutlinedLightButton = ({ onClick={nonInteractive ? undefined : onClick} sx={{ ...defaultSxProps, - ...sx, + ...sx }} {...props} > diff --git a/frontend/src/components/molecules/TableDivider.tsx b/frontend/src/components/molecules/TableDivider.tsx index 95e78e11..f4c6bb1b 100644 --- a/frontend/src/components/molecules/TableDivider.tsx +++ b/frontend/src/components/molecules/TableDivider.tsx @@ -1,5 +1,5 @@ import { customPalette } from "@/constants"; -import { Divider as MUIDivider, DividerProps, SxProps } from "@mui/material"; +import { DividerProps, Divider as MUIDivider, SxProps } from "@mui/material"; interface Props extends DividerProps { sx?: SxProps; @@ -16,10 +16,10 @@ export const TableDivider = ({ orientation={orientation} flexItem={flexItem} sx={{ - display: { xxs: "none", lg: "flex" }, + display: { xxs: "none", md: "flex" }, height: 38, alignSelf: "center", - ...sx, + ...sx }} {...props} /> diff --git a/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx b/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx index ef14f5c4..efe65a46 100644 --- a/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx +++ b/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx @@ -1,18 +1,15 @@ "use client"; -import React from "react"; -import { Card, TableDivider } from "@molecules"; -import { Box, Grid } from "@mui/material"; -import { UserAvatar, UserBasicInfo } from "@molecules"; +import { VotesTableI } from "@/lib/requests"; import { Button, OutlinedLightButton, Typography, VotePill } from "@atoms"; -import { GovActionModalState } from "../types"; import { customPalette, ICONS } from "@consts"; +import { useModal } from "@context"; +import { Card, TableDivider, UserAvatar, UserBasicInfo } from "@molecules"; +import { Box, Grid } from "@mui/material"; +import { getProposalTypeLabel, truncateText } from "@utils"; import { useTranslations } from "next-intl"; import Image from "next/image"; -import { truncateText } from "@utils"; -import { getProposalTypeLabel } from "@utils"; -import { useModal } from "@context"; -import { VotesTableI } from "@/lib/requests"; +import { GovActionModalState } from "../types"; interface Props { votes: VotesTableI; @@ -25,7 +22,7 @@ export const VotesTableRow = ({ votes, disabled, actionTitle, - onActionClick, + onActionClick }: Props) => { const t = useTranslations("LatestUpdates"); const { openModal } = useModal(); @@ -37,14 +34,14 @@ export const VotesTableRow = ({ reasoning_comment, gov_action_proposal_id, gov_action_proposal_title, - gov_action_proposal_type, + gov_action_proposal_type } = votes; const openGAModal = () => { openModal({ type: "govActionModal", state: { - id: gov_action_proposal_id, - }, + id: gov_action_proposal_id + } }); }; @@ -53,152 +50,163 @@ export const VotesTableRow = ({ item mb={3} sx={{ - opacity: disabled && 0.5, + opacity: disabled && 0.5 }} > - - + + - - - - - - - - + + + + + + - - - + + + + + - {t("govAction")} - + + {t("govAction")} + - - } + + } + > + {gov_action_proposal_title + ? truncateText(gov_action_proposal_title, 40) + : t("notAvailable")} + + + + + + - {gov_action_proposal_title - ? truncateText(gov_action_proposal_title, 40) - : t("notAvailable")} - + + {t("govActionCategoryShort")} + + + {getProposalTypeLabel(gov_action_proposal_type)} + + - - - + + - {t("govActionCategoryShort")} - - - {getProposalTypeLabel(gov_action_proposal_type)} - + + {t("voted")} + + + + + - - - + + + - {t("voted")} - - - - + + {t("rationale")} + + + {reasoning_comment + ? truncateText(reasoning_comment, 100) + : t("notAvailable")} + + - - - + - - - From 8f9ce00995cf84e38c05b1686a9ce8d4cbe48f55 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:15:51 +0200 Subject: [PATCH 030/111] Refactor/change logic for file upload to S3 (#328) * refactor: changed getFileUrl function in order to display images on website for longer than 7 days * refactor: changed getFileUrl function in order to display images on website for longer than 7 days --- backend/example.env | 1 + backend/src/s3/service/s3.service.ts | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/example.env b/backend/example.env index d2ec7064..e304ee60 100644 --- a/backend/example.env +++ b/backend/example.env @@ -37,6 +37,7 @@ MINIO_ACCESS_KEY=minio MINIO_SECRET_KEY=minio123 MINIO_USE_SSL=false MINIO_BUCKET=cc-portal +MINIO_FILE_UPLOAD_ADDRESS=http://209.38.192.168:9000/cc-portal # IPFS Service IPFS_SERVICE_URL=http://ipfs-service:3001 diff --git a/backend/src/s3/service/s3.service.ts b/backend/src/s3/service/s3.service.ts index 059e7b3d..60c495a3 100644 --- a/backend/src/s3/service/s3.service.ts +++ b/backend/src/s3/service/s3.service.ts @@ -23,7 +23,9 @@ export class S3Service { const buffer = Buffer.from(bufferArray); const fullName = this.getFullFileName(context, fileName); - await this.client.putObject(this.bucketName, fullName, buffer, file.size); + await this.client.putObject(this.bucketName, fullName, buffer, file.size, { + 'Content-Type': file.mimetype, + }); const fileUrl = await this.getFileUrl(context, fileName); return fileUrl; @@ -34,7 +36,8 @@ export class S3Service { fileName: string, ): Promise { const fullName = this.getFullFileName(context, fileName); - return await this.client.presignedUrl('GET', this.bucketName, fullName); + const fileUrl = `http://${this.configService.get('MINIO_ENDPOINT')}:${this.configService.get('MINIO_PORT')}/${this.bucketName}/${fullName}`; + return fileUrl; } async deleteFile(fileName: string) { From bf33164b4efdc2c1da33df36fa200c07fa7732c5 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:42:43 +0200 Subject: [PATCH 031/111] feat: dht-queue (#336) Added persistent dht-queue which will be responsible for publishing cid to dht --- ipfs-service/example.env | 15 +- ipfs-service/package-lock.json | 848 +++++++++++++++++- ipfs-service/package.json | 11 +- ipfs-service/src/app.module.ts | 14 +- ipfs-service/src/app.service.ts | 55 +- ipfs-service/src/bullmq/bullmq.module.ts | 31 + .../src/constants/bullmq.constants.ts | 2 + ipfs-service/src/main.ts | 19 + .../processors/provide-to-dht.processor.ts | 40 + .../producers/provide-to-dht.producer.ts | 30 + ipfs-service/src/util/logger-factory.ts | 37 + 11 files changed, 1055 insertions(+), 47 deletions(-) create mode 100644 ipfs-service/src/bullmq/bullmq.module.ts create mode 100644 ipfs-service/src/constants/bullmq.constants.ts create mode 100644 ipfs-service/src/queues/processors/provide-to-dht.processor.ts create mode 100644 ipfs-service/src/queues/producers/provide-to-dht.producer.ts create mode 100644 ipfs-service/src/util/logger-factory.ts diff --git a/ipfs-service/example.env b/ipfs-service/example.env index 62a72fb3..8da0740b 100644 --- a/ipfs-service/example.env +++ b/ipfs-service/example.env @@ -5,4 +5,17 @@ LISTEN_QUIC_ADDRESS='/ip4/0.0.0.0/udp/0/quic-v1' IPFS_PUBLIC_URL='https://ipfs.io/ipfs/' IPNS_PUBLIC_URL='https://ipfs.io/ipns/' -IPNS_CONSTITUTION_KEY_NAME='some-key-name' \ No newline at end of file +IPNS_CONSTITUTION_KEY_NAME='some-key-name' + +# Redis +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD=password + +## DHT QUEUE ## +# Attempts +DHT_QUEUE_ATTEMPTS=10 +# Backoff (exponential, fixed) +DHT_QUEUE_BACKOFF_TYPE=exponential +# Backoff delay (ms) +DHT_QUEUE_BACKOFF_DELAY=1000 \ No newline at end of file diff --git a/ipfs-service/package-lock.json b/ipfs-service/package-lock.json index 7077e8c1..bed1315f 100644 --- a/ipfs-service/package-lock.json +++ b/ipfs-service/package-lock.json @@ -10,24 +10,33 @@ "license": "UNLICENSED", "dependencies": { "@aws-sdk/client-s3": "^3.552.0", + "@bull-board/nestjs": "^5.17.1", "@chainsafe/libp2p-gossipsub": "^13.0.0", "@helia/ipns": "^7.2.0", "@helia/unixfs": "^3.0.3", + "@nestjs/bullmq": "^10.1.1", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.2", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "@types/morgan": "^1.9.7", "@types/multer": "^1.4.11", "blockstore-fs": "^1.1.10", "blockstore-s3": "^1.0.15", + "bull": "^4.12.3", + "bullmq": "^5.7.9", "datastore-level": "^10.1.8", "datastore-s3": "^11.1.11", "helia": "^4.0.1", "i": "^0.3.7", + "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", + "nest-winston": "^1.9.4", "npm": "^10.5.2", "reflect-metadata": "^0.2.1", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "winston": "^3.11.0", + "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -3265,6 +3274,59 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@bull-board/api": { + "version": "5.21.4", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-5.21.4.tgz", + "integrity": "sha512-mPWYIe7ucSKjvJuMGaYhY2A8miirFHtOnvhywxgXZi4342QiqfQCN/ZJiGqCWXN6yIzOQszopePlgemETupoaA==", + "license": "MIT", + "peer": true, + "dependencies": { + "redis-info": "^3.0.8" + }, + "peerDependencies": { + "@bull-board/ui": "5.21.4" + } + }, + "node_modules/@bull-board/express": { + "version": "5.21.4", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-5.21.4.tgz", + "integrity": "sha512-naB1h6edTfw698x1W2MKMd8yc49Vu6X+SyJr48FGX2O0x4oeYKZHf2ZfSs6zMVnCMnsycoASiGck085np/9d0w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@bull-board/api": "5.21.4", + "@bull-board/ui": "5.21.4", + "ejs": "^3.1.10", + "express": "^4.19.2" + } + }, + "node_modules/@bull-board/nestjs": { + "version": "5.21.4", + "resolved": "https://registry.npmjs.org/@bull-board/nestjs/-/nestjs-5.21.4.tgz", + "integrity": "sha512-pJaKf7wr/NnjUryrRuJb7jeO4mS8ejNY9GYY5k99RrEgqHkzzKaD8F9QmjsI6ZRfTKANfP7ALVXmPnjRHqSd8A==", + "license": "MIT", + "dependencies": { + "@nestjs/bull-shared": "^10.0.0" + }, + "peerDependencies": { + "@bull-board/api": "^5.21.4", + "@bull-board/express": "^5.21.4", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0", + "rxjs": "^7.8.1" + } + }, + "node_modules/@bull-board/ui": { + "version": "5.21.4", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-5.21.4.tgz", + "integrity": "sha512-z2otIDhN4EpKRXeBjZZrqbOPtX5pFwkeEs6uGG77/4m6bSjUl+RTRFpzBSVxrwctqxge+S+CNOCmfDiDMGqi8Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@bull-board/api": "5.21.4" + } + }, "node_modules/@chainsafe/as-chacha20poly1305": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@chainsafe/as-chacha20poly1305/-/as-chacha20poly1305-0.1.0.tgz", @@ -3386,6 +3448,17 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3649,6 +3722,12 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "license": "MIT" + }, "node_modules/@ipld/dag-cbor": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-9.2.0.tgz", @@ -4745,6 +4824,84 @@ "node": ">=8" } }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@multiformats/base-x": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@multiformats/base-x/-/base-x-4.0.1.tgz", @@ -4817,6 +4974,46 @@ "npm": ">=7.0.0" } }, + "node_modules/@nestjs/bull-shared": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-10.2.1.tgz", + "integrity": "sha512-zvnTvSq6OJ92omcsFUwaUmPbM3PRgWkIusHPB5TE3IFS7nNdM3OwF+kfe56sgKjMtQQMe/56lok0S04OtPMX5Q==", + "license": "MIT", + "dependencies": { + "tslib": "2.6.3" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/@nestjs/bull-shared/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "license": "0BSD" + }, + "node_modules/@nestjs/bullmq": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-10.2.1.tgz", + "integrity": "sha512-nDR0hDabmtXt5gsb5R786BJsGIJoWh/79sVmRETXf4S45+fvdqG1XkCKAeHF9TO9USodw9m+XBNKysTnkY41gw==", + "license": "MIT", + "dependencies": { + "@nestjs/bull-shared": "^10.2.1", + "tslib": "2.6.3" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "bullmq": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@nestjs/bullmq/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "license": "0BSD" + }, "node_modules/@nestjs/cli": { "version": "10.3.2", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.3.2.tgz", @@ -7412,6 +7609,15 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, + "node_modules/@types/morgan": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.9.tgz", + "integrity": "sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/multer": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", @@ -7516,6 +7722,12 @@ "@types/superagent": "^8.1.0" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -8216,6 +8428,12 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -8434,6 +8652,24 @@ } ] }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -8693,6 +8929,48 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/bull": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/bull/-/bull-4.16.2.tgz", + "integrity": "sha512-VCy33UdPGiIoZHDTrslGXKXWxcIUHNH5Z82pihr8HicbIfAH4SHug1HxlwKEbibVv85hq8rJ9tKAW/cuxv2T0A==", + "license": "MIT", + "dependencies": { + "cron-parser": "^4.2.1", + "get-port": "^5.1.1", + "ioredis": "^5.3.2", + "lodash": "^4.17.21", + "msgpackr": "^1.10.1", + "semver": "^7.5.2", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/bull/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/bullmq": { + "version": "5.12.14", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.12.14.tgz", + "integrity": "sha512-mcSQHq9EY+DKtAP6XSmkP+0f1ifFithcpLTwo8WmUauArE9dxk45Gae3Fls1Nwf0Er9MoaDhPcglfe6LV/XCOg==", + "license": "MIT", + "dependencies": { + "cron-parser": "^4.6.0", + "ioredis": "^5.4.1", + "msgpackr": "^1.10.1", + "node-abort-controller": "^3.1.1", + "semver": "^7.5.4", + "tslib": "^2.0.0", + "uuid": "^9.0.0" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -9095,6 +9373,15 @@ "node": ">=6" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -9111,6 +9398,16 @@ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -9127,12 +9424,47 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "peer": true }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -9470,6 +9802,18 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -9829,6 +10173,22 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.731", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.731.tgz", @@ -9851,6 +10211,12 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -10501,6 +10867,12 @@ "bser": "2.1.1" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -10537,6 +10909,48 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -10772,6 +11186,12 @@ "node": ">=0.4.0" } }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -10969,6 +11389,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -11476,6 +11908,30 @@ "loose-envify": "^1.0.0" } }, + "node_modules/ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -12186,6 +12642,25 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -13090,6 +13565,12 @@ "node": ">=6" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, "node_modules/level": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/level/-/level-8.0.1.tgz", @@ -13240,6 +13721,18 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "peer": true }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -13273,6 +13766,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/logform": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/logkitty": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", @@ -13411,6 +13930,15 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-string": { "version": "0.30.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", @@ -14164,6 +14692,58 @@ "node": ">=10" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/mortice": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/mortice/-/mortice-3.0.4.tgz", @@ -14179,6 +14759,37 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/msgpackr": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.0.tgz", + "integrity": "sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, "node_modules/multer": { "version": "1.4.5-lts.1", "license": "MIT", @@ -14306,6 +14917,19 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/nest-winston": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/nest-winston/-/nest-winston-1.9.7.tgz", + "integrity": "sha512-pTTgImRgv7urojsDvaTlenAjyJNLj7ywamfjzrhWKhLhp80AKLYNwf103dVHeqZWe+nzp/vd9DGRs/UN/YadOQ==", + "license": "MIT", + "dependencies": { + "fast-safe-stringify": "^2.1.1" + }, + "peerDependencies": { + "@nestjs/common": "^5.0.0 || ^6.6.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "winston": "^3.0.0" + } + }, "node_modules/netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", @@ -14420,6 +15044,21 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -17077,6 +17716,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -17109,7 +17757,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "peer": true, "engines": { "node": ">= 0.8" } @@ -17122,6 +17769,15 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -18163,6 +18819,37 @@ "node": ">= 0.10" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-info": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redis-info/-/redis-info-3.1.0.tgz", + "integrity": "sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==", + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.11" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", @@ -18538,6 +19225,15 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -18904,6 +19600,21 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -18993,6 +19704,15 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -19039,6 +19759,12 @@ "node": ">=8" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -19502,6 +20228,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -19609,6 +20341,15 @@ "tree-kill": "cli.js" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -20228,6 +20969,109 @@ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "peer": true }, + "node_modules/winston": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.6.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-daily-rotate-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz", + "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==", + "license": "MIT", + "dependencies": { + "file-stream-rotator": "^0.6.1", + "object-hash": "^3.0.0", + "triple-beam": "^1.4.1", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" + } + }, + "node_modules/winston-transport": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", + "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", + "license": "MIT", + "dependencies": { + "logform": "^2.6.1", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/winston/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", diff --git a/ipfs-service/package.json b/ipfs-service/package.json index c8c8ad8c..84e9c68e 100644 --- a/ipfs-service/package.json +++ b/ipfs-service/package.json @@ -19,7 +19,10 @@ "test:e2e": "NODE_OPTIONS=--experimental-vm-modules jest --config ./test/jest-e2e.json" }, "dependencies": { + "@bull-board/nestjs": "^5.17.1", + "@types/morgan": "^1.9.7", "@aws-sdk/client-s3": "^3.552.0", + "morgan": "^1.10.0", "@chainsafe/libp2p-gossipsub": "^13.0.0", "@helia/ipns": "^7.2.0", "@helia/unixfs": "^3.0.3", @@ -27,6 +30,7 @@ "@nestjs/config": "^3.2.2", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "@nestjs/bullmq": "^10.1.1", "@types/multer": "^1.4.11", "blockstore-fs": "^1.1.10", "blockstore-s3": "^1.0.15", @@ -37,7 +41,12 @@ "multer": "^1.4.5-lts.1", "npm": "^10.5.2", "reflect-metadata": "^0.2.1", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "winston": "^3.11.0", + "bull": "^4.12.3", + "bullmq": "^5.7.9", + "nest-winston": "^1.9.4", + "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/ipfs-service/src/app.module.ts b/ipfs-service/src/app.module.ts index b70e2554..fab71986 100644 --- a/ipfs-service/src/app.module.ts +++ b/ipfs-service/src/app.module.ts @@ -2,10 +2,20 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller.js'; import { AppService } from './app.service.js'; import { ConfigModule } from '@nestjs/config'; +import { BullModule } from '@nestjs/bullmq'; +import { BullmqModule } from './bullmq/bullmq.module.js'; +import { ProvideToDHTProcessor } from './queues/processors/provide-to-dht.processor.js'; +import { ProvideToDHTProducer } from './queues/producers/provide-to-dht.producer.js'; +import { QUEUE_NAME_PROVIDE_TO_DHT } from './constants/bullmq.constants.js'; @Module({ - imports: [ConfigModule.forRoot({ isGlobal: true })], + imports: [ + ConfigModule.forRoot({ isGlobal: true }), + BullmqModule, + BullModule.registerQueue({ + name: QUEUE_NAME_PROVIDE_TO_DHT, + }),], controllers: [AppController], - providers: [AppService], + providers: [AppService, ProvideToDHTProcessor, ProvideToDHTProducer], }) export class AppModule {} diff --git a/ipfs-service/src/app.service.ts b/ipfs-service/src/app.service.ts index 94a04430..9c2bec45 100644 --- a/ipfs-service/src/app.service.ts +++ b/ipfs-service/src/app.service.ts @@ -6,23 +6,19 @@ import { } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { createHelia } from 'helia'; -import type { HeliaLibp2p } from 'helia'; import { CID } from 'multiformats/cid'; -import { createLibp2p } from 'libp2p'; import { bootstrap } from '@libp2p/bootstrap'; import { identify } from '@libp2p/identify'; import { webSockets } from '@libp2p/websockets'; -import { all } from '@libp2p/websockets/filters'; import { tcp } from '@libp2p/tcp'; import { noise } from '@chainsafe/libp2p-noise'; import { yamux } from '@chainsafe/libp2p-yamux'; -import { mplex } from '@libp2p/mplex'; import { unixfs } from '@helia/unixfs'; import { FsBlockstore } from 'blockstore-fs'; import { LevelDatastore } from 'datastore-level'; import { IPNS, ipns } from '@helia/ipns'; import { keychain, type Keychain } from '@libp2p/keychain'; -import { kadDHT, removePrivateAddressesMapper } from '@libp2p/kad-dht'; +import { kadDHT } from '@libp2p/kad-dht'; import { ipnsSelector } from 'ipns/selector'; import { ipnsValidator } from 'ipns/validator'; import { dcutr } from '@libp2p/dcutr'; @@ -31,8 +27,6 @@ import { ping } from '@libp2p/ping'; import { uPnPNAT } from '@libp2p/upnp-nat'; import { mdns } from '@libp2p/mdns'; import { createDelegatedRoutingV1HttpApiClient } from '@helia/delegated-routing-v1-http-api-client'; -//import { gossipsub } from '@chainsafe/libp2p-gossipsub'; -//import { webRTC, webRTCDirect } from '@libp2p/webrtc'; import { circuitRelayTransport, circuitRelayServer, @@ -41,6 +35,7 @@ import { IpfsMapper } from './mapper/ipfs.mapper.js'; import { IpfsDto } from './dto/ipfs.dto.js'; import { PeerId } from '@libp2p/interface'; import { config } from 'dotenv'; +import { ProvideToDHTProducer } from './queues/producers/provide-to-dht.producer.js'; config(); const libp2pOptions = { @@ -112,7 +107,7 @@ export class AppService implements OnModuleInit { private ipnsPeerId: PeerId; private logger = new Logger(AppService.name); - constructor(private readonly configService: ConfigService) {} + constructor(private readonly provideToDHTProducer: ProvideToDHTProducer) {} async onModuleInit() { console.log(`Initialization helia...`); @@ -182,8 +177,8 @@ export class AppService implements OnModuleInit { const ret1 = this.helia.pins.add(cid); ret1.next().then((res) => this.logger.log(`Pinned: ${res.value}`)); - // Announce CID to the DHT - this.provideCidtoDHT(cid); + // Announce CID to the DHT via queue + await this.provideToDHTProducer.addToQueue(cid.toString()); // Publish the name await this.ipns.publish(this.ipnsPeerId, cid); @@ -202,17 +197,17 @@ export class AppService implements OnModuleInit { } } - async getDoc(cid: string): Promise { + async getDoc(cidString: string): Promise { this.fs = unixfs(this.helia); const decoder = new TextDecoder(); + const cid = CID.parse(cidString); let text = ''; - for await (const chunk of this.fs.cat(cid)) { text += decoder.decode(chunk, { stream: true, }); } - return IpfsMapper.ipfsToIpfsDto(cid, text); + return IpfsMapper.ipfsToIpfsDto(cidString, text); } async addJson(json: string): Promise { @@ -226,8 +221,8 @@ export class AppService implements OnModuleInit { const ret1 = this.helia.pins.add(cid); ret1.next().then((res) => this.logger.log(`Pinned json: ${res.value}`)); - // Announce CID to the DHT - this.provideCidtoDHT(cid); + // Announce CID to the DHT via queue + await this.provideToDHTProducer.addToQueue(cid.toString()); const url = process.env.IPFS_PUBLIC_URL + cid.toString(); @@ -238,32 +233,10 @@ export class AppService implements OnModuleInit { } } - private provideCidtoDHT(cid, retryDelay = 5000) { - let attempt = 0; - let errCode = null; - - const attemptToProvide = async () => { - try { - await this.helia.libp2p.contentRouting.provide(cid); - this.logger.log(`Announced CID to the DHT: ${cid.toString()}`); - } catch (error) { - this.logger.error(`Error announcing CID to the DHT: ${error}`); - errCode = error.code; - if (errCode === 'ERR_QUERY_ABORTED') { - attempt++; - this.logger.log(`Retrying... (${attempt})`); - await new Promise((resolve) => setTimeout(resolve, retryDelay)); - attemptToProvide(); // Retry - } else { - this.logger.log(`CID: ${cid} has not been announced`); - this.logger.error(error); - throw new InternalServerErrorException(error); - } - } - }; - - attemptToProvide(); - } + async provideCidtoDHTViaQueue(cid: CID) { + await this.helia.libp2p.contentRouting.provide(cid); + this.logger.log(`Announced CID to the DHT: ${cid.toString()}`); +} async getIpnsUrl(): Promise { if (!this.ipnsPeerId) { diff --git a/ipfs-service/src/bullmq/bullmq.module.ts b/ipfs-service/src/bullmq/bullmq.module.ts new file mode 100644 index 00000000..8dd4d1fd --- /dev/null +++ b/ipfs-service/src/bullmq/bullmq.module.ts @@ -0,0 +1,31 @@ +import { BullBoardModule } from "@bull-board/nestjs"; +import { BullModule } from "@nestjs/bullmq"; +import { Module } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { QUEUE_NAME_PROVIDE_TO_DHT } from "../constants/bullmq.constants.js"; +import { BullMQAdapter } from "@bull-board/api/bullMQAdapter.js"; +import { ExpressAdapter } from "@bull-board/express"; + +@Module({ + imports: [ + BullModule.forRootAsync({ + useFactory: async (configService: ConfigService) => ({ + connection: { + host: configService.getOrThrow("REDIS_HOST"), + port: configService.getOrThrow("REDIS_PORT"), + password: configService.getOrThrow("REDIS_PASSWORD"), + }, + }), + inject: [ConfigService], + }), + BullBoardModule.forRoot({ + route: "/bull-board", + adapter: ExpressAdapter, + }), + BullBoardModule.forFeature({ + name: QUEUE_NAME_PROVIDE_TO_DHT, + adapter: BullMQAdapter, + }), + ], +}) +export class BullmqModule {} diff --git a/ipfs-service/src/constants/bullmq.constants.ts b/ipfs-service/src/constants/bullmq.constants.ts new file mode 100644 index 00000000..b2df7f12 --- /dev/null +++ b/ipfs-service/src/constants/bullmq.constants.ts @@ -0,0 +1,2 @@ +export const QUEUE_NAME_PROVIDE_TO_DHT = "provide_to_dht_queue"; +export const JOB_NAME_PROVIDE_TO_DHT = "provide_to_dht_job"; diff --git a/ipfs-service/src/main.ts b/ipfs-service/src/main.ts index 3a13dd2f..91f07a94 100644 --- a/ipfs-service/src/main.ts +++ b/ipfs-service/src/main.ts @@ -1,8 +1,27 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module.js'; +import morgan from 'morgan'; +import { LoggerFactory } from './util/logger-factory.js'; +import { LoggerService } from '@nestjs/common'; + async function bootstrap() { const app = await NestFactory.create(AppModule); + + const logger: LoggerService = LoggerFactory('IPFS Service'); + + const morganFormat = + ':method :url :status :res[content-length] - :response-time ms'; + app.use( + morgan(morganFormat, { + stream: { + write: (message) => { + logger.log(message); + }, + }, + }), +); + await app.listen(3001); } bootstrap().catch((err) => { diff --git a/ipfs-service/src/queues/processors/provide-to-dht.processor.ts b/ipfs-service/src/queues/processors/provide-to-dht.processor.ts new file mode 100644 index 00000000..af9048e3 --- /dev/null +++ b/ipfs-service/src/queues/processors/provide-to-dht.processor.ts @@ -0,0 +1,40 @@ +import { OnWorkerEvent, Processor, WorkerHost } from '@nestjs/bullmq'; +import { Job } from 'bullmq'; +import { + JOB_NAME_PROVIDE_TO_DHT, + QUEUE_NAME_PROVIDE_TO_DHT, +} from '../../constants/bullmq.constants.js'; +import { Logger } from '@nestjs/common'; +import { AppService } from '../../app.service.js'; +import { CID } from 'multiformats/cid'; + +@Processor(QUEUE_NAME_PROVIDE_TO_DHT) +export class ProvideToDHTProcessor extends WorkerHost { + protected readonly logger = new Logger(ProvideToDHTProcessor.name); + constructor(private readonly appService: AppService) { + super(); + } + + async process(job: Job): Promise { + switch (job.name) { + case JOB_NAME_PROVIDE_TO_DHT: { + const cid = CID.parse(job.data); + this.logger.debug('Job triggered: Provide to DHT - CID:', cid.toString()); + try { + await this.appService.provideCidtoDHTViaQueue(cid); + } catch (error) { + this.logger.error(`Error processing job id: ${job.id}, name: ${job.name}. - Error: ${error}`); + throw error; + } + } + } + } + + @OnWorkerEvent('completed') + onCompleted(job: Job) { + const { id, name, queueName, finishedOn, returnvalue } = job; + const completionTime = finishedOn ? new Date(finishedOn).toISOString() : ''; + this.logger.log( + `Job Finished - id: ${id}, name: ${name} in queue ${queueName} on ${completionTime}.`,); + } +} diff --git a/ipfs-service/src/queues/producers/provide-to-dht.producer.ts b/ipfs-service/src/queues/producers/provide-to-dht.producer.ts new file mode 100644 index 00000000..4d890a90 --- /dev/null +++ b/ipfs-service/src/queues/producers/provide-to-dht.producer.ts @@ -0,0 +1,30 @@ +import { InjectQueue } from '@nestjs/bullmq'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Queue } from 'bullmq'; +import { + JOB_NAME_PROVIDE_TO_DHT, + QUEUE_NAME_PROVIDE_TO_DHT, +} from '../../constants/bullmq.constants.js'; +import { randomUUID } from 'crypto'; + +@Injectable() +export class ProvideToDHTProducer { + constructor( + @InjectQueue(QUEUE_NAME_PROVIDE_TO_DHT) private readonly provideToDHTQueue: Queue, + private readonly configService: ConfigService + ) {} + + async addToQueue(inputData: string) { + const job = await this.provideToDHTQueue.add(JOB_NAME_PROVIDE_TO_DHT, inputData, { + jobId: randomUUID(), + removeOnComplete: true, + removeOnFail: false, + attempts: this.configService.getOrThrow('DHT_QUEUE_ATTEMPTS'), + backoff: { + type: this.configService.getOrThrow('DHT_QUEUE_BACKOFF_TYPE'), + delay: this.configService.getOrThrow('DHT_QUEUE_BACKOFF_DELAY') }, + }); + return job; + } +} diff --git a/ipfs-service/src/util/logger-factory.ts b/ipfs-service/src/util/logger-factory.ts new file mode 100644 index 00000000..e6e21ed1 --- /dev/null +++ b/ipfs-service/src/util/logger-factory.ts @@ -0,0 +1,37 @@ +import { format, transports } from 'winston'; +const { json, timestamp } = format; +// require('winston-daily-rotate-file'); +import { + WinstonModule, + utilities as nestWinstonModuleUtilities, +} from 'nest-winston'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import DailyRotateFile from 'winston-daily-rotate-file'; + +export const LoggerFactory = (appName: string) => { + //DailyRotateFile func() + const fileRotateTransport = new DailyRotateFile({ + filename: 'logs/logs-%DATE%.log', + datePattern: 'YYYY-MM-DD', + maxFiles: '14d', + }); + + const consoleFormat = format.combine( + timestamp({ + format: 'MMM-DD-YYYY HH:mm:ss', + }), + json(), + nestWinstonModuleUtilities.format.nestLike(appName, { + colors: true, + prettyPrint: true, + }), + ); + + return WinstonModule.createLogger({ + level: 'info', + transports: [ + fileRotateTransport, + new transports.Console({ format: consoleFormat }), + ], + }); +}; From 998fbec38e25c19982175d5abb41e2bea18b2221 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:28:57 +0200 Subject: [PATCH 032/111] refactor: image upload to S3 (#337) --- backend/example.env | 5 +++-- backend/src/s3/service/s3.service.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/example.env b/backend/example.env index e304ee60..d15953a9 100644 --- a/backend/example.env +++ b/backend/example.env @@ -31,13 +31,14 @@ EMAIL_FROM=example@email.from NAME_FROM=Example Name From #minio -MINIO_ENDPOINT=minio +MINIO_ENDPOINT=localhost MINIO_PORT=9000 MINIO_ACCESS_KEY=minio MINIO_SECRET_KEY=minio123 MINIO_USE_SSL=false MINIO_BUCKET=cc-portal -MINIO_FILE_UPLOAD_ADDRESS=http://209.38.192.168:9000/cc-portal + +S3_BASE_URL=http://localhost:9000 # IPFS Service IPFS_SERVICE_URL=http://ipfs-service:3001 diff --git a/backend/src/s3/service/s3.service.ts b/backend/src/s3/service/s3.service.ts index 60c495a3..9031b76e 100644 --- a/backend/src/s3/service/s3.service.ts +++ b/backend/src/s3/service/s3.service.ts @@ -36,7 +36,7 @@ export class S3Service { fileName: string, ): Promise { const fullName = this.getFullFileName(context, fileName); - const fileUrl = `http://${this.configService.get('MINIO_ENDPOINT')}:${this.configService.get('MINIO_PORT')}/${this.bucketName}/${fullName}`; + const fileUrl = `${this.configService.get('S3_BASE_URL')}/${this.bucketName}/${fullName}`; return fileUrl; } From 80d62e929770c40f80c4c64474d6ab32f8d511b5 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:39:41 +0200 Subject: [PATCH 033/111] fitx/rationale-governance-format Rationale - Removed body.references since it will be empty until CC-portal enables that feature GAP title and abstract is now parsing in compatibility with CIP-0108 (body.title, body.abstract) --- backend/src/constitution/facade/constitution.facade.ts | 2 -- backend/src/governance/facade/governance.facade.ts | 7 +++++-- backend/src/governance/interfaces/icip100.interface.ts | 1 - worker-service/src/common/common-service.ts | 8 ++++++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/backend/src/constitution/facade/constitution.facade.ts b/backend/src/constitution/facade/constitution.facade.ts index 91892be8..6f31def6 100644 --- a/backend/src/constitution/facade/constitution.facade.ts +++ b/backend/src/constitution/facade/constitution.facade.ts @@ -3,7 +3,6 @@ import { ConstitutionRedisService } from 'src/redis/service/constitution-redis.s import { ConstitutionResponse } from '../api/response/constitution.response'; import { ConstitutionMapper } from '../mapper/constitution.mapper'; import { ConstitutionDto } from 'src/redis/dto/constitution.dto'; -import { ConstitutionService } from '../services/constitution.service'; import { IpfsService } from 'src/ipfs/services/ipfs.service'; import { ConstitutionMetadataResponse } from '../api/response/constitution-metadata.response'; import { ConstitutionIpnsUrlResponse } from '../api/response/constitutio-ipns-url.response'; @@ -14,7 +13,6 @@ export class ConstitutionFacade { constructor( private readonly constitutionRedisService: ConstitutionRedisService, - private readonly constitutionService: ConstitutionService, private readonly ipfsService: IpfsService, ) {} diff --git a/backend/src/governance/facade/governance.facade.ts b/backend/src/governance/facade/governance.facade.ts index c51f661a..f0f2f38b 100644 --- a/backend/src/governance/facade/governance.facade.ts +++ b/backend/src/governance/facade/governance.facade.ts @@ -45,7 +45,11 @@ export class GovernanceFacade { const response = await this.governanceService.addRationale(rationaleDto); return GovernanceMapper.rationaleDtoToResponse(response); } - + /** + * Creates a CIP 100 compatible JSON object. + * CIP 100 example: + * https://github.com/cardano-foundation/CIPs/blob/master/CIP-0100/example.body.json + **/ private async createRationaleJsonCip100( rationaleRequest: RationaleRequest, ): Promise { @@ -97,7 +101,6 @@ export class GovernanceFacade { hashAlgorithm: CIP100.hashAlgorithm, authors: [], body: { - references: [], comment: rationaleRequest.content, }, }; diff --git a/backend/src/governance/interfaces/icip100.interface.ts b/backend/src/governance/interfaces/icip100.interface.ts index 60f19b74..62c30a46 100644 --- a/backend/src/governance/interfaces/icip100.interface.ts +++ b/backend/src/governance/interfaces/icip100.interface.ts @@ -69,6 +69,5 @@ interface WitnessContext { } interface BodyContent { - references: any[]; comment: string; } diff --git a/worker-service/src/common/common-service.ts b/worker-service/src/common/common-service.ts index ace3ca5e..342cc970 100644 --- a/worker-service/src/common/common-service.ts +++ b/worker-service/src/common/common-service.ts @@ -86,6 +86,10 @@ export abstract class CommonService { } } + /** + * The response data structure example is located on this link: + * https://github.com/cardano-foundation/CIPs/blob/master/CIP-0108/examples/no-confidence.jsonld + **/ async getGovActionProposalFromUrl( url: string, ): Promise> { @@ -95,8 +99,8 @@ export abstract class CommonService { const title = jsonData.body?.title; const abstract = jsonData.body?.abstract; const govActionProposal: Partial = { - title: title?.['@value'], - abstract: abstract?.['@value'], + title: title, + abstract: abstract, govMetadataUrl: url, }; return govActionProposal; From 709924dd83d413c2f476c597f47ac39890044890 Mon Sep 17 00:00:00 2001 From: Vojimirovich Date: Wed, 11 Sep 2024 10:41:52 +0200 Subject: [PATCH 034/111] chore: change guides btn link --- frontend/src/constants/paths.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/constants/paths.ts b/frontend/src/constants/paths.ts index cc110112..1c1a12d6 100644 --- a/frontend/src/constants/paths.ts +++ b/frontend/src/constants/paths.ts @@ -8,13 +8,13 @@ export const PATHS = { logout: "/logout", admin: { home: "/admin", - dashboard: "/admin/dashboard", - }, + dashboard: "/admin/dashboard" + } }; export const EXTERNAL_LINKS = { guides: - "https://app.gitbook.com/o/Prbm1mtkwSsGWSvG1Bfd/s/IOUshfMdffqF4RObLhje/legal/terms-and-conditions", + "https://docs.gov.tools/about/what-is-the-constitutional-committee-portal" }; export const adminProtectedPath = PATHS.admin.dashboard; From 565adb03ab6f2bcf14cdb5417973c28c6790bd94 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:11:48 +0200 Subject: [PATCH 035/111] fix: rm-gap-title-abstract-len-constraint Remove len constraints for gap.title and gap.abstract (was 80 and 2500 char length) fixed error if db data is undefined when fetching from db sync --- ...2-governance-title-abstrant-len-removal.ts | 27 +++++++++++++++++++ .../entities/gov-action-proposal.entity.ts | 2 -- .../services/gov-action-proposal.service.ts | 2 +- .../src/governance/services/vote.service.ts | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts diff --git a/backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts b/backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts new file mode 100644 index 00000000..43ce92db --- /dev/null +++ b/backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts @@ -0,0 +1,27 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class Migrations1726048356332 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `begin; + ALTER TABLE gov_action_proposals + ALTER COLUMN title TYPE VARCHAR; + + ALTER TABLE gov_action_proposals + alter COLUMN abstract type VARCHAR; + commit;`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `begin; + ALTER TABLE gov_action_proposals + ALTER COLUMN title TYPE VARCHAR(80); + + ALTER TABLE gov_action_proposals + alter COLUMN abstract type VARCHAR(2500); + commit;`, + ); + } +} diff --git a/worker-service/src/governance/entities/gov-action-proposal.entity.ts b/worker-service/src/governance/entities/gov-action-proposal.entity.ts index 484298da..4972ed9f 100644 --- a/worker-service/src/governance/entities/gov-action-proposal.entity.ts +++ b/worker-service/src/governance/entities/gov-action-proposal.entity.ts @@ -21,7 +21,6 @@ export class GovActionProposal extends CommonEntity { @Column({ name: 'title', type: 'varchar', - length: 80, nullable: true, }) title: string; @@ -29,7 +28,6 @@ export class GovActionProposal extends CommonEntity { @Column({ name: 'abstract', type: 'varchar', - length: 2500, nullable: true, }) abstract: string; diff --git a/worker-service/src/governance/services/gov-action-proposal.service.ts b/worker-service/src/governance/services/gov-action-proposal.service.ts index a28d8366..bdb4b2bb 100644 --- a/worker-service/src/governance/services/gov-action-proposal.service.ts +++ b/worker-service/src/governance/services/gov-action-proposal.service.ts @@ -95,7 +95,7 @@ export class GovActionProposalService extends CommonService { ); const results: GovActionProposalRequest[] = []; - dbData.forEach((govActionProposal) => { + dbData?.forEach((govActionProposal) => { results.push( GovActionProposalMapper.dbSyncToGovActionProposalRequest( govActionProposal, diff --git a/worker-service/src/governance/services/vote.service.ts b/worker-service/src/governance/services/vote.service.ts index b7df82b1..04ee2d2f 100644 --- a/worker-service/src/governance/services/vote.service.ts +++ b/worker-service/src/governance/services/vote.service.ts @@ -167,7 +167,7 @@ export class VoteService extends CommonService { addresses, ); const results: VoteRequest[] = []; - dbData.forEach((vote) => { + dbData?.forEach((vote) => { results.push(VoteMapper.dbSyncToVoteRequest(vote, mapHotAddresses)); }); return results; From ea9bc401e2a0a7de3c7a97a6e7399de166e922f5 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:21:53 +0200 Subject: [PATCH 036/111] refactor: add redis username Added username env variable within backend/worker-service/ipfs-service --- backend/example.env | 1 + backend/src/redis/client/redis-client.ts | 1 + ipfs-service/example.env | 1 + ipfs-service/src/bullmq/bullmq.module.ts | 1 + worker-service/example.env | 1 + worker-service/src/bullmq/bullmq.module.ts | 1 + 6 files changed, 6 insertions(+) diff --git a/backend/example.env b/backend/example.env index d15953a9..6437ba86 100644 --- a/backend/example.env +++ b/backend/example.env @@ -21,6 +21,7 @@ JWT_REFRESH_TOKEN_EXPIRES_IN=7d # Redis REDIS_HOST=cache REDIS_PORT=6379 +REDIS_USERNAME=redis REDIS_PASSWORD=password # AWS SES diff --git a/backend/src/redis/client/redis-client.ts b/backend/src/redis/client/redis-client.ts index f11a65a3..1d08a87d 100644 --- a/backend/src/redis/client/redis-client.ts +++ b/backend/src/redis/client/redis-client.ts @@ -9,6 +9,7 @@ export const redisClientFactory: FactoryProvider = { const redisInstance = new Redis({ host: configService.getOrThrow('REDIS_HOST'), port: configService.getOrThrow('REDIS_PORT'), + username: configService.getOrThrow('REDIS_USERNAME'), password: configService.getOrThrow('REDIS_PASSWORD'), }); diff --git a/ipfs-service/example.env b/ipfs-service/example.env index 8da0740b..5f855877 100644 --- a/ipfs-service/example.env +++ b/ipfs-service/example.env @@ -10,6 +10,7 @@ IPNS_CONSTITUTION_KEY_NAME='some-key-name' # Redis REDIS_HOST=localhost REDIS_PORT=6379 +REDIS_USERNAME=redis REDIS_PASSWORD=password ## DHT QUEUE ## diff --git a/ipfs-service/src/bullmq/bullmq.module.ts b/ipfs-service/src/bullmq/bullmq.module.ts index 8dd4d1fd..9f661e19 100644 --- a/ipfs-service/src/bullmq/bullmq.module.ts +++ b/ipfs-service/src/bullmq/bullmq.module.ts @@ -13,6 +13,7 @@ import { ExpressAdapter } from "@bull-board/express"; connection: { host: configService.getOrThrow("REDIS_HOST"), port: configService.getOrThrow("REDIS_PORT"), + username: configService.getOrThrow("REDIS_USERNAME"), password: configService.getOrThrow("REDIS_PASSWORD"), }, }), diff --git a/worker-service/example.env b/worker-service/example.env index f15d8584..cfc77466 100644 --- a/worker-service/example.env +++ b/worker-service/example.env @@ -16,6 +16,7 @@ DB_SYNC_POSTGRES_PASSWORD=password # Redis REDIS_HOST=cache REDIS_PORT=6379 +REDIS_USERNAME=redis REDIS_PASSWORD=password # PAGINATION diff --git a/worker-service/src/bullmq/bullmq.module.ts b/worker-service/src/bullmq/bullmq.module.ts index 0b0cbb58..b3075bfa 100644 --- a/worker-service/src/bullmq/bullmq.module.ts +++ b/worker-service/src/bullmq/bullmq.module.ts @@ -16,6 +16,7 @@ import { ExpressAdapter } from '@bull-board/express'; connection: { host: configService.getOrThrow('REDIS_HOST'), port: configService.getOrThrow('REDIS_PORT'), + username: configService.getOrThrow('REDIS_USERNAME'), password: configService.getOrThrow('REDIS_PASSWORD'), }, }), From e4908f5098f9d506aaaafa78363351a7cf05a739 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:51:41 +0200 Subject: [PATCH 037/111] Fix add redis creds (#343) * feat: add redis user * fix: add config to worker-service * fix-add-redis-creds: fixed order within example.env * fix: add docker compose args --------- Co-authored-by: nike-getto Co-authored-by: BEDev24 --- docker-compose.yaml | 2 ++ example.env | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 example.env diff --git a/docker-compose.yaml b/docker-compose.yaml index 34d39536..c10fa866 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -75,6 +75,8 @@ services: image: redis/redis-stack:latest container_name: cache restart: always + environment: + REDIS_ARGS: "--requirepass ${REDIS_PASSWORD} --user ${REDIS_USERNAME} on >password ~* allcommands --user default off nopass nocommands" ports: - "6379:6379" volumes: diff --git a/example.env b/example.env new file mode 100644 index 00000000..9ad9e784 --- /dev/null +++ b/example.env @@ -0,0 +1,2 @@ +REDIS_USERNAME=redis +REDIS_PASSWORD=password \ No newline at end of file From 0195603c275f8430a2a27776809c7dbaaea953e6 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:56:55 +0200 Subject: [PATCH 038/111] Fix add redis creds (#344) * feat: add redis user * fix: add config to worker-service * fix-add-redis-creds: fixed order within example.env * fix: add docker compose args * refactor: cache docker-compose --------- Co-authored-by: nike-getto Co-authored-by: BEDev24 --- docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index c10fa866..ff9ce4ee 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -77,6 +77,7 @@ services: restart: always environment: REDIS_ARGS: "--requirepass ${REDIS_PASSWORD} --user ${REDIS_USERNAME} on >password ~* allcommands --user default off nopass nocommands" + command: redis=server --requirepass ${REDIS_PASSWORD} ports: - "6379:6379" volumes: From 134efbca0f4432e279f3ae994ab36235ef258226 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:59:22 +0200 Subject: [PATCH 039/111] fix: typo --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index ff9ce4ee..20709eae 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -77,7 +77,7 @@ services: restart: always environment: REDIS_ARGS: "--requirepass ${REDIS_PASSWORD} --user ${REDIS_USERNAME} on >password ~* allcommands --user default off nopass nocommands" - command: redis=server --requirepass ${REDIS_PASSWORD} + command: redis-server --requirepass ${REDIS_PASSWORD} ports: - "6379:6379" volumes: From 3623193f8a40c7db8e7197f5e2c2951485e0e649 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:08:28 +0200 Subject: [PATCH 040/111] fix: typo --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 20709eae..1049e3b4 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -76,7 +76,7 @@ services: container_name: cache restart: always environment: - REDIS_ARGS: "--requirepass ${REDIS_PASSWORD} --user ${REDIS_USERNAME} on >password ~* allcommands --user default off nopass nocommands" + REDIS_ARGS: "--requirepass ${REDIS_PASSWORD} --user ${REDIS_USERNAME} on >${REDIS_PASSWORD} ~* allcommands --user default off nopass nocommands" command: redis-server --requirepass ${REDIS_PASSWORD} ports: - "6379:6379" From 7347e4b36482029781f4d4fb4dd28399607c81db Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:15:06 +0200 Subject: [PATCH 041/111] refactor: cache service --- docker-compose.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 1049e3b4..f0d46cfe 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -76,8 +76,12 @@ services: container_name: cache restart: always environment: - REDIS_ARGS: "--requirepass ${REDIS_PASSWORD} --user ${REDIS_USERNAME} on >${REDIS_PASSWORD} ~* allcommands --user default off nopass nocommands" - command: redis-server --requirepass ${REDIS_PASSWORD} + - REDIS_USERNAME=${REDIS_USERNAME} + - REDIS_PASSWORD=${REDIS_PASSWORD} + command: > + redis-server --requirepass ${REDIS_PASSWORD} + --user ${REDIS_USERNAME} on >${REDIS_PASSWORD} ~* +@all + --user default off ports: - "6379:6379" volumes: From 69fc6174876ee514dcd4e5c24eddbbcb04cc22e2 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:23:23 +0200 Subject: [PATCH 042/111] refactor/redis-auth (#345) --- backend/example.env | 1 - backend/src/redis/client/redis-client.ts | 1 - docker-compose.yaml | 8 ++------ example.env | 1 - ipfs-service/example.env | 1 - ipfs-service/src/bullmq/bullmq.module.ts | 1 - worker-service/example.env | 1 - worker-service/src/bullmq/bullmq.module.ts | 1 - 8 files changed, 2 insertions(+), 13 deletions(-) diff --git a/backend/example.env b/backend/example.env index 6437ba86..d15953a9 100644 --- a/backend/example.env +++ b/backend/example.env @@ -21,7 +21,6 @@ JWT_REFRESH_TOKEN_EXPIRES_IN=7d # Redis REDIS_HOST=cache REDIS_PORT=6379 -REDIS_USERNAME=redis REDIS_PASSWORD=password # AWS SES diff --git a/backend/src/redis/client/redis-client.ts b/backend/src/redis/client/redis-client.ts index 1d08a87d..f11a65a3 100644 --- a/backend/src/redis/client/redis-client.ts +++ b/backend/src/redis/client/redis-client.ts @@ -9,7 +9,6 @@ export const redisClientFactory: FactoryProvider = { const redisInstance = new Redis({ host: configService.getOrThrow('REDIS_HOST'), port: configService.getOrThrow('REDIS_PORT'), - username: configService.getOrThrow('REDIS_USERNAME'), password: configService.getOrThrow('REDIS_PASSWORD'), }); diff --git a/docker-compose.yaml b/docker-compose.yaml index f0d46cfe..1868441a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -76,12 +76,8 @@ services: container_name: cache restart: always environment: - - REDIS_USERNAME=${REDIS_USERNAME} - - REDIS_PASSWORD=${REDIS_PASSWORD} - command: > - redis-server --requirepass ${REDIS_PASSWORD} - --user ${REDIS_USERNAME} on >${REDIS_PASSWORD} ~* +@all - --user default off + - REDIS_PASSWORD=your-redis-password + command: redis-server --requirepass ${REDIS_PASSWORD} ports: - "6379:6379" volumes: diff --git a/example.env b/example.env index 9ad9e784..dfbd622f 100644 --- a/example.env +++ b/example.env @@ -1,2 +1 @@ -REDIS_USERNAME=redis REDIS_PASSWORD=password \ No newline at end of file diff --git a/ipfs-service/example.env b/ipfs-service/example.env index 5f855877..8da0740b 100644 --- a/ipfs-service/example.env +++ b/ipfs-service/example.env @@ -10,7 +10,6 @@ IPNS_CONSTITUTION_KEY_NAME='some-key-name' # Redis REDIS_HOST=localhost REDIS_PORT=6379 -REDIS_USERNAME=redis REDIS_PASSWORD=password ## DHT QUEUE ## diff --git a/ipfs-service/src/bullmq/bullmq.module.ts b/ipfs-service/src/bullmq/bullmq.module.ts index 9f661e19..8dd4d1fd 100644 --- a/ipfs-service/src/bullmq/bullmq.module.ts +++ b/ipfs-service/src/bullmq/bullmq.module.ts @@ -13,7 +13,6 @@ import { ExpressAdapter } from "@bull-board/express"; connection: { host: configService.getOrThrow("REDIS_HOST"), port: configService.getOrThrow("REDIS_PORT"), - username: configService.getOrThrow("REDIS_USERNAME"), password: configService.getOrThrow("REDIS_PASSWORD"), }, }), diff --git a/worker-service/example.env b/worker-service/example.env index cfc77466..f15d8584 100644 --- a/worker-service/example.env +++ b/worker-service/example.env @@ -16,7 +16,6 @@ DB_SYNC_POSTGRES_PASSWORD=password # Redis REDIS_HOST=cache REDIS_PORT=6379 -REDIS_USERNAME=redis REDIS_PASSWORD=password # PAGINATION diff --git a/worker-service/src/bullmq/bullmq.module.ts b/worker-service/src/bullmq/bullmq.module.ts index b3075bfa..0b0cbb58 100644 --- a/worker-service/src/bullmq/bullmq.module.ts +++ b/worker-service/src/bullmq/bullmq.module.ts @@ -16,7 +16,6 @@ import { ExpressAdapter } from '@bull-board/express'; connection: { host: configService.getOrThrow('REDIS_HOST'), port: configService.getOrThrow('REDIS_PORT'), - username: configService.getOrThrow('REDIS_USERNAME'), password: configService.getOrThrow('REDIS_PASSWORD'), }, }), From 9ade333d2d813b5c8d54a2eb1b6120b248326957 Mon Sep 17 00:00:00 2001 From: Vojimirovich Date: Wed, 11 Sep 2024 14:49:39 +0200 Subject: [PATCH 043/111] fix: plural grammar corrections --- frontend/messages/de.json | 10 +++++----- frontend/messages/en.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/messages/de.json b/frontend/messages/de.json index 07970556..18830c42 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -243,15 +243,15 @@ }, "members": { "title": "No members found", - "description": "There is currently no members. Please check back later for updates" + "description": "There are currently no members. Please check back later for updates" }, "latestUpdates": { "title": "No votes found", - "description": "There is currently no votes. Please check back later for updates" + "description": "There are currently no votes. Please check back later for updates" }, "myActions": { "title": "No votes found", - "description": "There is currently no votes. Please check back later for updates" + "description": "There are currently no votes. Please check back later for updates" }, "constitution": { "title": "No Constitution available", @@ -259,11 +259,11 @@ }, "constitutionMetadata": { "title": "No revisions available", - "description": "There is currently no revisions available. Please check back later for updates" + "description": "There are currently no revisions available. Please check back later for updates" }, "governanceAction": { "title": "No governance actions available", - "description": "There is currently no governance actions. Please check back later for updates" + "description": "There are currently no governance actions. Please check back later for updates" } }, "Snackbar": { diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 6ec76645..fbae8799 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -243,15 +243,15 @@ }, "members": { "title": "No members found", - "description": "There is currently no members. Please check back later for updates" + "description": "There are currently no members. Please check back later for updates" }, "latestUpdates": { "title": "No votes found", - "description": "There is currently no votes. Please check back later for updates" + "description": "There are currently no votes. Please check back later for updates" }, "myActions": { "title": "No votes found", - "description": "There is currently no votes. Please check back later for updates" + "description": "There are currently no votes. Please check back later for updates" }, "constitution": { "title": "No Constitution available", @@ -259,11 +259,11 @@ }, "constitutionMetadata": { "title": "No revisions available", - "description": "There is currently no revisions available. Please check back later for updates" + "description": "There are currently no revisions available. Please check back later for updates" }, "governanceAction": { "title": "No governance actions available", - "description": "There is currently no governance actions. Please check back later for updates" + "description": "There are currently no governance actions. Please check back later for updates" } }, "Snackbar": { From dec65074e2bdfd18518605d96d079c6689aba795 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:54:25 +0200 Subject: [PATCH 044/111] refactor: pruning worker processors (#346) --- worker-service/src/common/common-service.ts | 4 +-- .../gov-action-proposal.processor.ts | 28 ++++++++++--------- .../queues/processors/vote.processor.ts | 28 +++++++++++-------- .../services/gov-action-proposal.service.ts | 7 ----- .../src/governance/services/vote.service.ts | 1 + 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/worker-service/src/common/common-service.ts b/worker-service/src/common/common-service.ts index 342cc970..fff75a1d 100644 --- a/worker-service/src/common/common-service.ts +++ b/worker-service/src/common/common-service.ts @@ -105,8 +105,8 @@ export abstract class CommonService { }; return govActionProposal; } catch (e) { - this.logger.log( - `There has been an exception when fetching data for governance action proposal: ${e}`, + this.logger.warn( + `Error when fetching GAP metadata from url ${url}; Message: ${e}`, ); return null; } diff --git a/worker-service/src/governance/queues/processors/gov-action-proposal.processor.ts b/worker-service/src/governance/queues/processors/gov-action-proposal.processor.ts index dde7dab3..56597e54 100644 --- a/worker-service/src/governance/queues/processors/gov-action-proposal.processor.ts +++ b/worker-service/src/governance/queues/processors/gov-action-proposal.processor.ts @@ -7,30 +7,32 @@ import { import { Logger } from '@nestjs/common'; import { GovActionProposalService } from '../../services/gov-action-proposal.service'; + @Processor(QUEUE_NAME_DB_SYNC_GOV_ACTIONS) export class GovActionsProposalProcessor extends WorkerHost { protected readonly logger = new Logger(GovActionsProposalProcessor.name); constructor( - private readonly govActionProposalService: GovActionProposalService, + private readonly govActionProposalService: GovActionProposalService ) { super(); } async process(job: Job): Promise { switch (job.name) { case JOB_NAME_GOV_ACTIONS_SYNC: { - this.logger.debug('Data from db-sync for gov action proposals job'); - return job.data; + try { + this.logger.debug( + `Processing GAP - amount of GAPs to be processed, ${job.data?.length}`, + ); + await this.govActionProposalService.storeGovActionProposalData( + job.data, + ); + } catch (error) { + this.logger.error( + `Error processing job id: ${job.id}, name: ${job.name}. - Error: ${error}`, + ); + throw error; + } } } } - - @OnWorkerEvent('completed') - onCompleted(job: Job) { - const { id, name, queueName, finishedOn, returnvalue } = job; - const completionTime = finishedOn ? new Date(finishedOn).toISOString() : ''; - this.govActionProposalService.storeGovActionProposalData(returnvalue); - this.logger.log( - `Job id: ${id}, name: ${name} completed in queue ${queueName} on ${completionTime}.`, - ); - } } diff --git a/worker-service/src/governance/queues/processors/vote.processor.ts b/worker-service/src/governance/queues/processors/vote.processor.ts index c8f946af..7cdf2a6c 100644 --- a/worker-service/src/governance/queues/processors/vote.processor.ts +++ b/worker-service/src/governance/queues/processors/vote.processor.ts @@ -6,6 +6,7 @@ import { } from '../../../common/constants/bullmq.constants'; import { Logger } from '@nestjs/common'; import { VoteService } from '../../services/vote.service'; +import { VoteRequest } from 'src/governance/dto/vote.request'; @Processor(QUEUE_NAME_DB_SYNC_VOTES) export class VoteProcessor extends WorkerHost { @@ -17,19 +18,22 @@ export class VoteProcessor extends WorkerHost { async process(job: Job): Promise { switch (job.name) { case JOB_NAME_VOTE_SYNC: { - this.logger.debug('Data from db-sync for votes job'); - return job.data; + try { + const voteRequests: VoteRequest[] = job.data; + const addresses = voteRequests.map( + (voteRequest) => voteRequest.hotAddress, + ); + this.logger.debug( + `Processing votes for addresses:, ${JSON.stringify(addresses)}`, + ); + await this.voteService.storeVoteData(voteRequests); + } catch (error) { + this.logger.error( + `Error processing job id: ${job.id}, name: ${job.name}. - Error: ${error}`, + ); + throw error; + } } } } - - @OnWorkerEvent('completed') - onCompleted(job: Job) { - const { id, name, queueName, finishedOn, returnvalue } = job; - const completionTime = finishedOn ? new Date(finishedOn).toISOString() : ''; - this.voteService.storeVoteData(returnvalue); - this.logger.log( - `Job id: ${id}, name: ${name} completed in queue ${queueName} on ${completionTime}.`, - ); - } } diff --git a/worker-service/src/governance/services/gov-action-proposal.service.ts b/worker-service/src/governance/services/gov-action-proposal.service.ts index bdb4b2bb..93309977 100644 --- a/worker-service/src/governance/services/gov-action-proposal.service.ts +++ b/worker-service/src/governance/services/gov-action-proposal.service.ts @@ -33,16 +33,9 @@ export class GovActionProposalService extends CommonService { private readonly configService: ConfigService, ) { super(dataSource); - // this.cronInterval = - // this.configService.get('GOV_ACTION_PROPOSALS_JOB_FREQUENCY') || - // '0 * * * * *'; this.logger = new Logger(GovActionProposalService.name); } - // getCronExpression(): string { - // return this.cronInterval; - // } - async storeGovActionProposalData( govActionProposalRequests: GovActionProposalRequest[], ): Promise { diff --git a/worker-service/src/governance/services/vote.service.ts b/worker-service/src/governance/services/vote.service.ts index 04ee2d2f..cac690b2 100644 --- a/worker-service/src/governance/services/vote.service.ts +++ b/worker-service/src/governance/services/vote.service.ts @@ -162,6 +162,7 @@ export class VoteService extends CommonService { try { const prefix = '\\x'; // prefix for each hot address const addresses = [...mapHotAddresses.keys()].map((key) => prefix + key); + this.logger.debug(`Addresses for fetching from db sync: ${addresses}`); const dbData = await this.getDataFromSqlFile( SQL_FILE_PATH.GET_VOTES, addresses, From fcd49aa1a9667d39c24aa6c39307dd360adff27f Mon Sep 17 00:00:00 2001 From: Vojimirovich Date: Thu, 12 Sep 2024 10:00:33 +0200 Subject: [PATCH 045/111] fix: latest updates content overlaping --- .../components/atoms/OutlinedLightButton.tsx | 5 +++-- .../organisms/VotesTable/VotesTableRow.tsx | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/atoms/OutlinedLightButton.tsx b/frontend/src/components/atoms/OutlinedLightButton.tsx index 74d929fe..819357d1 100644 --- a/frontend/src/components/atoms/OutlinedLightButton.tsx +++ b/frontend/src/components/atoms/OutlinedLightButton.tsx @@ -23,11 +23,12 @@ export const OutlinedLightButton = ({ fontWeight: 400, fontSize: 12, lineHeight: "18px", - whiteSpace: "normal;", + wordBreak: "break-word", + "&": { pointerEvents: nonInteractive ? "none" : "all" } - }; + } as React.CSSProperties; return ( - - - diff --git a/frontend/src/components/organisms/GovernanceActions.tsx b/frontend/src/components/organisms/GovernanceActions.tsx index c3e2c25a..60af9d9f 100644 --- a/frontend/src/components/organisms/GovernanceActions.tsx +++ b/frontend/src/components/organisms/GovernanceActions.tsx @@ -1,27 +1,27 @@ "use client"; -import React, { useCallback, useEffect, useState } from "react"; -import { Box } from "@mui/material"; -import { NotFound } from "./NotFound"; -import { countSelectedFilters, isEmpty } from "@utils"; -import { DataActionsBar } from "../molecules"; +import { getGovernanceActions } from "@/lib/api"; +import { GovernanceActionTableI, PaginationMeta } from "@/lib/requests"; import { GOVERNANCE_ACTIONS_FILTERS, GOVERNANCE_ACTIONS_SORTING, MY_ACTIONS_TABS, - PATHS, + PATHS } from "@consts"; -import { PageTitleTabs } from "./PageTitleTabs"; -import { GovActionTable } from "./GovActionTable"; -import { GovernanceActionTableI, PaginationMeta } from "@/lib/requests"; -import { getGovernanceActions } from "@/lib/api"; -import { usePagination, useManageQueryParams } from "@hooks"; -import { ShowMoreButton } from "../atoms"; +import { useManageQueryParams, usePagination } from "@hooks"; +import { Box } from "@mui/material"; +import { countSelectedFilters, isEmpty } from "@utils"; import { useRouter } from "next/navigation"; +import { useCallback, useEffect, useState } from "react"; +import { ShowMoreButton } from "../atoms"; +import { DataActionsBar } from "../molecules"; +import { GovActionTable } from "./GovActionTable"; +import { NotFound } from "./NotFound"; +import { PageTitleTabs } from "./PageTitleTabs"; export const GovernanceActions = ({ actions, - paginationMeta, + paginationMeta }: { actions: GovernanceActionTableI[]; paginationMeta: PaginationMeta; @@ -43,7 +43,7 @@ export const GovernanceActions = ({ : null, status: chosenFilters.status?.length > 0 ? chosenFilters.status?.join(",") : null, - sortBy: chosenSorting || null, + sortBy: chosenSorting || null }; const { data, pagination, isLoading, loadMore } = usePagination( @@ -70,10 +70,10 @@ export const GovernanceActions = ({ paddingBottom={4} display="flex" justifyContent={{ xxs: "flex-start", md: "space-between" }} - flexDirection={{ xxs: "column", md: "row" }} + flexDirection={{ xxs: "column", md: "column", lg: "row" }} alignItems={{ xxs: "left", md: "center" }} > - + router.push(tab.value)} diff --git a/frontend/src/components/organisms/Modals/GovActionModal.tsx b/frontend/src/components/organisms/Modals/GovActionModal.tsx index 38e15acb..46d531ba 100644 --- a/frontend/src/components/organisms/Modals/GovActionModal.tsx +++ b/frontend/src/components/organisms/Modals/GovActionModal.tsx @@ -1,29 +1,29 @@ "use client"; -import React, { useEffect, useState } from "react"; +import { useSnackbar } from "@/context/snackbar"; +import { getGovernanceMetadata } from "@/lib/api"; import { - ModalWrapper, - ModalHeader, - Typography, Button, + ModalHeader, + ModalWrapper, OutlinedLightButton, + Typography } from "@atoms"; import { customPalette, IMAGES } from "@consts"; -import { useTranslations } from "next-intl"; import { useModal } from "@context"; +import { useScreenDimension } from "@hooks"; +import { CopyCard, Loading } from "@molecules"; import { Box } from "@mui/material"; import { GovActionModalState } from "@organisms"; -import { CopyCard, Loading } from "@molecules"; -import { GovActionMetadata } from "../types"; -import { getGovernanceMetadata } from "@/lib/api"; import { formatDisplayDate, getProposalTypeLabel, getShortenedGovActionId, - isResponseErrorI, + isResponseErrorI } from "@utils"; -import { useSnackbar } from "@/context/snackbar"; -import { useScreenDimension } from "@hooks"; +import { useTranslations } from "next-intl"; +import { useEffect, useState } from "react"; +import { GovActionMetadata } from "../types"; export const GovActionModal = () => { const t = useTranslations("Modals"); @@ -83,7 +83,7 @@ export const GovActionModal = () => { diff --git a/frontend/src/components/organisms/MyActions.tsx b/frontend/src/components/organisms/MyActions.tsx index b7f494ea..8d589d85 100644 --- a/frontend/src/components/organisms/MyActions.tsx +++ b/frontend/src/components/organisms/MyActions.tsx @@ -1,31 +1,31 @@ "use client"; -import React, { useCallback, useEffect, useState } from "react"; -import { Box } from "@mui/material"; -import { useTranslations } from "next-intl"; -import { NotFound } from "./NotFound"; -import { VotesTable } from "./VotesTable"; -import { countSelectedFilters, isEmpty } from "@utils"; -import { DataActionsBar } from "../molecules"; +import { useModal } from "@/context"; +import { getUserVotes } from "@/lib/api"; +import { PaginationMeta, VotesTableI } from "@/lib/requests"; import { LATEST_UPDATES_FILTERS, LATEST_UPDATES_SORTING, MY_ACTIONS_TABS, - PATHS, + PATHS } from "@consts"; +import { useManageQueryParams, usePagination } from "@hooks"; +import { Box } from "@mui/material"; +import { countSelectedFilters, isEmpty } from "@utils"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { useCallback, useEffect, useState } from "react"; +import { ShowMoreButton } from "../atoms"; +import { DataActionsBar } from "../molecules"; +import { NotFound } from "./NotFound"; import { PageTitleTabs } from "./PageTitleTabs"; -import { useModal } from "@/context"; -import { PaginationMeta, VotesTableI } from "@/lib/requests"; import { OpenPreviewReasoningModal } from "./types"; -import { getUserVotes } from "@/lib/api"; -import { usePagination, useManageQueryParams } from "@hooks"; -import { ShowMoreButton } from "../atoms"; -import { useRouter } from "next/navigation"; +import { VotesTable } from "./VotesTable"; export const MyActions = ({ actions, paginationMeta, - error, + error }: { actions: VotesTableI[]; paginationMeta: PaginationMeta; @@ -54,9 +54,9 @@ export const MyActions = ({ submit_time: null, //todo, update BE response end_time: action.gov_action_proposal_end_time, vote_submit_time: action.vote_submit_time, - vote: action.value, - }, - }, + vote: action.value + } + } }); }; @@ -67,7 +67,7 @@ export const MyActions = ({ ? chosenFilters.govActionType?.join(",") : null, vote: chosenFilters.vote?.length > 0 ? chosenFilters.vote?.join(",") : null, - sortBy: chosenSorting || null, + sortBy: chosenSorting || null }; const { data, pagination, isLoading, loadMore } = usePagination( @@ -94,10 +94,10 @@ export const MyActions = ({ paddingBottom={4} display="flex" justifyContent={{ xxs: "flex-start", md: "space-between" }} - flexDirection={{ xxs: "column", md: "row" }} + flexDirection={{ xxs: "column", md: "column", lg: "row" }} alignItems={{ xxs: "left", md: "center" }} > - + router.push(tab.value)} diff --git a/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx b/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx index f87b8b11..3600e49d 100644 --- a/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx +++ b/frontend/src/components/organisms/VotesTable/VotesTableRow.tsx @@ -99,7 +99,7 @@ export const VotesTableRow = ({ Date: Thu, 12 Sep 2024 15:35:24 +0200 Subject: [PATCH 049/111] fix: don't show top banner on mainnet --- frontend/src/context/topBanner.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/src/context/topBanner.tsx b/frontend/src/context/topBanner.tsx index 718cfd9a..375c2425 100644 --- a/frontend/src/context/topBanner.tsx +++ b/frontend/src/context/topBanner.tsx @@ -1,19 +1,19 @@ "use client"; -import { createContext, useContext, useEffect, useState } from "react"; import { Alert, Snackbar, SnackbarOrigin } from "@mui/material"; import { getItemFromSessionStorage, setItemToSessionStorage, - TOP_BANER, + TOP_BANER } from "@utils"; import { useTranslations } from "next-intl"; +import { createContext, useContext, useEffect, useState } from "react"; interface TopBannerContextI {} const defaultPosition = { vertical: "top", - horizontal: "center", + horizontal: "center" } as SnackbarOrigin; /** @@ -31,8 +31,12 @@ export function TopBannerContextProvider({ children }) { const t = useTranslations("TopBanner"); useEffect(() => { - let returningUser = getItemFromSessionStorage(TOP_BANER); - setDisplayPopUp(!returningUser); + if (process.env.NEXT_PUBLIC_IS_MAINNET === "true") { + setDisplayPopUp(false); + } else { + let returningUser = getItemFromSessionStorage(TOP_BANER); + setDisplayPopUp(!returningUser); + } }, [displayPopUp]); const handleClose = ( @@ -62,7 +66,7 @@ export function TopBannerContextProvider({ children }) { variant="filled" sx={{ minWidth: { xxs: "100vw" }, - backgroundColor: "#DEA029", + backgroundColor: "#DEA029" }} > {t("message")} From e06cccee75f12f219cc3b655a2b3e763d30ed897 Mon Sep 17 00:00:00 2001 From: Vojimirovich Date: Thu, 12 Sep 2024 18:26:58 +0200 Subject: [PATCH 050/111] fix: show more btn not loading more content, update env example --- frontend/example.env | 6 +++++- frontend/src/app/[locale]/my-actions/page.tsx | 14 +++++++------- frontend/src/components/organisms/MyActions.tsx | 7 +++++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/frontend/example.env b/frontend/example.env index b2481661..7ae18bfb 100644 --- a/frontend/example.env +++ b/frontend/example.env @@ -1,3 +1,7 @@ NEXT_PUBLIC_API_URL=http://backend:1337 # Container name goes here NEXT_PUBLIC_USERSNAP_SPACE_API_KEY= -NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS= \ No newline at end of file +NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY = +NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS= +NEXT_PUBLIC_IS_MAINNET=false + + diff --git a/frontend/src/app/[locale]/my-actions/page.tsx b/frontend/src/app/[locale]/my-actions/page.tsx index d4a789c7..15e134c4 100644 --- a/frontend/src/app/[locale]/my-actions/page.tsx +++ b/frontend/src/app/[locale]/my-actions/page.tsx @@ -1,15 +1,15 @@ -import React, { Suspense } from "react"; +import { Suspense } from "react"; -import { unstable_setRequestLocale } from "next-intl/server"; // Import function to set the request-specific locale (unstable API). -import { Footer, TopNav, MyActions } from "@organisms"; -import { Loading } from "@molecules"; import { decodeUserToken, getUserVotes } from "@/lib/api"; import { ContentWrapper } from "@atoms"; +import { Loading } from "@molecules"; +import { Footer, MyActions, TopNav } from "@organisms"; import { isResponseErrorI } from "@utils"; +import { unstable_setRequestLocale } from "next-intl/server"; // Import function to set the request-specific locale (unstable API). export default async function MyActionsPage({ params: { locale }, - searchParams, + searchParams }) { unstable_setRequestLocale(locale); // Sets the locale for the request. Use cautiously due to its unstable nature. const user = await decodeUserToken(); @@ -19,10 +19,9 @@ export default async function MyActionsPage({ govActionType: searchParams?.govActionType, vote: searchParams?.vote, sortBy: searchParams?.sortBy, - userId: user?.userId, + userId: user?.userId }); const hasError = isResponseErrorI(userActions); - return ( <> @@ -32,6 +31,7 @@ export default async function MyActionsPage({ actions={!hasError && userActions?.data} paginationMeta={!hasError && userActions?.meta} error={hasError && userActions.error} + userId={user?.userId} /> ; diff --git a/frontend/src/components/organisms/MyActions.tsx b/frontend/src/components/organisms/MyActions.tsx index 8d589d85..3fea4807 100644 --- a/frontend/src/components/organisms/MyActions.tsx +++ b/frontend/src/components/organisms/MyActions.tsx @@ -25,11 +25,13 @@ import { VotesTable } from "./VotesTable"; export const MyActions = ({ actions, paginationMeta, - error + error, + userId }: { actions: VotesTableI[]; paginationMeta: PaginationMeta; error?: string; + userId?: string; }) => { const t = useTranslations("MyActions"); const router = useRouter(); @@ -67,7 +69,8 @@ export const MyActions = ({ ? chosenFilters.govActionType?.join(",") : null, vote: chosenFilters.vote?.length > 0 ? chosenFilters.vote?.join(",") : null, - sortBy: chosenSorting || null + sortBy: chosenSorting || null, + userId }; const { data, pagination, isLoading, loadMore } = usePagination( From 12568b91e1951cefe2e2958d150e494016f368d9 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:37:16 +0200 Subject: [PATCH 051/111] refactor: renamed migration name for gap len constraint removal --- .../1726048356332-governance-title-abstrant-len-removal.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts b/backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts index 43ce92db..91abd829 100644 --- a/backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts +++ b/backend/migrations/1726048356332-governance-title-abstrant-len-removal.ts @@ -1,6 +1,8 @@ import { MigrationInterface, QueryRunner } from 'typeorm'; -export class Migrations1726048356332 implements MigrationInterface { +export class GovTitleAbstractLenRemoval1726048356332 + implements MigrationInterface +{ public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( `begin; From 90505b952ee78eda60273a6c4b48beb0b9d0eb77 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:43:02 +0200 Subject: [PATCH 052/111] refactor: ipfs volume --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 1868441a..6e855821 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -37,7 +37,7 @@ services: - "5001:5001" - "8080:8080" volumes: - - ipfs_data:/app/ + - ipfs_data:/ipfs/datastore networks: - bloxico_local extra_hosts: From f222be06e827ae323cb6b8256184bfad6d2abf30 Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:07:28 +0200 Subject: [PATCH 053/111] add correct vars (#352) Co-authored-by: nike-getto --- frontend/Dockerfile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 95ab907e..594771ba 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -28,13 +28,17 @@ ENV NEXT_TELEMETRY_DISABLED 1 # Define build arguments for environment variables ARG NEXT_PUBLIC_API_URL -ARG NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY +ARG NEXT_PUBLIC_USERSNAP_SPACE_API_KEY ARG NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY +ARG NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS +ARG NEXT_PUBLIC_IS_MAINNET # Set the environment variables inside the container ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} -ENV NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY=${NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY} +ENV NEXT_PUBLIC_USERSNAP_SPACE_API_KEY=${NEXT_PUBLIC_USERSNAP_SPACE_API_KEY} ENV NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY=${NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY} +ENV NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS=${NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS} +ENV NEXT_PUBLIC_IS_MAINNET=${NEXT_PUBLIC_IS_MAINNET} RUN \ if [ -f yarn.lock ]; then yarn run build; \ @@ -52,8 +56,10 @@ ENV NODE_ENV production # Disabled telemetry during runtime. ENV NEXT_TELEMETRY_DISABLED 1 ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} -ENV NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY=${NEXT_PUBLIC_USERSNAP_GLOBAL_API_KEY} +ENV NEXT_PUBLIC_USERSNAP_SPACE_API_KEY=${NEXT_PUBLIC_USERSNAP_SPACE_API_KEY} ENV NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY=${NEXT_PUBLIC_USERSNAP_PROJECT_API_KEY} +ENV NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS=${NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS} +ENV NEXT_PUBLIC_IS_MAINNET=${NEXT_PUBLIC_IS_MAINNET} RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs From 5277d150cdc935d419a129b8082791e27325b39b Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:25:33 +0200 Subject: [PATCH 054/111] fix: ipfs-redis-service (#360) * fix/ipfs-redis-service: tls connection to redis * fix/ipfs-redis-service: tls connection to redis --- backend/example.env | 1 + backend/src/redis/client/redis-client.ts | 1 + ipfs-service/example.env | 1 + ipfs-service/src/bullmq/bullmq.module.ts | 1 + worker-service/example.env | 1 + worker-service/src/bullmq/bullmq.module.ts | 1 + 6 files changed, 6 insertions(+) diff --git a/backend/example.env b/backend/example.env index d15953a9..1c9e1497 100644 --- a/backend/example.env +++ b/backend/example.env @@ -22,6 +22,7 @@ JWT_REFRESH_TOKEN_EXPIRES_IN=7d REDIS_HOST=cache REDIS_PORT=6379 REDIS_PASSWORD=password +REDIS_TLS=false # AWS SES AWS_ACCESS_KEY_ID=your_access_key_id diff --git a/backend/src/redis/client/redis-client.ts b/backend/src/redis/client/redis-client.ts index f11a65a3..763d9d5f 100644 --- a/backend/src/redis/client/redis-client.ts +++ b/backend/src/redis/client/redis-client.ts @@ -10,6 +10,7 @@ export const redisClientFactory: FactoryProvider = { host: configService.getOrThrow('REDIS_HOST'), port: configService.getOrThrow('REDIS_PORT'), password: configService.getOrThrow('REDIS_PASSWORD'), + ...(configService.get('REDIS_TLS') === 'false' ? {} : { tls: {} }), }); redisInstance.on('error', (e) => { diff --git a/ipfs-service/example.env b/ipfs-service/example.env index 8da0740b..e5bf4a07 100644 --- a/ipfs-service/example.env +++ b/ipfs-service/example.env @@ -11,6 +11,7 @@ IPNS_CONSTITUTION_KEY_NAME='some-key-name' REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD=password +REDIS_TLS=false ## DHT QUEUE ## # Attempts diff --git a/ipfs-service/src/bullmq/bullmq.module.ts b/ipfs-service/src/bullmq/bullmq.module.ts index 8dd4d1fd..a616bf14 100644 --- a/ipfs-service/src/bullmq/bullmq.module.ts +++ b/ipfs-service/src/bullmq/bullmq.module.ts @@ -14,6 +14,7 @@ import { ExpressAdapter } from "@bull-board/express"; host: configService.getOrThrow("REDIS_HOST"), port: configService.getOrThrow("REDIS_PORT"), password: configService.getOrThrow("REDIS_PASSWORD"), + ...(configService.get('REDIS_TLS') === 'false' ? {} : { tls: {} }), }, }), inject: [ConfigService], diff --git a/worker-service/example.env b/worker-service/example.env index f15d8584..1265c48c 100644 --- a/worker-service/example.env +++ b/worker-service/example.env @@ -17,6 +17,7 @@ DB_SYNC_POSTGRES_PASSWORD=password REDIS_HOST=cache REDIS_PORT=6379 REDIS_PASSWORD=password +REDIS_TLS=false # PAGINATION HOT_ADDRESSES_PER_PAGE=10 diff --git a/worker-service/src/bullmq/bullmq.module.ts b/worker-service/src/bullmq/bullmq.module.ts index 0b0cbb58..68523009 100644 --- a/worker-service/src/bullmq/bullmq.module.ts +++ b/worker-service/src/bullmq/bullmq.module.ts @@ -17,6 +17,7 @@ import { ExpressAdapter } from '@bull-board/express'; host: configService.getOrThrow('REDIS_HOST'), port: configService.getOrThrow('REDIS_PORT'), password: configService.getOrThrow('REDIS_PASSWORD'), + ...(configService.get('REDIS_TLS') === 'false' ? {} : { tls: {} }), }, }), inject: [ConfigService], From 80830ac12ff489de3478a8458cfb29047b1fd64f Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:45:58 +0200 Subject: [PATCH 055/111] fix/gov-search-rationale-votes: user votes and rationale are being checked when data is fetched (#359) Co-authored-by: BEDev24 --- .../governance/dto/gov-action-proposal-dto.ts | 11 ++++ .../governance/facade/governance.facade.ts | 9 ++- .../governance/mapper/governance-mapper.ts | 59 +++++++++++-------- .../governance/services/governance.service.ts | 24 +------- 4 files changed, 54 insertions(+), 49 deletions(-) diff --git a/backend/src/governance/dto/gov-action-proposal-dto.ts b/backend/src/governance/dto/gov-action-proposal-dto.ts index 4a1d750c..91d7b6e9 100644 --- a/backend/src/governance/dto/gov-action-proposal-dto.ts +++ b/backend/src/governance/dto/gov-action-proposal-dto.ts @@ -11,6 +11,17 @@ export class GovActionProposalDto { status: GovActionProposalStatus; voteStatus: VoteStatus; hasRationale: boolean; + votedBy: string[]; + rationaleBy: string[]; submitTime: Date; endTime: Date; } + +export class VoteStatusRationaleInfoDto { + constructor(voteStatus: VoteStatus, hasRationale: boolean) { + this.voteStatus = voteStatus; + this.hasRationale = hasRationale; + } + voteStatus: VoteStatus; + hasRationale: boolean; +} diff --git a/backend/src/governance/facade/governance.facade.ts b/backend/src/governance/facade/governance.facade.ts index f0f2f38b..29ba4777 100644 --- a/backend/src/governance/facade/governance.facade.ts +++ b/backend/src/governance/facade/governance.facade.ts @@ -139,7 +139,14 @@ export class GovernanceFacade { userId: string, ): Promise> { const gapPaginatedDto = - await this.governanceService.searchGovActionProposals(query, userId); + await this.governanceService.searchGovActionProposals(query); + + gapPaginatedDto.items.forEach((gap) => { + const userVotesRationaleInfo = + GovernanceMapper.returnUserVoteRationaleInfo(gap, userId); + gap.voteStatus = userVotesRationaleInfo.voteStatus; + gap.hasRationale = userVotesRationaleInfo.hasRationale; + }); return new PaginationDtoMapper< GovActionProposalDto, diff --git a/backend/src/governance/mapper/governance-mapper.ts b/backend/src/governance/mapper/governance-mapper.ts index 44b867d7..f13a3c99 100644 --- a/backend/src/governance/mapper/governance-mapper.ts +++ b/backend/src/governance/mapper/governance-mapper.ts @@ -1,7 +1,10 @@ import { IpfsContentDto } from 'src/ipfs/dto/ipfs-content.dto'; import { GovernanceActionProposalResponse } from '../api/response/gov-action-proposal.response'; import { VoteResponse } from '../api/response/vote.response'; -import { GovActionProposalDto } from '../dto/gov-action-proposal-dto'; +import { + GovActionProposalDto, + VoteStatusRationaleInfoDto as UserVoteStatusRationaleDto, +} from '../dto/gov-action-proposal-dto'; import { VoteDto } from '../dto/vote.dto'; import { GovActionProposal } from '../entities/gov-action-proposal.entity'; import { Vote } from '../entities/vote.entity'; @@ -65,39 +68,45 @@ export class GovernanceMapper { govActionProposalDto.type = govActionProposal.govActionType; govActionProposalDto.status = GovActionProposalStatus[govActionProposal.status]; - govActionProposalDto.voteStatus = - GovernanceMapper.returnVoteStatusForGovActionProposal(govActionProposal); - govActionProposalDto.hasRationale = !GovernanceMapper.emptyArray( - govActionProposal.rationales, - ); govActionProposalDto.submitTime = govActionProposal.submitTime; govActionProposalDto.endTime = govActionProposal.endTime; + govActionProposalDto.votedBy = govActionProposal.votes?.map( + (vote) => vote.userId, + ); + govActionProposalDto.rationaleBy = govActionProposal.rationales?.map( + (rationale) => rationale.userId, + ); return govActionProposalDto; } - private static returnVoteStatusForGovActionProposal( - govActionProposal: GovActionProposal, - ): VoteStatus { - if ( - GovernanceMapper.emptyArray(govActionProposal.votes) && - GovernanceMapper.emptyArray(govActionProposal.rationales) - ) { - return VoteStatus.Unvoted; - } else if ( - GovernanceMapper.emptyArray(govActionProposal.votes) && - !GovernanceMapper.emptyArray(govActionProposal.rationales) - ) { - return VoteStatus.Pending; + static returnUserVoteRationaleInfo( + gapDto: GovActionProposalDto, + userId: string, + ): UserVoteStatusRationaleDto { + const userHasVote = GovernanceMapper.userHasVote(gapDto.votedBy, userId); + const userHasRationale = GovernanceMapper.userHasRationale( + gapDto.rationaleBy, + userId, + ); + let voteStatus: VoteStatus; + if (!userHasVote && !userHasRationale) { + voteStatus = VoteStatus.Unvoted; + } else if (!userHasVote && userHasRationale) { + voteStatus = VoteStatus.Pending; + } else { + voteStatus = VoteStatus.Voted; } - return VoteStatus.Voted; + + return new UserVoteStatusRationaleDto(voteStatus, userHasRationale); } - private static emptyArray(array: any[]): boolean { - if (Array.isArray(array) && array.length) { - return false; - } - return true; + private static userHasVote(votedBy: string[], userId: string): boolean { + return votedBy?.some((vote) => vote === userId); + } + + static userHasRationale(rationaleBy: string[], userId: string): boolean { + return rationaleBy?.some((rationale) => rationale === userId); } static govActionProposalDtoToResponse( diff --git a/backend/src/governance/services/governance.service.ts b/backend/src/governance/services/governance.service.ts index 80ce9941..57709e58 100644 --- a/backend/src/governance/services/governance.service.ts +++ b/backend/src/governance/services/governance.service.ts @@ -79,13 +79,10 @@ export class GovernanceService { async searchGovActionProposals( query: PaginateQuery, - userId?: string, ): Promise> { - const customQuery = this.returnGapQuery(userId); - const result = await this.paginator.paginate( query, - customQuery, + this.govActionMetadataRepository, GOVERNANCE_ACTION_PROPOSAL_CONFIG, ); @@ -95,25 +92,6 @@ export class GovernanceService { >().paginatedToDto(result, GovernanceMapper.govActionProposalToDto); } - private returnGapQuery( - userId: string, - ): SelectQueryBuilder { - return this.govActionMetadataRepository - .createQueryBuilder('governanceActionProposals') - .leftJoinAndSelect( - 'governanceActionProposals.votes', - 'vote', - 'vote.userId = :userId', - { userId: userId }, - ) - .leftJoinAndSelect( - 'governanceActionProposals.rationales', - 'rationale', - 'rationale.userId = :userId', - { userId: userId }, - ); - } - async addRationale(rationaleDto: RationaleDto): Promise { const rationale = this.rationaleRepository.create(rationaleDto); const savedRationale = await this.rationaleRepository.save(rationale); From 112c100d97f55e11aeafcc809e80d82ce7b9f9d8 Mon Sep 17 00:00:00 2001 From: Vukasin Date: Tue, 24 Sep 2024 10:28:48 +0200 Subject: [PATCH 056/111] fix: usersnap showing all on path change --- frontend/src/context/usersnap.tsx | 36 ++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/frontend/src/context/usersnap.tsx b/frontend/src/context/usersnap.tsx index 7d81596d..caa42bcc 100644 --- a/frontend/src/context/usersnap.tsx +++ b/frontend/src/context/usersnap.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useState, useContext } from "react"; import { InitOptions, loadSpace, SpaceApi } from "@usersnap/browser"; +import { usePathname } from "next/navigation"; export const UsersnapContext = React.createContext(null); @@ -11,26 +12,41 @@ export const UsersnapProvider = ({ children, }: UsersnapProviderProps) => { const [usersnapApi, setUsersnapApi] = useState(null); + const pathname = usePathname(); useEffect(() => { + let api: SpaceApi | null = null; + + const hideHiddenProjects = () => { + if (api) { + const hiddenProjects = process.env.NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS?.split("||") || []; + hiddenProjects.forEach(p => { + api.hide(p); + }); + } + }; + if (process.env.NEXT_PUBLIC_USERSNAP_SPACE_API_KEY) { - const hiddenProjects = process.env.NEXT_PUBLIC_HIDDEN_USERSNAP_PROJECT_IDS?.split("||") || [] loadSpace(process.env.NEXT_PUBLIC_USERSNAP_SPACE_API_KEY).then( - (api: SpaceApi) => { + (loadedApi: SpaceApi) => { + api = loadedApi; api.init(initParams); setUsersnapApi(api); - const hideHiddenProjects = () => { - hiddenProjects.forEach(p => { - api.hide(p); - }); - }; + + api.on("submit", hideHiddenProjects); + api.on("open", hideHiddenProjects); hideHiddenProjects(); - api.on("close", hideHiddenProjects) - api.on("submit", hideHiddenProjects) } ); } - }, [initParams]); + + return () => { + if (api) { + api.off("submit", hideHiddenProjects); + api.off("open", hideHiddenProjects); + } + }; + }, [initParams, pathname]); return ( From 50624a1de1a6a0b95d03156df6a476d6d25c3187 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 24 Sep 2024 10:36:07 +0200 Subject: [PATCH 057/111] fix: Remove comments for selector ids --- frontend/src/components/molecules/GovernanceActionsSorting.tsx | 1 - frontend/src/components/organisms/Modals/SignInModal.tsx | 1 - frontend/src/components/organisms/Modals/SignUpModal.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/frontend/src/components/molecules/GovernanceActionsSorting.tsx b/frontend/src/components/molecules/GovernanceActionsSorting.tsx index 3e0d758f..ec177609 100644 --- a/frontend/src/components/molecules/GovernanceActionsSorting.tsx +++ b/frontend/src/components/molecules/GovernanceActionsSorting.tsx @@ -73,7 +73,6 @@ export const GovernanceActionsSorting = ({ key={item.key} value={item.key} control={} - // todo: check this with kiki label={ {item.label} } diff --git a/frontend/src/components/organisms/Modals/SignInModal.tsx b/frontend/src/components/organisms/Modals/SignInModal.tsx index 26f20aa9..19ab4414 100644 --- a/frontend/src/components/organisms/Modals/SignInModal.tsx +++ b/frontend/src/components/organisms/Modals/SignInModal.tsx @@ -35,7 +35,6 @@ export const SignInModal = () => { return ( - // todo: check this with kiki {t("signIn.headline")} diff --git a/frontend/src/components/organisms/Modals/SignUpModal.tsx b/frontend/src/components/organisms/Modals/SignUpModal.tsx index 81b20548..df665042 100644 --- a/frontend/src/components/organisms/Modals/SignUpModal.tsx +++ b/frontend/src/components/organisms/Modals/SignUpModal.tsx @@ -170,7 +170,6 @@ export const SignUpModal = () => { ) : ( + ); +} diff --git a/frontend/src/components/organisms/UsersList/UsersList.tsx b/frontend/src/components/organisms/UsersList/UsersList.tsx index 37d61285..b0702af2 100644 --- a/frontend/src/components/organisms/UsersList/UsersList.tsx +++ b/frontend/src/components/organisms/UsersList/UsersList.tsx @@ -1,22 +1,22 @@ "use client"; -import React, { useEffect } from "react"; +import { useEffect } from "react"; -import { Grid } from "@mui/material"; -import { UsersListItem } from "./UsersListItem"; -import { UserListItem } from "../types"; -import { NotFound } from "../NotFound"; +import { useSnackbar } from "@/context/snackbar"; import { getUsersAdmin } from "@/lib/api"; import { PaginationMeta } from "@/lib/requests"; -import { isEmpty } from "@utils"; -import { usePagination } from "@hooks"; import { ShowMoreButton } from "@atoms"; +import { usePagination } from "@hooks"; +import { Grid } from "@mui/material"; +import { isEmpty } from "@utils"; import { useSearchParams } from "next/navigation"; -import { useSnackbar } from "@/context/snackbar"; +import { NotFound } from "../NotFound"; +import { UserListItem } from "../types"; +import { UsersListItem } from "./UsersListItem"; export function UsersList({ usersList, paginationMeta, - error, + error }: { usersList: UserListItem[]; paginationMeta: PaginationMeta; @@ -50,7 +50,7 @@ export function UsersList({ py={{ xxs: 3, md: 6 }} container direction="column" - gap={0} + gap={2} > {data && data.map((users) => { diff --git a/frontend/src/components/organisms/UsersList/UsersListItem.tsx b/frontend/src/components/organisms/UsersList/UsersListItem.tsx index e6689938..93c5f843 100644 --- a/frontend/src/components/organisms/UsersList/UsersListItem.tsx +++ b/frontend/src/components/organisms/UsersList/UsersListItem.tsx @@ -1,20 +1,25 @@ -import React from "react"; - -import { Card, TableDivider } from "@molecules"; -import { Grid } from "@mui/material"; -import { UserAvatar, UserBasicInfo, UserRole, UserStatus } from "@molecules"; -import { UserStatus as UserStatusType } from "@atoms"; -import { OpenDeleteRoleModalState, UserListItem } from "../types"; +import { Button } from "@/components/atoms"; +import { + Card, + TableDivider, + UserAvatar, + UserBasicInfo, + UserRole, + UserStatus +} from "@/components/molecules"; import { useAppContext, useModal } from "@/context"; +import { UserStatus as UserStatusType } from "@atoms"; +import { Box, Grid } from "@mui/material"; import { hasManageUserPermission } from "@utils"; - +import { OpenDeleteRoleModalState, UserListItem } from "../types"; +import UserListStatusSwitchButton from "./UserListStatusSwitchButton"; export function UsersListItem({ id, name, email, role, status, - profile_photo_url, + profile_photo_url }: Pick< UserListItem, "id" | "name" | "email" | "role" | "status" | "profile_photo_url" @@ -30,45 +35,120 @@ export function UsersListItem({ type: "deleteRole", state: { userId: id, - status: "inactive", - }, + status: "inactive" + } }); }; + const UserInfoColumn = () => { + return ( + + + + + + + + + ); + }; + + const UserRoleColumn = () => { + return ( + + + + + + + ); + }; + + const UserStatusColumn = () => { + return ( + + + + + + + + + + ); + }; + + const DeleteButtonColumn = () => { + return ( + + + + ); + }; + return ( - + - - - - - - - - - - - - - - - - - - - + - + + + + + diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 214f3006..bbdebefd 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -1,28 +1,28 @@ "use server"; -import axiosInstance from "./axiosInstance"; +import { + ConstitutionByCid, + ConstitutionMetadata, + GovActionMetadata +} from "@organisms"; +import { getAccessToken, isEmpty, setAuthCookies } from "@utils"; import jwt from "jsonwebtoken"; -import { getAccessToken, setAuthCookies, isEmpty } from "@utils"; +import axiosInstance from "./axiosInstance"; import { + AddReasoningRequestI, DecodedToken, FetchUserData, - LoginResponse, - PaginationMeta, - Permissions, - ResponseErrorI, FetchUsersAdminI, GetGovernanceActionsI, - VotesTableI, GovernanceActionTableI, + LoginResponse, + PaginationMeta, + Permissions, ReasoningResponseI, - AddReasoningRequestI, + ResponseErrorI, UserAuthStatus, + VotesTableI } from "./requests"; -import { - ConstitutionByCid, - ConstitutionMetadata, - GovActionMetadata, -} from "@organisms"; import { getTranslations } from "next-intl/server"; const baseURL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:1337"; @@ -114,14 +114,14 @@ export async function login( ): Promise { try { const res: LoginResponse = await axiosInstance.post("/api/auth/login", { - destination: email, + destination: email }); return res; } catch (error) { const t = await getTranslations(); return { error: t("Modals.signIn.alerts.error"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -138,7 +138,7 @@ export async function loginAuthCallback(token: string) { const t = await getTranslations(); return { error: t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -155,7 +155,7 @@ export async function registerAuthCallback(token: string) { const t = await getTranslations(); return { error: t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -166,8 +166,8 @@ export async function refreshToken(refresh_token: string) { method: "POST", body: JSON.stringify({ refresh_token }), headers: { - "Content-Type": "application/json", - }, + "Content-Type": "application/json" + } }); const responseData = await res.json(); @@ -189,7 +189,7 @@ export async function getUser( error.res?.statusCode === 401 && t(`General.errors.sessionExpired`); return { error: customErrorMessage || t(`General.errors.somethingWentWrong`), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -205,12 +205,12 @@ export async function toggleUserStatus( `/api/users/${decodedToken?.userId}/toggle-status`, { user_id, - status, + status }, { headers: { - Authorization: `Bearer ${token}`, - }, + Authorization: `Bearer ${token}` + } } ); return res; @@ -222,7 +222,7 @@ export async function toggleUserStatus( t(`General.errors.sessionExpired`); return { error: customErrorMessage || t("Modals.deleteRole.alerts.error"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -230,7 +230,7 @@ export async function toggleUserStatus( export async function getUsersAdmin({ search, page = 1, - limit = DEFAULT_PAGINATION_LIMIT, + limit = DEFAULT_PAGINATION_LIMIT }: { search?: string; page?: number; @@ -245,14 +245,14 @@ export async function getUsersAdmin({ { search, page, - limit, + limit } ); const res: { data: FetchUserData[]; meta: PaginationMeta } = await axiosInstance.get(path, { headers: { - Authorization: `bearer ${token}`, - }, + Authorization: `bearer ${token}` + } }); return res; } catch (error) { @@ -263,7 +263,7 @@ export async function getUsersAdmin({ t(`General.errors.sessionExpired`); return { error: customErrorMessage || t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -272,7 +272,7 @@ export async function getMembers({ search, sortBy, page = 1, - limit = DEFAULT_PAGINATION_LIMIT, + limit = DEFAULT_PAGINATION_LIMIT }: { search?: string; sortBy?: string; @@ -284,7 +284,7 @@ export async function getMembers({ search, sortBy, page, - limit, + limit }); const res: { data: FetchUserData[]; meta: PaginationMeta } = await axiosInstance.get(path); @@ -293,7 +293,7 @@ export async function getMembers({ const t = await getTranslations(); return { error: t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -304,7 +304,7 @@ export async function getLatestUpdates({ search, govActionType, vote, - sortBy, + sortBy }: { page?: number; limit?: number; @@ -321,7 +321,7 @@ export async function getLatestUpdates({ sortBy, "filter.govActionProposal.govActionType": govActionType && `$in:${govActionType}`, - "filter.vote": vote && `$in:${vote}`, + "filter.vote": vote && `$in:${vote}` }); const res: { data: VotesTableI[]; meta: PaginationMeta } = await axiosInstance.get(path); @@ -331,7 +331,7 @@ export async function getLatestUpdates({ const t = await getTranslations(); return { error: t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -343,7 +343,7 @@ export async function getUserVotes({ govActionType, vote, sortBy, - userId, + userId }: { page?: number; limit?: number; @@ -362,7 +362,7 @@ export async function getUserVotes({ "filter.govActionProposal.govActionType": govActionType && `$in:${govActionType}`, "filter.vote": vote && `$in:${vote}`, - "filter.userId": `$eq:${userId}`, + "filter.userId": `$eq:${userId}` }); const res: { data: VotesTableI[]; meta: PaginationMeta } = await axiosInstance.get(path); @@ -371,7 +371,7 @@ export async function getUserVotes({ const t = await getTranslations(); return { error: t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -388,7 +388,7 @@ export async function getGovernanceMetadata( const t = await getTranslations(); return { error: t("Modals.govActionModal.alerts.error"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -399,7 +399,7 @@ export async function getGovernanceActions({ search, govActionType, status, - sortBy, + sortBy }: { page?: number; limit?: number; @@ -420,14 +420,14 @@ export async function getGovernanceActions({ search, sortBy, "filter.govActionType": govActionType && `$in:${govActionType}`, - "filter.status": status && `$in:${status}`, + "filter.status": status && `$in:${status}` } ); const res: { data: GovernanceActionTableI[]; meta: PaginationMeta } = await axiosInstance.get(path, { headers: { - Authorization: `bearer ${token}`, - }, + Authorization: `bearer ${token}` + } }); return res; @@ -439,7 +439,7 @@ export async function getGovernanceActions({ t(`General.errors.sessionExpired`); return { error: customErrorMessage || t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -456,8 +456,8 @@ export async function addOrUpdateReasoning({ data, { headers: { - Authorization: `bearer ${token}`, - }, + Authorization: `bearer ${token}` + } } ); return response; @@ -469,7 +469,7 @@ export async function addOrUpdateReasoning({ t(`General.errors.sessionExpired`); return { error: customErrorMessage || t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -484,8 +484,8 @@ export async function getReasoningData( `/api/governance/users/${user?.userId}/proposals/${proposalId}/rationale`, { headers: { - Authorization: `bearer ${token}`, - }, + Authorization: `bearer ${token}` + } } ); @@ -494,7 +494,7 @@ export async function getReasoningData( const t = await getTranslations(); return { error: t("Modals.previewRationale.alerts.error"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -506,12 +506,12 @@ export async function registerUser(email: string) { const res = await axiosInstance.post( "/api/auth/register-user", { - destination: email, + destination: email }, { headers: { - Authorization: `bearer ${token}`, - }, + Authorization: `bearer ${token}` + } } ); return res; @@ -523,7 +523,7 @@ export async function registerUser(email: string) { t(`General.errors.sessionExpired`); return { error: customErrorMessage || t("Modals.addMember.alerts.error"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -536,12 +536,12 @@ export async function registerAdmin(email: string, permissions: Permissions[]) { "/api/auth/register-admin", { destination: email, - permissions, + permissions }, { headers: { - Authorization: `bearer ${token}`, - }, + Authorization: `bearer ${token}` + } } ); return res; @@ -553,7 +553,7 @@ export async function registerAdmin(email: string, permissions: Permissions[]) { t(`General.errors.sessionExpired`); return { error: customErrorMessage || t("Modals.addMember.alerts.error"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -569,8 +569,8 @@ export async function editUser( data, { headers: { - Authorization: `Bearer ${token}`, - }, + Authorization: `Bearer ${token}` + } } ); return response; @@ -582,7 +582,7 @@ export async function editUser( t(`General.errors.sessionExpired`); return { error: customErrorMessage || t("Modals.signUp.alerts.error"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -599,7 +599,7 @@ export async function getConstitutionMetadata(): Promise< const t = await getTranslations(); return { error: t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -616,7 +616,7 @@ export async function getConstitutionByCid( const t = await getTranslations(); return { error: t("General.errors.somethingWentWrong"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -627,8 +627,8 @@ export async function uploadConstitution(data: FormData) { try { const response = await axiosInstance.post("/api/constitution", data, { headers: { - Authorization: `Bearer ${token}`, - }, + Authorization: `Bearer ${token}` + } }); return response.data; } catch (error) { @@ -642,7 +642,7 @@ export async function uploadConstitution(data: FormData) { return { error: errorMessage, - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null }; } } @@ -658,8 +658,8 @@ export async function uploadUserPhoto( data, { headers: { - Authorization: `Bearer ${token}`, - }, + Authorization: `Bearer ${token}` + } } ); @@ -673,7 +673,23 @@ export async function uploadUserPhoto( return { error: customErrorMessage || t("Modals.signUp.alerts.errorUploadProfilePhoto"), - statusCode: error.res?.statusCode || null, + statusCode: error.res?.statusCode || null + }; + } +} + +export async function resendRegisterEmail(email: string) { + try { + const res = await axiosInstance.post(`/api/auth/resend-register-invite`, { + destination: email + }); + + return res; + } catch (error) { + const t = await getTranslations(); + return { + error: t("General.errors.somethingWentWrong"), + statusCode: error.res?.statusCode || null }; } } diff --git a/frontend/src/lib/requests/types.ts b/frontend/src/lib/requests/types.ts index e063ad83..bc742190 100644 --- a/frontend/src/lib/requests/types.ts +++ b/frontend/src/lib/requests/types.ts @@ -1,7 +1,12 @@ import { Vote } from "@atoms"; export type UserRole = "super_admin" | "admin" | "user" | "alumni" | null; - +export enum UserRoleEnum { + SuperAdmin = "super_admin", + Admin = "admin", + User = "user", + Alumni = "alumni" +} export interface RoleListObject { value: UserRole; label: string; @@ -67,13 +72,13 @@ export enum GovActionProposalStatus { EXPIRED = "EXPIRED", RATIFIED = "RATIFIED", ENACTED = "ENACTED", - DROPPED = "DROPPED", + DROPPED = "DROPPED" } export enum GovActionStatus { PENDING = "PENDING", VOTED = "VOTED", - UNVOTED = "UNVOTED", + UNVOTED = "UNVOTED" } export interface GovernanceActionTableI { From 0b05f0d6d0b5b551b2c15c033202e2146897cebe Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:49:49 +0200 Subject: [PATCH 064/111] feature/hard-delete-user: added hard delete user ability (#398) --- .../1728062638428-users-relations.ts | 35 +++++++++++++++++++ .../CC Portal develop.postman_collection.json | 34 ++++++++++++++++++ .../governance/entities/rationale.entity.ts | 7 ++++ .../src/governance/entities/vote.entity.ts | 7 ++++ .../users/api/request/remove-user.request.ts | 12 +++++++ backend/src/users/api/users.controller.ts | 34 ++++++++++++++++++ .../src/users/entities/hotaddress.entity.ts | 4 ++- backend/src/users/entities/user.entity.ts | 12 +++++++ backend/src/users/facade/users.facade.ts | 17 +++++++++ backend/src/users/services/users.service.ts | 5 +++ 10 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 backend/migrations/1728062638428-users-relations.ts create mode 100644 backend/src/users/api/request/remove-user.request.ts diff --git a/backend/migrations/1728062638428-users-relations.ts b/backend/migrations/1728062638428-users-relations.ts new file mode 100644 index 00000000..2ca1c5f0 --- /dev/null +++ b/backend/migrations/1728062638428-users-relations.ts @@ -0,0 +1,35 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UsersRelations1728062638428 implements MigrationInterface { + name = 'UsersRelations1728062638428'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "hot_addresses" DROP CONSTRAINT "FK_e125763f26d4736a5701f6c4d4b"`, + ); + await queryRunner.query( + `ALTER TABLE "hot_addresses" ADD CONSTRAINT "FK_e125763f26d4736a5701f6c4d4b" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "votes" ADD CONSTRAINT "FK_27be2cab62274f6876ad6a31641" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "rationales" ADD CONSTRAINT "FK_182656ae5052a7efd72a02c64e9" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "rationales" DROP CONSTRAINT "FK_182656ae5052a7efd72a02c64e9"`, + ); + await queryRunner.query( + `ALTER TABLE "votes" DROP CONSTRAINT "FK_27be2cab62274f6876ad6a31641"`, + ); + await queryRunner.query( + `ALTER TABLE "hot_addresses" DROP CONSTRAINT "FK_e125763f26d4736a5701f6c4d4b"`, + ); + await queryRunner.query( + `ALTER TABLE "hot_addresses" ADD CONSTRAINT "FK_e125763f26d4736a5701f6c4d4b" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } +} diff --git a/backend/postman/CC Portal develop.postman_collection.json b/backend/postman/CC Portal develop.postman_collection.json index 803bc80a..90b6489d 100644 --- a/backend/postman/CC Portal develop.postman_collection.json +++ b/backend/postman/CC Portal develop.postman_collection.json @@ -244,6 +244,40 @@ } }, "response": [] + }, + { + "name": "Delete User", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "{{accessToken}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"user_id\": \"a132f533-875f-466a-b194-79f4afe0937b\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base-url}}/api/users/{{userId}}", + "host": [ + "{{base-url}}" + ], + "path": [ + "api", + "users", + "{{userId}}" + ] + } + }, + "response": [] } ] }, diff --git a/backend/src/governance/entities/rationale.entity.ts b/backend/src/governance/entities/rationale.entity.ts index 2caab91f..80e0c7ca 100644 --- a/backend/src/governance/entities/rationale.entity.ts +++ b/backend/src/governance/entities/rationale.entity.ts @@ -8,10 +8,17 @@ import { Unique, } from 'typeorm'; import { GovActionProposal } from './gov-action-proposal.entity'; +import { User } from '../../users/entities/user.entity'; @Entity('rationales') @Unique(['userId', 'govActionProposalId']) export class Rationale extends CommonEntity { + @ManyToOne(() => User, (user) => user.rationales, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'user_id' }) + user: User; + @PrimaryColumn({ name: 'user_id', type: 'uuid', diff --git a/backend/src/governance/entities/vote.entity.ts b/backend/src/governance/entities/vote.entity.ts index a307436b..c609c3dc 100644 --- a/backend/src/governance/entities/vote.entity.ts +++ b/backend/src/governance/entities/vote.entity.ts @@ -8,6 +8,7 @@ import { PrimaryColumn, } from 'typeorm'; import { GovActionProposal } from './gov-action-proposal.entity'; +import { User } from '../../users/entities/user.entity'; @Entity('votes') export class Vote extends CommonEntity { @@ -24,6 +25,12 @@ export class Vote extends CommonEntity { }) userId: string; + @ManyToOne(() => User, (user) => user.votes, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'user_id' }) + user: User; + @Column({ name: 'hot_address', type: 'varchar', diff --git a/backend/src/users/api/request/remove-user.request.ts b/backend/src/users/api/request/remove-user.request.ts new file mode 100644 index 00000000..3a140c0f --- /dev/null +++ b/backend/src/users/api/request/remove-user.request.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsUUID } from 'class-validator'; + +export class RemoveUserRequest { + @ApiProperty({ + description: 'Identification number of the user', + example: '82dbbfb1-2552-4aaf-a9a7-1195497410c0', + name: 'user_id', + }) + @IsUUID() + userId: string; +} diff --git a/backend/src/users/api/users.controller.ts b/backend/src/users/api/users.controller.ts index 9c086d03..12cb0dd4 100644 --- a/backend/src/users/api/users.controller.ts +++ b/backend/src/users/api/users.controller.ts @@ -13,6 +13,7 @@ import { UseGuards, Request, Delete, + BadRequestException, } from '@nestjs/common'; import { UsersFacade } from '../facade/users.facade'; import { UpdateUserRequest } from './request/update-user.request'; @@ -40,6 +41,8 @@ import { PermissionEnum } from '../enums/permission.enum'; import { PermissionGuard } from 'src/auth/guard/permission.guard'; import { ToggleStatusRequest } from './request/toggle-status.request'; import { ApiConditionalExcludeEndpoint } from 'src/common/decorators/api-conditional-exclude-endpoint.decorator'; +import { Permissions } from 'src/auth/guard/permission.decorator'; +import { RemoveUserRequest } from './request/remove-user.request'; @ApiTags('Users') @Controller('users') @@ -272,4 +275,35 @@ export class UsersController { permissions, ); } + + @ApiConditionalExcludeEndpoint() + @ApiBearerAuth('JWT-auth') + @ApiOperation({ summary: 'Delete user' }) + @ApiResponse({ status: 200, description: 'User deleted successfully' }) + @ApiResponse({ status: 400, description: 'Bad requset' }) + @ApiResponse({ status: 401, description: 'Unauthorized' }) + @ApiResponse({ status: 404, description: 'Not found' }) + @ApiParam({ + name: 'id', + type: String, + description: 'identifactor of user', + }) + @ApiBody({ type: RemoveUserRequest }) + @HttpCode(200) + @Permissions(PermissionEnum.MANAGE_ADMINS) // Superadmin only + @UseGuards(JwtAuthGuard, UserPathGuard, PermissionGuard) + @Delete(':id') + async removeUser( + @Param('id', ParseUUIDPipe) id: string, + @Body() removeUserRequest: RemoveUserRequest, + ) { + if (id === removeUserRequest.userId) { + throw new BadRequestException(`You cannot delete yourself`); + } + await this.usersFacade.removeUser(removeUserRequest.userId); + return { + success: true, + message: 'User deleted successfully', + }; + } } diff --git a/backend/src/users/entities/hotaddress.entity.ts b/backend/src/users/entities/hotaddress.entity.ts index 8b29238e..c226eb66 100644 --- a/backend/src/users/entities/hotaddress.entity.ts +++ b/backend/src/users/entities/hotaddress.entity.ts @@ -20,7 +20,9 @@ export class HotAddress extends CommonEntity { }) address: string; - @ManyToOne(() => User, (user) => user.hotAddresses) + @ManyToOne(() => User, (user) => user.hotAddresses, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'user_id' }) user: User; } diff --git a/backend/src/users/entities/user.entity.ts b/backend/src/users/entities/user.entity.ts index e636a57a..6cbecf6d 100644 --- a/backend/src/users/entities/user.entity.ts +++ b/backend/src/users/entities/user.entity.ts @@ -14,6 +14,8 @@ import { CommonEntity } from '../../common/entities/common.entity'; import { Permission } from './permission.entity'; import { HotAddress } from './hotaddress.entity'; import { UserStatusEnum } from '../enums/user-status.enum'; +import { Rationale } from '../../governance/entities/rationale.entity'; +import { Vote } from '../../governance/entities/vote.entity'; @Entity('users') export class User extends CommonEntity { @@ -65,6 +67,16 @@ export class User extends CommonEntity { }) hotAddresses: HotAddress[]; + @OneToMany(() => Rationale, (rationale) => rationale.user, { + cascade: true, + }) + rationales: Rationale[]; + + @OneToMany(() => Vote, (vote) => vote.user, { + cascade: true, + }) + votes: Vote[]; + @Index('users_role_id_idx') @ManyToOne(() => Role, (role) => role.users, { eager: true, diff --git a/backend/src/users/facade/users.facade.ts b/backend/src/users/facade/users.facade.ts index 947d40c4..496fb4ca 100644 --- a/backend/src/users/facade/users.facade.ts +++ b/backend/src/users/facade/users.facade.ts @@ -2,6 +2,7 @@ import { BadRequestException, ConflictException, Injectable, + Logger, } from '@nestjs/common'; import { UpdateUserRequest } from '../api/request/update-user.request'; import { UsersService } from '../services/users.service'; @@ -21,6 +22,7 @@ import { ToggleStatusRequest } from '../api/request/toggle-status.request'; import { UserStatusEnum } from '../enums/user-status.enum'; @Injectable() export class UsersFacade { + private logger = new Logger(UsersService.name); constructor( private readonly usersService: UsersService, private readonly s3Service: S3Service, @@ -112,4 +114,19 @@ export class UsersFacade { ); return UserMapper.mapUserDtoToResponse(result); } + + async removeUser(userId: string): Promise { + const userDto = await this.usersService.findById(userId); + try { + await this.usersService.removeUser(userDto.id); + if (userDto.profilePhotoUrl) { + const fileName = S3Service.extractFileNameFromUrl( + userDto.profilePhotoUrl, + ); + await this.s3Service.deleteFile(fileName); + } + } catch (e) { + this.logger.error(`Error when removing user: ${e.message}`); + } + } } diff --git a/backend/src/users/services/users.service.ts b/backend/src/users/services/users.service.ts index 4f926f9f..cc190992 100644 --- a/backend/src/users/services/users.service.ts +++ b/backend/src/users/services/users.service.ts @@ -287,4 +287,9 @@ export class UsersService { throw new ForbiddenException(`You have no permission for this action`); } } + + async removeUser(userId: string): Promise { + const user = await this.findEntityById(userId); + await this.userRepository.remove(user); + } } From 3ffd92caed412af97e3a07d8101bb01dedfb3e8a Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:44:26 +0200 Subject: [PATCH 065/111] Feat: hard delete user (#400) * feature/hard-delete-user: added hard delete user ability * feature/hard-delete-user: updated swagger --- backend/src/users/api/users.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/users/api/users.controller.ts b/backend/src/users/api/users.controller.ts index 12cb0dd4..71f851c3 100644 --- a/backend/src/users/api/users.controller.ts +++ b/backend/src/users/api/users.controller.ts @@ -282,6 +282,7 @@ export class UsersController { @ApiResponse({ status: 200, description: 'User deleted successfully' }) @ApiResponse({ status: 400, description: 'Bad requset' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) + @ApiResponse({ status: 403, description: 'Forbidden resource' }) @ApiResponse({ status: 404, description: 'Not found' }) @ApiParam({ name: 'id', From 77784e884d08416ef622796e34cc4b91172fe8ce Mon Sep 17 00:00:00 2001 From: Milos <161627443+BEdev24@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:28:34 +0200 Subject: [PATCH 066/111] feat: hard delete user (#401) * feature/hard-delete-user: added hard delete user ability * feature/hard-delete-user: updated swagger * feature/hard-delete-user: try catch for removing profile photo --- backend/src/users/facade/users.facade.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/users/facade/users.facade.ts b/backend/src/users/facade/users.facade.ts index 496fb4ca..441f46f5 100644 --- a/backend/src/users/facade/users.facade.ts +++ b/backend/src/users/facade/users.facade.ts @@ -117,8 +117,8 @@ export class UsersFacade { async removeUser(userId: string): Promise { const userDto = await this.usersService.findById(userId); + await this.usersService.removeUser(userDto.id); try { - await this.usersService.removeUser(userDto.id); if (userDto.profilePhotoUrl) { const fileName = S3Service.extractFileNameFromUrl( userDto.profilePhotoUrl, @@ -126,7 +126,9 @@ export class UsersFacade { await this.s3Service.deleteFile(fileName); } } catch (e) { - this.logger.error(`Error when removing user: ${e.message}`); + this.logger.error( + `Error when removing profile photo of the user with id ${userId}: ${e.message}`, + ); } } } From 6b8845067b8b52a155253126ed4a6b71d898895f Mon Sep 17 00:00:00 2001 From: Vojimirovich Date: Wed, 9 Oct 2024 15:14:06 +0200 Subject: [PATCH 067/111] feat: implement delete user --- frontend/messages/de.json | 20 ++++- frontend/messages/en.json | 20 ++++- .../organisms/Modals/DeleteUser.tsx | 74 +++++++++++++++++++ .../UsersList/UserListStatusDeleteButton.tsx | 35 +++++++++ .../UsersList/UserListStatusSwitchButton.tsx | 13 +++- .../organisms/UsersList/UsersListItem.tsx | 13 +--- frontend/src/components/organisms/types.ts | 11 ++- frontend/src/context/modal.tsx | 53 +++++++------ frontend/src/lib/api.ts | 37 +++++++++- 9 files changed, 231 insertions(+), 45 deletions(-) create mode 100644 frontend/src/components/organisms/Modals/DeleteUser.tsx create mode 100644 frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx diff --git a/frontend/messages/de.json b/frontend/messages/de.json index 5b5c54f8..a125b5d1 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -130,6 +130,20 @@ "error": "Error changing user status" } }, + "deleteUser": { + "headline": "Delete user", + "description": "Are you sure you want to delete this user?", + "fields": { + "confirm": { + "label": "Please confirm the action by typing expected below", + "placeholder": "Write DELETE" + } + }, + "alerts": { + "success": "User deleted successfully", + "error": "Error deleting user" + } + }, "addMember": { "headline": "Add member", "fields": { @@ -294,6 +308,10 @@ "resendInv": "Resend Invitation", "makeInactive": "Make Inactive", "makeActive": "Make Active", - "delteUser": "Delete User" + "deleteUser": "Delete User", + "resendAlerts": { + "success": "Email successfully resent", + "error": "Error resending email" + } } } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index c970bd09..f5af9c30 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -130,6 +130,20 @@ "error": "Error changing user status" } }, + "deleteUser": { + "headline": "Delete user", + "description": "Are you sure you want to delete this user?", + "fields": { + "confirm": { + "label": "Please confirm the action by typing expected below", + "placeholder": "Write DELETE" + } + }, + "alerts": { + "success": "User deleted successfully", + "error": "Error deleting user" + } + }, "addMember": { "headline": "Add member", "fields": { @@ -294,6 +308,10 @@ "resendInv": "Resend Invitation", "makeInactive": "Make Inactive", "makeActive": "Make Active", - "delteUser": "Delete User" + "deleteUser": "Delete User", + "resendAlerts": { + "success": "Email successfully resent", + "error": "Error resending email" + } } } diff --git a/frontend/src/components/organisms/Modals/DeleteUser.tsx b/frontend/src/components/organisms/Modals/DeleteUser.tsx new file mode 100644 index 00000000..5d8a6bd8 --- /dev/null +++ b/frontend/src/components/organisms/Modals/DeleteUser.tsx @@ -0,0 +1,74 @@ +"use client"; +import { Typography } from "@mui/material"; + +import { useModal } from "@/context"; +import { useSnackbar } from "@/context/snackbar"; +import { deleteUser } from "@/lib/api"; +import { ModalActions, ModalContents, ModalHeader, ModalWrapper } from "@atoms"; +import { IMAGES } from "@consts"; +import { isResponseErrorI } from "@utils"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { useForm } from "react-hook-form"; +import { ControlledField } from "../ControlledField"; +import { OpenDeleteUserModalState } from "../types"; + +export const DeleteUser = () => { + const t = useTranslations("Modals"); + const { addSuccessAlert, addErrorAlert } = useSnackbar(); + const router = useRouter(); + + const { + closeModal, + state: { sAdminId, userId } + } = useModal(); + + const { + register, + handleSubmit, + formState: { errors }, + control + } = useForm(); + + const onSubmit = async () => { + const res = await deleteUser(sAdminId, userId); + + if (!isResponseErrorI(res)) { + addSuccessAlert(t("deleteUser.alerts.success")); + closeModal(); + router.refresh(); + } else { + addErrorAlert(res.error); + } + }; + + return ( + + {t("deleteUser.headline")} +
+ + + {t("deleteUser.description")} + + value === "DELETE" || "Please write DELETE" + })} + /> + + +
+
+ ); +}; diff --git a/frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx b/frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx new file mode 100644 index 00000000..72b8849e --- /dev/null +++ b/frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { Button } from "@/components/atoms"; +import { useAppContext, useModal } from "@/context"; +import { isSuperAdminRole } from "@utils"; +import { useTranslations } from "next-intl"; +import { OpenDeleteUserModalState } from "../types"; + +export function UserListStatusDeleteButton({ userId }: { userId: string }) { + const { userSession } = useAppContext(); + const isSuperAdmin = isSuperAdminRole(userSession.role); + if (!isSuperAdmin) return null; + const { openModal } = useModal(); + const deleteUser = () => { + openModal({ + type: "deleteUser", + state: { + sAdminId: userSession.userId, + userId + } + }); + }; + const t = useTranslations("UsersList"); + + return ( + + ); +} diff --git a/frontend/src/components/organisms/UsersList/UserListStatusSwitchButton.tsx b/frontend/src/components/organisms/UsersList/UserListStatusSwitchButton.tsx index 83f9c19d..3c1c5e94 100644 --- a/frontend/src/components/organisms/UsersList/UserListStatusSwitchButton.tsx +++ b/frontend/src/components/organisms/UsersList/UserListStatusSwitchButton.tsx @@ -1,10 +1,11 @@ "use client"; import { useAppContext } from "@/context"; +import { useSnackbar } from "@/context/snackbar"; import { resendRegisterEmail, toggleUserStatus } from "@/lib/api"; import { UserRole, UserRoleEnum } from "@/lib/requests"; import { Button, UserStatus as UserStatusType } from "@atoms"; -import { isAdminRole, isSuperAdminRole } from "@utils"; +import { isAdminRole, isResponseErrorI, isSuperAdminRole } from "@utils"; import { useRouter } from "next/navigation"; import { useTranslations } from "use-intl"; @@ -29,6 +30,7 @@ export default function UserListStatusSwitchButton({ const isAdmin = isAdminRole(userSession.role); const router = useRouter(); const t = useTranslations("UsersList"); + const { addSuccessAlert, addErrorAlert } = useSnackbar(); // super admin can switch status of admins (not users) and admin can switch status of users but not of admins // both super admin and admin can resend invitation @@ -37,7 +39,14 @@ export default function UserListStatusSwitchButton({ return { buttonText: t("resendInv"), action: async () => { - await resendRegisterEmail(email); + const res = await resendRegisterEmail(email); + if (!isResponseErrorI(res)) { + addSuccessAlert(t("resendAlerts.success")); + router.refresh(); + } else { + addErrorAlert(res.error); + } + router.refresh(); } }; diff --git a/frontend/src/components/organisms/UsersList/UsersListItem.tsx b/frontend/src/components/organisms/UsersList/UsersListItem.tsx index 93c5f843..f385ce87 100644 --- a/frontend/src/components/organisms/UsersList/UsersListItem.tsx +++ b/frontend/src/components/organisms/UsersList/UsersListItem.tsx @@ -1,4 +1,3 @@ -import { Button } from "@/components/atoms"; import { Card, TableDivider, @@ -12,6 +11,7 @@ import { UserStatus as UserStatusType } from "@atoms"; import { Box, Grid } from "@mui/material"; import { hasManageUserPermission } from "@utils"; import { OpenDeleteRoleModalState, UserListItem } from "../types"; +import { UserListStatusDeleteButton } from "./UserListStatusDeleteButton"; import UserListStatusSwitchButton from "./UserListStatusSwitchButton"; export function UsersListItem({ id, @@ -85,7 +85,7 @@ export function UsersListItem({ xxs={12} sm={12} md={4} - lg={3} + lg={4} display="flex" flexDirection="row" gap={3} @@ -117,14 +117,7 @@ export function UsersListItem({ justifyContent="flex-end" alignItems="center" > - +
); }; diff --git a/frontend/src/components/organisms/types.ts b/frontend/src/components/organisms/types.ts index 02bc738a..0effda0b 100644 --- a/frontend/src/components/organisms/types.ts +++ b/frontend/src/components/organisms/types.ts @@ -1,12 +1,12 @@ -import { MDXRemoteSerializeResult } from "next-mdx-remote"; import { - ReasoningResponseI, FetchUserData, GovActionProposalStatus, - UserRole, GovernanceActionTableI, + ReasoningResponseI, UserAuthStatus, + UserRole } from "@/lib/requests"; +import { MDXRemoteSerializeResult } from "next-mdx-remote"; import { Vote } from "../atoms"; export interface ConstitutionMetadata { @@ -68,6 +68,11 @@ export interface OpenDeleteRoleModalState { status: UserAuthStatus; } +export interface OpenDeleteUserModalState { + sAdminId: string; + userId: string; +} + export interface OpenReasoningLinkModalState { hash: string; link: string; diff --git a/frontend/src/context/modal.tsx b/frontend/src/context/modal.tsx index 22e25b1d..e8efc0f7 100644 --- a/frontend/src/context/modal.tsx +++ b/frontend/src/context/modal.tsx @@ -1,21 +1,22 @@ "use client"; import { createContext, useContext, useMemo, useReducer } from "react"; -import { callAll, basicReducer, BasicReducer } from "@utils"; +import { CompareConstitutionModal } from "@/components/organisms/Modals/CompareConstitutionModal"; +import { DeleteUser } from "@/components/organisms/Modals/DeleteUser"; +import { GovActionModal } from "@/components/organisms/Modals/GovActionModal"; +import { PreviewReasoningModal } from "@/components/organisms/Modals/PreviewReasoningModal"; +import { SignOutModal } from "@/components/organisms/Modals/SignOutModal"; +import { SignUpModal } from "@/components/organisms/Modals/SignUpModal"; import { MuiModalChildren } from "@atoms"; import { - SignInModal, AddMemberModal, - UploadConstitution, - DeleteRole, AddReasoningModal, + DeleteRole, ReasoningLinkModal, + SignInModal, + UploadConstitution } from "@organisms"; -import { SignUpModal } from "@/components/organisms/Modals/SignUpModal"; -import { SignOutModal } from "@/components/organisms/Modals/SignOutModal"; -import { CompareConstitutionModal } from "@/components/organisms/Modals/CompareConstitutionModal"; -import { PreviewReasoningModal } from "@/components/organisms/Modals/PreviewReasoningModal"; -import { GovActionModal } from "@/components/organisms/Modals/GovActionModal"; +import { basicReducer, BasicReducer, callAll } from "@utils"; interface ProviderProps { children: React.ReactNode; @@ -36,6 +37,7 @@ export type ModalType = | "signOutModal" | "uploadConstitution" | "deleteRole" + | "deleteUser" | "compareConstitutionModal" | "addReasoningModal" | "reasoningLinkModal" @@ -44,42 +46,45 @@ export type ModalType = const modals: Record = { none: { - component: null, + component: null }, signIn: { - component: , + component: }, signUpModal: { component: , - preventDismiss: true, + preventDismiss: true }, signOutModal: { - component: , + component: }, addMember: { - component: , + component: }, uploadConstitution: { - component: , + component: }, deleteRole: { - component: , + component: + }, + deleteUser: { + component: }, compareConstitutionModal: { - component: , + component: }, addReasoningModal: { - component: , + component: }, reasoningLinkModal: { - component: , + component: }, previewReasoningModal: { - component: , + component: }, govActionModal: { - component: , - }, + component: + } }; type Optional = Pick, K> & Omit; @@ -106,7 +111,7 @@ function ModalProvider(props: ProviderProps) { basicReducer, { state: null, - type: "none", + type: "none" } ); @@ -118,7 +123,7 @@ function ModalProvider(props: ProviderProps) { openModal, closeModal: callAll(modals[modal.type]?.onClose, () => openModal({ type: "none", state: null }) - ), + ) }), [modal, openModal] ); diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index bbdebefd..18341b9d 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -680,15 +680,44 @@ export async function uploadUserPhoto( export async function resendRegisterEmail(email: string) { try { - const res = await axiosInstance.post(`/api/auth/resend-register-invite`, { - destination: email - }); + const token = getAccessToken(); + const res = await axiosInstance.post( + `/api/auth/resend-register-invite`, + { + destination: email + }, + { + headers: { + Authorization: `Bearer ${token}` + } + } + ); return res; } catch (error) { const t = await getTranslations(); return { - error: t("General.errors.somethingWentWrong"), + error: t("UsersList.resendAlerts.error"), + statusCode: error.res?.statusCode || null + }; + } +} +export async function deleteUser(sAdminId: string, userId: string) { + const token = getAccessToken(); + try { + const res = await axiosInstance.delete(`/api/users/${sAdminId}`, { + data: { + user_id: userId + }, + headers: { + Authorization: `Bearer ${token}` + } + }); + return res; + } catch (error) { + const t = await getTranslations(); + return { + error: t("Modals.deleteUser.alerts.error"), statusCode: error.res?.statusCode || null }; } From b896baa05f0707210836054938ffbbf0b8d8a4dd Mon Sep 17 00:00:00 2001 From: Vojimirovich Date: Wed, 9 Oct 2024 15:20:05 +0200 Subject: [PATCH 068/111] chore: refactor delete button component --- .../UsersList/UserListStatusDeleteButton.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx b/frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx index 72b8849e..750dfeef 100644 --- a/frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx +++ b/frontend/src/components/organisms/UsersList/UserListStatusDeleteButton.tsx @@ -8,10 +8,12 @@ import { OpenDeleteUserModalState } from "../types"; export function UserListStatusDeleteButton({ userId }: { userId: string }) { const { userSession } = useAppContext(); - const isSuperAdmin = isSuperAdminRole(userSession.role); - if (!isSuperAdmin) return null; const { openModal } = useModal(); - const deleteUser = () => { + const t = useTranslations("UsersList"); + + if (!isSuperAdminRole(userSession.role)) return null; + + const handleDeleteUser = () => { openModal({ type: "deleteUser", state: { @@ -20,12 +22,11 @@ export function UserListStatusDeleteButton({ userId }: { userId: string }) { } }); }; - const t = useTranslations("UsersList"); return ( diff --git a/frontend/src/components/organisms/Constitution/Constitution.tsx b/frontend/src/components/organisms/Constitution/Constitution.tsx index 60103815..b02f24f5 100644 --- a/frontend/src/components/organisms/Constitution/Constitution.tsx +++ b/frontend/src/components/organisms/Constitution/Constitution.tsx @@ -1,9 +1,16 @@ "use client"; -import { Card } from "@molecules"; +import { ContentWrapper, Typography } from "@/components/atoms"; +import { customPalette, IMAGES } from "@consts"; +import { useScreenDimension } from "@hooks"; import { Box, Grid, IconButton } from "@mui/material"; +import { useTranslations } from "next-intl"; import { MDXRemote } from "next-mdx-remote"; import { useEffect, useState } from "react"; +import { Footer } from "../Footer"; +import { DrawerMobile } from "../TopNavigation"; +import { ConstitutionProps } from "../types"; +import { ConstitutionSidebar } from "./ConstitutionSidebar"; import { Code, Heading1, @@ -12,16 +19,9 @@ import { ListItem, NavDrawerDesktop, Paragraph, - TABLE_OF_CONTENTS_WRAPPER_STYLE_PROPS, + TABLE_OF_CONTENTS_WRAPPER_STYLE_PROPS } from "./MDXComponents"; -import { ConstitutionProps } from "../types"; -import { useTranslations } from "next-intl"; -import { Footer } from "../Footer"; -import { customPalette, IMAGES } from "@consts"; -import { ContentWrapper, Typography } from "@/components/atoms"; -import { useScreenDimension } from "@hooks"; -import { DrawerMobile } from "../TopNavigation"; -import { ConstitutionSidebar } from "./ConstitutionSidebar"; +import { TocAccordion } from "./TOCAccordion"; import TOCLink from "./TOCLink"; export function Constitution({ constitution, metadata }: ConstitutionProps) { @@ -57,7 +57,7 @@ export function Constitution({ constitution, metadata }: ConstitutionProps) { setIsOpen(!isOpen)} - top={{ xxs: 0, md: 90 }} + top={{ xxs: 0, md: 85 }} left={0} > @@ -69,12 +69,19 @@ export function Constitution({ constitution, metadata }: ConstitutionProps) { p: Paragraph, li: ListItem, code: Code, + ol: (props) => { + //make sure we render ol from toc with TocAccordion otherwise return default + if (props.className && props.className.includes("toc-level")) { + return ; + } + return
    ; + }, a: (props) => { if (props.href && props.href.startsWith("#")) { return ; } return ; - }, + } }; return ( @@ -82,9 +89,17 @@ export function Constitution({ constitution, metadata }: ConstitutionProps) { data-testid="constitution-page-wrapper" container position="relative" - justifyContent={{ xxs: "flex-start", md: "flex-end" }} + justifyContent={{ xxs: "flex-start" }} flex={1} > + {/* fake elemnt to push content to the right since tos is fixed on left */} + +   + @@ -93,7 +108,9 @@ export function Constitution({ constitution, metadata }: ConstitutionProps) { display="flex" justifyContent="space-between" alignItems="center" - pb={4} + position="sticky" + top="72px" + bgcolor={customPalette.bgWhite} > {t("title")} - + - + - + -