feat(p8): /v1/calendar REST + kernel-client methods + @insignia/iios-meeting-web

Task 8.6: CalendarController exposes schedule/request(+fromCallback/fromClaim)/
list/get/consent/transcript/summarize/action-items/providers(+sync) (session-auth).
kernel-client RestClient gains scheduleMeeting/requestMeeting/listMeetings/
getMeeting/setConsent/generateTranscript/summarizeMeeting/listActionItems/
connect+syncCalendarProvider + Meeting/MeetingParticipant/MeetingActionItem types.
New @insignia/iios-meeting-web (MeetingProvider + useMeetings/useMeeting/useSchedule/
useConsent/useSummarize/useActionItems). Boundary: meeting-web -> contracts +
kernel-client (fails on meeting-web->service).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 18:09:18 +05:30
parent 83369f2055
commit ca9498ce83
12 changed files with 366 additions and 1 deletions
@@ -0,0 +1,89 @@
import { BadRequestException, Body, Controller, Get, Headers, Param, Post, Query } from '@nestjs/common';
import { IiosConsentStatus, IiosMeetingType } from '@prisma/client';
import { CalendarService } from './calendar.service';
import { CalendarAdapter } from './calendar-adapter';
import { SessionVerifier } from '../platform/session.verifier';
import type { MessagePrincipal } from '../identity/actor.resolver';
import { ConsentDto, RequestMeetingDto, ScheduleMeetingDto } from './calendar.dto';
@Controller('v1/calendar')
export class CalendarController {
constructor(
private readonly calendar: CalendarService,
private readonly adapter: CalendarAdapter,
private readonly session: SessionVerifier,
) {}
@Post('meetings')
async schedule(@Body() body: ScheduleMeetingDto, @Headers('authorization') auth?: string) {
return this.calendar.scheduleDirect(this.principal(auth), { ...body, meetingType: body.meetingType as IiosMeetingType });
}
@Post('requests')
async request(
@Body() body: RequestMeetingDto,
@Query('fromCallback') fromCallback?: string,
@Query('fromClaim') fromClaim?: string,
@Headers('authorization') auth?: string,
) {
const principal = this.principal(auth);
if (fromCallback) return this.calendar.fromCallback(principal, fromCallback, body.requestedWindow);
if (fromClaim) return this.calendar.fromEventClaim(principal, fromClaim, body.requestedWindow);
return this.calendar.requestMeeting(principal, {
requestedWindow: body.requestedWindow,
meetingType: body.meetingType as IiosMeetingType | undefined,
interactionId: body.interactionId,
targetActorId: body.targetActorId,
});
}
@Get('meetings')
async list(@Headers('authorization') auth?: string) {
return this.calendar.listMeetings(this.principal(auth));
}
@Get('meetings/:id')
async get(@Param('id') id: string, @Headers('authorization') auth?: string) {
this.principal(auth);
return this.calendar.getMeeting(id);
}
@Post('meetings/:id/consent')
async consent(@Param('id') id: string, @Body() body: ConsentDto, @Headers('authorization') auth?: string) {
this.principal(auth);
return this.calendar.setConsent(id, body.actorRefId, body.status as IiosConsentStatus);
}
@Post('meetings/:id/transcript')
async transcript(@Param('id') id: string, @Headers('authorization') auth?: string) {
return this.calendar.generateTranscript(id, this.principal(auth));
}
@Post('meetings/:id/summarize')
async summarize(@Param('id') id: string, @Headers('authorization') auth?: string) {
return this.calendar.summarize(id, this.principal(auth));
}
@Get('meetings/:id/action-items')
async actionItems(@Param('id') id: string, @Headers('authorization') auth?: string) {
this.principal(auth);
return this.calendar.listActionItems(id);
}
@Post('providers')
async connect(@Headers('authorization') auth?: string) {
return this.adapter.connectProvider(this.principal(auth));
}
@Post('providers/:id/sync')
async sync(@Param('id') id: string, @Headers('authorization') auth?: string) {
this.principal(auth);
return this.adapter.syncOnce(id);
}
private principal(authorization?: string): MessagePrincipal {
const token = (authorization ?? '').replace(/^Bearer\s+/i, '');
if (!token) throw new BadRequestException('Authorization bearer token is required');
return this.session.verify(token);
}
}