mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 03:28:58 +00:00
Address greptile comments
This commit is contained in:
@@ -115,7 +115,7 @@ export const useUpdateRenewalConfig = () => {
|
||||
return useMutation<
|
||||
{ message: string; renewBeforeDays?: number },
|
||||
object,
|
||||
TUpdateRenewalConfigDTO & { disableAutoRenewal?: boolean }
|
||||
TUpdateRenewalConfigDTO
|
||||
>({
|
||||
mutationFn: async ({ certificateId, renewBeforeDays, disableAutoRenewal }) => {
|
||||
const { data } = await apiRequest.patch<{ message: string; renewBeforeDays?: number }>(
|
||||
|
||||
@@ -67,5 +67,6 @@ export type TRenewCertificateResponse = {
|
||||
export type TUpdateRenewalConfigDTO = {
|
||||
certificateId: string;
|
||||
renewBeforeDays?: number;
|
||||
disableAutoRenewal?: boolean;
|
||||
projectSlug: string;
|
||||
};
|
||||
|
||||
+100
-125
@@ -13,24 +13,91 @@ const DEFAULT_RENEWAL_BEFORE_DAYS = 20;
|
||||
const MIN_RENEWAL_BEFORE_DAYS = 1;
|
||||
const MAX_RENEWAL_BEFORE_DAYS = 30;
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
const createFormSchema = (ttlDays: number, notAfter: string) =>
|
||||
z.object({
|
||||
renewBeforeDays: z
|
||||
.number()
|
||||
.min(MIN_RENEWAL_BEFORE_DAYS, `Renewal days must be at least ${MIN_RENEWAL_BEFORE_DAYS}`)
|
||||
.max(MAX_RENEWAL_BEFORE_DAYS, `Renewal days cannot exceed ${MAX_RENEWAL_BEFORE_DAYS}`)
|
||||
})
|
||||
.refine(() => {
|
||||
return true;
|
||||
}, "Invalid renewal configuration");
|
||||
.refine(
|
||||
(value) => value < ttlDays,
|
||||
(value) => ({
|
||||
message: `Renewal days (${value}) must be less than certificate TTL (${ttlDays} days)`
|
||||
})
|
||||
)
|
||||
.refine(
|
||||
(value) => {
|
||||
const expiryDate = new Date(notAfter);
|
||||
const renewalDate = new Date(expiryDate.getTime() - value * 24 * 60 * 60 * 1000);
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
tomorrow.setHours(0, 0, 0, 0);
|
||||
return renewalDate >= tomorrow;
|
||||
},
|
||||
() => ({
|
||||
message: "Renewals can only be scheduled from tomorrow onwards."
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
type FormData = z.infer<typeof formSchema>;
|
||||
type FormData = z.infer<ReturnType<typeof createFormSchema>>;
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["manageRenewal"]>;
|
||||
handlePopUpToggle: (popUpName: keyof UsePopUpState<["manageRenewal"]>, state?: boolean) => void;
|
||||
};
|
||||
|
||||
const RenewalConfigForm = ({
|
||||
control,
|
||||
errors,
|
||||
onSubmit,
|
||||
isLoading,
|
||||
buttonText,
|
||||
onCancel
|
||||
}: {
|
||||
control: any;
|
||||
errors: { renewBeforeDays?: { message?: string } };
|
||||
onSubmit: (e?: React.BaseSyntheticEvent) => Promise<void>;
|
||||
isLoading: boolean;
|
||||
buttonText: string;
|
||||
onCancel: () => void;
|
||||
}) => (
|
||||
<form onSubmit={onSubmit}>
|
||||
<FormControl
|
||||
label="Renewal Days Before Expiration"
|
||||
errorText={errors.renewBeforeDays?.message}
|
||||
className="mb-6"
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name="renewBeforeDays"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
type="number"
|
||||
min={MIN_RENEWAL_BEFORE_DAYS}
|
||||
max={MAX_RENEWAL_BEFORE_DAYS}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value, 10);
|
||||
field.onChange(value);
|
||||
}}
|
||||
placeholder="Enter days before expiration"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button type="button" colorSchema="secondary" variant="plain" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" colorSchema="primary" isLoading={isLoading} isDisabled={isLoading}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
const { currentProject } = useProject();
|
||||
const { mutateAsync: updateRenewalConfig, isPending: isUpdatingConfig } =
|
||||
@@ -41,7 +108,7 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
commonName: string;
|
||||
profileId: string;
|
||||
renewBeforeDays?: number;
|
||||
ttlDays: number;
|
||||
ttlDays?: number;
|
||||
notAfter: string;
|
||||
renewalError?: string;
|
||||
renewedFromId?: string;
|
||||
@@ -54,6 +121,11 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
|
||||
const hasRenewalError = Boolean(certificateData?.renewalError);
|
||||
|
||||
const formSchema = createFormSchema(
|
||||
certificateData?.ttlDays || 365,
|
||||
certificateData?.notAfter || ""
|
||||
);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -84,30 +156,6 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.renewBeforeDays >= certificateData.ttlDays) {
|
||||
createNotification({
|
||||
text: `Renewal days (${data.renewBeforeDays}) must be less than certificate TTL (${certificateData.ttlDays} days)`,
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const expiryDate = new Date(certificateData.notAfter);
|
||||
const renewalDate = new Date(
|
||||
expiryDate.getTime() - data.renewBeforeDays * 24 * 60 * 60 * 1000
|
||||
);
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
tomorrow.setHours(0, 0, 0, 0);
|
||||
|
||||
if (renewalDate < tomorrow) {
|
||||
createNotification({
|
||||
text: "The renewal date cannot be set to today or any past date. Renewals can only be scheduled from tomorrow onwards.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateRenewalConfig({
|
||||
certificateId: certificateData.certificateId,
|
||||
renewBeforeDays: data.renewBeforeDays,
|
||||
@@ -133,8 +181,6 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
}
|
||||
};
|
||||
|
||||
const isLoading = isUpdatingConfig;
|
||||
|
||||
const getModalTitle = () => {
|
||||
if (hasRenewalError) {
|
||||
return `Fix Auto-Renewal: ${certificateData?.commonName || ""}`;
|
||||
@@ -145,6 +191,10 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
return `Enable Auto-Renewal for ${certificateData?.commonName || ""}`;
|
||||
};
|
||||
|
||||
if (!certificateData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={popUp?.manageRenewal?.isOpen}
|
||||
@@ -153,7 +203,6 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
}}
|
||||
>
|
||||
<ModalContent title={getModalTitle()}>
|
||||
{/* Show renewal error if present */}
|
||||
{hasRenewalError && (
|
||||
<div className="mb-6 rounded-md border border-red-600 bg-red-900/20 p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
@@ -173,100 +222,26 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Configuration form - shown for all cases except when enabled and no error */}
|
||||
{(!isAutoRenewalEnabled || hasRenewalError) && (
|
||||
<form onSubmit={handleSubmit(onUpdateRenewal)}>
|
||||
<FormControl
|
||||
label="Renewal Days Before Expiration"
|
||||
errorText={errors.renewBeforeDays?.message}
|
||||
className="mb-6"
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name="renewBeforeDays"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
type="number"
|
||||
min={MIN_RENEWAL_BEFORE_DAYS}
|
||||
max={MAX_RENEWAL_BEFORE_DAYS}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value, 10);
|
||||
field.onChange(value);
|
||||
}}
|
||||
placeholder="Enter days before expiration"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button
|
||||
type="button"
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpToggle("manageRenewal", false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
colorSchema="primary"
|
||||
isLoading={isUpdatingConfig}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
{isAutoRenewalEnabled ? "Update Configuration" : "Enable Auto-Renewal"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<RenewalConfigForm
|
||||
control={control}
|
||||
errors={errors}
|
||||
onSubmit={handleSubmit(onUpdateRenewal)}
|
||||
isLoading={isUpdatingConfig}
|
||||
buttonText={isAutoRenewalEnabled ? "Update Configuration" : "Enable Auto-Renewal"}
|
||||
onCancel={() => handlePopUpToggle("manageRenewal", false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Show edit form for enabled auto-renewal without errors */}
|
||||
{isAutoRenewalEnabled && !hasRenewalError && (
|
||||
<form onSubmit={handleSubmit(onUpdateRenewal)}>
|
||||
<FormControl
|
||||
label="Renewal Days Before Expiration"
|
||||
errorText={errors.renewBeforeDays?.message}
|
||||
className="mb-6"
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name="renewBeforeDays"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
type="number"
|
||||
min={MIN_RENEWAL_BEFORE_DAYS}
|
||||
max={MAX_RENEWAL_BEFORE_DAYS}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value, 10);
|
||||
field.onChange(value);
|
||||
}}
|
||||
placeholder="Enter days before expiration"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button
|
||||
type="button"
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpToggle("manageRenewal", false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
colorSchema="primary"
|
||||
isLoading={isUpdatingConfig}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
Update Configuration
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<RenewalConfigForm
|
||||
control={control}
|
||||
errors={errors}
|
||||
onSubmit={handleSubmit(onUpdateRenewal)}
|
||||
isLoading={isUpdatingConfig}
|
||||
buttonText="Update Configuration"
|
||||
onCancel={() => handlePopUpToggle("manageRenewal", false)}
|
||||
/>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
+18
-17
@@ -8,14 +8,21 @@ import { useProject } from "@app/context";
|
||||
import { useUpdateRenewalConfig } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const formSchema = z.object({
|
||||
renewBeforeDays: z
|
||||
.number()
|
||||
.min(1, "Renewal days must be at least 1")
|
||||
.max(365, "Renewal days cannot exceed 365")
|
||||
});
|
||||
const createFormSchema = (ttlDays: number) =>
|
||||
z.object({
|
||||
renewBeforeDays: z
|
||||
.number()
|
||||
.min(1, "Renewal days must be at least 1")
|
||||
.max(365, "Renewal days cannot exceed 365")
|
||||
.refine(
|
||||
(value) => value < ttlDays,
|
||||
(value) => ({
|
||||
message: `Renewal days (${value}) must be less than certificate TTL (${ttlDays} days)`
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
type FormData = z.infer<typeof formSchema>;
|
||||
type FormData = z.infer<ReturnType<typeof createFormSchema>>;
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["configureRenewal"]>;
|
||||
@@ -37,6 +44,8 @@ export const CertificateRenewalConfigModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
ttlDays: number;
|
||||
};
|
||||
|
||||
const formSchema = createFormSchema(certificateData.ttlDays);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -45,7 +54,7 @@ export const CertificateRenewalConfigModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
renewBeforeDays: certificateData?.renewBeforeDays || 7
|
||||
renewBeforeDays: certificateData?.renewBeforeDays || 1
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,14 +62,6 @@ export const CertificateRenewalConfigModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
|
||||
const onSubmit = async (data: FormData) => {
|
||||
try {
|
||||
if (data.renewBeforeDays >= certificateData.ttlDays) {
|
||||
createNotification({
|
||||
text: `Renewal days (${data.renewBeforeDays}) must be less than certificate TTL (${certificateData.ttlDays} days)`,
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Project not found",
|
||||
@@ -144,7 +145,7 @@ export const CertificateRenewalConfigModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
{renewBeforeDays && certificateData?.ttlDays && (
|
||||
<div className="mt-2 rounded bg-primary-900/20 p-2">
|
||||
<p className="text-sm text-primary-300">
|
||||
{renewBeforeDays >= (certificateData.ttlDays || 0)
|
||||
{renewBeforeDays >= certificateData.ttlDays
|
||||
? "⚠️ Renewal days must be less than certificate TTL"
|
||||
: `✓ Certificate will be renewed ${renewBeforeDays} days before expiration`}
|
||||
</p>
|
||||
|
||||
@@ -91,10 +91,14 @@ const getAutoRenewalInfo = (certificate: TCertificate) => {
|
||||
return { text: "Due Now", variant: "danger" as const };
|
||||
}
|
||||
|
||||
const daysUntilRenewal = Math.ceil(
|
||||
const daysUntilRenewal = Math.floor(
|
||||
(renewalDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)
|
||||
);
|
||||
|
||||
if (daysUntilRenewal === 0) {
|
||||
return { text: "Renews today", variant: "primary" as const };
|
||||
}
|
||||
|
||||
if (daysUntilRenewal <= 7) {
|
||||
return { text: `Renews in ${daysUntilRenewal}d`, variant: "primary" as const };
|
||||
}
|
||||
@@ -204,6 +208,14 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
data?.certificates.map((certificate) => {
|
||||
const { variant, label } = getCertValidUntilBadgeDetails(certificate.notAfter);
|
||||
const autoRenewalInfo = getAutoRenewalInfo(certificate);
|
||||
|
||||
const isRevoked = certificate.status === CertStatus.REVOKED;
|
||||
const isExpired = new Date(certificate.notAfter) < new Date();
|
||||
const isExpiringWithinDay = isExpiringWithinOneDay(certificate.notAfter);
|
||||
const hasFailed = Boolean(certificate.renewalError);
|
||||
const isAutoRenewalEnabled = Boolean(
|
||||
certificate.renewBeforeDays && certificate.renewBeforeDays > 0
|
||||
);
|
||||
return (
|
||||
<Tr className="h-10" key={`certificate-${certificate.id}`}>
|
||||
<Td>{certificate.commonName}</Td>
|
||||
@@ -297,10 +309,6 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
</ProjectPermissionCan>
|
||||
{/* Manage auto renewal option - not shown for failed renewals */}
|
||||
{(() => {
|
||||
const isRevoked = certificate.status === CertStatus.REVOKED;
|
||||
const isExpired = new Date(certificate.notAfter) < new Date();
|
||||
const hasFailed = Boolean(certificate.renewalError);
|
||||
const isExpiringWithinDay = isExpiringWithinOneDay(certificate.notAfter);
|
||||
const canManageRenewal =
|
||||
certificate.profileId &&
|
||||
!certificate.renewedById &&
|
||||
@@ -317,10 +325,6 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
>
|
||||
{(isAllowed) => {
|
||||
const isAutoRenewalEnabled = Boolean(
|
||||
certificate.renewBeforeDays && certificate.renewBeforeDays > 0
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
@@ -329,10 +333,17 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
)}
|
||||
onClick={async () => {
|
||||
const notAfterDate = new Date(certificate.notAfter);
|
||||
const notBeforeDate = new Date(certificate.notBefore);
|
||||
const ttlDays = Math.ceil(
|
||||
(notAfterDate.getTime() - notBeforeDate.getTime()) /
|
||||
(24 * 60 * 60 * 1000)
|
||||
const notBeforeDate = certificate.notBefore
|
||||
? new Date(certificate.notBefore)
|
||||
: new Date(
|
||||
notAfterDate.getTime() - 365 * 24 * 60 * 60 * 1000
|
||||
);
|
||||
const ttlDays = Math.max(
|
||||
1,
|
||||
Math.ceil(
|
||||
(notAfterDate.getTime() - notBeforeDate.getTime()) /
|
||||
(24 * 60 * 60 * 1000)
|
||||
)
|
||||
);
|
||||
handlePopUpOpen("manageRenewal", {
|
||||
certificateId: certificate.id,
|
||||
@@ -360,12 +371,6 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
})()}
|
||||
{/* Disable auto renewal option - only shown when auto renewal is active */}
|
||||
{(() => {
|
||||
const isRevoked = certificate.status === CertStatus.REVOKED;
|
||||
const isExpired = new Date(certificate.notAfter) < new Date();
|
||||
const isExpiringWithinDay = isExpiringWithinOneDay(certificate.notAfter);
|
||||
const isAutoRenewalEnabled = Boolean(
|
||||
certificate.renewBeforeDays && certificate.renewBeforeDays > 0
|
||||
);
|
||||
const canDisableRenewal =
|
||||
certificate.profileId &&
|
||||
!certificate.renewedById &&
|
||||
@@ -404,8 +409,6 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
})()}
|
||||
{/* Manual renewal action for profile-issued certificates that are not revoked/expired (including failed ones) */}
|
||||
{(() => {
|
||||
const isRevoked = certificate.status === CertStatus.REVOKED;
|
||||
const isExpired = new Date(certificate.notAfter) < new Date();
|
||||
const canRenew =
|
||||
certificate.profileId &&
|
||||
!certificate.renewedById &&
|
||||
|
||||
Reference in New Issue
Block a user