@@ -100,6 +100,112 @@ export class ReferenceController {
100100 }
101101 } ;
102102
103+ /**
104+ * Maps reference status from DB format to display format.
105+ */
106+ private mapStatus ( status : string ) : string {
107+ switch ( status ?. toLowerCase ( ) ) {
108+ case "revoked" :
109+ return "Revoked" ;
110+ case "signed" :
111+ case "active" :
112+ return "Signed" ;
113+ case "pending" :
114+ return "Pending" ;
115+ default :
116+ return "Unknown" ;
117+ }
118+ }
119+
120+ /**
121+ * Combined references (sent + received) for the authenticated user with pagination.
122+ * Uses DB-level pagination to avoid loading all records into memory.
123+ */
124+ getAllUserReferences = async ( req : Request , res : Response ) => {
125+ try {
126+ const userId = req . user ! . id ;
127+ const MAX_LIMIT = 100 ;
128+ const MAX_FETCH = 500 ; // Maximum records to fetch for merging
129+
130+ // Sanitize and clamp inputs
131+ let page = parseInt ( req . query . page as string , 10 ) || 1 ;
132+ let limit = parseInt ( req . query . limit as string , 10 ) || 10 ;
133+ page = Math . max ( 1 , page ) ;
134+ limit = Math . min ( Math . max ( 1 , limit ) , MAX_LIMIT ) ;
135+ const offset = ( page - 1 ) * limit ;
136+
137+ // Calculate how many records we need to fetch to cover the requested page
138+ // We need at least (page * limit) records from each query to ensure we have enough
139+ // after merging, but cap it to avoid loading too much
140+ const recordsNeeded = page * limit ;
141+ const fetchLimit = Math . min ( Math . max ( recordsNeeded , limit * 2 ) , MAX_FETCH ) ;
142+
143+ // Fetch paginated results from both queries using DB-level pagination
144+ const sentResult = await this . referenceService . getUserReferencesPaginated ( userId , 1 , fetchLimit ) ;
145+ const receivedResult = await this . referenceService . getReferencesForTargetPaginated ( "user" , userId , 1 , fetchLimit , false ) ;
146+
147+ // Format and merge results
148+ const formatted = [
149+ ...sentResult . references . map ( ( ref ) => ( {
150+ id : ref . id ,
151+ type : "Sent" as const ,
152+ forFrom : ref . targetName ,
153+ targetType : ref . targetType ,
154+ targetName : ref . targetName ,
155+ referenceType : ref . referenceType ,
156+ numericScore : ref . numericScore ,
157+ content : ref . content ,
158+ status : this . mapStatus ( ref . status ) ,
159+ date : ref . createdAt ,
160+ } ) ) ,
161+ ...receivedResult . references . map ( ( ref ) => ( {
162+ id : ref . id ,
163+ type : "Received" as const ,
164+ forFrom : ref . author ?. name || ref . author ?. ename || "Unknown" ,
165+ targetType : ref . targetType ,
166+ targetName : ref . targetName ,
167+ referenceType : ref . referenceType ,
168+ numericScore : ref . numericScore ,
169+ content : ref . content ,
170+ status : this . mapStatus ( ref . status ) ,
171+ date : ref . createdAt ,
172+ author : {
173+ id : ref . author ?. id ,
174+ ename : ref . author ?. ename ,
175+ name : ref . author ?. name ,
176+ } ,
177+ } ) ) ,
178+ ] ;
179+
180+ // Sort newest first (already sorted by DB, but merge may need re-sort)
181+ formatted . sort (
182+ ( a , b ) => new Date ( b . date ) . getTime ( ) - new Date ( a . date ) . getTime ( ) ,
183+ ) ;
184+
185+ // Calculate total from both queries
186+ const total = sentResult . total + receivedResult . total ;
187+
188+ // Apply final pagination to merged results
189+ const totalPages = Math . max ( 1 , Math . ceil ( total / limit ) ) ;
190+ const paginated = formatted . slice ( offset , offset + limit ) ;
191+
192+ res . json ( {
193+ references : paginated ,
194+ pagination : {
195+ page,
196+ limit,
197+ total,
198+ totalPages,
199+ hasNext : page < totalPages ,
200+ hasPrev : page > 1 ,
201+ } ,
202+ } ) ;
203+ } catch ( error ) {
204+ console . error ( "Error getting user references:" , error ) ;
205+ res . status ( 500 ) . json ( { error : "Internal server error" } ) ;
206+ }
207+ } ;
208+
103209 getUserReferences = async ( req : Request , res : Response ) => {
104210 try {
105211 const userId = req . user ! . id ;
0 commit comments