Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 7
Make report generation asynchronous using a job queue#663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
bb1db2e536fdc75e20729e5743edFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -15,6 +15,9 @@ import { FollowupPanel } from '@/components/followup-panel' | ||
| import { inquire, researcher, taskManager, querySuggestor, resolutionSearch, type DrawnFeature } from '@/lib/agents' | ||
| import { writer } from '@/lib/agents/writer' | ||
| import { saveChat, getSystemPrompt, generateReportContext } from '@/lib/actions/chat' | ||
| import { enqueueJob } from '@/lib/actions/jobs' | ||
| import { getCurrentUserIdOnServer } from '@/lib/auth/get-current-user' | ||
| import { send } from '@vercel/queue' | ||
| import { Chat, AIMessage } from '@/lib/types' | ||
| import { UserMessage } from '@/components/user-message' | ||
| import { BotMessage } from '@/components/message' | ||
| @@ -57,9 +60,23 @@ async function submit(formData?: FormData, skip?: boolean) { | ||
| } | ||
| try { | ||
| const messages = JSON.parse(messagesString) as AIMessage[]; | ||
| return await generateReportContext(messages); | ||
| const userId = await getCurrentUserIdOnServer() || 'anonymous'; | ||
| // Enqueue job for asynchronous processing | ||
| const jobId = await enqueueJob(userId, 'generate_report_context', { messages }); | ||
| // Send to Vercel Queue | ||
| try { | ||
| await send('report-generation', { jobId }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested fix: if | ||
| } catch (queueError) { | ||
| console.error('Failed to send to Vercel Queue:', queueError); | ||
| // We continue because the worker might still poll if it's running, | ||
| // but ideally Vercel Queue handles the trigger. | ||
| } | ||
| return { jobId, status: 'processing' }; | ||
| } catch (e) { | ||
| console.error('Failed to parse messages for report context:', e); | ||
| console.error('Failed to enqueue report context job:', e); | ||
| return { title: 'QCX Intelligence Analysis', summary: 'Automated executive summary is currently unavailable.' }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { handleCallback } from '@vercel/queue'; | ||
| import { generateReportContext } from '@/lib/actions/chat'; | ||
| import { updateJob, getJob } from '@/lib/actions/jobs'; | ||
| import { NextRequest } from 'next/server'; | ||
| const queueHandler = handleCallback(async (payload: any) => { | ||
| const { jobId } = payload; | ||
| if (!jobId) { | ||
| console.error('No jobId in queue payload'); | ||
| return; | ||
| } | ||
Comment on lines
+8
to
+11
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Early returns prevent Vercel Queue from retrying failures. Lines 9 and 16 return early on missing If 🔁 Recommended fix: throw instead of return const { jobId } = payload;
if (!jobId) {
console.error('No jobId in queue payload');
- return;+ throw new Error('No jobId in queue payload');
}
// ...
const job = await getJob(jobId);
if (!job) {
console.error(`Job ${jobId} not found`);
- return;+ throw new Error(`Job ${jobId} not found`);
}Also applies to: 14-17 🤖 Prompt for AI Agents | ||
| try { | ||
| const job = await getJob(jobId); | ||
| if (!job) { | ||
| console.error(`Job ${jobId} not found`); | ||
| return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This branch returns success from Suggested fix: make DB read errors throw from | ||
| } | ||
| await updateJob(jobId, { status: 'processing' }); | ||
| const { messages } = job.payload as { messages: any[] }; | ||
| const result = await generateReportContext(messages); | ||
| await updateJob(jobId, { | ||
| status: 'completed', | ||
| result, | ||
| updatedAt: new Date(), | ||
| }); | ||
| console.log(`Job ${jobId} completed via Vercel Queue`); | ||
| } catch (error) { | ||
| console.error(`Job ${jobId} failed in Vercel Queue:`, error); | ||
| await updateJob(jobId, { | ||
| status: 'failed', | ||
| error: error instanceof Error ? error.message : String(error), | ||
| updatedAt: new Date(), | ||
| }); | ||
| } | ||
| }); | ||
Comment on lines
+1
to
+39
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash# Verify route structure and Redis integration# Check for the expected route path
fd -t f 'route.ts' app/api/queues/
# Search for Upstash Redis setup and usage
rg -n 'Redis|UPSTASH' --type ts --type env -C2
# Check vercel.json for queue configuration
cat vercel.json 2>/dev/null ||echo"vercel.json not found"Repository: QueueLab/QCX Length of output: 227 🏁 Script executed: #!/bin/bashset -euo pipefail
# 1) Locate the queue callback route implementation(s)echo"== handleCallback routes =="
rg -n "handleCallback" -S --glob "**/*.ts" --glob "**/*.tsx".||true# 2) Inspect app/api tree relevant to queue/generate-reportechoecho"== List api route directories (shallow) =="
ls -la app/api 2>/dev/null ||true
find app/api -maxdepth 3 -type f -name "route.ts"2>/dev/null | sed 's|^\./||'| sort ||true# 3) Read the provided route file if it existsechoecho"== app/api/queue/route.ts (first 120 lines) =="if [ -f"app/api/queue/route.ts" ];then
sed -n '1,120p' app/api/queue/route.ts
elseecho"MISSING: app/api/queue/route.ts"fi# 4) Inspect jobs helpers for updatedAt behaviorechoecho"== updateJob/getJob implementations =="
rg -n "function updateJob|export (async )?function updateJob|updateJob\\(" app/lib/actions/jobs.ts app/lib/actions/jobs.* app/lib/actions -S 2>/dev/null ||trueforfin$(fd -t f "jobs.ts" app/lib/actions 2>/dev/null || true);doechoecho"---- $f (first 200 lines) ----"
sed -n '1,200p'"$f"done# 5) Search for Upstash Redis usage and env varsechoecho"== Upstash/Redis usage =="
rg -n "UPSTASH|Upstash|upstash|Redis" -S --glob "**/*.ts" --glob "**/*.js" --glob "**/*.tsx" --glob "**/*.env*" --glob "vercel.json".||true# 6) Check queue-related vercel config (if present)echoecho"== vercel.json (contents if exists) =="if [ -f"vercel.json" ];then
cat vercel.json
elseecho"MISSING: vercel.json"fi# 7) Check other queue/route paths possibly matching the PR summary expectationechoecho"== Look for generate-report routes =="
find app/api -type f -name "route.ts"2>/dev/null | xargs -r -n 1 basename | head -n 200 ||true
rg -n "generate-report|generateReport|report" app/api -S --glob "**/*.ts" --glob "**/*.tsx"||trueRepository: QueueLab/QCX Length of output: 2420 🏁 Script executed: #!/bin/bashset -euo pipefail
# Locate jobs helper filesecho"== Locate jobs helper(s) =="
fd -t f -i "*jobs*.ts" app/lib ||true
fd -t f -i "jobs.ts" app/lib ||true
fd -t f -i "*job*.ts" app/lib/actions ||true# Read likely jobs filesforfin$(fd -t f -i "*jobs*.ts" app/lib 2>/dev/null);doechoecho"---- $f (first 200 lines) ----"
sed -n '1,200p'"$f"done# Search for upstash/redis clients (beyond env mentions)echoecho"== Redis/Upstash client code usage =="
rg -n "(upstash|UPSTASH|`@upstash`\/redis|Redis|createClient|redis\.)" -S --glob "**/*.ts" --glob "**/*.tsx" app lib src .||true# Ensure we understand queue callback retry behavior usage in codebaseechoecho"== Other `@vercel/queue` usage patterns =="
rg -n "`@vercel/queue`|handleCallback" -S --glob "**/*.ts".||trueRepository: QueueLab/QCX Length of output: 791 🏁 Script executed: #!/bin/bashset -euo pipefail
echo"== Find updateJob/getJob definitions =="
rg -n "updateJob\\b|getJob\\b" -S --glob "**/*.ts" --glob "**/*.tsx".||trueechoecho"== Find generateReportContext definition =="
rg -n "generateReportContext\\b" -S --glob "**/*.ts" --glob "**/*.tsx".||trueechoecho"== List files named jobs.ts / actions/jobs =="
fd -t f -i "jobs.ts".||true
fd -t f -i "jobs".||trueechoecho"== Inspect any found jobs-related file(s) (first 200 lines) =="# Take a limited set of likely matches to avoid huge outputforfin$(rg -l "export (async )?function updateJob|export (async )?function getJob|updateJob\\(|getJob\\(" -S --glob "**/*.ts".| head -n 20);doechoecho"---- $f (first 200 lines) ----"
sed -n '1,200p'"$f"doneechoecho"== Redis/Upstash code usage (broader search) =="
rg -n "(upstash|UPSTASH|`@upstash/redis`|upstash_redis|redis)" -S --glob "**/*.ts" --glob "**/*.tsx".||trueechoecho"== Find vercel/queue config docs/examples usage =="
rg -n "handleCallback|`@vercel/queue`" -S --glob "**/*.ts" --glob "**/*.tsx".||trueechoecho"== Locate any queue payload fields or TTL write logic =="
rg -n "(TTL|ttl|expire|expiration|setex|hset|write.*redis|redis.*set)" -S --glob "**/*.ts" --glob "**/*.tsx".||trueRepository: QueueLab/QCX Length of output: 4958 🏁 Script executed: #!/bin/bashset -euo pipefail
echo"== generateReportContext implementation =="
sed -n '1,200p' lib/actions/chat.ts
echoecho"== generateReportContext (tail if longer) =="
sed -n '200,400p' lib/actions/chat.ts 2>/dev/null ||trueRepository: QueueLab/QCX Length of output: 7912 🌐 Web query:
💡 Result: In Citations:
🏁 Script executed: #!/bin/bashset -euo pipefail
echo"== Search for enqueueJob/send usage =="
rg -n "enqueueJob\\b|`@vercel/queue`\\b|send\\b\\(" -S --glob "**/*.ts" --glob "**/*.tsx".||trueechoecho"== Inspect app/actions.tsx around Vercel Queue send =="if [ -f"app/actions.tsx" ];then
nl -ba app/actions.tsx | sed -n '1,120p'
nl -ba app/actions.tsx | sed -n '120,240p'elseecho"MISSING: app/actions.tsx"fiechoecho"== Search for generate-report / queues route strings =="
rg -n "(generate-report|generateReport|/api/queues|api/queues|queues/)" -S --glob "**/*.ts" --glob "**/*.tsx".||trueechoecho"== Inspect jobs table schema =="
rg -n "export const jobs\\b|jobs\\b.*schema|updatedAt|result|error" -S --glob "**/*.ts" --glob "**/*.tsx" lib/app src .||true# Print jobs schema file if we can locate itforfin$(rg -l "export const jobs\\b".|| true);doechoecho"---- $f (first 200 lines) ----"
sed -n '1,200p'"$f"doneRepository: QueueLab/QCX Length of output: 699 🏁 Script executed: #!/bin/bashset -euo pipefail
echo"== Inspect app/actions.tsx around Vercel Queue send =="if [ -f"app/actions.tsx" ];then
sed -n '1,140p' app/actions.tsx
elseecho"MISSING: app/actions.tsx"fiechoecho"== Inspect app/actions.tsx near enqueueJob/send call =="if [ -f"app/actions.tsx" ];then
rg -n "enqueueJob|send\\(" app/actions.tsx
fiechoecho"== Search for generate-report / queues route strings =="
rg -n "(generate-report|generateReport|/api/queues|api/queues|queues/)" -S --glob "**/*.ts" --glob "**/*.tsx".||trueechoecho"== Locate jobs table schema (drizzle) =="
rg -n "jobs\\s*=\\s*|export const jobs\\b|updatedAt\\b|result\\b|error\\b" -S --glob "**/*.ts" --glob "**/*.tsx" lib .||trueechoecho"== Print any file containing 'export const jobs' =="forfin$(rg -l "export const jobs\\b" lib .2>/dev/null || true);doechoecho"---- $f (first 220 lines) ----"
sed -n '1,220p'"$f"doneRepository: QueueLab/QCX Length of output: 50368 Critical: Vercel Queue worker swallows failures, so jobs won’t retry
Also, the earlier “wrong route path” and “Upstash Redis storage” assertions don’t match this codebase: the producer uses 🤖 Prompt for AI Agents | ||
| export async function POST(request: NextRequest) { | ||
| return queueHandler(request); | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -11,6 +11,7 @@ import { AI } from '@/app/actions' | ||
| import { useActions } from 'ai/rsc' | ||
| import { toast } from 'sonner' | ||
| import { ReportTemplate } from './report-template' | ||
| import { getJobStatus } from '@/lib/actions/jobs-client' | ||
| import { createPortal } from 'react-dom' | ||
| export const DownloadReportButton = () => { | ||
| @@ -57,7 +58,34 @@ export const DownloadReportButton = () => { | ||
| formData.append('action', 'generate_report_context'); | ||
| formData.append('messages', JSON.stringify(aiState.messages)); | ||
| const { title, summary } = await (actions as any).submit(formData); | ||
| const { jobId } = await (actions as any).submit(formData); | ||
| if (!jobId) { | ||
| throw new Error('Failed to start report generation job'); | ||
| } | ||
| // Polling for job completion | ||
| let jobResult = null; | ||
| let attempts = 0; | ||
| const maxAttempts = 60; // 60 seconds | ||
| while (attempts < maxAttempts) { | ||
| const { status, result, error } = await getJobStatus(jobId); | ||
| if (status === 'completed') { | ||
| jobResult = result; | ||
| break; | ||
| } else if (status === 'failed') { | ||
| throw new Error(error || 'Job failed'); | ||
| } | ||
| attempts++; | ||
| await new Promise(resolve => setTimeout(resolve, 1000)); | ||
| } | ||
| if (!jobResult) { | ||
| throw new Error('Report generation timed out'); | ||
| } | ||
| const { title, summary } = jobResult as { title: string, summary: string }; | ||
Comment on lines
+61
to
+88
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Polling implementation has multiple critical issues.
🤖 Prompt for AI Agents | ||
| const finalTitle = title || 'QCX Intelligence Analysis' | ||
| setReportTitle(finalTitle) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| 'use server' | ||
| import { getJobStatus as getJobStatusServer } from './jobs' | ||
| export async function getJobStatus(jobId: string) { | ||
| return await getJobStatusServer(jobId) | ||
| } | ||
Comment on lines
+1
to
+7
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: This file should have been removed or replaced. Per the PR summary and bot migration plan:
The current implementation is an unnecessary wrapper that imports 🤖 Prompt for AI Agents | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| 'use server' | ||
| import { db } from '@/lib/db' | ||
| import { jobs } from '@/lib/db/schema' | ||
| import { eq } from 'drizzle-orm' | ||
| import { revalidatePath } from 'next/cache' | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win Remove unused import.
🤖 Prompt for AI Agents | ||
| export async function enqueueJob(userId: string, type: string, payload: any) { | ||
| try { | ||
| const result = await db.insert(jobs).values({ | ||
| userId, | ||
| type, | ||
| payload, | ||
| status: 'pending', | ||
| }).returning({ id: jobs.id }) | ||
| return result[0].id | ||
| } catch (error) { | ||
| console.error('Error enqueuing job:', error) | ||
| throw error | ||
| } | ||
| } | ||
| export async function getJob(jobId: string) { | ||
| try { | ||
| const result = await db.select().from(jobs).where(eq(jobs.id, jobId)).limit(1) | ||
| return result[0] || null | ||
| } catch (error) { | ||
| console.error('Error getting job:', error) | ||
| return null | ||
| } | ||
| } | ||
Comment on lines
+8
to
+32
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win Inconsistent error handling across CRUD functions.
Consider a unified strategy: either propagate all errors or return Result<T, Error> types consistently. 🤖 Prompt for AI Agents | ||
| export async function updateJob(jobId: string, updates: Partial<typeof jobs.$inferInsert>) { | ||
| try { | ||
| await db.update(jobs) | ||
| .set({ ...updates, updatedAt: new Date() }) | ||
| .where(eq(jobs.id, jobId)) | ||
| } catch (error) { | ||
| console.error('Error updating job:', error) | ||
| throw error | ||
| } | ||
| } | ||
Comment on lines
+34
to
+43
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win Type mismatch: Line 37 manually sets Prefer a dedicated update shape type or explicitly omit non-updatable fields: typeJobUpdate=Partial<Pick<typeofjobs.$inferInsert,'status'|'result'|'error'|'payload'>>exportasyncfunctionupdateJob(jobId: string,updates: JobUpdate){// ...}🤖 Prompt for AI Agents | ||
| export async function getJobStatus(jobId: string) { | ||
| 'use server' | ||
Comment on lines
+45
to
+46
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win Remove redundant Line 1 already declares 🤖 Prompt for AI Agents | ||
| const job = await getJob(jobId) | ||
| if (!job) return { status: 'not_found' } | ||
| return { | ||
| status: job.status, | ||
| result: job.result, | ||
| error: job.error | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -102,6 +102,18 @@ export const calendarNotes = pgTable('calendar_notes', { | ||
| updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), | ||
| }); | ||
| export const jobs = pgTable('jobs', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Suggested fix: add a migration that creates | ||
| id: uuid('id').primaryKey().defaultRandom(), | ||
| userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }), | ||
| type: text('type').notNull(), | ||
| status: text('status').notNull().default('pending'), | ||
| payload: jsonb('payload'), | ||
| result: jsonb('result'), | ||
| error: text('error'), | ||
| createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), | ||
| updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), | ||
| }); | ||
Comment on lines
+105
to
+115
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash# Check for Upstash Redis usage and migration to drop jobs table# Search for Upstash Redis imports/usage
rg -n '`@upstash/redis`' --type ts -C2
# Search for migration files that drop jobs table
fd -e sql -e ts 'migrat' --exec rg -l 'DROP TABLE.*jobs'# Verify if jobs table is referenced in queue handler
rg -n "db.*jobs|from.*jobs|jobs\\..*" app/api/queue/route.ts app/api/queues/ -A2Repository: QueueLab/QCX Length of output: 323 🏁 Script executed: #!/bin/bashset -euo pipefail
# 1) Upstash Redis usageecho"=== Upstash Redis imports/usage ==="
rg -n "`@upstash/redis`" --type ts --type js -S .||true# 2) Where migrations live, and whether any drop jobs table existsechoecho"=== Migration files mentioning jobs table drops ==="# Try common migration dirs/patterns without assuming structurefordirin ./.;do
fd -t f -e sql -e ts -e js -e mjs -e cjs -e kys -e drizzle -e knex . \
--hidden --exclude-dir node_modules --exclude-dir dist --exclude-dir build \
| rg -n "migrat|migration|drizzle|knex|sequelize|schema" -S -g'*.{sql,ts,js,mjs,cjs}' \
| head -n 2000 >/tmp/migration_candidates.txt ||trueif [ -s /tmp/migration_candidates.txt ];then
xargs -r rg -n --no-heading -S "DROP TABLE.*jobs|drop table.*jobs|DROP\\s+TABLE\\s+jobs|alter table.*jobs" \
< /tmp/migration_candidates.txt ||trueelseecho"No migration candidate files found by heuristic."fidone# 3) References to the jobs table/schemaechoecho"=== References to jobs schema/table ==="
rg -n "lib/db/schema|schema\\.ts|\\bjobs\\b" --type ts --type js -S lib app ||true# 4) Check queue handlers for job persistenceechoecho"=== Queue handler references to jobs/db ==="
rg -n "handleCallback|queue|jobs|updateJob|getJob|jobs\\." --type ts -S app lib ||true# 5) Show the exact schema.ts around the jobs definition for contextechoecho"=== lib/db/schema.ts around jobs definition ==="if [ -f"lib/db/schema.ts" ];then
nl -ba lib/db/schema.ts | sed -n '80,140p'elseecho"lib/db/schema.ts not found"fiRepository: QueueLab/QCX Length of output: 4933 🏁 Script executed: #!/bin/bashset -euo pipefail
echo"=== Search: `@upstash/redis` and upstash usage ==="
rg -n "`@upstash/redis`|`@upstash`|upstash" --type ts --type js --hidden -S .||trueecho
rg -n "UPSTASH|UPSTASH_REDIS|REDIS_URL|REDIS" --hidden -S .||trueechoecho"=== Search: queue + DB jobs persistence ==="
rg -n "handleCallback|`@vercel/queue`|enqueueJob|updateJob|getJob" --type ts --type js -S app lib ||trueechoecho"=== Migration directories/files discovery ==="# List likely migration-related files/dirs without assuming structure
rg -n "drizzle|migration" --hidden -S . --glob '!**/node_modules/**' --glob '!**/dist/**'||true# Find candidate SQL/TS migration files by name (heuristic)echoecho"=== Candidate migration files (by filename) ==="
find . -maxdepth 6 -type f \
\( -iname "*migration*" -o -iname "*migrations*" -o -iname "*drizzle*" -o -iname "*schema*" -o -iname "*ops*"\) \
\( -iname "*.sql" -o -iname "*.ts" -o -iname "*.js" -o -iname "*.mjs"\)2>/dev/null | head -n 200 ||trueechoecho"=== Search migrations for jobs table drop ==="# Search SQL/TS/JS for drop/alter statements on jobs (heuristic)
rg -n "DROP\\s+TABLE\\s+jobs|drop\\s+table\\s+jobs|ALTER\\s+TABLE\\s+jobs|alter\\s+table\\s+jobs" --hidden -S . \
--glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' --glob '!**/.next/**'||trueechoecho"=== Show lib/db/schema.ts jobs section (lines ~100-140) ==="if [ -f lib/db/schema.ts ];then
sed -n '95,145p' lib/db/schema.ts
elseecho"lib/db/schema.ts not found"fiRepository: QueueLab/QCX Length of output: 11180 🏁 Script executed: #!/bin/bashset -euo pipefail
echo"=== Directory listing: drizzle/* ==="
ls -la drizzle 2>/dev/null ||true
ls -la drizzle/migrations 2>/dev/null ||true
find drizzle -maxdepth 2 -type f 2>/dev/null | head -n 200 ||trueechoecho"=== Search drizzle/migrations for jobs table changes ==="
rg -n "DROP\\s+TABLE\\s+jobs|drop\\s+table\\s+jobs|ALTER\\s+TABLE\\s+jobs|alter\\s+table\\s+jobs|jobs\\b" -S drizzle/migrations 2>/dev/null ||trueechoecho"=== Inspect lib/actions/jobs.ts ==="if [ -f lib/actions/jobs.ts ];then
sed -n '1,120p' lib/actions/jobs.ts
elseecho"lib/actions/jobs.ts not found"fiechoecho"=== Inspect app/api/queue/route.ts ==="if [ -f app/api/queue/route.ts ];then
sed -n '1,120p' app/api/queue/route.ts
elseecho"app/api/queue/route.ts not found"fiechoecho"=== Status values used in queue handler code ==="
rg -n "updateJob\\(.*status\\s*:\\s*'|status\\s*:\\s*'|status:\\s*\"|set\\(\"status\"|processing|pending|completed|failed|error" -S app lib
echoecho"=== Queries filtering by jobs.userId or jobs.status ==="
rg -n "from\\(jobs\\)|\\.select\\(\\).*jobs|where\\(eq\\(jobs\\.(userId|status)" -S lib appRepository: QueueLab/QCX Length of output: 25508 🏁 Script executed: #!/bin/bashset -euo pipefail
echo"=== Show drizzle/migrations/*.sql files headers and any jobs-related statements ==="forfin drizzle/migrations/*.sql;doechoecho"--- $f ---"# Print lines around any mention of jobs
rg -n "\\bjobs\\b""$f" -n ||true# Print full file if small-ish, else only relevant ranges
lines=$(wc -l <"$f")echo"Lines: $lines"if [ "$lines"-le 200 ];then
cat "$f"else# best-effort: show sections near jobs keywords
rg -n -C3 "\\bjobs\\b""$f"||truefidoneechoecho"=== Search for DROP/ALTER jobs in the whole migrations folder ==="
rg -n "DROP\\s+TABLE\\s+jobs|drop\\s+table\\s+jobs|ALTER\\s+TABLE\\s+jobs|alter\\s+table\\s+jobs" drizzle/migrations ||trueechoecho"=== Check drizzle.config.ts and migrate.ts migrationsFolder setting ==="
sed -n '1,120p' drizzle.config.ts
sed -n '1,120p' lib/db/migrate.tsRepository: QueueLab/QCX Length of output: 5253 🏁 Script executed: #!/bin/bashset -euo pipefail
echo"=== Search for indexes/constraints on jobs in lib/db/schema.ts ==="
rg -n "index\\(|unique\\(|check\\(|constraint\\(|enum\\(" lib/db/schema.ts -S ||trueechoecho"=== Show jobs table block again with nearby lines (for context) ==="
sed -n '90,140p' lib/db/schema.tsRepository: QueueLab/QCX Length of output: 2259 Critical: Queue worker still persists into Postgres
🤖 Prompt for AI Agents | ||
| // Relations | ||
| export const usersRelations = relations(users, ({ many }) => ({ | ||
| chats: many(chats), | ||
| @@ -111,6 +123,14 @@ export const usersRelations = relations(users, ({ many }) => ({ | ||
| systemPrompts: many(systemPrompts), | ||
| locations: many(locations), | ||
| visualizations: many(visualizations), | ||
| jobs: many(jobs), | ||
| })); | ||
Comment on lines
+126
to
+127
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove relations for jobs table. Since the Also applies to: 129-134 🤖 Prompt for AI Agents | ||
| export const jobsRelations = relations(jobs, ({ one }) => ({ | ||
| user: one(users, { | ||
| fields: [jobs.userId], | ||
| references: [users.id], | ||
| }), | ||
| })); | ||
| export const chatsRelations = relations(chats, ({ one, many }) => ({ | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getCurrentUserIdOnServer() || 'anonymous'is unsafe here becausejobs.userIdis a UUID FK (lib/db/schema.ts), and'anonymous'is not a valid UUID/user reference. In unauthenticated sessions this causesenqueueJobto fail before the queue step, so report generation fails immediately.Suggested fix: require auth before enqueueing, or use a real persisted UUID-backed system user (not a string fallback).