feat: caller cache invalidation endpoint + worklist auth fix

- POST /api/caller/invalidate — clears Redis cache for a phone number
- WorklistController: resolves agent name from login cache (avoids currentUser query)
- AuthController: caches agent name in Redis during login (keyed by token suffix)
- WorklistModule: imports AuthModule (forwardRef for circular dep)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 12:14:56 +05:30
parent d0df6618b5
commit 1d1f27607f
4 changed files with 27 additions and 2 deletions

View File

@@ -166,6 +166,12 @@ export class AuthController {
}
}
// Cache agent name for worklist resolution (avoids re-querying currentUser with user JWT)
const agentFullName = `${workspaceMember?.name?.firstName ?? ''} ${workspaceMember?.name?.lastName ?? ''}`.trim();
if (agentFullName) {
await this.sessionService.setCache(`agent:name:${accessToken.slice(-16)}`, agentFullName, 86400);
}
return {
accessToken,
refreshToken: tokens.refreshToken.token,

View File

@@ -23,4 +23,14 @@ export class CallerResolutionController {
const result = await this.resolution.resolve(phone, auth);
return result;
}
@Post('invalidate')
async invalidate(@Body('phone') phone: string) {
if (!phone) {
throw new HttpException('phone is required', HttpStatus.BAD_REQUEST);
}
this.logger.log(`[RESOLVE] Invalidating cache for: ${phone}`);
await this.resolution.invalidate(phone);
return { status: 'ok' };
}
}

View File

@@ -2,6 +2,7 @@ import { Controller, Get, Patch, Headers, Param, Body, HttpException, Logger } f
import { PlatformGraphqlService } from '../platform/platform-graphql.service';
import { WorklistService } from './worklist.service';
import { MissedQueueService } from './missed-queue.service';
import { SessionService } from '../auth/session.service';
@Controller('api/worklist')
export class WorklistController {
@@ -11,6 +12,7 @@ export class WorklistController {
private readonly worklist: WorklistService,
private readonly missedQueue: MissedQueueService,
private readonly platform: PlatformGraphqlService,
private readonly session: SessionService,
) {}
@Get()
@@ -44,6 +46,12 @@ export class WorklistController {
}
private async resolveAgentName(authHeader: string): Promise<string> {
// Check cached name from login (avoids currentUser query that CC agents can't access)
const token = authHeader.replace(/^Bearer\s+/i, '');
const cached = await this.session.getCache(`agent:name:${token.slice(-16)}`);
if (cached) return cached;
// Fallback: try querying platform (works for admin/supervisor tokens)
try {
const data = await this.platform.queryWithAuth<any>(
`{ currentUser { workspaceMember { name { firstName lastName } } } }`,
@@ -54,7 +62,7 @@ export class WorklistController {
const full = `${name?.firstName ?? ''} ${name?.lastName ?? ''}`.trim();
if (full) return full;
} catch (err) {
this.logger.warn(`Failed to resolve agent name: ${err}`);
this.logger.warn(`Failed to resolve agent name via platform: ${err}`);
}
throw new HttpException('Could not determine agent identity', 400);
}

View File

@@ -1,6 +1,7 @@
import { Module, forwardRef } from '@nestjs/common';
import { PlatformModule } from '../platform/platform.module';
import { OzonetelAgentModule } from '../ozonetel/ozonetel-agent.module';
import { AuthModule } from '../auth/auth.module';
import { RulesEngineModule } from '../rules-engine/rules-engine.module';
import { WorklistController } from './worklist.controller';
import { WorklistService } from './worklist.service';
@@ -9,7 +10,7 @@ import { MissedCallWebhookController } from './missed-call-webhook.controller';
import { KookooCallbackController } from './kookoo-callback.controller';
@Module({
imports: [PlatformModule, forwardRef(() => OzonetelAgentModule), RulesEngineModule],
imports: [PlatformModule, forwardRef(() => OzonetelAgentModule), forwardRef(() => AuthModule), RulesEngineModule],
controllers: [WorklistController, MissedCallWebhookController, KookooCallbackController],
providers: [WorklistService, MissedQueueService],
exports: [MissedQueueService],