1+ import { getServerPB } from '@/lib/pb' ;
2+ import { NextResponse } from 'next/server' ;
3+ import { ClientResponseError } from 'pocketbase' ;
4+
5+ // Define the structure for the response object
6+ interface JobStatusResponse {
7+ [ jobId : string ] : {
8+ status : string ;
9+ dateChanged : string | null ;
10+ durationChanged : number | null ; // Duration in milliseconds
11+ } ;
12+ }
13+
14+ export async function GET ( request : Request ) {
15+ try {
16+ // --- auth ---
17+ const authHeader = request . headers . get ( 'authorization' ) ;
18+ if ( ! authHeader ?. startsWith ( 'Bearer ' ) ) {
19+ return NextResponse . json ( { error : 'Unauthorized' } , { status : 401 } ) ;
20+ }
21+ const token = authHeader . split ( ' ' ) [ 1 ] ;
22+ const pb = getServerPB ( ) ;
23+ pb . authStore . save ( token , null ) ;
24+
25+ let authModel ;
26+ try {
27+ // Verify the token is valid and get the user record
28+ authModel = await pb . collection ( 'users' ) . authRefresh ( ) ;
29+ } catch ( err ) {
30+ if ( err instanceof ClientResponseError && err . status === 401 ) {
31+ // Token is invalid or expired
32+ return NextResponse . json ( { error : 'Unauthorized' } , { status : 401 } ) ;
33+ }
34+ // Re-throw other errors
35+ throw err ;
36+ }
37+
38+ const { searchParams } = new URL ( request . url ) ;
39+ const jobId = searchParams . get ( 'jobId' ) ;
40+
41+ let monitoringJobs ;
42+
43+ if ( jobId ) {
44+ monitoringJobs = await pb
45+ . collection ( 'monitoringJobs' )
46+ . getFullList ( {
47+ filter : `id = "${ jobId } " && userId = "${ authModel . record . id } "` ,
48+ } ) ;
49+
50+ // If jobId exists but doesn't belong to this user or doesn't exist → return empty
51+ if ( ! monitoringJobs || monitoringJobs . length === 0 ) {
52+ return NextResponse . json ( { } , { status : 200 } ) ;
53+ }
54+ } else {
55+ monitoringJobs = await pb
56+ . collection ( 'monitoringJobs' )
57+ . getFullList ( {
58+ filter : `userId = "${ authModel . record . id } "` ,
59+ } ) ;
60+ }
61+
62+ if ( ! monitoringJobs || monitoringJobs . length === 0 ) {
63+ return NextResponse . json ( { } , { status : 200 } ) ;
64+ }
65+
66+ // --- Process Jobs and Fetch Logs Iteratively ---
67+ const results : JobStatusResponse = { } ;
68+
69+ for ( const job of monitoringJobs ) {
70+
71+
72+ const jobLogsResponse = await pb
73+ . collection ( 'monitoringJobStatusLogs' )
74+ . getList ( 1 , 2 , {
75+ filter : `job = "${ job . id } " && job.userId = "${ authModel . record . id } "` ,
76+ sort : '-created'
77+ } ) ;
78+
79+
80+ const jobLogs = jobLogsResponse . items ; // this contains the 2 most recent logs
81+
82+ // Default values
83+ let latestStatus = job . status ; // Default to the job's own status (e.g., 'disabled')
84+ let dateChanged : string | null = null ;
85+ let durationChanged : number | null = null ;
86+
87+ if ( jobLogs && jobLogs . length > 0 ) {
88+ // We have at least one log.
89+ const latestLog = jobLogs [ 0 ] ;
90+
91+ latestStatus = latestLog . status ;
92+ dateChanged = latestLog . created ;
93+
94+
95+ if ( jobLogs . length > 1 ) {
96+ const secondLatestLog = jobLogs [ 1 ] ;
97+
98+ const latestDate = new Date ( latestLog . created ) ;
99+ const secondLatestDate = new Date ( secondLatestLog . created ) ;
100+
101+ durationChanged = ( latestDate . getTime ( ) - secondLatestDate . getTime ( ) ) / 1000 ; //in seconds
102+ }
103+
104+ }
105+
106+ // Add the computed data to our result object
107+ results [ job . source ] = {
108+ status : latestStatus ,
109+ dateChanged : dateChanged ,
110+ durationChanged : durationChanged ,
111+ } ;
112+ }
113+
114+ // Return the latest map
115+ return NextResponse . json ( results , { status : 200 } ) ;
116+
117+ } catch ( error ) {
118+ // enhanced logging so PocketBase error payload is visible
119+ console . error ( 'Error fetching monitoring job statuses:' , error ) ;
120+ try {
121+ // Log the full PocketBase error response if available
122+ console . error ( 'error.response:' , JSON . stringify ( ( error as any ) . response , Object . getOwnPropertyNames ( error ) ) ) ;
123+ console . error ( 'error.response?.data:' , JSON . stringify ( ( error as any ) . response ?. data ) ) ;
124+ } catch ( e ) {
125+ console . error ( 'failed to stringify pocketbase error details' , e ) ;
126+ }
127+ return NextResponse . json ( { error : 'Failed to fetch monitoring job statuses' } , { status : 500 } ) ;
128+ }
129+ }
0 commit comments