feat: admin slack configuration

This commit is contained in:
Sheen Capadngan
2024-09-04 23:06:30 +08:00
parent 71b8c59050
commit 956719f797
13 changed files with 465 additions and 15 deletions
+12 -2
View File
@@ -1,2 +1,12 @@
export { useAdminDeleteUser, useCreateAdminUser, useUpdateServerConfig } from "./mutation";
export { useAdminGetUsers, useGetServerConfig } from "./queries";
export {
useAdminDeleteUser,
useCreateAdminUser,
useUpdateAdminSlackConfig,
useUpdateServerConfig
} from "./mutation";
export {
useAdminGetUsers,
useGetAdminSlackConfig,
useGetCustomSlackAppCreationUrl,
useGetServerConfig
} from "./queries";
+23 -1
View File
@@ -5,7 +5,12 @@ import { apiRequest } from "@app/config/request";
import { organizationKeys } from "../organization/queries";
import { User } from "../users/types";
import { adminQueryKeys, adminStandaloneKeys } from "./queries";
import { TCreateAdminUserDTO, TServerConfig } from "./types";
import {
AdminSlackConfig,
TCreateAdminUserDTO,
TServerConfig,
TUpdateAdminSlackConfigDTO
} from "./types";
export const useCreateAdminUser = () => {
const queryClient = useQueryClient();
@@ -59,3 +64,20 @@ export const useAdminDeleteUser = () => {
}
});
};
export const useUpdateAdminSlackConfig = () => {
const queryClient = useQueryClient();
return useMutation<AdminSlackConfig, {}, TUpdateAdminSlackConfigDTO>({
mutationFn: async (dto) => {
const { data } = await apiRequest.put<AdminSlackConfig>(
"/api/v1/admin/integrations/slack/config",
dto
);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries(adminQueryKeys.getAdminSlackConfig());
}
});
};
+28 -2
View File
@@ -3,7 +3,7 @@ import { useInfiniteQuery, useQuery, UseQueryOptions } from "@tanstack/react-que
import { apiRequest } from "@app/config/request";
import { User } from "../types";
import { AdminGetUsersFilters, TServerConfig } from "./types";
import { AdminGetUsersFilters, AdminSlackConfig, TServerConfig } from "./types";
export const adminStandaloneKeys = {
getUsers: "get-users"
@@ -11,7 +11,9 @@ export const adminStandaloneKeys = {
export const adminQueryKeys = {
serverConfig: () => ["server-config"] as const,
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const,
getCustomSlackAppCreationUrl: () => ["custom-slack-app-creation-url"] as const,
getAdminSlackConfig: () => ["admin-slack-config"] as const
};
const fetchServerConfig = async () => {
@@ -59,3 +61,27 @@ export const useAdminGetUsers = (filters: AdminGetUsersFilters) => {
lastPage.length !== 0 ? pages.length * filters.limit : undefined
});
};
export const useGetCustomSlackAppCreationUrl = () =>
useQuery({
queryKey: adminQueryKeys.getCustomSlackAppCreationUrl(),
queryFn: async () => {
const { data } = await apiRequest.get<string>(
"/api/v1/admin/integrations/slack/bot-creation-url"
);
return data;
}
});
export const useGetAdminSlackConfig = () =>
useQuery({
queryKey: adminQueryKeys.getAdminSlackConfig(),
queryFn: async () => {
const { data } = await apiRequest.get<AdminSlackConfig>(
"/api/v1/admin/integrations/slack/config"
);
return data;
}
});
+10
View File
@@ -38,7 +38,17 @@ export type TCreateAdminUserDTO = {
salt: string;
};
export type TUpdateAdminSlackConfigDTO = {
clientId: string;
clientSecret: string;
};
export type AdminGetUsersFilters = {
limit: number;
searchTerm: string;
};
export type AdminSlackConfig = {
clientId: string;
clientSecret: string;
};
@@ -25,6 +25,7 @@ import { useOrganization, useServerConfig, useUser } from "@app/context";
import { useGetOrganizations, useUpdateServerConfig } from "@app/hooks/api";
import { AuthPanel } from "./AuthPanel";
import { IntegrationPanel } from "./IntegrationPanel";
import { RateLimitPanel } from "./RateLimitPanel";
import { UserPanel } from "./UserPanel";
@@ -32,6 +33,7 @@ enum TabSections {
Settings = "settings",
Auth = "auth",
RateLimit = "rate-limit",
Integrations = "integrations",
Users = "users"
}
@@ -137,6 +139,7 @@ export const AdminDashboardPage = () => {
<Tab value={TabSections.Settings}>General</Tab>
<Tab value={TabSections.Auth}>Authentication</Tab>
<Tab value={TabSections.RateLimit}>Rate Limit</Tab>
<Tab value={TabSections.Integrations}>Integrations</Tab>
<Tab value={TabSections.Users}>Users</Tab>
</div>
</TabList>
@@ -323,6 +326,9 @@ export const AdminDashboardPage = () => {
<TabPanel value={TabSections.RateLimit}>
<RateLimitPanel />
</TabPanel>
<TabPanel value={TabSections.Integrations}>
<IntegrationPanel />
</TabPanel>
<TabPanel value={TabSections.Users}>
<UserPanel />
</TabPanel>
@@ -0,0 +1,118 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input } from "@app/components/v2";
import {
useGetAdminSlackConfig,
useGetCustomSlackAppCreationUrl,
useUpdateAdminSlackConfig
} from "@app/hooks/api";
const slackFormSchema = z.object({
clientId: z.string(),
clientSecret: z.string()
});
type TSlackForm = z.infer<typeof slackFormSchema>;
export const IntegrationPanel = () => {
const {
control,
handleSubmit,
setValue,
formState: { isSubmitting, isDirty }
} = useForm<TSlackForm>({
resolver: zodResolver(slackFormSchema)
});
const { data: customSlackAppCreationUrl } = useGetCustomSlackAppCreationUrl();
const { data: adminSlackConfig } = useGetAdminSlackConfig();
const { mutateAsync: updateAdminSlackConfig } = useUpdateAdminSlackConfig();
useEffect(() => {
if (adminSlackConfig) {
setValue("clientId", adminSlackConfig.clientId);
setValue("clientSecret", adminSlackConfig.clientSecret);
}
}, [adminSlackConfig]);
const onSlackFormSubmit = async (data: TSlackForm) => {
await updateAdminSlackConfig(data);
createNotification({
text: "Updated admin slack configuration",
type: "success"
});
};
return (
<form
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
onSubmit={handleSubmit(onSlackFormSubmit)}
>
<div className="flex flex-col justify-start">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Slack Integration</div>
<div className="mb-4 max-w-lg text-sm text-mineshaft-300">
Step 1: Create your Infisical Slack App
</div>
<div className="mb-6">
<Button colorSchema="secondary" onClick={() => window.open(customSlackAppCreationUrl)}>
Create Slack App
</Button>
</div>
<div className="mb-4 max-w-lg text-sm text-mineshaft-300">
Step 2: Configure your instance-wide settings to enable integration with Slack. Copy the
values from the App Credentials page of your custom Slack App.
</div>
<Controller
control={control}
name="clientId"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Client ID"
className="w-96"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
/>
</FormControl>
)}
/>
<Controller
control={control}
name="clientSecret"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Client Secret"
className="w-96"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
/>
</FormControl>
)}
/>
</div>
<Button
className="mt-2"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting || !isDirty}
>
Save
</Button>
</form>
);
};