Files
simstudioai--sim/apps/sim/app/(interfaces)/chat/[identifier]/page.tsx
T
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

74 lines
2.5 KiB
TypeScript

import { db } from '@sim/db'
import { chat } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { and, eq, isNull } from 'drizzle-orm'
import type { Metadata } from 'next'
import ChatClient from '@/app/(interfaces)/chat/[identifier]/chat'
import { OfficeEmbedInit } from '@/app/(interfaces)/chat/[identifier]/office-embed-init'
const logger = createLogger('ChatMetadata')
/**
* Only fully public, active deployments are indexable. Auth-gated (password,
* email, SSO) and inactive/nonexistent chats are noindexed at the page level
* so Google never indexes an auth wall — narrower than blocking `/chat/`
* entirely in robots.ts, which would also hide genuinely public deployments.
*
* Errors from the lookup fail toward noindex rather than throwing: unlike
* the identical query in app/api/chat/[identifier]/route.ts (which must
* surface failures to the caller), a metadata resolution error has no
* error.tsx boundary in this route to catch it — throwing here would take
* the whole page down instead of just skipping indexability, and "can't
* confirm this is safe to index" should default to not indexing it anyway.
*/
export async function generateMetadata({
params,
}: {
params: Promise<{ identifier: string }>
}): Promise<Metadata> {
const { identifier } = await params
let isIndexable = false
try {
const [deployment] = await db
.select({ authType: chat.authType, isActive: chat.isActive })
.from(chat)
.where(and(eq(chat.identifier, identifier), isNull(chat.archivedAt)))
.limit(1)
isIndexable = Boolean(deployment?.isActive && deployment.authType === 'public')
} catch (error) {
logger.error('Failed to resolve chat deployment for metadata', {
identifier,
error: getErrorMessage(error),
})
}
return {
title: 'Chat',
...(!isIndexable && { robots: { index: false, follow: false } }),
}
}
export const dynamic = 'force-dynamic'
export default async function ChatPage({
params,
searchParams,
}: {
params: Promise<{ identifier: string }>
searchParams: Promise<Record<string, string | string[] | undefined>>
}) {
const { identifier } = await params
const { embed } = await searchParams
const isOfficeEmbed = embed === 'office' || (Array.isArray(embed) && embed.includes('office'))
return (
<>
{isOfficeEmbed && <OfficeEmbedInit />}
<ChatClient key={identifier} identifier={identifier} />
</>
)
}