mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 08:27:36 +00:00
feat(secret-sync): add support for secret sync error notifications in project router and Slack integration, including channel selection enhancements
This commit is contained in:
@@ -614,8 +614,10 @@ export const registerDeprecatedProjectRouter = async (server: FastifyZodProvider
|
|||||||
integrationId: z.string(),
|
integrationId: z.string(),
|
||||||
accessRequestChannels: validateSlackChannelsField,
|
accessRequestChannels: validateSlackChannelsField,
|
||||||
secretRequestChannels: validateSlackChannelsField,
|
secretRequestChannels: validateSlackChannelsField,
|
||||||
|
secretSyncErrorChannels: validateSlackChannelsField,
|
||||||
isAccessRequestNotificationEnabled: z.boolean(),
|
isAccessRequestNotificationEnabled: z.boolean(),
|
||||||
isSecretRequestNotificationEnabled: z.boolean()
|
isSecretRequestNotificationEnabled: z.boolean(),
|
||||||
|
isSecretSyncErrorNotificationEnabled: z.boolean()
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
integration: z.literal(WorkflowIntegration.MICROSOFT_TEAMS),
|
integration: z.literal(WorkflowIntegration.MICROSOFT_TEAMS),
|
||||||
@@ -633,7 +635,9 @@ export const registerDeprecatedProjectRouter = async (server: FastifyZodProvider
|
|||||||
isAccessRequestNotificationEnabled: true,
|
isAccessRequestNotificationEnabled: true,
|
||||||
accessRequestChannels: true,
|
accessRequestChannels: true,
|
||||||
isSecretRequestNotificationEnabled: true,
|
isSecretRequestNotificationEnabled: true,
|
||||||
secretRequestChannels: true
|
secretRequestChannels: true,
|
||||||
|
isSecretSyncErrorNotificationEnabled: true,
|
||||||
|
secretSyncErrorChannels: true
|
||||||
}).merge(
|
}).merge(
|
||||||
z.object({
|
z.object({
|
||||||
integration: z.literal(WorkflowIntegration.SLACK),
|
integration: z.literal(WorkflowIntegration.SLACK),
|
||||||
|
|||||||
@@ -1573,8 +1573,8 @@ export const projectServiceFactory = ({
|
|||||||
isSecretSyncErrorNotificationEnabled
|
isSecretSyncErrorNotificationEnabled
|
||||||
}: TUpdateProjectWorkflowIntegration & {
|
}: TUpdateProjectWorkflowIntegration & {
|
||||||
// workaround intersection type while we don't have the microsoft teams integration for failed secret syncs
|
// workaround intersection type while we don't have the microsoft teams integration for failed secret syncs
|
||||||
isSecretSyncErrorNotificationEnabled: boolean;
|
isSecretSyncErrorNotificationEnabled?: boolean;
|
||||||
secretSyncErrorChannels: string;
|
secretSyncErrorChannels?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
if (!project) {
|
if (!project) {
|
||||||
|
|||||||
+63
-60
@@ -44,6 +44,69 @@ const formSchema = z.object({
|
|||||||
|
|
||||||
type TSlackConfigForm = z.infer<typeof formSchema>;
|
type TSlackConfigForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
type TChannelSelectorProps = {
|
||||||
|
value: string[];
|
||||||
|
onChange: (value: string[]) => void;
|
||||||
|
error?: { message?: string };
|
||||||
|
slackChannelIdToName: Record<string, string>;
|
||||||
|
sortedSlackChannels?: { id: string; name: string }[];
|
||||||
|
keyPrefix: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChannelSelector = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
error,
|
||||||
|
slackChannelIdToName,
|
||||||
|
sortedSlackChannels,
|
||||||
|
keyPrefix
|
||||||
|
}: TChannelSelectorProps) => (
|
||||||
|
<FormControl label="Slack channels" isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Input
|
||||||
|
isReadOnly
|
||||||
|
value={value
|
||||||
|
?.filter(Boolean)
|
||||||
|
.map((entry) => slackChannelIdToName[entry])
|
||||||
|
.join(", ")}
|
||||||
|
className="text-left"
|
||||||
|
/>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
style={{
|
||||||
|
width: "var(--radix-dropdown-menu-trigger-width)",
|
||||||
|
maxHeight: "350px",
|
||||||
|
overflowY: "auto"
|
||||||
|
}}
|
||||||
|
side="bottom"
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
{sortedSlackChannels?.map((slackChannel) => {
|
||||||
|
const isChecked = value?.includes(slackChannel.id);
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={(evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
onChange(
|
||||||
|
isChecked
|
||||||
|
? value?.filter((el: string) => el !== slackChannel.id)
|
||||||
|
: [...(value || []), slackChannel.id]
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
key={`${keyPrefix}-slack-channel-${slackChannel.id}`}
|
||||||
|
iconPos="right"
|
||||||
|
icon={isChecked && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||||
|
>
|
||||||
|
{slackChannel.name}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
@@ -320,63 +383,3 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
|||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type TChannelSelectorProps = {
|
|
||||||
value: string[];
|
|
||||||
onChange: (value: string[]) => void;
|
|
||||||
error?: { message?: string };
|
|
||||||
slackChannelIdToName: Record<string, string>;
|
|
||||||
sortedSlackChannels?: { id: string; name: string }[];
|
|
||||||
keyPrefix: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ChannelSelector = ({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
error,
|
|
||||||
slackChannelIdToName,
|
|
||||||
sortedSlackChannels,
|
|
||||||
keyPrefix
|
|
||||||
}: TChannelSelectorProps) => (
|
|
||||||
<FormControl label="Slack channels" isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Input
|
|
||||||
isReadOnly
|
|
||||||
value={value?.filter(Boolean).map((entry) => slackChannelIdToName[entry]).join(", ")}
|
|
||||||
className="text-left"
|
|
||||||
/>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
style={{
|
|
||||||
width: "var(--radix-dropdown-menu-trigger-width)",
|
|
||||||
maxHeight: "350px",
|
|
||||||
overflowY: "auto"
|
|
||||||
}}
|
|
||||||
side="bottom"
|
|
||||||
align="start"
|
|
||||||
>
|
|
||||||
{sortedSlackChannels?.map((slackChannel) => {
|
|
||||||
const isChecked = value?.includes(slackChannel.id);
|
|
||||||
return (
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={(evt) => {
|
|
||||||
evt.preventDefault();
|
|
||||||
onChange(
|
|
||||||
isChecked
|
|
||||||
? value?.filter((el: string) => el !== slackChannel.id)
|
|
||||||
: [...(value || []), slackChannel.id]
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
key={`${keyPrefix}-slack-channel-${slackChannel.id}`}
|
|
||||||
iconPos="right"
|
|
||||||
icon={isChecked && <FontAwesomeIcon icon={faCheckCircle} />}
|
|
||||||
>
|
|
||||||
{slackChannel.name}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
Reference in New Issue
Block a user