mirror of https://github.com/buster-so/buster.git
create security endpoints strucutre
This commit is contained in:
parent
f5bdb9d8da
commit
9bd7824586
|
@ -0,0 +1,83 @@
|
|||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { securityQueryKeys } from '@/api/query_keys/security';
|
||||
import {
|
||||
getWorkspaceSettings,
|
||||
getInviteLink,
|
||||
getApprovedDomains,
|
||||
updateWorkspaceSettings,
|
||||
updateInviteLinks,
|
||||
refreshInviteLink,
|
||||
addApprovedDomain,
|
||||
removeApprovedDomain
|
||||
} from './requests';
|
||||
|
||||
export const useGetWorkspaceSettings = () => {
|
||||
return useQuery({
|
||||
...securityQueryKeys.securityGetWorkspaceSettings,
|
||||
queryFn: getWorkspaceSettings
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetInviteLink = () => {
|
||||
return useQuery({
|
||||
...securityQueryKeys.securityInviteLink,
|
||||
queryFn: getInviteLink
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetApprovedDomains = () => {
|
||||
return useQuery({
|
||||
...securityQueryKeys.securityApprovedDomains,
|
||||
queryFn: getApprovedDomains
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateWorkspaceSettings = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: updateWorkspaceSettings,
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(securityQueryKeys.securityGetWorkspaceSettings.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateInviteLinks = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: updateInviteLinks,
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(securityQueryKeys.securityInviteLink.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useRefreshInviteLink = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: refreshInviteLink,
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(securityQueryKeys.securityInviteLink.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useAddApprovedDomain = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: addApprovedDomain,
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(securityQueryKeys.securityApprovedDomains.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoveApprovedDomain = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: removeApprovedDomain,
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(securityQueryKeys.securityApprovedDomains.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
import { mainApiV2 } from '../instances';
|
||||
import {
|
||||
type UpdateInviteLinkRequest,
|
||||
type AddApprovedDomainRequest,
|
||||
type RemoveApprovedDomainRequest,
|
||||
type UpdateWorkspaceSettingsRequest,
|
||||
type GetApprovedDomainsResponse,
|
||||
type GetInviteLinkResponse,
|
||||
type RefreshInviteLinkResponse,
|
||||
type UpdateInviteLinkResponse,
|
||||
type AddApprovedDomainsResponse,
|
||||
type GetWorkspaceSettingsResponse,
|
||||
type UpdateWorkspaceSettingsResponse
|
||||
} from '@buster/server-shared/security';
|
||||
|
||||
export const updateInviteLinks = async (request: UpdateInviteLinkRequest) => {
|
||||
return await mainApiV2
|
||||
.post<UpdateInviteLinkResponse>('/security/invite-links', request)
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
export const refreshInviteLink = async () => {
|
||||
return await mainApiV2
|
||||
.post<RefreshInviteLinkResponse>('/security/invite-links/refresh')
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
export const getInviteLink = async () => {
|
||||
return await mainApiV2
|
||||
.get<GetInviteLinkResponse>('/security/invite-links')
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
export const getApprovedDomains = async () => {
|
||||
return await mainApiV2
|
||||
.get<GetApprovedDomainsResponse>('/security/approved-domains')
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
export const addApprovedDomain = async (request: AddApprovedDomainRequest) => {
|
||||
return await mainApiV2
|
||||
.post<AddApprovedDomainsResponse>('/security/approved-domains', request)
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
export const removeApprovedDomain = async (request: RemoveApprovedDomainRequest) => {
|
||||
return await mainApiV2
|
||||
.delete<GetApprovedDomainsResponse>('/security/approved-domains', {
|
||||
data: request
|
||||
})
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
export const getWorkspaceSettings = async () => {
|
||||
return await mainApiV2
|
||||
.get<GetWorkspaceSettingsResponse>('/security/settings')
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
export const updateWorkspaceSettings = async (request: UpdateWorkspaceSettingsRequest) => {
|
||||
return await mainApiV2
|
||||
.put<UpdateWorkspaceSettingsResponse>('/security/settings', request)
|
||||
.then((res) => res.data);
|
||||
};
|
|
@ -10,6 +10,7 @@ import { permissionGroupQueryKeys } from './permission_groups';
|
|||
import { searchQueryKeys } from './search';
|
||||
import { termsQueryKeys } from './terms';
|
||||
import { userQueryKeys } from './users';
|
||||
import { securityQueryKeys } from './security';
|
||||
|
||||
export const queryKeys = {
|
||||
...datasetQueryKeys,
|
||||
|
@ -23,5 +24,6 @@ export const queryKeys = {
|
|||
...datasourceQueryKeys,
|
||||
...datasetGroupQueryKeys,
|
||||
...permissionGroupQueryKeys,
|
||||
...currencyQueryKeys
|
||||
...currencyQueryKeys,
|
||||
...securityQueryKeys
|
||||
};
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import type {
|
||||
GetWorkspaceSettingsResponse,
|
||||
GetApprovedDomainsResponse,
|
||||
GetInviteLinkResponse
|
||||
} from '@buster/server-shared/security';
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
|
||||
export const securityGetWorkspaceSettings = queryOptions<GetWorkspaceSettingsResponse>({
|
||||
queryKey: ['security', 'workspace-settings']
|
||||
});
|
||||
|
||||
export const securityApprovedDomains = queryOptions<GetApprovedDomainsResponse>({
|
||||
queryKey: ['security', 'approved-domains']
|
||||
});
|
||||
|
||||
export const securityInviteLink = queryOptions<GetInviteLinkResponse>({
|
||||
queryKey: ['security', 'invite-link']
|
||||
});
|
||||
|
||||
export const securityQueryKeys = {
|
||||
securityGetWorkspaceSettings,
|
||||
securityApprovedDomains,
|
||||
securityInviteLink
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
import { SettingsPageHeader } from '../../../_components/SettingsPageHeader';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div>
|
||||
<SettingsPageHeader
|
||||
title="Security"
|
||||
description="Manage security and general permission settings"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { InviteLinks } from './InviteLinks';
|
||||
|
||||
const meta: Meta<typeof InviteLinks> = {
|
||||
title: 'Features/InviteLinks',
|
||||
component: InviteLinks,
|
||||
parameters: {
|
||||
layout: 'padded',
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'A security feature component that allows administrators to manage invite links for workspace access. Users can enable/disable invite links, generate new links, and copy them to clipboard.'
|
||||
}
|
||||
}
|
||||
},
|
||||
tags: ['autodocs']
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof InviteLinks>;
|
||||
|
||||
export const Default: Story = {
|
||||
name: 'Default',
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
'The default state of the InviteLinks component showing the toggle switch, link input field, and action buttons.'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
import React, { useState } from 'react';
|
||||
import { SecurityCards } from './SecurityCards';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Input } from '@/components/ui/inputs';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { Copy2, Refresh } from '@/components/ui/icons';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { AppTooltip } from '@/components/ui/tooltip';
|
||||
|
||||
export const InviteLinks = () => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const [link, setLink] = useState('');
|
||||
const { openInfoMessage } = useBusterNotifications();
|
||||
|
||||
const onClickCopy = () => {
|
||||
navigator.clipboard.writeText(link);
|
||||
openInfoMessage('Invite link copied to clipboard');
|
||||
};
|
||||
|
||||
const onClickRefresh = () => {
|
||||
setLink(Math.random().toString(36).substring(2, 15));
|
||||
openInfoMessage('Invite link refreshed');
|
||||
};
|
||||
|
||||
return (
|
||||
<SecurityCards
|
||||
title="Invite links"
|
||||
description="A uniquely generated invite link allows anyone with the link to join your workspace"
|
||||
cards={[
|
||||
{
|
||||
sections: [
|
||||
<div key="title" className="flex items-center justify-between">
|
||||
<Text>Enable invite links</Text>
|
||||
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
||||
</div>,
|
||||
enabled && (
|
||||
<div key="link" className="flex items-center justify-between space-x-2">
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
className="w-full bg-transparent!"
|
||||
disabled
|
||||
value={link}
|
||||
placeholder="Invite link"
|
||||
/>
|
||||
<div className="absolute top-1/2 right-1 -translate-y-1/2">
|
||||
<AppTooltip title="Refresh the invite link" side="top" sideOffset={8}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size={'small'}
|
||||
onClick={onClickRefresh}
|
||||
suffix={<Refresh />}
|
||||
/>
|
||||
</AppTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outlined" size={'tall'} onClick={onClickCopy} suffix={<Copy2 />}>
|
||||
Copy
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
].filter(Boolean)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,268 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { SecurityCards } from './SecurityCards';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { Pill } from '@/components/ui/pills/Pill';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
|
||||
const meta: Meta<typeof SecurityCards> = {
|
||||
title: 'Features/SecurityCards',
|
||||
component: SecurityCards,
|
||||
parameters: {
|
||||
layout: 'padded',
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'A security cards component that displays security-related information in structured card sections.'
|
||||
}
|
||||
}
|
||||
},
|
||||
tags: ['autodocs']
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof SecurityCards>;
|
||||
|
||||
// Mock data for different use cases
|
||||
const basicSections = [
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Text className="font-medium">Two-Factor Authentication</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Add an extra layer of security to your account
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small" variant="outlined">
|
||||
Enable
|
||||
</Button>
|
||||
</div>,
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Text className="font-medium">API Keys</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Manage your API access tokens
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small" variant="outlined">
|
||||
Manage
|
||||
</Button>
|
||||
</div>,
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Text className="font-medium">Invite Links</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Share your account with others
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small" variant="outlined">
|
||||
Manage
|
||||
</Button>
|
||||
</div>
|
||||
];
|
||||
|
||||
const detailedSections = [
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="mb-1 flex items-center gap-2">
|
||||
<Text className="font-medium">Password Policy</Text>
|
||||
<Pill variant="success">Active</Pill>
|
||||
</div>
|
||||
<Text variant="secondary" size="sm">
|
||||
Minimum 8 characters with uppercase, lowercase, numbers, and symbols
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small">Update</Button>
|
||||
</div>,
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="mb-1 flex items-center gap-2">
|
||||
<Text className="font-medium">Session Timeout</Text>
|
||||
<Pill variant="gray">30 minutes</Pill>
|
||||
</div>
|
||||
<Text variant="secondary" size="sm">
|
||||
Automatic logout after period of inactivity
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small" variant="outlined">
|
||||
Configure
|
||||
</Button>
|
||||
</div>,
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Text className="font-medium">IP Restrictions</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Limit access to specific IP addresses
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small" variant="ghost">
|
||||
Setup
|
||||
</Button>
|
||||
</div>
|
||||
];
|
||||
|
||||
const accessLogSections = [
|
||||
<div>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<Text className="font-medium">Recent Login Activity</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Last 7 days
|
||||
</Text>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div>
|
||||
<Text size="sm">Chrome on macOS</Text>
|
||||
<Text variant="secondary" size="xs">
|
||||
192.168.1.100 • 2 hours ago
|
||||
</Text>
|
||||
</div>
|
||||
<Pill variant="success">Current</Pill>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div>
|
||||
<Text size="sm">Safari on iPhone</Text>
|
||||
<Text variant="secondary" size="xs">
|
||||
10.0.1.50 • 1 day ago
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small" variant="ghost">
|
||||
Revoke
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
];
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
title: 'Account Security',
|
||||
description: 'Manage your security settings and authentication methods',
|
||||
cards: [
|
||||
{
|
||||
sections: basicSections
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export const MultipleCards: Story = {
|
||||
args: {
|
||||
title: 'Security Overview',
|
||||
description: 'Complete security configuration for your organization',
|
||||
cards: [
|
||||
{
|
||||
sections: detailedSections
|
||||
},
|
||||
{
|
||||
sections: accessLogSections
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export const SingleSection: Story = {
|
||||
args: {
|
||||
title: 'Quick Setup',
|
||||
description: 'Essential security setting that needs immediate attention',
|
||||
cards: [
|
||||
{
|
||||
sections: [
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Text className="font-medium">Enable Two-Factor Authentication</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Protect your account with an additional verification step
|
||||
</Text>
|
||||
</div>
|
||||
<Button>Get Started</Button>
|
||||
</div>
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export const EmptyState: Story = {
|
||||
args: {
|
||||
title: 'Security Settings',
|
||||
description: 'No security configurations available',
|
||||
cards: []
|
||||
}
|
||||
};
|
||||
|
||||
export const ComplexContent: Story = {
|
||||
args: {
|
||||
title: 'Advanced Security Configuration',
|
||||
description: 'Comprehensive security settings with detailed controls',
|
||||
cards: [
|
||||
{
|
||||
sections: [
|
||||
<div>
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<div>
|
||||
<Text className="font-medium">Organization Policies</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Configure security policies for all team members
|
||||
</Text>
|
||||
</div>
|
||||
<Button size="small">Manage</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Text size="sm" className="font-medium">
|
||||
Password Requirements
|
||||
</Text>
|
||||
<ul className="space-y-1">
|
||||
<li className="flex items-center gap-2">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-green-500" />
|
||||
<Text size="xs" variant="secondary">
|
||||
Minimum 12 characters
|
||||
</Text>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-green-500" />
|
||||
<Text size="xs" variant="secondary">
|
||||
Special characters required
|
||||
</Text>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Text size="sm" className="font-medium">
|
||||
Access Controls
|
||||
</Text>
|
||||
<ul className="space-y-1">
|
||||
<li className="flex items-center gap-2">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-blue-500" />
|
||||
<Text size="xs" variant="secondary">
|
||||
Role-based permissions
|
||||
</Text>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-blue-500" />
|
||||
<Text size="xs" variant="secondary">
|
||||
IP whitelisting enabled
|
||||
</Text>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Text className="font-medium">Audit Logging</Text>
|
||||
<Text variant="secondary" size="sm">
|
||||
Track all security-related events and user actions
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Pill variant="success">Enabled</Pill>
|
||||
<Button size="small" variant="outlined">
|
||||
View Logs
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
import React from 'react';
|
||||
import { Title, Paragraph } from '@/components/ui/typography';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
|
||||
interface SecurityCardsProps {
|
||||
title: string;
|
||||
description: string;
|
||||
cards: {
|
||||
sections: React.ReactNode[];
|
||||
}[];
|
||||
}
|
||||
|
||||
export const SecurityCards: React.FC<SecurityCardsProps> = ({ title, description, cards }) => {
|
||||
return (
|
||||
<div className="flex flex-col space-y-3.5">
|
||||
<div className="flex flex-col space-y-1.5">
|
||||
<Title as="h3" className="text-lg">
|
||||
{title}
|
||||
</Title>
|
||||
<Paragraph variant="secondary">{description}</Paragraph>
|
||||
</div>
|
||||
{cards.map((card, index) => (
|
||||
<SecurityCard key={index} sections={card.sections} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SecurityCard = ({ sections }: { sections: React.ReactNode[] }) => {
|
||||
return (
|
||||
<div className="flex flex-col rounded border">
|
||||
{sections.map((section, index) => (
|
||||
<div key={index} className={cn(index !== sections.length - 1 && 'border-b', 'px-4 py-2.5')}>
|
||||
{section}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -52,6 +52,11 @@ const permissionAndSecurityItems = (currentParentRoute: BusterRoutes): ISidebarG
|
|||
id: 'permission-and-security',
|
||||
icon: <LockCircle />,
|
||||
items: [
|
||||
{
|
||||
label: 'Security',
|
||||
route: createBusterRoute({ route: BusterRoutes.SETTINGS_SECURITY }),
|
||||
id: createBusterRoute({ route: BusterRoutes.SETTINGS_SECURITY })
|
||||
},
|
||||
{
|
||||
label: 'Users',
|
||||
route: createBusterRoute({ route: BusterRoutes.SETTINGS_USERS }),
|
||||
|
@ -89,8 +94,18 @@ export const SidebarSettings: React.FC = React.memo(() => {
|
|||
return (
|
||||
<Sidebar
|
||||
content={content}
|
||||
header={useMemo(() => <SidebarSettingsHeader />, [])}
|
||||
footer={useMemo(() => <SidebarUserFooter />, [])}
|
||||
header={useMemo(
|
||||
() => (
|
||||
<SidebarSettingsHeader />
|
||||
),
|
||||
[]
|
||||
)}
|
||||
footer={useMemo(
|
||||
() => (
|
||||
<SidebarUserFooter />
|
||||
),
|
||||
[]
|
||||
)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
"./teams": {
|
||||
"types": "./dist/teams/index.d.ts",
|
||||
"default": "./dist/teams/index.js"
|
||||
},
|
||||
"./security": {
|
||||
"types": "./dist/security/index.d.ts",
|
||||
"default": "./dist/security/index.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export * from './requests';
|
||||
export * from './responses';
|
|
@ -0,0 +1,41 @@
|
|||
import { z } from 'zod/v4';
|
||||
import { OrganizationRoleSchema } from '../organization';
|
||||
|
||||
export const UpdateInviteLinkRequestSchema = z.object({
|
||||
enabled: z.boolean(),
|
||||
refresh_link: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type UpdateInviteLinkRequest = z.infer<typeof UpdateInviteLinkRequestSchema>;
|
||||
|
||||
export const AddApprovedDomainRequestSchema = z.object({
|
||||
domains: z.array(z.string()),
|
||||
});
|
||||
|
||||
export type AddApprovedDomainRequest = z.infer<typeof AddApprovedDomainRequestSchema>;
|
||||
|
||||
export const RemoveApprovedDomainRequestSchema = z.object({
|
||||
domains: z.array(z.string()),
|
||||
});
|
||||
|
||||
export type RemoveApprovedDomainRequest = z.infer<typeof RemoveApprovedDomainRequestSchema>;
|
||||
|
||||
export const UpdateWorkspaceSettingsRequestSchema = z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
default_role: OrganizationRoleSchema.optional(),
|
||||
// this can either be a uuid or "all"
|
||||
default_datasets_ids: z
|
||||
.array(
|
||||
z.union([
|
||||
z
|
||||
.string()
|
||||
.regex(
|
||||
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
|
||||
),
|
||||
z.literal('all'),
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type UpdateWorkspaceSettingsRequest = z.infer<typeof UpdateWorkspaceSettingsRequestSchema>;
|
|
@ -0,0 +1,61 @@
|
|||
import { z } from "zod/v4";
|
||||
import { OrganizationRoleSchema } from "../organization";
|
||||
|
||||
export const GetInviteLinkResponseSchema = z.object({
|
||||
link: z.string(),
|
||||
enabled: z.boolean(),
|
||||
});
|
||||
export const UpdateInviteLinkResponseSchema = GetInviteLinkResponseSchema;
|
||||
export const RefreshInviteLinkResponseSchema = GetInviteLinkResponseSchema;
|
||||
|
||||
export const GetApprovedDomainsResponseSchema = z.array(
|
||||
z.object({
|
||||
domain: z.string(),
|
||||
created_at: z.string(),
|
||||
})
|
||||
);
|
||||
export const AddApprovedDomainsResponseSchema =
|
||||
GetApprovedDomainsResponseSchema;
|
||||
export const UpdateApprovedDomainsResponseSchema =
|
||||
GetApprovedDomainsResponseSchema;
|
||||
export const RemoveApprovedDomainsResponseSchema =
|
||||
GetApprovedDomainsResponseSchema;
|
||||
|
||||
export const GetWorkspaceSettingsResponseSchema = z.object({
|
||||
enabled: z.boolean(),
|
||||
default_role: OrganizationRoleSchema,
|
||||
default_datasets: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
})
|
||||
),
|
||||
});
|
||||
export const UpdateWorkspaceSettingsResponseSchema =
|
||||
GetWorkspaceSettingsResponseSchema;
|
||||
|
||||
export type RefreshInviteLinkResponse = z.infer<
|
||||
typeof RefreshInviteLinkResponseSchema
|
||||
>;
|
||||
export type UpdateInviteLinkResponse = z.infer<
|
||||
typeof UpdateInviteLinkResponseSchema
|
||||
>;
|
||||
export type GetInviteLinkResponse = z.infer<typeof GetInviteLinkResponseSchema>;
|
||||
export type GetApprovedDomainsResponse = z.infer<
|
||||
typeof GetApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type AddApprovedDomainsResponse = z.infer<
|
||||
typeof AddApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type UpdateApprovedDomainsResponse = z.infer<
|
||||
typeof UpdateApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type RemoveApprovedDomainsResponse = z.infer<
|
||||
typeof RemoveApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type GetWorkspaceSettingsResponse = z.infer<
|
||||
typeof GetWorkspaceSettingsResponseSchema
|
||||
>;
|
||||
export type UpdateWorkspaceSettingsResponse = z.infer<
|
||||
typeof UpdateWorkspaceSettingsResponseSchema
|
||||
>;
|
Loading…
Reference in New Issue