chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import { getWorkflowStateContract } from '@/lib/api/contracts/workflows'
|
||||
import { createWorkspaceContract } from '@/lib/api/contracts/workspaces'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { WorkspaceRecencyStorage } from '@/lib/core/utils/browser-storage'
|
||||
import { useWorkspacesWithMetadata, type WorkspaceCreationPolicy } from '@/hooks/queries/workspace'
|
||||
|
||||
const logger = createLogger('WorkspacePage')
|
||||
|
||||
export default function WorkspacePage() {
|
||||
const router = useRouter()
|
||||
const { data: session, isPending: isSessionPending } = useSession()
|
||||
const isAuthenticated = !isSessionPending && !!session?.user
|
||||
const hasRedirectedRef = useRef(false)
|
||||
|
||||
const { data, isLoading: isWorkspacesLoading } = useWorkspacesWithMetadata(isAuthenticated)
|
||||
|
||||
useEffect(() => {
|
||||
if (isSessionPending || hasRedirectedRef.current) return
|
||||
|
||||
if (!session?.user) {
|
||||
logger.info('User not authenticated, redirecting to login')
|
||||
router.replace('/login')
|
||||
return
|
||||
}
|
||||
|
||||
if (isWorkspacesLoading || !data) return
|
||||
|
||||
hasRedirectedRef.current = true
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const redirectWorkflowId = urlParams.get('redirect_workflow')
|
||||
|
||||
const { workspaces, lastActiveWorkspaceId, creationPolicy } = data
|
||||
|
||||
if (workspaces.length === 0) {
|
||||
handleNoWorkspaces(router, creationPolicy)
|
||||
return
|
||||
}
|
||||
|
||||
const localRecentId = WorkspaceRecencyStorage.getMostRecent()
|
||||
const findWorkspace = (id: string | null) =>
|
||||
id ? workspaces.find((w) => w.id === id) : undefined
|
||||
|
||||
const targetWorkspace =
|
||||
findWorkspace(localRecentId) ?? findWorkspace(lastActiveWorkspaceId) ?? workspaces[0]
|
||||
|
||||
if (redirectWorkflowId) {
|
||||
handleWorkflowRedirect(redirectWorkflowId, targetWorkspace.id, router)
|
||||
return
|
||||
}
|
||||
|
||||
logger.info(`Redirecting to workspace: ${targetWorkspace.id}`)
|
||||
router.replace(`/workspace/${targetWorkspace.id}/home`)
|
||||
}, [session, isSessionPending, isWorkspacesLoading, data, router])
|
||||
|
||||
if (isSessionPending || isWorkspacesLoading) {
|
||||
return (
|
||||
<div className='flex h-screen w-full items-center justify-center'>
|
||||
<div
|
||||
className='size-[18px] animate-spin rounded-full'
|
||||
style={{
|
||||
background:
|
||||
'conic-gradient(from 0deg, hsl(var(--muted-foreground)) 0deg 120deg, transparent 120deg 180deg, hsl(var(--muted-foreground)) 180deg 300deg, transparent 300deg 360deg)',
|
||||
mask: 'radial-gradient(farthest-side, transparent calc(100% - 1.5px), black calc(100% - 1.5px))',
|
||||
WebkitMask:
|
||||
'radial-gradient(farthest-side, transparent calc(100% - 1.5px), black calc(100% - 1.5px))',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
async function handleWorkflowRedirect(
|
||||
workflowId: string,
|
||||
fallbackWorkspaceId: string,
|
||||
router: ReturnType<typeof useRouter>
|
||||
): Promise<void> {
|
||||
try {
|
||||
const workflowData = await requestJson(getWorkflowStateContract, {
|
||||
params: { id: workflowId },
|
||||
})
|
||||
const workspaceId = workflowData.data.workspaceId
|
||||
if (workspaceId) {
|
||||
logger.info(`Redirecting workflow ${workflowId} to workspace ${workspaceId}`)
|
||||
router.replace(`/workspace/${workspaceId}/w/${workflowId}`)
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error fetching workflow for redirect:', error)
|
||||
}
|
||||
router.replace(`/workspace/${fallbackWorkspaceId}/home`)
|
||||
}
|
||||
|
||||
async function handleNoWorkspaces(
|
||||
router: ReturnType<typeof useRouter>,
|
||||
creationPolicy: WorkspaceCreationPolicy | null
|
||||
): Promise<void> {
|
||||
if (creationPolicy && !creationPolicy.canCreate) {
|
||||
logger.warn('No workspaces found and workspace creation is blocked', {
|
||||
reason: creationPolicy.reason,
|
||||
workspaceMode: creationPolicy.workspaceMode,
|
||||
organizationId: creationPolicy.organizationId,
|
||||
})
|
||||
router.replace('/')
|
||||
return
|
||||
}
|
||||
|
||||
logger.warn('No workspaces found, creating default workspace')
|
||||
try {
|
||||
const data = await requestJson(createWorkspaceContract, {
|
||||
body: { name: 'My Workspace' },
|
||||
})
|
||||
if (data.workspace?.id) {
|
||||
logger.info(`Created default workspace: ${data.workspace.id}`)
|
||||
router.replace(`/workspace/${data.workspace.id}/home`)
|
||||
return
|
||||
}
|
||||
logger.error('Failed to create default workspace')
|
||||
} catch (error) {
|
||||
logger.error('Error creating default workspace:', error)
|
||||
}
|
||||
router.replace('/login')
|
||||
}
|
||||
Reference in New Issue
Block a user