diff --git a/apps/masterbots.ai/.env.local b/apps/masterbots.ai/.env.local index f7715790..173f5035 100644 --- a/apps/masterbots.ai/.env.local +++ b/apps/masterbots.ai/.env.local @@ -8,7 +8,6 @@ NEXT_PUBLIC_APP_ENV=test # AUTH_REDIRECT_PROXY_URL=https://YOURAPP.vercel.app/api/auth # https://hasura.io/learn/graphql/hasura-authentication/integrations/nextjs-auth/ -NEXTAUTH_URL=http://localhost:3000 AUTH_SECRET=bb755cba466058b2e6a195541468e84c JWT_TOKEN_EXPIRATION=2630016 diff --git a/apps/masterbots.ai/app/(browse)/[category]/[threadId]/page.tsx b/apps/masterbots.ai/app/(browse)/[category]/[threadId]/page.tsx index 811d8fae..3cf95e88 100644 --- a/apps/masterbots.ai/app/(browse)/[category]/[threadId]/page.tsx +++ b/apps/masterbots.ai/app/(browse)/[category]/[threadId]/page.tsx @@ -3,6 +3,8 @@ import { ThreadAccordion } from '@/components/shared/thread-accordion' import { CategoryTabs } from '@/components/shared/category-tabs/category-tabs' import { SearchInput } from '@/components/shared/search-input' +export { generateMbMetadata as generateMetadata } from '@/lib/metadata' + export default async function ThreadPage({ params }: ThreadPageProps) { const categories = await getCategories() const thread = await getThread({ diff --git a/apps/masterbots.ai/app/(browse)/page.tsx b/apps/masterbots.ai/app/(browse)/page.tsx index 0a7613de..4e8870dd 100644 --- a/apps/masterbots.ai/app/(browse)/page.tsx +++ b/apps/masterbots.ai/app/(browse)/page.tsx @@ -1,11 +1,18 @@ import { ThreadList } from '@/components/shared/thread-list' import { CategoryTabs } from '@/components/shared/category-tabs/category-tabs' import { SearchInput } from '@/components/shared/search-input' -import { getBrowseThreads, getCategories } from '@/services/hasura' +import { getBrowseThreads, getCategories, getThread } from '@/services/hasura' import { Card } from '@/components/ui/card' import { decodeQuery } from '@/lib/url' +import { permanentRedirect } from 'next/navigation' +import { getThreadLink } from '@/lib/threads' export default async function HomePage({ searchParams }: HomePageProps) { + if (searchParams.threadId) { + const thread = await getThread({ threadId: searchParams.threadId }) + permanentRedirect(getThreadLink({ thread })) + } + const categories = await getCategories() const query = searchParams.query ? decodeQuery(searchParams.query) : null const limit = searchParams.limit ? parseInt(searchParams.limit) : 20 @@ -44,5 +51,10 @@ export default async function HomePage({ searchParams }: HomePageProps) { } interface HomePageProps { - searchParams?: { query: string; page: string; limit: string } + searchParams?: { + query: string + page: string + limit: string + threadId: string + } } diff --git a/apps/masterbots.ai/app/og/route.tsx b/apps/masterbots.ai/app/og/route.tsx new file mode 100644 index 00000000..a7ebd824 --- /dev/null +++ b/apps/masterbots.ai/app/og/route.tsx @@ -0,0 +1,58 @@ +import { ImageResponse } from '@vercel/og' +import { NextRequest } from 'next/server' +import { GeistMono } from 'geist/font/mono' // Import the GeistMono font + +export const runtime = 'edge' + +export async function GET(req: NextRequest) { + const { searchParams } = req.nextUrl + const postTitle = searchParams.get('title') + + // You may need to convert GeistMono or fetch it as ArrayBuffer if needed + // const font = GeistMono; // Assuming GeistMono can be directly used, modify as needed + + return new ImageResponse( + ( +
+
+ {postTitle} +
+
+ ), + { + width: 1920, + height: 1080 + // Optionally, if font needs to be loaded as data + // fonts: [ + // { + // name: 'GeistMono', + // data: font, // Assuming font data is handled accordingly + // style: 'normal' + // } + // ] + } + ) +} diff --git a/apps/masterbots.ai/components/layout/footer-ct.tsx b/apps/masterbots.ai/components/layout/footer-ct.tsx index 50cdde07..d0fd3e2a 100644 --- a/apps/masterbots.ai/components/layout/footer-ct.tsx +++ b/apps/masterbots.ai/components/layout/footer-ct.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link' import type { ElementType } from 'react' export default function FooterCT({ nonFooterTag }: { nonFooterTag?: boolean }) { @@ -24,13 +25,13 @@ export default function FooterCT({ nonFooterTag }: { nonFooterTag?: boolean }) { > robohash.org - {' • '} - terms & policies - + ) diff --git a/apps/masterbots.ai/components/routes/c/chat-input-new.tsx b/apps/masterbots.ai/components/routes/c/chat-input-new.tsx index 546d3358..a3d28164 100644 --- a/apps/masterbots.ai/components/routes/c/chat-input-new.tsx +++ b/apps/masterbots.ai/components/routes/c/chat-input-new.tsx @@ -25,7 +25,6 @@ export interface ChatInputProps showReload?: boolean placeholder: string className?: string - showSubmitButton: boolean } export function ChatInputNew({ diff --git a/apps/masterbots.ai/components/bot-params-form.tsx b/apps/masterbots.ai/components/shared/bot-params-form.tsx similarity index 100% rename from apps/masterbots.ai/components/bot-params-form.tsx rename to apps/masterbots.ai/components/shared/bot-params-form.tsx diff --git a/apps/masterbots.ai/lib/metadata.ts b/apps/masterbots.ai/lib/metadata.ts new file mode 100644 index 00000000..5942030e --- /dev/null +++ b/apps/masterbots.ai/lib/metadata.ts @@ -0,0 +1,48 @@ +import { getThread } from '@/services/hasura'; +import type { Metadata } from 'next'; +import { format } from 'date-fns'; +import { getThreadLink } from './threads'; + +export async function generateMbMetadata({ + params, +}): Promise { + const thread = await getThread({threadId: params.threadId}) + if (!thread) return + + + const firstQuestion= + thread.messages.find(m => m.role === 'user')?.content || 'not found' + const firstResponse = + thread.messages.find(m => m.role === 'assistant')?.content || 'not found' + + const data = { + title: firstQuestion, + publishedAt: thread.updatedAt, // format(thread.updatedAt, 'MMMM dd, yyyy'), + summary: firstResponse, + image: `https://alpha.masterbots.ai/og?title=${encodeURIComponent(firstQuestion)}`, + pathname: getThreadLink({thread:thread, chat:false}) + } + + return { + title:data.title, + description:data.summary, + openGraph: { + title:data.title, + description:data.summary, + type: 'article', + publishedTime: data.publishedAt, + url: `https://alpha.masterbots.ai/${data.pathname}`, + images: [ + { + url: data.image, + }, + ], + }, + twitter: { + card: 'summary_large_image', + title:data.title, + description:data.summary, + images: [data.image], + }, + }; +} \ No newline at end of file diff --git a/apps/masterbots.ai/lib/threads.ts b/apps/masterbots.ai/lib/threads.ts index 466f6648..042ebcbf 100644 --- a/apps/masterbots.ai/lib/threads.ts +++ b/apps/masterbots.ai/lib/threads.ts @@ -82,6 +82,6 @@ export function getAllUserMessagesAsStringArray( return cleanMessages.join(', ') } -export function getThreadLink({chat=false, thread}:{chat:boolean, thread: Thread}){ +export function getThreadLink({chat=false, thread}:{chat?:boolean, thread: Thread}){ return chat ? `/c/${toSlug(thread.chatbot.name)}/${thread.threadId}` : `/${toSlug(thread.chatbot.categories[0]?.category.name)}/${thread.threadId}` } \ No newline at end of file diff --git a/apps/masterbots.ai/package.json b/apps/masterbots.ai/package.json index cd9f0db6..ad348eee 100644 --- a/apps/masterbots.ai/package.json +++ b/apps/masterbots.ai/package.json @@ -39,7 +39,7 @@ "@supabase/ssr": "^0.1.0", "@tanstack/react-query": "^5.29.0", "@vercel/analytics": "^1.1.1", - "@vercel/og": "^0.5.20", + "@vercel/og": "^0.6.2", "ai": "^2.2.25", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", diff --git a/bun.lockb b/bun.lockb index cbc47fe0..df9b6152 100755 Binary files a/bun.lockb and b/bun.lockb differ