From d8b01fe5c2a5bc5eccad23485f013df8ebfec609 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Sat, 3 Jan 2026 11:32:19 +1100 Subject: [PATCH 1/7] Added way to trigger email workflow when running --- examples/email-analyzer-o1/app/actions.ts | 52 + .../app/api/analyze/route.ts | 27 + examples/email-analyzer-o1/app/page.tsx | 203 +-- examples/email-analyzer-o1/bun.lock | 1121 +++++++++++++++++ 4 files changed, 1312 insertions(+), 91 deletions(-) create mode 100644 examples/email-analyzer-o1/app/actions.ts create mode 100644 examples/email-analyzer-o1/bun.lock diff --git a/examples/email-analyzer-o1/app/actions.ts b/examples/email-analyzer-o1/app/actions.ts new file mode 100644 index 00000000..afd6bc11 --- /dev/null +++ b/examples/email-analyzer-o1/app/actions.ts @@ -0,0 +1,52 @@ +"use server" + +import { Client as WorkflowClient } from '@upstash/workflow'; + +type EmailPayload = { + message: string; + subject: string; + to: string; + attachment?: string; +} + +function getWorkflowClient(): WorkflowClient { + const token = process.env.QSTASH_TOKEN; + + if (!token) { + throw new Error( + 'QSTASH_TOKEN environment variable is required' + ); + } + + return new WorkflowClient({ + token, + // VERCEL AUTOMATION BYPASS SECRET is used to bypass the verification of the request + headers: process.env.VERCEL_AUTOMATION_BYPASS_SECRET + ? { + 'Upstash-Forward-X-Vercel-Protection-Bypass': + process.env.VERCEL_AUTOMATION_BYPASS_SECRET, + 'x-vercel-protection-bypass': + process.env.VERCEL_AUTOMATION_BYPASS_SECRET, + } + : undefined, + }); +} + +export async function triggerEmailAnalysis(formData: EmailPayload) { + try { + const workflowClient = getWorkflowClient() + + const result = await workflowClient.trigger({ + url: `${process.env.UPSTASH_WORKFLOW_URL ?? process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : 'http://localhost:3000'}/api/analyze`, + body: formData, + }); + + return { success: true, workflowRunId: result.workflowRunId }; + } catch (error) { + console.error('Error triggering workflow:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to trigger workflow' + }; + } +} diff --git a/examples/email-analyzer-o1/app/api/analyze/route.ts b/examples/email-analyzer-o1/app/api/analyze/route.ts index d5116f8e..26082581 100644 --- a/examples/email-analyzer-o1/app/api/analyze/route.ts +++ b/examples/email-analyzer-o1/app/api/analyze/route.ts @@ -1,5 +1,7 @@ import { serve } from "@upstash/workflow/nextjs" import pdf from "pdf-parse" +import { Client as WorkflowClient } from '@upstash/workflow'; +import { Client as QStashClient } from '@upstash/qstash'; type EmailPayload = { @@ -9,6 +11,28 @@ type EmailPayload = { attachment?: string, } +function getQStashClient(): QStashClient { + const token = process.env.QSTASH_TOKEN; + + if (!token) { + throw new Error( + 'QSTASH_TOKEN environment variable is required' + ); + } + + return new QStashClient({ + token, + headers: process.env.VERCEL_AUTOMATION_BYPASS_SECRET + ? { + 'Upstash-Forward-X-Vercel-Protection-Bypass': + process.env.VERCEL_AUTOMATION_BYPASS_SECRET, + 'x-vercel-protection-bypass': + process.env.VERCEL_AUTOMATION_BYPASS_SECRET, + } + : undefined, + }); + } + export const { POST } = serve(async (context) => { const { message, subject, to, attachment } = context.requestPayload; @@ -68,4 +92,7 @@ export const { POST } = serve(async (context) => { text: aiResponse.body.choices[0].message.content } }) +}, +{ + qstashClient: getQStashClient(), }) diff --git a/examples/email-analyzer-o1/app/page.tsx b/examples/email-analyzer-o1/app/page.tsx index 9007252c..b2329187 100644 --- a/examples/email-analyzer-o1/app/page.tsx +++ b/examples/email-analyzer-o1/app/page.tsx @@ -1,101 +1,122 @@ -import Image from "next/image"; +"use client" + +import { useState } from "react"; +import { triggerEmailAnalysis } from "./actions"; export default function Home() { + const [formData, setFormData] = useState({ + to: "", + subject: "", + message: "", + attachment: "", + }); + const [loading, setLoading] = useState(false); + const [result, setResult] = useState<{ success: boolean; workflowRunId?: string; error?: string } | null>(null); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setResult(null); + + const payload = { + to: formData.to, + subject: formData.subject, + message: formData.message, + attachment: formData.attachment || undefined, + }; + + const response = await triggerEmailAnalysis(payload); + setResult(response); + setLoading(false); + }; + return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
+
+
+

Email Analyzer Test

-
- - Vercel logomark +
+ + setFormData({ ...formData, to: e.target.value })} + className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + required + /> +
+ +
+ + setFormData({ ...formData, subject: e.target.value })} + className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + required + /> +
+ +
+ +