This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import type { ActorContext } from '@devrunbook/application'
|
||||
import { cookies, headers } from 'next/headers'
|
||||
import { redirect } from 'next/navigation'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
import type { CommandPaletteItem } from '../../components/command-palette/command-palette'
|
||||
import { AppShell } from '../../components/shell/app-shell'
|
||||
import { isThemePreference } from '../../components/theme/theme-model'
|
||||
import {
|
||||
detectLocale,
|
||||
isPresentationMode,
|
||||
} from '../../components/presentation/presentation-model'
|
||||
import { getAuth } from '../../auth/auth'
|
||||
import {
|
||||
AuthenticatedWorkspaceContextError,
|
||||
resolveAuthenticatedWorkspaceContext,
|
||||
} from '../../server/authenticated-workspace-context'
|
||||
import { DrizzleWorkspaceSelectionLookup } from '@devrunbook/db'
|
||||
import {
|
||||
authenticatedCommands,
|
||||
buildAuthenticatedShellPresentation,
|
||||
buildLoginHref,
|
||||
commandsForWorkspaceRole,
|
||||
localizeAuthenticatedCommands,
|
||||
} from './authenticated-app-presentation'
|
||||
|
||||
export interface AuthenticatedAppLayoutProps {
|
||||
readonly children: ReactNode
|
||||
readonly requestPath: string
|
||||
readonly loginReturnTo: string
|
||||
readonly activeNavigationId: string
|
||||
readonly commands?: readonly CommandPaletteItem[]
|
||||
readonly loadCommands?: (
|
||||
actor: ActorContext,
|
||||
) => Promise<readonly CommandPaletteItem[]>
|
||||
}
|
||||
|
||||
export async function AuthenticatedAppLayout({
|
||||
children,
|
||||
requestPath,
|
||||
loginReturnTo,
|
||||
activeNavigationId,
|
||||
commands = authenticatedCommands,
|
||||
loadCommands,
|
||||
}: AuthenticatedAppLayoutProps) {
|
||||
let actor: ActorContext
|
||||
const requestHeaders = await headers()
|
||||
try {
|
||||
actor = await resolveAuthenticatedWorkspaceContext(
|
||||
new Request(new URL(requestPath, 'http://devrunbook.local'), {
|
||||
headers: requestHeaders,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof AuthenticatedWorkspaceContextError &&
|
||||
error.code === 'authentication_required'
|
||||
) {
|
||||
redirect(buildLoginHref(loginReturnTo))
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
const cookieStore = await cookies()
|
||||
const themeCookie = cookieStore.get('devrunbook_theme')?.value
|
||||
const initialTheme = isThemePreference(themeCookie) ? themeCookie : 'system'
|
||||
const modeCookie = cookieStore.get('devrunbook_mode')?.value
|
||||
const mode = isPresentationMode(modeCookie) ? modeCookie : 'simple'
|
||||
const locale = detectLocale(
|
||||
cookieStore.get('devrunbook_locale')?.value,
|
||||
requestHeaders.get('accept-language'),
|
||||
)
|
||||
const session = await getAuth().api.getSession({ headers: requestHeaders })
|
||||
const workspaces =
|
||||
await new DrizzleWorkspaceSelectionLookup().listAuthorizedWorkspaces(
|
||||
actor.userId,
|
||||
)
|
||||
const presentation = buildAuthenticatedShellPresentation({
|
||||
...actor,
|
||||
mode,
|
||||
locale,
|
||||
workspaces: workspaces.map(({ id, name, type }) => ({ id, name, type })),
|
||||
...(session?.user.name ? { displayName: session.user.name } : {}),
|
||||
...(session?.user.email ? { email: session.user.email } : {}),
|
||||
})
|
||||
const resolvedCommands = localizeAuthenticatedCommands(
|
||||
commandsForWorkspaceRole(
|
||||
loadCommands ? await loadCommands(actor) : commands,
|
||||
actor.workspaceRole,
|
||||
),
|
||||
locale,
|
||||
)
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
activeNavigationId={activeNavigationId}
|
||||
navigation={presentation.navigation}
|
||||
workspaces={presentation.workspaces}
|
||||
activeWorkspaceId={presentation.activeWorkspaceId}
|
||||
actor={presentation.actor}
|
||||
initialTheme={initialTheme}
|
||||
locale={locale}
|
||||
commands={resolvedCommands}
|
||||
>
|
||||
{children}
|
||||
</AppShell>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user