Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import Link from 'next/link'
import type { Metadata } from 'next/types'
import { authHeaders } from '@/configs/api'
import { AUTH_URLS } from '@/configs/urls'
import type { TeamModel } from '@/core/modules/teams/models'
import { AUTH_URLS, PROTECTED_URLS } from '@/configs/urls'
import { createUserTeamsRepository } from '@/core/modules/teams/user-teams-repository.server'
import {
createDefaultTemplatesRepository,
createTemplatesRepository,
} from '@/core/modules/templates/repository.server'
import { getAuthContext } from '@/core/server/auth'
import { resolveUserTeam } from '@/core/server/functions/team/resolve-user-team'
import { infra } from '@/core/shared/clients/api'
import type { components as InfraComponents } from '@/core/shared/contracts/infra-api.types'
import { createSandboxManagementAuth } from '@/core/shared/sandbox-management-auth.server'
import { SandboxIdSchema } from '@/core/shared/schemas/api'
import DashboardTerminal from '@/features/dashboard/terminal/dashboard-terminal'
Expand All @@ -22,20 +21,25 @@ export const metadata: Metadata = {
robots: 'noindex, nofollow',
}

interface TerminalPageProps {
interface TeamTerminalPageProps {
params: Promise<{
teamSlug: string
}>
searchParams: Promise<{
command?: string
sandboxId?: string
template?: string
}>
}

export default async function TerminalPage({
export default async function TeamTerminalPage({
params,
searchParams,
}: TerminalPageProps) {
const { command = '', sandboxId, template } = await searchParams
const terminalSandboxId = normalizeTerminalSandboxId(sandboxId)
}: TeamTerminalPageProps) {
const [{ teamSlug }, { command = '', sandboxId, template }] =
await Promise.all([params, searchParams])
const requestedTemplate = normalizeTerminalTemplate(template)
const terminalSandboxId = normalizeTerminalSandboxId(sandboxId)

if (!terminalSandboxId && !requestedTemplate) {
return <TerminalUnavailable message="The terminal template is invalid." />
Expand All @@ -50,8 +54,12 @@ export default async function TerminalPage({
if (!authContext) {
return (
<TerminalSignIn
command={command}
sandboxId={terminalSandboxId}
template={requestedTemplate ?? 'base'}
teamSlug={teamSlug}
template={
terminalSandboxId ? template : (requestedTemplate ?? undefined)
}
/>
)
}
Expand All @@ -65,41 +73,40 @@ export default async function TerminalPage({
return <TerminalUnavailable />
}

const resolvedTeam = await resolveUserTeam(
authContext.user.id,
authContext.accessToken
)
const team = teamsResult.data.find((candidate) => candidate.slug === teamSlug)

if (!team) {
return <TerminalUnavailable />
}

const terminalSandbox = terminalSandboxId
? await resolveTerminalSandbox({
? await getSandboxInTeam({
accessToken: authContext.accessToken,
preferredTeamId: resolvedTeam?.id,
sandboxId: terminalSandboxId,
teams: teamsResult.data,
teamId: team.id,
})
: null
const team = terminalSandbox
? terminalSandbox.team
: teamsResult.data.find((candidate) => candidate.id === resolvedTeam?.id)
const terminalTemplate = terminalSandbox?.template ?? requestedTemplate
: undefined

if (!team) {
if (terminalSandboxId && !terminalSandbox) {
return (
<TerminalUnavailable
message={
terminalSandboxId
? 'Sandbox not found or you do not have access to it.'
: undefined
}
/>
<TerminalUnavailable message="Sandbox not found or you do not have access to it." />
)
}

const terminalTemplate = terminalSandbox
? (terminalSandbox.alias ?? terminalSandbox.templateID)
: requestedTemplate

if (!terminalTemplate) {
return <TerminalUnavailable message="The terminal template is invalid." />
}

const templateAvailable = terminalSandboxId
? { ok: true as const, available: true }
: await isTerminalTemplateAvailable({
accessToken: authContext.accessToken,
teamId: team.id,
template: terminalTemplate ?? 'base',
template: terminalTemplate,
})

if (!templateAvailable.ok) {
Expand All @@ -117,21 +124,21 @@ export default async function TerminalPage({
}

return (
<main className="h-dvh min-h-[360px] bg-bg p-3">
<div className="flex h-full min-h-0 overflow-hidden p-3 md:p-6">
<DashboardTerminal
autoStart
launchTarget={{
command,
sandboxId: terminalSandboxId,
template: terminalTemplate ?? 'base',
template: terminalTemplate,
}}
sandboxManagementAuth={createSandboxManagementAuth(
authContext,
team.id
)}
teamSlug={team.slug}
/>
</main>
</div>
)
}

Expand All @@ -143,66 +150,6 @@ function normalizeTerminalSandboxId(sandboxId?: string) {
return parsedSandboxId.success ? parsedSandboxId.data : null
}

async function resolveTerminalSandbox({
accessToken,
preferredTeamId,
sandboxId,
teams,
}: {
accessToken: string
preferredTeamId?: string
sandboxId: string
teams: TeamModel[]
}) {
if (preferredTeamId) {
const preferredTeam = teams.find((team) => team.id === preferredTeamId)
if (preferredTeam) {
const preferredSandbox = await getSandboxInTeam({
accessToken,
sandboxId,
teamId: preferredTeam.id,
})

if (preferredSandbox) {
return {
team: preferredTeam,
template: getSandboxTemplate(preferredSandbox),
}
}
}
}

const candidateTeams = teams.filter((team) => team.id !== preferredTeamId)
const teamMatches = await Promise.all(
candidateTeams.map(async (team) => ({
sandbox: await getSandboxInTeam({
accessToken,
sandboxId,
teamId: team.id,
}),
team,
}))
)

const match = teamMatches.find(({ sandbox }) => sandbox)

return match?.sandbox
? {
team: match.team,
template: getSandboxTemplate(match.sandbox),
}
: null
}

type TerminalSandbox = {
alias?: string
templateID: string
}

function getSandboxTemplate(sandbox: TerminalSandbox) {
return sandbox.alias ?? sandbox.templateID
}

async function getSandboxInTeam({
accessToken,
sandboxId,
Expand All @@ -211,7 +158,7 @@ async function getSandboxInTeam({
accessToken: string
sandboxId: string
teamId: string
}) {
}): Promise<InfraComponents['schemas']['SandboxDetail'] | null> {
try {
const result = await infra.GET('/sandboxes/{sandboxID}', {
params: {
Expand All @@ -225,7 +172,8 @@ async function getSandboxInTeam({
cache: 'no-store',
})

return result.response.ok && result.data ? result.data : null
if (!result.response.ok || !result.data) return null
return result.data
} catch {
return null
}
Expand Down Expand Up @@ -278,24 +226,22 @@ async function isTerminalTemplateAvailable({
}

function TerminalSignIn({
command,
sandboxId,
teamSlug,
template,
}: {
command?: string
sandboxId?: string
template: string
teamSlug: string
template?: string
}) {
const returnToParams = new URLSearchParams()

if (template) {
returnToParams.set('template', template)
}

if (sandboxId) {
returnToParams.set('sandboxId', sandboxId)
}

const returnToQuery = returnToParams.toString()
const returnTo = `/dashboard/terminal${
const returnToQuery = new URLSearchParams({
...(command ? { command } : {}),
Comment thread
matthewlouisbrockman marked this conversation as resolved.
...(sandboxId ? { sandboxId } : {}),
...(template ? { template } : {}),
}).toString()
const returnTo = `${PROTECTED_URLS.TERMINAL(teamSlug)}${
returnToQuery ? `?${returnToQuery}` : ''
}`
const signInHref = `${AUTH_URLS.SIGN_IN}?${new URLSearchParams({
Expand Down
Loading
Loading