1- // @ts -nocheck
2-
3- import { Injectable , NotFoundException } from '@nestjs/common' ;
1+ import { Injectable , Logger , NotFoundException } from '@nestjs/common' ;
42import { PrismaService } from '../database/prisma.service' ;
53import { BackupService } from '../backup/backup.service' ;
64import { UpdateBackupScheduleDto } from '../backup/dto/backup.dto' ;
@@ -22,6 +20,8 @@ import { FraudService } from '../fraud/fraud.service';
2220import { SessionsService } from '../sessions/sessions.service' ;
2321import { TransactionsService } from '../transactions/transactions.service' ;
2422
23+ const logger = new Logger ( 'AdminService' ) ;
24+
2525@Injectable ( )
2626export class AdminService {
2727 constructor (
@@ -109,9 +109,11 @@ export class AdminService {
109109 async listUsers ( query : AdminUsersQueryDto ) {
110110 const page = query . page ?? 1 ;
111111 const limit = query . limit ?? 20 ;
112- const skip = ( page - 1 ) * limit ;
112+ const skip = ( query as any ) . cursor
113+ ? undefined
114+ : ( page - 1 ) * limit ;
113115
114- const where = {
116+ const where : any = {
115117 role : query . role ,
116118 OR : query . search
117119 ? [
@@ -122,10 +124,14 @@ export class AdminService {
122124 : undefined ,
123125 } ;
124126
127+ if ( ( query as any ) . cursor ) {
128+ where . createdAt = { lt : new Date ( Buffer . from ( ( query as any ) . cursor , 'base64' ) . toString ( ) ) } ;
129+ }
130+
125131 const [ items , total ] = await Promise . all ( [
126132 this . prisma . user . findMany ( {
127133 where,
128- skip,
134+ ... ( skip !== undefined ? { skip } : { } ) ,
129135 take : limit ,
130136 orderBy : { createdAt : 'desc' } ,
131137 select : {
@@ -142,13 +148,17 @@ export class AdminService {
142148 this . prisma . user . count ( { where } ) ,
143149 ] ) ;
144150
145- return { total, page, limit, items } ;
151+ const nextCursor = items . length === limit
152+ ? Buffer . from ( ( items [ items . length - 1 ] as any ) . createdAt . toISOString ( ) ) . toString ( 'base64' )
153+ : null ;
154+
155+ return { total, page, limit, items, nextCursor, previousCursor : ( query as any ) . cursor || null } ;
146156 }
147157
148- async updateUser ( userId : string , payload : AdminUpdateUserDto ) {
158+ async updateUser ( userId : string , payload : AdminUpdateUserDto , actorId ?: string ) {
149159 const existingUser = await this . prisma . user . findUnique ( {
150160 where : { id : userId } ,
151- select : { role : true } ,
161+ select : { role : true , email : true } ,
152162 } ) ;
153163
154164 if ( ! existingUser ) {
@@ -171,10 +181,48 @@ export class AdminService {
171181 } ,
172182 } ) ;
173183
184+ // Audit log for role changes (#886)
174185 if ( payload . role && payload . role !== existingUser . role ) {
186+ await this . prisma . activityLog
187+ . create ( {
188+ data : {
189+ userId : actorId ?? 'system' ,
190+ action : 'ROLE_CHANGE' ,
191+ entityType : 'USER' ,
192+ entityId : userId ,
193+ description : `Role changed from ${ existingUser . role } to ${ payload . role } for user ${ existingUser . email } ` ,
194+ metadata : {
195+ previousRole : existingUser . role ,
196+ newRole : payload . role ,
197+ targetUserId : userId ,
198+ } ,
199+ } ,
200+ } )
201+ . catch ( ( err : unknown ) => {
202+ logger . error ( `Failed to audit-log role change for ${ userId } : ${ err } ` ) ;
203+ } ) ;
204+
175205 await this . sessionsService . revokeAllSessions ( userId ) ;
176206 }
177207
208+ // Audit log for block state changes
209+ if ( payload . isBlocked !== undefined ) {
210+ await this . prisma . activityLog
211+ . create ( {
212+ data : {
213+ userId : actorId ?? 'system' ,
214+ action : payload . isBlocked ? 'USER_BLOCKED' : 'USER_UNBLOCKED' ,
215+ entityType : 'USER' ,
216+ entityId : userId ,
217+ description : `User ${ existingUser . email } ${ payload . isBlocked ? 'blocked' : 'unblocked' } ` ,
218+ metadata : { targetUserId : userId , isBlocked : payload . isBlocked } ,
219+ } ,
220+ } )
221+ . catch ( ( err : unknown ) => {
222+ logger . error ( `Failed to audit-log block state for ${ userId } : ${ err } ` ) ;
223+ } ) ;
224+ }
225+
178226 return updatedUser ;
179227 }
180228
@@ -193,16 +241,22 @@ export class AdminService {
193241 async getModerationQueue ( query : ModerationQueueQueryDto ) {
194242 const page = query . page ?? 1 ;
195243 const limit = query . limit ?? 20 ;
196- const skip = ( page - 1 ) * limit ;
244+ const skip = ( query as any ) . cursor
245+ ? undefined
246+ : ( page - 1 ) * limit ;
197247
198- const where = {
248+ const where : any = {
199249 status : query . status ?? PropertyStatus . PENDING ,
200250 } ;
201251
252+ if ( ( query as any ) . cursor ) {
253+ where . createdAt = { lt : new Date ( Buffer . from ( ( query as any ) . cursor , 'base64' ) . toString ( ) ) } ;
254+ }
255+
202256 const [ items , total ] = await Promise . all ( [
203257 this . prisma . property . findMany ( {
204258 where,
205- skip,
259+ ... ( skip !== undefined ? { skip } : { } ) ,
206260 take : limit ,
207261 orderBy : { createdAt : 'asc' } ,
208262 include : {
@@ -219,7 +273,11 @@ export class AdminService {
219273 this . prisma . property . count ( { where } ) ,
220274 ] ) ;
221275
222- return { total, page, limit, items } ;
276+ const nextCursor = items . length === limit
277+ ? Buffer . from ( ( items [ items . length - 1 ] as any ) . createdAt . toISOString ( ) ) . toString ( 'base64' )
278+ : null ;
279+
280+ return { total, page, limit, items, nextCursor, previousCursor : ( query as any ) . cursor || null } ;
223281 }
224282
225283 async approveProperty ( propertyId : string ) {
@@ -292,18 +350,24 @@ export class AdminService {
292350 async monitorTransactions ( query : TransactionMonitoringQueryDto ) {
293351 const page = query . page ?? 1 ;
294352 const limit = query . limit ?? 20 ;
295- const skip = ( page - 1 ) * limit ;
353+ const skip = ( query as any ) . cursor
354+ ? undefined
355+ : ( page - 1 ) * limit ;
296356
297- const where = {
357+ const where : any = {
298358 status : query . status ,
299359 type : query . type ,
300360 propertyId : query . propertyId ,
301361 } ;
302362
363+ if ( ( query as any ) . cursor ) {
364+ where . createdAt = { lt : new Date ( Buffer . from ( ( query as any ) . cursor , 'base64' ) . toString ( ) ) } ;
365+ }
366+
303367 const [ items , total ] = await Promise . all ( [
304368 this . prisma . transaction . findMany ( {
305369 where,
306- skip,
370+ ... ( skip !== undefined ? { skip } : { } ) ,
307371 take : limit ,
308372 orderBy : { createdAt : 'desc' } ,
309373 include : {
@@ -321,7 +385,11 @@ export class AdminService {
321385 this . prisma . transaction . count ( { where } ) ,
322386 ] ) ;
323387
324- return { total, page, limit, items } ;
388+ const nextCursor = items . length === limit
389+ ? Buffer . from ( ( items [ items . length - 1 ] as any ) . createdAt . toISOString ( ) ) . toString ( 'base64' )
390+ : null ;
391+
392+ return { total, page, limit, items, nextCursor, previousCursor : ( query as any ) . cursor || null } ;
325393 }
326394
327395 async transactionMonitoringSummary ( ) {
0 commit comments