Address PR suggestions

This commit is contained in:
Carlos Monastyrski
2025-10-24 03:02:26 -03:00
parent e364eb15db
commit 0ba6b5e86b
21 changed files with 578 additions and 439 deletions
@@ -117,10 +117,10 @@ export const useUpdateRenewalConfig = () => {
object,
TUpdateRenewalConfigDTO
>({
mutationFn: async ({ certificateId, renewBeforeDays, disableAutoRenewal }) => {
mutationFn: async ({ certificateId, renewBeforeDays, enableAutoRenewal }) => {
const { data } = await apiRequest.patch<{ message: string; renewBeforeDays?: number }>(
`/api/v3/certificates/${certificateId}/config`,
{ renewBeforeDays, disableAutoRenewal }
{ renewBeforeDays, enableAutoRenewal }
);
return data;
},
+3 -3
View File
@@ -16,8 +16,8 @@ export type TCertificate = {
extendedKeyUsages: CertExtendedKeyUsage[];
renewBeforeDays?: number;
renewedBy?: string;
renewedFromId?: string;
renewedById?: string;
renewedFromCertificateId?: string;
renewedByCertificateId?: string;
renewalError?: string;
};
@@ -67,6 +67,6 @@ export type TRenewCertificateResponse = {
export type TUpdateRenewalConfigDTO = {
certificateId: string;
renewBeforeDays?: number;
disableAutoRenewal?: boolean;
enableAutoRenewal?: boolean;
projectSlug: string;
};
@@ -1,4 +1,4 @@
import { useEffect } from "react";
import { useEffect, useMemo } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
@@ -7,6 +7,7 @@ import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
import { useProject } from "@app/context";
import { useUpdateRenewalConfig } from "@app/hooks/api";
import { useGetCertificateProfileById } from "@app/hooks/api/certificateProfiles";
import { UsePopUpState } from "@app/hooks/usePopUp";
const DEFAULT_RENEWAL_BEFORE_DAYS = 20;
@@ -64,7 +65,8 @@ const RenewalConfigForm = ({
}) => (
<form onSubmit={onSubmit}>
<FormControl
label="Renewal Days Before Expiration"
label="Auto-renew days before expiry"
isError={Boolean(errors.renewBeforeDays)}
errorText={errors.renewBeforeDays?.message}
className="mb-6"
>
@@ -111,10 +113,24 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
ttlDays?: number;
notAfter: string;
renewalError?: string;
renewedFromId?: string;
renewedById?: string;
renewedFromCertificateId?: string;
renewedByCertificateId?: string;
};
const { data: profileData } = useGetCertificateProfileById({
profileId: certificateData?.profileId || ""
});
const defaultRenewalDays = useMemo(() => {
if (certificateData?.renewBeforeDays) {
return certificateData.renewBeforeDays;
}
if (profileData?.apiConfig?.renewBeforeDays) {
return profileData.apiConfig.renewBeforeDays;
}
return DEFAULT_RENEWAL_BEFORE_DAYS;
}, [certificateData?.renewBeforeDays, profileData?.apiConfig?.renewBeforeDays]);
const isAutoRenewalEnabled = Boolean(
certificateData?.renewBeforeDays && certificateData.renewBeforeDays > 0
);
@@ -134,17 +150,17 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
} = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
renewBeforeDays: DEFAULT_RENEWAL_BEFORE_DAYS
renewBeforeDays: defaultRenewalDays
}
});
useEffect(() => {
if (popUp.manageRenewal.isOpen) {
reset({
renewBeforeDays: certificateData?.renewBeforeDays || DEFAULT_RENEWAL_BEFORE_DAYS
renewBeforeDays: defaultRenewalDays
});
}
}, [popUp.manageRenewal.isOpen, certificateData?.renewBeforeDays, reset]);
}, [popUp.manageRenewal.isOpen, defaultRenewalDays, reset]);
const onUpdateRenewal = async (data: FormData) => {
try {
@@ -31,7 +31,7 @@ export const CertificateRenewalDisableModal = ({ popUp, handlePopUpToggle }: Pro
await updateRenewalConfig({
certificateId: certificateData.certificateId,
projectSlug: currentProject.slug,
disableAutoRenewal: true
enableAutoRenewal: false
});
createNotification({
@@ -36,7 +36,8 @@ import {
import {
ProjectPermissionCertificateActions,
ProjectPermissionSub,
useProject
useProject,
useSubscription
} from "@app/context";
import { useListWorkspaceCertificates, useUpdateRenewalConfig } from "@app/hooks/api";
import { caSupportsCapability } from "@app/hooks/api/ca/constants";
@@ -56,8 +57,8 @@ const isExpiringWithinOneDay = (notAfter: string): boolean => {
};
const getAutoRenewalInfo = (certificate: TCertificate) => {
if (certificate.renewedById) {
return { text: "Renewed", variant: "success" as const };
if (certificate.renewedByCertificateId) {
return { text: "Renewed", variant: "instance" as const };
}
const isRevoked = certificate.status === CertStatus.REVOKED;
@@ -65,8 +66,36 @@ const getAutoRenewalInfo = (certificate: TCertificate) => {
const hasNoProfile = !certificate.profileId;
const isExpiringWithinDay = isExpiringWithinOneDay(certificate.notAfter);
if (isRevoked || isExpired || hasNoProfile || isExpiringWithinDay) {
return null;
if (isRevoked) {
return {
text: "Not Available",
variant: "instance" as const,
tooltip: "Auto-renewal is not available for revoked certificates"
};
}
if (isExpired) {
return {
text: "Not Available",
variant: "instance" as const,
tooltip: "Auto-renewal is not available for expired certificates"
};
}
if (hasNoProfile) {
return {
text: "Not Available",
variant: "instance" as const,
tooltip: "Auto-renewal requires a certificate profile"
};
}
if (isExpiringWithinDay) {
return {
text: "Not Available",
variant: "instance" as const,
tooltip: "Auto-renewal is not available for certificates expiring within 24 hours"
};
}
if (certificate.renewalError) {
@@ -127,8 +156,8 @@ type Props = {
ttlDays?: number;
notAfter?: string;
renewalError?: string;
renewedFromId?: string;
renewedById?: string;
renewedFromCertificateId?: string;
renewedByCertificateId?: string;
}
) => void;
};
@@ -138,6 +167,7 @@ const PER_PAGE_INIT = 25;
export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(PER_PAGE_INIT);
const { subscription } = useSubscription();
const { currentProject } = useProject();
const { data, isPending } = useListWorkspaceCertificates({
@@ -147,6 +177,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
});
const { mutateAsync: updateRenewalConfig } = useUpdateRenewalConfig();
const isLegacyTemplatesEnabled = subscription.pkiLegacyTemplates;
const { data: caData } = useListCasByProjectId(currentProject?.id ?? "");
@@ -173,7 +204,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
await updateRenewalConfig({
certificateId,
projectSlug: currentProject.slug,
disableAutoRenewal: true
enableAutoRenewal: false
});
createNotification({
@@ -198,7 +229,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
<Th>Status</Th>
<Th>Not Before</Th>
<Th>Not After</Th>
<Th>Auto Renewal</Th>
<Th>Renewal Status</Th>
<Th />
</Tr>
</THead>
@@ -286,32 +317,34 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
</DropdownMenuItem>
)}
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionCertificateActions.Read}
a={ProjectPermissionSub.Certificates}
>
{(isAllowed) => (
<DropdownMenuItem
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={async () =>
handlePopUpOpen("certificate", {
serialNumber: certificate.serialNumber
})
}
disabled={!isAllowed}
icon={<FontAwesomeIcon icon={faEye} />}
>
View Details
</DropdownMenuItem>
)}
</ProjectPermissionCan>
{isLegacyTemplatesEnabled && (
<ProjectPermissionCan
I={ProjectPermissionCertificateActions.Read}
a={ProjectPermissionSub.Certificates}
>
{(isAllowed) => (
<DropdownMenuItem
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={async () =>
handlePopUpOpen("certificate", {
serialNumber: certificate.serialNumber
})
}
disabled={!isAllowed}
icon={<FontAwesomeIcon icon={faEye} />}
>
View Details
</DropdownMenuItem>
)}
</ProjectPermissionCan>
)}
{/* Manage auto renewal option - not shown for failed renewals */}
{(() => {
const canManageRenewal =
certificate.profileId &&
!certificate.renewedById &&
!certificate.renewedByCertificateId &&
!isRevoked &&
!isExpired &&
!hasFailed &&
@@ -353,8 +386,9 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
ttlDays,
notAfter: certificate.notAfter,
renewalError: certificate.renewalError,
renewedFromId: certificate.renewedFromId,
renewedById: certificate.renewedById
renewedFromCertificateId:
certificate.renewedFromCertificateId,
renewedByCertificateId: certificate.renewedByCertificateId
});
}}
disabled={!isAllowed}
@@ -373,7 +407,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
{(() => {
const canDisableRenewal =
certificate.profileId &&
!certificate.renewedById &&
!certificate.renewedByCertificateId &&
!isRevoked &&
!isExpired &&
!isExpiringWithinDay &&
@@ -411,7 +445,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
{(() => {
const canRenew =
certificate.profileId &&
!certificate.renewedById &&
!certificate.renewedByCertificateId &&
!isRevoked &&
!isExpired;
@@ -11,6 +11,26 @@ import {
mapTemplateSignatureAlgorithmToApi
} from "@app/pages/cert-manager/PoliciesPage/components/CertificateTemplatesV2Tab/shared/certificate-constants";
const convertTemplateTtlToCertificateTtl = (templateTtl: string): string => {
const match = templateTtl.match(/^(\d+)([dmyh])$/);
if (!match) return templateTtl;
const [, value, unit] = match;
const numValue = parseInt(value, 10);
switch (unit) {
case "m":
return `${numValue * 30}d`;
case "y":
return `${numValue * 365}d`;
case "d":
case "h":
return templateTtl;
default:
return templateTtl;
}
};
export type TemplateConstraints = {
allowedKeyUsages: string[];
allowedExtendedKeyUsages: string[];
@@ -118,7 +138,7 @@ export const useCertificateTemplate = (
// Set TTL if available
if (templateData.validity?.max) {
setValue("ttl", templateData.validity.max);
setValue("ttl", convertTemplateTtlToCertificateTtl(templateData.validity.max));
}
// Handle SAN types