From ead95487441dc2d4d8b9ded9a686e5379500c89d Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Tue, 31 Mar 2026 15:29:47 +0200 Subject: [PATCH] server auth stuff, prooompt engineering --- src/app/@modal/(.)assistant/page.tsx | 2 +- src/app/actions/currentTime.ts | 8 ++++++++ src/app/actions/scheduleMeeting.ts | 19 +++++-------------- src/app/api/chat/route.ts | 11 ++++++----- src/app/chat/_components/ChatInterface.tsx | 4 ++-- src/server/routers/chat.ts | 13 +++++++------ 6 files changed, 29 insertions(+), 28 deletions(-) create mode 100644 src/app/actions/currentTime.ts diff --git a/src/app/@modal/(.)assistant/page.tsx b/src/app/@modal/(.)assistant/page.tsx index c05d97c..8fd4f7d 100644 --- a/src/app/@modal/(.)assistant/page.tsx +++ b/src/app/@modal/(.)assistant/page.tsx @@ -8,7 +8,7 @@ export default function AssistantModalPage() { const { data: session, error, isLoading } = trpc.chat.getSession.useQuery(); return ( <> - && + {error &&
{error.message}
} {isLoading && } diff --git a/src/app/actions/currentTime.ts b/src/app/actions/currentTime.ts new file mode 100644 index 0000000..9b8ae6d --- /dev/null +++ b/src/app/actions/currentTime.ts @@ -0,0 +1,8 @@ +export default function currentTime() { + let now = Date.now(); + console.log(now); + return { + success: true, + time: now + } +} diff --git a/src/app/actions/scheduleMeeting.ts b/src/app/actions/scheduleMeeting.ts index 47a7ea6..f642083 100644 --- a/src/app/actions/scheduleMeeting.ts +++ b/src/app/actions/scheduleMeeting.ts @@ -1,5 +1,5 @@ 'use server' -import { clerkClient } from '@clerk/nextjs/server' +import { clerkClient, auth } from '@clerk/nextjs/server' import { google } from 'googleapis' import { env } from '~/env' @@ -10,7 +10,6 @@ export async function scheduleMeeting({ durationMinutes, attendeeEmail, attendeeName, - userId, }: { title: string description: string @@ -18,11 +17,11 @@ export async function scheduleMeeting({ durationMinutes: number attendeeEmail?: string attendeeName?: string - userId: string }) { try { const clerk = await clerkClient() - + const userAuth = await auth() + const user = await clerk.users.getUser(userAuth.userId?userAuth.userId:"") // Get admin's Google OAuth token to create the event on Gregor's calendar const adminTokenResponse = await clerk.users.getUserOauthAccessToken( env.ADMIN_USER_CLERK_ID, @@ -37,16 +36,7 @@ export async function scheduleMeeting({ // Try to resolve visitor's Google email for the invite let visitorEmail: string | undefined = attendeeEmail if (!visitorEmail) { - try { - const visitorTokenResponse = await clerk.users.getUserOauthAccessToken(userId, 'oauth_google') - if (visitorTokenResponse.data[0]) { - const user = await clerk.users.getUser(userId) - const googleAccount = user.externalAccounts.find((a) => a.provider === 'google') - visitorEmail = googleAccount?.emailAddress ?? undefined - } - } catch { - // Visitor not signed in with Google — no invite - } + visitorEmail = user?.emailAddresses.at(0)?.emailAddress ?? undefined } const oAuth2Client = new google.auth.OAuth2() @@ -71,6 +61,7 @@ export async function scheduleMeeting({ end: { dateTime: endTime.toISOString(), timeZone: 'UTC' }, attendees, }, + sendNotifications: true }) return { diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index e2dd062..b4386a7 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -8,6 +8,7 @@ import { db } from '~/server/db' import { chatSession, chatMessage } from '~/server/dbschema/schema' import { servTrpc } from '~/app/_trpc/ServerClient' import { scheduleMeeting } from '~/app/actions/scheduleMeeting' +import currentTime from '~/app/actions/currentTime'; const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY }) @@ -45,7 +46,7 @@ export async function POST(req: Request) { } const result = streamText({ - model: openai('gpt-4o'), + model: openai('gpt-5-mini'), system: systemPrompt, messages: await convertToModelMessages(messages), tools: { @@ -64,22 +65,22 @@ export async function POST(req: Request) { .int() .min(15) .max(120) - .describe('Duration of the meeting in minutes'), + .describe('Duration of the meeting in minutes, if none provided ask if 20 minutes is ok'), attendeeEmail: z .string() .email() .optional() - .describe('Email of the visitor to invite (if provided)'), + .describe('Optional Email of the visitor to invite (if provided)'), attendeeName: z.string().optional().describe('Name of the visitor'), }), - execute: async (input) => scheduleMeeting({ ...input, userId }), + execute: async (input) => scheduleMeeting({ ...input }), }), getCurrentUnixTime: tool({ description: 'Get the current unix time to reference for meeting dates', inputSchema: z.object({ none: z.string().optional().describe("no inputs are needed") }), - execute: async () => { return {success: true, currentTime: Date.now()} } + execute: async () => currentTime() }) }, stopWhen: stepCountIs(5), diff --git a/src/app/chat/_components/ChatInterface.tsx b/src/app/chat/_components/ChatInterface.tsx index 4248f7e..16ac614 100644 --- a/src/app/chat/_components/ChatInterface.tsx +++ b/src/app/chat/_components/ChatInterface.tsx @@ -59,7 +59,7 @@ export default function ChatInterface({ sessionId }: ChatInterfaceProps) { }) const handleSend = () => { const text = input.trim() - if (!text || status != 'ready') return + if (!text || status != 'ready' || sessionId == undefined) return setInput('') sendMessage({ text }) addMessage({ @@ -140,7 +140,7 @@ export default function ChatInterface({ sessionId }: ChatInterfaceProps) {
diff --git a/src/server/routers/chat.ts b/src/server/routers/chat.ts index 88bf03b..700bfb8 100644 --- a/src/server/routers/chat.ts +++ b/src/server/routers/chat.ts @@ -1,4 +1,3 @@ -import { auth } from '@clerk/nextjs/server' import { publicProcedure, router } from "../trpc"; import { TRPCError } from "@trpc/server"; import { db } from '~/server/db' @@ -7,27 +6,29 @@ chatSession, systemSettings } from "../dbschema/schema"; import { isAdmin } from '~/app/actions'; import { z } from 'zod'; import { eq } from 'drizzle-orm'; +import { clerkClient, auth } from '@clerk/nextjs/server' export const chatRouter = router({ getSession: publicProcedure.query(async () => { + const clerk = await clerkClient() const { userId } = await auth(); - if (userId == null) { + const user = await clerk.users.getUser(userId?userId:"") + if (user == undefined) { throw new TRPCError({ message: "chat is only available to signed in users", code: 'UNAUTHORIZED' }); } let session = await db.query.chatSession.findFirst({ where(fields, operators) { - return operators.eq(fields.userId, userId) + return operators.eq(fields.userId, user.id) }, }) if (session !== undefined) { return session; } - let newSession = await db.insert(chatSession).values({ userId: userId }).returning().execute().then((r) => r.at(0)); - if (newSession == undefined) { + let newSession = await db.insert(chatSession).values({ userId: user.id}).returning().execute().then((r) => r.at(0)); if (newSession == undefined) { throw new TRPCError({ message: "failed to create session", code: "INTERNAL_SERVER_ERROR" }); } session = await db.query.chatSession.findFirst({ where(fields, operators) { - return operators.eq(fields.userId, userId) + return operators.eq(fields.userId, user.id) }, }) if (session == undefined) {