11import type { Request , Response } from 'express'
22import { z } from 'zod'
33
4+ import { listPackagesForApi } from '@crowd/data-access-layer'
5+
6+ import { getPackagesQx } from '@/db/packagesDb'
47import { ok } from '@/utils/api'
58import { validateOrThrow } from '@/utils/validation'
69
7- import { MOCK_DETAILS , MOCK_PACKAGES } from './mockData '
10+ import type { StewardshipStatus } from './types '
811
912const DEFAULT_PAGE_SIZE = 20
1013const MAX_PAGE_SIZE = 100
11- const STALE_MONTHS = 18
1214
1315const booleanQueryParam = z . preprocess ( ( v ) => v === 'true' , z . boolean ( ) ) . default ( false )
1416
@@ -18,15 +20,14 @@ const querySchema = z.object({
1820 page : z . coerce . number ( ) . int ( ) . min ( 1 ) . default ( 1 ) ,
1921 pageSize : z . coerce . number ( ) . int ( ) . min ( 1 ) . max ( MAX_PAGE_SIZE ) . default ( DEFAULT_PAGE_SIZE ) ,
2022 ecosystem : z . string ( ) . trim ( ) . optional ( ) ,
21- lifecycle : z . enum ( lifecycleValues ) . optional ( ) ,
22- busFactor1Only : booleanQueryParam ,
23+ lifecycle : z . enum ( lifecycleValues ) . optional ( ) , // TODO: filter not yet implemented in DAL
24+ busFactor1Only : booleanQueryParam , // TODO: filter not yet implemented in DAL
2325 staleOnly : booleanQueryParam ,
2426 unstewardedOnly : booleanQueryParam ,
2527 sortBy : z . enum ( [ 'name' , 'health' , 'impact' , 'openVulns' ] ) . default ( 'name' ) ,
2628 sortDir : z . enum ( [ 'asc' , 'desc' ] ) . default ( 'asc' ) ,
2729} )
2830
29- // TODO: replace with real DB queries once packages DB is wired into the backend
3031export async function listPackages ( req : Request , res : Response ) : Promise < void > {
3132 const {
3233 page,
@@ -40,40 +41,32 @@ export async function listPackages(req: Request, res: Response): Promise<void> {
4041 sortDir,
4142 } = validateOrThrow ( querySchema , req . query )
4243
43- const staleThreshold = new Date ( )
44- staleThreshold . setMonth ( staleThreshold . getMonth ( ) - STALE_MONTHS )
45-
46- let filtered = MOCK_PACKAGES . filter ( ( p ) => {
47- if ( ecosystem && p . ecosystem !== ecosystem ) return false
48- if ( lifecycle && p . lifecycle !== lifecycle ) return false
49- if ( busFactor1Only && p . maintainerBusFactor !== 1 ) return false
50- if ( unstewardedOnly && p . stewardship !== null && p . stewardship !== 'unassigned' ) return false
51- if ( staleOnly ) {
52- const lastRelease = MOCK_DETAILS [ p . purl ] ?. general . riskSignals . lastRelease
53- if ( ! lastRelease || new Date ( lastRelease ) >= staleThreshold ) return false
54- }
55- return true
56- } )
44+ // health is a v2 field with no backing column yet — fall back to name sort
45+ const effectiveSortBy = sortBy === 'health' ? 'name' : sortBy
5746
58- filtered = filtered . sort ( ( a , b ) => {
59- let cmp = 0
60- if ( sortBy === 'name' ) {
61- cmp = a . name . localeCompare ( b . name )
62- } else if ( sortBy === 'health' ) {
63- cmp = ( a . health ?? 0 ) - ( b . health ?? 0 )
64- } else if ( sortBy === 'impact' ) {
65- cmp = ( a . impact ?? 0 ) - ( b . impact ?? 0 )
66- } else if ( sortBy === 'openVulns' ) {
67- const sumA = a . openVulns . low + a . openVulns . medium + a . openVulns . high + a . openVulns . critical
68- const sumB = b . openVulns . low + b . openVulns . medium + b . openVulns . high + b . openVulns . critical
69- cmp = sumA - sumB
70- }
71- return sortDir === 'desc' ? - cmp : cmp
47+ const qx = await getPackagesQx ( )
48+ const { rows, total } = await listPackagesForApi ( qx , {
49+ page,
50+ pageSize,
51+ ecosystem,
52+ staleOnly,
53+ unstewardedOnly,
54+ sortBy : effectiveSortBy ,
55+ sortDir,
7256 } )
7357
74- const total = filtered . length
75- const start = ( page - 1 ) * pageSize
76- const packages = filtered . slice ( start , start + pageSize )
58+ const packages = rows . map ( ( r ) => ( {
59+ purl : r . purl ,
60+ name : r . name ,
61+ ecosystem : r . ecosystem ,
62+ health : null ,
63+ impact : r . criticalityScore != null ? Math . round ( Number ( r . criticalityScore ) * 100 ) : null ,
64+ lifecycle : null ,
65+ maintainerBusFactor : r . maintainerCount ,
66+ openVulns : r . openVulns ,
67+ stewardship : ( r . stewardshipStatus ?? 'unassigned' ) as StewardshipStatus ,
68+ stewards : null ,
69+ } ) )
7770
7871 ok ( res , {
7972 page,
@@ -86,7 +79,7 @@ export async function listPackages(req: Request, res: Response): Promise<void> {
8679 staleOnly,
8780 unstewardedOnly,
8881 } ,
89- sort : { by : sortBy , dir : sortDir } ,
82+ sort : { by : effectiveSortBy , dir : sortDir } ,
9083 packages,
9184 } )
9285}
0 commit comments