1+ import { db } from '@sim/db'
2+ import { templateCreators , templates } from '@sim/db/schema'
3+ import { eq } from 'drizzle-orm'
4+ import type { Metadata } from 'next'
15import { redirect } from 'next/navigation'
26import { getSession } from '@/lib/auth'
7+ import { getBaseUrl } from '@/lib/core/utils/urls'
8+ import { createLogger } from '@/lib/logs/console/logger'
39import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
410import TemplateDetails from '@/app/templates/[id]/template'
511
12+ const logger = createLogger ( 'WorkspaceTemplateMetadata' )
13+
614interface TemplatePageProps {
715 params : Promise < {
816 workspaceId : string
917 id : string
1018 } >
1119}
1220
21+ /**
22+ * Generate dynamic metadata for workspace template pages.
23+ * This provides OpenGraph images for social media sharing.
24+ */
25+ export async function generateMetadata ( {
26+ params,
27+ } : {
28+ params : Promise < { workspaceId : string ; id : string } >
29+ } ) : Promise < Metadata > {
30+ const { workspaceId, id } = await params
31+
32+ try {
33+ const result = await db
34+ . select ( {
35+ template : templates ,
36+ creator : templateCreators ,
37+ } )
38+ . from ( templates )
39+ . leftJoin ( templateCreators , eq ( templates . creatorId , templateCreators . id ) )
40+ . where ( eq ( templates . id , id ) )
41+ . limit ( 1 )
42+
43+ if ( result . length === 0 ) {
44+ return {
45+ title : 'Template Not Found' ,
46+ description : 'The requested template could not be found.' ,
47+ }
48+ }
49+
50+ const { template, creator } = result [ 0 ]
51+ const baseUrl = getBaseUrl ( )
52+
53+ const details = template . details as { tagline ?: string ; about ?: string } | null
54+ const description = details ?. tagline || 'AI workflow template on Sim'
55+
56+ const hasOgImage = ! ! template . ogImageUrl
57+ const ogImageUrl = template . ogImageUrl || `${ baseUrl } /logo/primary/rounded.png`
58+
59+ return {
60+ title : template . name ,
61+ description,
62+ openGraph : {
63+ title : template . name ,
64+ description,
65+ type : 'website' ,
66+ url : `${ baseUrl } /workspace/${ workspaceId } /templates/${ id } ` ,
67+ siteName : 'Sim' ,
68+ images : [
69+ {
70+ url : ogImageUrl ,
71+ width : hasOgImage ? 1200 : 512 ,
72+ height : hasOgImage ? 630 : 512 ,
73+ alt : `${ template . name } - Workflow Preview` ,
74+ } ,
75+ ] ,
76+ } ,
77+ twitter : {
78+ card : hasOgImage ? 'summary_large_image' : 'summary' ,
79+ title : template . name ,
80+ description,
81+ images : [ ogImageUrl ] ,
82+ creator : creator ?. details
83+ ? ( ( creator . details as Record < string , unknown > ) . xHandle as string ) || undefined
84+ : undefined ,
85+ } ,
86+ }
87+ } catch ( error ) {
88+ logger . error ( 'Failed to generate workspace template metadata:' , error )
89+ return {
90+ title : 'Template' ,
91+ description : 'AI workflow template on Sim' ,
92+ }
93+ }
94+ }
95+
1396/**
1497 * Workspace-scoped template detail page.
1598 * Requires authentication and workspace membership to access.
@@ -19,12 +102,10 @@ export default async function TemplatePage({ params }: TemplatePageProps) {
19102 const { workspaceId, id } = await params
20103 const session = await getSession ( )
21104
22- // Redirect unauthenticated users to public template detail page
23105 if ( ! session ?. user ?. id ) {
24106 redirect ( `/templates/${ id } ` )
25107 }
26108
27- // Verify workspace membership
28109 const hasPermission = await verifyWorkspaceMembership ( session . user . id , workspaceId )
29110 if ( ! hasPermission ) {
30111 redirect ( '/' )
0 commit comments