import { useIsLoggedIn, useParams } from 'common'
import { Terminal } from 'lucide-react'
import Head from 'next/head'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useRef, useState, type ReactNode } from 'react'
import { Button, Card, CardContent } from 'ui'
import { Admonition, ShimmeringLoader } from 'ui-patterns'
import {
InterstitialAccountRow,
InterstitialLayout,
LogoBox,
LogoPair,
BrivenLogo,
} from '@/components/layouts/InterstitialLayout'
import CopyButton from '@/components/ui/CopyButton'
import { InlineLink } from '@/components/ui/InlineLink'
import { createCliLoginSession } from '@/data/cli/login'
import { withAuth } from '@/hooks/misc/withAuth'
import { useStaticEffectEvent } from '@/hooks/useStaticEffectEvent'
import { buildStudioPageTitle } from '@/lib/page-title'
import { useProfile } from '@/lib/profile'
import type { NextPageWithLayout } from '@/types'
const PAGE_TITLE = buildStudioPageTitle({ section: 'Authorize CLI', brand: 'Briven' })
const CliLogo = () => (
)
const CliLoginInterstitial = ({
title,
description,
children,
}: {
title: ReactNode
description?: ReactNode
children: ReactNode
}) => (
} right={} />}
title={title}
description={description}
>
{children}
)
function getErrorMessage(error: unknown): string {
if (error instanceof Error) return error.message
if (
typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof (error as { message: unknown }).message === 'string'
) {
return (error as { message: string }).message
}
return 'Unknown error'
}
const CliLoginPage: NextPageWithLayout = () => {
const router = useRouter()
const { session_id, public_key, token_name, device_code } = useParams()
const isLoggedIn = useIsLoggedIn()
if (!router.isReady) return null
return (
<>
{PAGE_TITLE}
router.push(destination)}
/>
>
)
}
type CliLoginStatus =
| { _tag: 'loading' }
| { _tag: 'ready'; deviceCode: string }
| { _tag: 'missing-params'; missingParameters: string[] }
| { _tag: 'error'; message?: string }
export const CliLoginScreen = ({
isLoggedIn,
routerReady,
sessionId,
publicKey,
tokenName,
deviceCode,
navigate: navigateProp,
}: {
isLoggedIn: boolean
routerReady: boolean
sessionId?: string
publicKey?: string
tokenName?: string
deviceCode?: string
navigate: (destination: string) => void
}) => {
const { profile } = useProfile()
const [status, setStatus] = useState({ _tag: 'loading' })
const startedForSessionIdRef = useRef(undefined)
// Keep navigate in a ref so changing the prop never re-triggers the effect
// or cancels an in-flight POST via the isActive cleanup.
const navigate = useStaticEffectEvent(navigateProp)
const displayName = profile?.primary_email ?? profile?.username
useEffect(() => {
if (!isLoggedIn || !routerReady) return
if (deviceCode) {
setStatus({ _tag: 'ready', deviceCode })
return
}
const missingParameters = [
!sessionId ? 'session_id' : undefined,
!publicKey ? 'public_key' : undefined,
].filter(Boolean) as string[]
if (missingParameters.length > 0) {
setStatus({ _tag: 'missing-params', missingParameters })
return
}
// Guard against re-render loops triggered by unstable deps (e.g. a new
// `navigate` reference on each parent render) firing the POST more than
// once per session_id. Without this, the dashboard creates several
// identical access tokens before navigating to the device_code view.
if (startedForSessionIdRef.current === sessionId) return
startedForSessionIdRef.current = sessionId
let isActive = true
setStatus({ _tag: 'loading' })
async function createSession() {
try {
const { nonce } = await createCliLoginSession(sessionId!, publicKey!, tokenName)
if (!isActive) return
if (nonce) {
navigate(`/cli/login?device_code=${nonce.substring(0, 8)}`)
} else {
setStatus({ _tag: 'error', message: 'The CLI sign-in session did not return a code.' })
}
} catch (error: unknown) {
if (!isActive) return
setStatus({ _tag: 'error', message: getErrorMessage(error) })
}
}
createSession()
return () => {
isActive = false
}
}, [deviceCode, isLoggedIn, publicKey, routerReady, sessionId, tokenName, navigate])
if (status._tag === 'loading') {
return (
}
description={}
>
)
}
if (status._tag === 'missing-params') {
const isPlural = status.missingParameters.length > 1
return (
)
}
if (status._tag === 'error') {
return (
Briven could not create the CLI sign-in session.
{status.message && (
Error: {status.message}
)}
>
}
/>
)
}
return (
{
event.preventDefault()
event.clipboardData.setData('text/plain', status.deviceCode)
}}
>
{Array.from(status.deviceCode.padEnd(8, ' ')).map((character, index) => (
{character}
))}
After authorizing, you can close this tab or manage tokens like this one in{' '}
Access Tokens.
)
}
export default withAuth(CliLoginPage)