* chore: local engines should not allow reinit * fix: remote engines should not have install action
121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
import { Fragment, useCallback, useEffect, useState } from 'react'
|
|
|
|
import Image from 'next/image'
|
|
|
|
import { EngineStatus } from '@janhq/core'
|
|
import { Button, Input, Modal } from '@janhq/joi'
|
|
import { useAtom, useSetAtom } from 'jotai'
|
|
import { ArrowUpRight } from 'lucide-react'
|
|
|
|
import useEngineMutation from '@/hooks/useEngineMutation'
|
|
import useEngineQuery from '@/hooks/useEngineQuery'
|
|
|
|
import { getTitleByCategory } from '@/utils/model-engine'
|
|
|
|
import { isAnyRemoteModelConfiguredAtom } from '@/helpers/atoms/SetupRemoteModel.atom'
|
|
|
|
import { setUpRemoteModelStageAtom } from '@/helpers/atoms/SetupRemoteModel.atom'
|
|
|
|
const SetUpApiKeyModal: React.FC = () => {
|
|
const updateEngineConfig = useEngineMutation()
|
|
const isAnyRemoteModelConfigured = useSetAtom(isAnyRemoteModelConfiguredAtom)
|
|
const { data: engineData } = useEngineQuery()
|
|
|
|
const [{ stage, remoteEngine, metadata }, setUpRemoteModelStage] = useAtom(
|
|
setUpRemoteModelStageAtom
|
|
)
|
|
const [apiKey, setApiKey] = useState<string>('')
|
|
|
|
useEffect(() => {
|
|
if (!remoteEngine || !engineData) return
|
|
const isEngineReady =
|
|
engineData.find((e) => e.name === remoteEngine)?.status ===
|
|
EngineStatus.Ready
|
|
const fakeApiKey = '******************************************'
|
|
setApiKey(isEngineReady ? fakeApiKey : '')
|
|
}, [remoteEngine, engineData])
|
|
|
|
const onSaveClicked = useCallback(async () => {
|
|
if (!remoteEngine) {
|
|
alert('Does not have engine')
|
|
return
|
|
}
|
|
const normalizedApiKey = apiKey.trim().replaceAll('*', '')
|
|
if (normalizedApiKey.length === 0) return
|
|
|
|
updateEngineConfig.mutate({
|
|
engine: remoteEngine,
|
|
config: {
|
|
config: 'apiKey',
|
|
value: apiKey,
|
|
},
|
|
})
|
|
isAnyRemoteModelConfigured(true)
|
|
}, [remoteEngine, updateEngineConfig, apiKey, isAnyRemoteModelConfigured])
|
|
|
|
const onDismiss = useCallback(() => {
|
|
setUpRemoteModelStage('NONE', undefined)
|
|
}, [setUpRemoteModelStage])
|
|
|
|
if (remoteEngine == null) return null
|
|
const owner: string = getTitleByCategory(remoteEngine)
|
|
const logoUrl: string = (metadata?.logo ?? '') as string
|
|
const apiKeyUrl: string | undefined = (metadata?.api_key_url ?? '') as
|
|
| string
|
|
| undefined
|
|
|
|
return (
|
|
<Modal
|
|
data-testid="setup-api-key-modal"
|
|
onOpenChange={onDismiss}
|
|
open={stage === 'SETUP_API_KEY'}
|
|
content={
|
|
<Fragment>
|
|
<div className="my-4 flex items-center gap-2 text-black">
|
|
{logoUrl && (
|
|
<Image width={24} height={24} src={logoUrl} alt="Model owner" />
|
|
)}
|
|
<h1 className="text-lg font-semibold leading-7 text-[hsla(var(--text-primary))]">
|
|
{owner}
|
|
</h1>
|
|
</div>
|
|
|
|
<div className="mb-3 text-sm font-medium leading-4">API Key</div>
|
|
|
|
<Input
|
|
placeholder="Input API Key"
|
|
type="text"
|
|
value={apiKey}
|
|
onChange={(e) => setApiKey(e.target.value)}
|
|
/>
|
|
|
|
{apiKeyUrl && (
|
|
<span className="mt-3 flex items-center justify-start gap-1 text-xs font-medium leading-3 text-blue-600">
|
|
<a
|
|
href={apiKeyUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="relative flex no-underline hover:underline"
|
|
>
|
|
Get your API key from {owner} <ArrowUpRight size={12} />
|
|
</a>
|
|
</span>
|
|
)}
|
|
|
|
<span className="my-4 flex items-center gap-1 text-xs text-blue-500"></span>
|
|
<div className="flex items-center justify-end gap-3">
|
|
<Button theme="ghost" variant="outline" onClick={onDismiss}>
|
|
Cancel
|
|
</Button>
|
|
<Button disabled={apiKey.length === 0} onClick={onSaveClicked}>
|
|
Save
|
|
</Button>
|
|
</div>
|
|
</Fragment>
|
|
}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export default SetUpApiKeyModal
|