@@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config';
33import { Prisma } from '@prisma/client' ;
44import { SorobanService } from '../rpc/soroban.service' ;
55import { PrismaService } from '../prisma/prisma.service' ;
6+ import { PrismaReplicaService } from '../prisma/prisma-replica.service' ;
67import { RedisService } from '../cache/redis.service' ;
78import { TenantContextService } from '../tenant/tenant-context.service' ;
89import { claimTenantWhere , assertTenantOwnership } from '../tenant/tenant-filter.helper' ;
@@ -34,6 +35,7 @@ export class ClaimsService {
3435
3536 constructor (
3637 private readonly prisma : PrismaService ,
38+ private readonly prismaReplica : PrismaReplicaService ,
3739 private readonly redis : RedisService ,
3840 private readonly claimViewMapper : ClaimViewMapper ,
3941 private readonly config : ConfigService ,
@@ -47,6 +49,11 @@ export class ClaimsService {
4749 this . indexerNetwork = this . config . get < string > ( 'STELLAR_NETWORK' , 'testnet' ) ;
4850 }
4951
52+ /** Get the appropriate client for reads — replica if enabled, otherwise primary. */
53+ private getReadClient ( ) {
54+ return this . prismaReplica . isEnabled ( ) ? this . prismaReplica : this . prisma ;
55+ }
56+
5057 async listClaims ( params : ListClaimsParams ) : Promise < ClaimsListResponseDto > {
5158 const { after, status } = params ;
5259 const limit = clampLimit ( params . limit ) ;
@@ -66,16 +73,18 @@ export class ClaimsService {
6673 ...( keysetWhere ?? { } ) ,
6774 } ) ;
6875
76+ const readClient = this . getReadClient ( ) ;
6977 const [ claims , total ] = await Promise . all ( [
70- this . prisma . claim . findMany ( {
78+ readClient . claim . findMany ( {
7179 where,
7280 include : {
7381 votes : { where : { deletedAt : null } , select : { vote : true } } ,
82+ evidenceMetadata : true ,
7483 } ,
7584 orderBy : [ { createdAt : 'desc' } , { id : 'desc' } ] ,
7685 take : limit ,
7786 } ) ,
78- this . prisma . claim . count ( { where : claimTenantWhere ( tenantId , statusFilter ) } ) ,
87+ readClient . claim . count ( { where : claimTenantWhere ( tenantId , statusFilter ) } ) ,
7988 ] ) ;
8089
8190 return {
@@ -106,7 +115,8 @@ export class ClaimsService {
106115 const tenantId = this . tenantCtx . tenantId ;
107116 const lastLedger = await this . getLastLedger ( ) ;
108117
109- const votedClaimIds = await this . prisma . vote . findMany ( {
118+ const readClient = this . getReadClient ( ) ;
119+ const votedClaimIds = await readClient . vote . findMany ( {
110120 where : { voterAddress : walletAddress . toLowerCase ( ) , deletedAt : null } ,
111121 select : { claimId : true } ,
112122 } ) ;
@@ -119,11 +129,12 @@ export class ClaimsService {
119129 } ) ;
120130
121131 const [ allOpen , page ] = await Promise . all ( [
122- this . prisma . claim . count ( { where : baseWhere } ) ,
123- this . prisma . claim . findMany ( {
132+ readClient . claim . count ( { where : baseWhere } ) ,
133+ readClient . claim . findMany ( {
124134 where : { ...baseWhere , ...( keysetWhere ?? { } ) } ,
125135 include : {
126136 votes : { where : { deletedAt : null } , select : { vote : true } } ,
137+ evidenceMetadata : true ,
127138 } ,
128139 orderBy : [ { createdAt : 'desc' } , { id : 'desc' } ] ,
129140 take : limit ,
@@ -163,13 +174,15 @@ export class ClaimsService {
163174 }
164175
165176 const lastLedger = await this . getLastLedger ( ) ;
166- const claim = await this . prisma . claim . findFirst ( {
177+ const readClient = this . getReadClient ( ) ;
178+ const claim = await readClient . claim . findFirst ( {
167179 where : claimTenantWhere ( tenantId , { id } ) ,
168180 include : {
169181 votes : {
170182 where : { deletedAt : null } ,
171183 select : { vote : true } ,
172184 } ,
185+ evidenceMetadata : true ,
173186 } ,
174187 } ) ;
175188
@@ -214,7 +227,8 @@ export class ClaimsService {
214227 }
215228
216229 const lastLedger = await this . getLastLedger ( ) ;
217- const claims = await this . prisma . claim . findMany ( {
230+ const readClient = this . getReadClient ( ) ;
231+ const claims = await readClient . claim . findMany ( {
218232 where : claimTenantWhere ( tenantId , {
219233 policyId : { in : uniquePolicyIds } ,
220234 } ) ,
@@ -223,6 +237,7 @@ export class ClaimsService {
223237 where : { deletedAt : null } ,
224238 select : { vote : true } ,
225239 } ,
240+ evidenceMetadata : true ,
226241 } ,
227242 orderBy : [ { createdAt : 'desc' } , { id : 'desc' } ] ,
228243 } ) ;
@@ -303,6 +318,45 @@ export class ClaimsService {
303318 this . logger . log ( `Cache invalidated for claim ${ claimId || 'all' } ` ) ;
304319 }
305320
321+ /**
322+ * Store evidence metadata for a claim
323+ */
324+ async storeEvidenceMetadata (
325+ claimId : number ,
326+ metadata : { cid ?: string ; url ?: string ; fileSizeBytes ?: number ; mimeType ?: string }
327+ ) : Promise < void > {
328+ const tenantId = this . tenantCtx . tenantId ;
329+
330+ // Verify claim exists and belongs to tenant
331+ const claim = await this . prisma . claim . findFirst ( {
332+ where : claimTenantWhere ( tenantId , { id : claimId } ) ,
333+ select : { id : true } ,
334+ } ) ;
335+
336+ if ( ! claim ) {
337+ throw new NotFoundException ( `Claim with ID ${ claimId } not found` ) ;
338+ }
339+
340+ await this . prisma . evidenceMetadata . upsert ( {
341+ where : { claimId } ,
342+ create : {
343+ claimId,
344+ cid : metadata . cid ,
345+ url : metadata . url ,
346+ fileSizeBytes : metadata . fileSizeBytes ,
347+ mimeType : metadata . mimeType ,
348+ } ,
349+ update : {
350+ cid : metadata . cid ,
351+ url : metadata . url ,
352+ fileSizeBytes : metadata . fileSizeBytes ,
353+ mimeType : metadata . mimeType ,
354+ } ,
355+ } ) ;
356+
357+ await this . invalidateCache ( claimId ) ;
358+ }
359+
306360 /**
307361 * Build an unsigned file_claim transaction
308362 */
0 commit comments