Enable Container Insights on the seahaven-socket-mode ECS cluster and add the seahaven-socket-mode-running-tasks alarm (fires when running tasks < 1 against desiredCount=1). RunningTaskCount is only published with Container Insights enabled, which adds CloudWatch metric + log-ingestion cost. This is isolated in its own commit so it can be dropped if the cost/config change is declined — revert this commit (cluster containerInsightsV2 + enableRunningTaskAlarm flag) and the rest of the alarm coverage is unaffected.
137 lines
6.8 KiB
TypeScript
137 lines
6.8 KiB
TypeScript
import * as cdk from 'aws-cdk-lib';
|
|
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
import { Construct } from 'constructs';
|
|
import { ConversationLogConstruct } from './constructs/conversation-log';
|
|
import { KnowledgeBaseConstruct } from './constructs/knowledge-base';
|
|
import { BedrockAgentConstruct } from './constructs/bedrock-agent';
|
|
import { SlackHandlerConstruct } from './constructs/slack-handler';
|
|
import { SocketModeConstruct } from './constructs/socket-mode';
|
|
import { NotionSyncConstruct } from './constructs/notion-sync';
|
|
import { PoSyncConstruct } from './constructs/po-sync';
|
|
import { WorkorderSyncConstruct } from './constructs/workorder-sync';
|
|
import { MonitoringConstruct } from './constructs/monitoring';
|
|
|
|
export class SeahavenSlackBotStack extends cdk.Stack {
|
|
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
|
super(scope, id, props);
|
|
|
|
const wildcardCertArn = this.node.tryGetContext('wildcardCertArn') as string | undefined;
|
|
if (!wildcardCertArn || wildcardCertArn.includes('CHANGE-ME')) {
|
|
throw new Error(
|
|
'Set wildcardCertArn in cdk.json or pass --context wildcardCertArn=arn:aws:acm:...\n' +
|
|
'Run: aws acm list-certificates --region us-east-1 to find your cert ARN.',
|
|
);
|
|
}
|
|
|
|
// ── VPC (existing) — QBO Lambdas + Socket Mode run here ──────────────────
|
|
const vpc = ec2.Vpc.fromLookup(this, 'SeahavenVpc', { vpcId: 'vpc-0d3d4b67bd0cf8a68' });
|
|
|
|
const lambdaSecurityGroup = new ec2.SecurityGroup(this, 'QBOLambdaSG', {
|
|
vpc,
|
|
securityGroupName: 'seahaven-qbo-lambda',
|
|
description: 'QBO Lambdas - outbound HTTPS only',
|
|
allowAllOutbound: true,
|
|
});
|
|
|
|
// ── Conversation history + unanswered questions (DynamoDB) ────────────────
|
|
const conversationLog = new ConversationLogConstruct(this, 'ConversationLog');
|
|
|
|
// ── Bedrock Knowledge Base (OpenSearch Serverless + S3) ───────────────────
|
|
const knowledgeBase = new KnowledgeBaseConstruct(this, 'KnowledgeBase', {
|
|
accountId: this.account,
|
|
region: this.region,
|
|
});
|
|
|
|
// ── Bedrock Agent (Alex — Claude Sonnet + action groups) ─────────────────
|
|
const bedrockAgent = new BedrockAgentConstruct(this, 'BedrockAgent', {
|
|
accountId: this.account,
|
|
region: this.region,
|
|
knowledgeBaseId: knowledgeBase.knowledgeBase.knowledgeBaseId,
|
|
knowledgeBaseArn: knowledgeBase.knowledgeBase.knowledgeBaseArn,
|
|
vpc,
|
|
lambdaSecurityGroup,
|
|
});
|
|
|
|
// ── Notion → KB daily sync (EventBridge + Lambda) ────────────────────────
|
|
const notionSync = new NotionSyncConstruct(this, 'NotionSync', {
|
|
region: this.region,
|
|
kbDocsBucket: knowledgeBase.docsBucket,
|
|
knowledgeBaseId: knowledgeBase.knowledgeBase.knowledgeBaseId,
|
|
dataSourceId: knowledgeBase.dataSource.dataSourceId,
|
|
});
|
|
|
|
// ── Purchase Orders → KB daily sync (EventBridge + Lambda) ────────────────
|
|
const poSync = new PoSyncConstruct(this, 'PoSync', {
|
|
region: this.region,
|
|
kbDocsBucket: knowledgeBase.docsBucket,
|
|
knowledgeBaseId: knowledgeBase.knowledgeBase.knowledgeBaseId,
|
|
dataSourceId: knowledgeBase.dataSource.dataSourceId,
|
|
});
|
|
|
|
// ── Work Orders → KB daily sync (EventBridge + Lambda) ────────────────────
|
|
const workorderSync = new WorkorderSyncConstruct(this, 'WorkorderSync', {
|
|
region: this.region,
|
|
kbDocsBucket: knowledgeBase.docsBucket,
|
|
knowledgeBaseId: knowledgeBase.knowledgeBase.knowledgeBaseId,
|
|
dataSourceId: knowledgeBase.dataSource.dataSourceId,
|
|
});
|
|
|
|
// ── Slack handler (processor + app home + QBO OAuth + API Gateway) ────────
|
|
const slackHandler = new SlackHandlerConstruct(this, 'SlackHandler', {
|
|
accountId: this.account,
|
|
region: this.region,
|
|
agentId: bedrockAgent.agent.attrAgentId,
|
|
agentAliasId: bedrockAgent.agentAlias.attrAgentAliasId,
|
|
conversationTable: conversationLog.table,
|
|
unansweredTable: conversationLog.unansweredTable,
|
|
wildcardCertArn,
|
|
vpc,
|
|
lambdaSecurityGroup,
|
|
});
|
|
|
|
// ── Socket Mode (ECS Fargate — replaces webhook Lambda) ──────────────────
|
|
const socketMode = new SocketModeConstruct(this, 'SocketMode', {
|
|
vpc,
|
|
processorLambda: slackHandler.processorLambda,
|
|
appHomeLambda: slackHandler.appHomeLambda,
|
|
});
|
|
|
|
// ── CloudWatch alarm coverage → site-alerts SNS ───────────────────────────
|
|
new MonitoringConstruct(this, 'Monitoring', {
|
|
lambdas: [
|
|
{ name: 'slack-processor', fn: slackHandler.processorLambda, timeout: cdk.Duration.minutes(5) },
|
|
{ name: 'app-home', fn: slackHandler.appHomeLambda, timeout: cdk.Duration.seconds(10) },
|
|
{ name: 'qbo-oauth', fn: slackHandler.qboOAuthLambda, timeout: cdk.Duration.seconds(15) },
|
|
{ name: 'qbo-lookup', fn: bedrockAgent.qboLambda, timeout: cdk.Duration.seconds(30) },
|
|
{ name: 'maps-lookup', fn: bedrockAgent.mapsLambda, timeout: cdk.Duration.seconds(30) },
|
|
{ name: 'wo-po-lookup', fn: bedrockAgent.woPoLambda, timeout: cdk.Duration.seconds(30) },
|
|
{ name: 'po-sync', fn: poSync.syncLambda, timeout: cdk.Duration.minutes(15) },
|
|
{ name: 'workorder-sync', fn: workorderSync.syncLambda, timeout: cdk.Duration.minutes(5) },
|
|
{ name: 'notion-sync', fn: notionSync.syncLambda, timeout: cdk.Duration.minutes(5) },
|
|
],
|
|
tables: [
|
|
{ name: 'ddb-conversations', table: conversationLog.table },
|
|
{ name: 'ddb-unanswered-questions', table: conversationLog.unansweredTable },
|
|
],
|
|
httpApi: slackHandler.api,
|
|
ecsService: socketMode.service,
|
|
ecsCluster: socketMode.cluster,
|
|
// RunningTaskCount alarm requires Container Insights, enabled on the
|
|
// socket-mode cluster (see socket-mode.ts). This adds CloudWatch metric +
|
|
// log cost — flagged in the PR as "NEEDS ADAM SIGN-OFF (cost/config)".
|
|
// Drop this commit (and the cluster's containerInsightsV2 line) to decline.
|
|
enableRunningTaskAlarm: true,
|
|
});
|
|
|
|
// ── Stack outputs ─────────────────────────────────────────────────────────
|
|
new cdk.CfnOutput(this, 'KBDocsBucketName', {
|
|
value: knowledgeBase.docsBucket.bucketName,
|
|
description: 'Upload SA8000 docs, SOPs, and employee handbook here to populate the KB',
|
|
});
|
|
|
|
new cdk.CfnOutput(this, 'AgentId', {
|
|
value: bedrockAgent.agent.attrAgentId,
|
|
description: 'Bedrock Agent ID (Alex)',
|
|
});
|
|
}
|
|
}
|