Skip to content

Commit

Permalink
feat(servers-&&-keys): fulls integrated
Browse files Browse the repository at this point in the history
  • Loading branch information
tomjeannesson committed Oct 15, 2023
1 parent 1309bb4 commit 830d6f1
Show file tree
Hide file tree
Showing 10 changed files with 775 additions and 141 deletions.
1 change: 1 addition & 0 deletions desktop-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"dependencies": {
"@hookform/resolvers": "^3.3.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.5",
Expand Down
54 changes: 52 additions & 2 deletions desktop-app/renderer/api/key/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ export interface Key {

export async function getKey(
searchParams: ReturnType<typeof useSearchParams>,
id: string
id: string,
space: string | null = null
): Promise<AxiosResponse<Key>> {
const response = await request(searchParams, 'GET', `/api/key/${id}/`)
const response = await request(
searchParams,
'GET',
space ? `/api/key/${id}/?space=${space}` : `/api/key/${id}/`
)
return response as AxiosResponse<Key>
}
export async function listKey(
Expand All @@ -41,3 +46,48 @@ export async function createKey(
})
return response as AxiosResponse<{ key: string }>
}

export async function deleteKey(
searchParams: ReturnType<typeof useSearchParams>,
id: string
): Promise<AxiosResponse<null>> {
const response = await request(searchParams, 'DELETE', `/api/key/${id}/`)
return response as AxiosResponse<null>
}

export async function possiblePermissions(
searchParams: ReturnType<typeof useSearchParams>
): Promise<AxiosResponse<string[]>> {
const response = await request(
searchParams,
'GET',
'/api/key/possible_permissions/'
)
return response as AxiosResponse<string[]>
}

export async function updateKey(
searchParams: ReturnType<typeof useSearchParams>,
id: string,
name?: string,
description?: string,
revoked?: boolean,
permissions?: string[],
space?: string
): Promise<AxiosResponse<null>> {
if (permissions && !space) {
throw new Error('space is required when updating permissions')
}
const response = await request(
searchParams,
'PATCH',
space ? `/api/key/${id}/?space=${space}` : `/api/key/${id}/`,
{
name,
description,
revoked,
permissions
}
)
return response as AxiosResponse<null>
}
5 changes: 4 additions & 1 deletion desktop-app/renderer/components/custom/copyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import { cn } from '@/lib/utils'
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
value: string
src?: string
copyTrigger: () => void
}

export async function copyToClipboardWithMeta(value: string, event?: Event) {
export async function copyToClipboardWithMeta(value: string) {
navigator.clipboard.writeText(value)
}

export default function CopyButton({
value,
className,
src,
copyTrigger,
...props
}: CopyButtonProps) {
const [hasCopied, setHasCopied] = React.useState(false)
Expand All @@ -38,6 +40,7 @@ export default function CopyButton({
onClick={() => {
copyToClipboardWithMeta(value)
setHasCopied(true)
copyTrigger()
}}
{...props}
>
Expand Down
141 changes: 141 additions & 0 deletions desktop-app/renderer/components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
'use client'

import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
import * as React from 'react'

import { buttonVariants } from '@/components/ui/button'
import { cn } from '@/lib/utils'

const AlertDialog = AlertDialogPrimitive.Root

const AlertDialogTrigger = AlertDialogPrimitive.Trigger

const AlertDialogPortal = AlertDialogPrimitive.Portal

const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, children, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
'bg-background/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 backdrop-blur-sm',
className
)}
{...props}
ref={ref}
/>
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName

const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
className
)}
{...props}
/>
</AlertDialogPortal>
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName

const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col space-y-2 text-center sm:text-left',
className
)}
{...props}
/>
)
AlertDialogHeader.displayName = 'AlertDialogHeader'

const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className
)}
{...props}
/>
)
AlertDialogFooter.displayName = 'AlertDialogFooter'

const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold', className)}
{...props}
/>
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName

const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName

const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
{...props}
/>
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName

const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: 'outline' }),
'mt-2 sm:mt-0',
className
)}
{...props}
/>
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName

export {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogTitle,
AlertDialogTrigger
}
41 changes: 25 additions & 16 deletions desktop-app/renderer/pages/keys/[slug]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { Key } from '@/api/key/key'
import ContextHeader from '@/components/layout/contextHeader'
import { Server, getServer } from '@/lib/localStorage'
import { useSearchParams } from 'next/navigation'
import { useEffect, useState } from 'react'
export default function Servers(): JSX.Element {
const searchParams = useSearchParams()
const [server, setServer] = useState<Server>(
getServer(searchParams.get('server') || '')
)
useEffect(
() => setServer(getServer(searchParams.get('server') || '')),
[searchParams]
)
import KeyGeneralAttributes from './keyGeneralAttributes'
import KeyPermissions from './keyPermissions'

const defaultKey: Key = {
name: '',
prefix: '',
permissions: [],
is_master_key: false,
revoked: false,
description: ''
}

export default function Key(): JSX.Element {
return (
<ContextHeader isBot isBreadcrumb={false}>
<div className="container mt-12 space-y-6 align-middle">
<h1 className="text-5xl font-bold leading-tight tracking-tighter">
Settings - Servers
</h1>
<p className="text-xl">Here is where you can manage your servers.</p>
<div className="container mt-12 space-y-6 align-middle">
<h1 className="text-5xl font-bold leading-tight tracking-tighter">
Settings - Key
</h1>
<p className="text-xl">
Here is where you can manage your distibuted API keys.
</p>
</div>
<div className="flex flex-row space-x-10">
<KeyGeneralAttributes />
<KeyPermissions />
</div>
</div>
</ContextHeader>
)
Expand Down
Loading

0 comments on commit 830d6f1

Please sign in to comment.