feat: add domain-based access control for API keys (#198)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
KM Koushik
parent
dbc6996d9a
commit
0817b0c7a5
@@ -27,11 +27,20 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@usesend/ui/src/form";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@unsend/ui/src/select";
|
||||
|
||||
|
||||
const apiKeySchema = z.object({
|
||||
name: z.string({ required_error: "Name is required" }).min(1, {
|
||||
message: "Name is required",
|
||||
}),
|
||||
domainId: z.string().optional(),
|
||||
});
|
||||
|
||||
export default function AddApiKey() {
|
||||
@@ -40,6 +49,8 @@ export default function AddApiKey() {
|
||||
const createApiKeyMutation = api.apiKey.createToken.useMutation();
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const [showApiKey, setShowApiKey] = useState(false);
|
||||
|
||||
const domainsQuery = api.domain.domains.useQuery();
|
||||
|
||||
const utils = api.useUtils();
|
||||
|
||||
@@ -47,6 +58,7 @@ export default function AddApiKey() {
|
||||
resolver: zodResolver(apiKeySchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
domainId: "all",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -55,6 +67,7 @@ export default function AddApiKey() {
|
||||
{
|
||||
name: values.name,
|
||||
permission: "FULL",
|
||||
domainId: values.domainId === "all" ? undefined : Number(values.domainId),
|
||||
},
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
@@ -180,6 +193,33 @@ export default function AddApiKey() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={apiKeyForm.control}
|
||||
name="domainId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Domain access</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select domain access" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Domains</SelectItem>
|
||||
{domainsQuery.data?.map((domain: { id: number; name: string }) => (
|
||||
<SelectItem key={domain.id} value={domain.id.toString()}>
|
||||
{domain.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
Choose which domain this API key can send emails from.
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
className=" w-[100px] hover:bg-gray-100 focus:bg-gray-100"
|
||||
|
||||
@@ -25,6 +25,7 @@ export default function ApiList() {
|
||||
<TableHead className="rounded-tl-xl">Name</TableHead>
|
||||
<TableHead>Token</TableHead>
|
||||
<TableHead>Permission</TableHead>
|
||||
<TableHead>Domain Access</TableHead>
|
||||
<TableHead>Last used</TableHead>
|
||||
<TableHead>Created at</TableHead>
|
||||
<TableHead className="rounded-tr-xl">Action</TableHead>
|
||||
@@ -33,7 +34,7 @@ export default function ApiList() {
|
||||
<TableBody>
|
||||
{apiKeysQuery.isLoading ? (
|
||||
<TableRow className="h-32">
|
||||
<TableCell colSpan={6} className="text-center py-4">
|
||||
<TableCell colSpan={7} className="text-center py-4">
|
||||
<Spinner
|
||||
className="w-6 h-6 mx-auto"
|
||||
innerSvgClass="stroke-primary"
|
||||
@@ -42,7 +43,7 @@ export default function ApiList() {
|
||||
</TableRow>
|
||||
) : apiKeysQuery.data?.length === 0 ? (
|
||||
<TableRow className="h-32">
|
||||
<TableCell colSpan={6} className="text-center py-4">
|
||||
<TableCell colSpan={7} className="text-center py-4">
|
||||
<p>No API keys added</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -52,9 +53,14 @@ export default function ApiList() {
|
||||
<TableCell>{apiKey.name}</TableCell>
|
||||
<TableCell>{apiKey.partialToken}</TableCell>
|
||||
<TableCell>{apiKey.permission}</TableCell>
|
||||
<TableCell>
|
||||
{apiKey.domainId
|
||||
? apiKey.domain?.name ?? "Domain removed"
|
||||
: "All domains"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{apiKey.lastUsed
|
||||
? formatDistanceToNow(apiKey.lastUsed)
|
||||
? formatDistanceToNow(apiKey.lastUsed, { addSuffix: true })
|
||||
: "Never"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
|
||||
Reference in New Issue
Block a user