11export const fetchCache = 'force-no-store' ;
2- export const maxDuration = 60 ;
2+ export const maxDuration = 300 ;
33
44import { type NextRequest } from 'next/server' ;
5+ import { after } from 'next/server' ;
56import { createClient , type SanityClient } from 'next-sanity' ;
67import { apiVersion , dataset , projectId } from '@/sanity/lib/api' ;
78import { NotebookLMClient } from '@/lib/services/notebooklm/client' ;
@@ -43,7 +44,7 @@ interface ResearchingDoc {
4344 } > ;
4445 cta : string ;
4546 } ;
46- _createdAt : string ;
47+ _updatedAt : string ;
4748}
4849
4950interface EnrichedScript {
@@ -402,7 +403,7 @@ function buildResearchPayload(
402403 return {
403404 topic : doc . title ,
404405 notebookId : doc . researchNotebookId ,
405- createdAt : doc . _createdAt ,
406+ createdAt : doc . _updatedAt ,
406407 completedAt : new Date ( ) . toISOString ( ) ,
407408 sources : sources . map ( ( s ) => ( {
408409 title : s . title ,
@@ -600,16 +601,21 @@ async function processResearchingDoc(
600601// ---------------------------------------------------------------------------
601602
602603export async function GET ( request : NextRequest ) {
604+ const cronSecret = process . env . CRON_SECRET ;
605+ if ( ! cronSecret ) {
606+ console . error ( '[check-research] CRON_SECRET not configured' ) ;
607+ return Response . json ( { error : 'Server misconfigured' } , { status : 503 } ) ;
608+ }
603609 const authHeader = request . headers . get ( 'authorization' ) ;
604- if ( authHeader !== `Bearer ${ process . env . CRON_SECRET } ` ) {
610+ if ( authHeader !== `Bearer ${ cronSecret } ` ) {
605611 console . error ( '[check-research] Unauthorized request: invalid authorization header' ) ;
606612 return Response . json ( { error : 'Unauthorized' } , { status : 401 } ) ;
607613 }
608614
609615 try {
610616 const sanity = getSanityWriteClient ( ) ;
611617
612- // Query for docs in " researching" status with a notebook ID
618+ // Phase 1: Query all researching docs
613619 const docs = await sanity . fetch < ResearchingDoc [ ] > (
614620 `*[_type == "automatedVideo" && status == "researching" && defined(researchNotebookId)] {
615621 _id,
@@ -620,7 +626,7 @@ export async function GET(request: NextRequest) {
620626 trendScore,
621627 trendSources,
622628 script,
623- _createdAt
629+ _updatedAt
624630 }` ,
625631 ) ;
626632
@@ -630,17 +636,13 @@ export async function GET(request: NextRequest) {
630636 return Response . json ( { success : true , message : 'No docs to process' , processed : 0 } ) ;
631637 }
632638
633- // Initialize NotebookLM client
634- console . log ( '[check-research] Initializing NotebookLM client...' ) ;
635- const auth = await initAuth ( ) ;
636- const nbClient = new NotebookLMClient ( auth ) ;
637-
638- const results : ProcessResult [ ] = [ ] ;
639+ // Phase 2: Separate stuck from active (no NotebookLM needed)
640+ const stuckResults : ProcessResult [ ] = [ ] ;
641+ const activeDocs : ResearchingDoc [ ] = [ ] ;
639642 const now = Date . now ( ) ;
640643
641644 for ( const doc of docs ) {
642- // Stuck detection: flag docs stuck in "researching" for >30 minutes
643- const docAge = now - new Date ( doc . _createdAt ) . getTime ( ) ;
645+ const docAge = now - new Date ( doc . _updatedAt ) . getTime ( ) ;
644646 if ( docAge > STUCK_THRESHOLD_MS ) {
645647 console . warn (
646648 `[check-research] Doc ${ doc . _id } ("${ doc . title } ") stuck in "researching" for ${ Math . round ( docAge / 60_000 ) } min — flagging` ,
@@ -652,31 +654,36 @@ export async function GET(request: NextRequest) {
652654 flaggedReason : `Stuck in "researching" for ${ Math . round ( docAge / 60_000 ) } minutes (threshold: ${ STUCK_THRESHOLD_MS / 60_000 } min). Research may have failed.` ,
653655 } )
654656 . commit ( ) ;
655- results . push ( { id : doc . _id , title : doc . title , status : 'flagged' , error : 'Stuck in researching' } ) ;
656- continue ;
657+ stuckResults . push ( { id : doc . _id , title : doc . title , status : 'flagged' , error : 'Stuck in researching' } ) ;
658+ } else {
659+ activeDocs . push ( doc ) ;
657660 }
661+ }
658662
659- try {
660- const result = await processResearchingDoc ( doc , nbClient , sanity ) ;
661- results . push ( result ) ;
662- } catch ( err ) {
663- console . error ( `[check-research] Error processing doc ${ doc . _id } :` , err ) ;
664- results . push ( {
665- id : doc . _id ,
666- title : doc . title ,
667- status : 'error' ,
668- error : err instanceof Error ? err . message : String ( err ) ,
663+ // Phase 3: Process active docs (needs NotebookLM)
664+ if ( activeDocs . length > 0 ) {
665+ console . log ( '[check-research] Initializing NotebookLM client...' ) ;
666+ const auth = await initAuth ( ) ;
667+ const nbClient = new NotebookLMClient ( auth ) ;
668+
669+ for ( const doc of activeDocs ) {
670+ after ( async ( ) => {
671+ try {
672+ await processResearchingDoc ( doc , nbClient , sanity ) ;
673+ } catch ( err ) {
674+ console . error ( `[check-research] after() error for ${ doc . _id } :` , err ) ;
675+ try {
676+ await sanity . patch ( doc . _id ) . set ( {
677+ status : 'flagged' ,
678+ flaggedReason : `Research processing error: ${ err instanceof Error ? err . message : String ( err ) } ` ,
679+ } ) . commit ( ) ;
680+ } catch { /* best-effort */ }
681+ }
669682 } ) ;
670683 }
671684 }
672685
673- console . log ( '[check-research] Done!' , JSON . stringify ( results ) ) ;
674-
675- return Response . json ( {
676- success : true ,
677- processed : results . length ,
678- results,
679- } ) ;
686+ return Response . json ( { success : true , processing : activeDocs . length , flagged : stuckResults . length } ) ;
680687 } catch ( err ) {
681688 console . error ( '[check-research] Unexpected error:' , err ) ;
682689 return Response . json (
0 commit comments