import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs'; import * as logs from 'aws-cdk-lib/aws-logs'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; import * as events from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as path from 'path'; export interface NotionSyncProps { region: string; kbDocsBucket: s3.Bucket; knowledgeBaseId: string; dataSourceId: string; } export class NotionSyncConstruct extends Construct { public readonly syncLambda: lambdaNodejs.NodejsFunction; public readonly notionSecret: secretsmanager.Secret; constructor(scope: Construct, id: string, props: NotionSyncProps) { super(scope, id); // Notion integration token — placeholder created here, filled in post-deploy. // Update via: AWS Console → Secrets Manager → seahaven/notion/api-key → Retrieve and edit this.notionSecret = new secretsmanager.Secret(this, 'NotionSecret', { secretName: 'seahaven/notion/api-key', description: 'Notion integration token for the Office Operations teamspace KB sync', secretObjectValue: { apiKey: cdk.SecretValue.unsafePlainText('REPLACE_ME_after_deploy'), }, }); // Lambda — fetches Notion pages, uploads markdown to S3, triggers KB ingestion this.syncLambda = new lambdaNodejs.NodejsFunction(this, 'SyncLambda', { functionName: 'seahaven-notion-sync', entry: path.join(__dirname, '../../lambda/notion-sync/index.ts'), runtime: lambda.Runtime.NODEJS_24_X, architecture: lambda.Architecture.ARM_64, logRetention: logs.RetentionDays.TWO_MONTHS, memorySize: 512, timeout: cdk.Duration.minutes(5), environment: { NOTION_SECRET_ARN: this.notionSecret.secretArn, KB_BUCKET_NAME: props.kbDocsBucket.bucketName, KNOWLEDGE_BASE_ID: props.knowledgeBaseId, DATA_SOURCE_ID: props.dataSourceId, REGION: props.region, }, }); // Allow Lambda to read the Notion secret this.notionSecret.grantRead(this.syncLambda); // Allow Lambda to read existing notion/ objects and write new ones props.kbDocsBucket.grantReadWrite(this.syncLambda); // Allow Lambda to start a Bedrock KB ingestion job this.syncLambda.addToRolePolicy( new iam.PolicyStatement({ actions: ['bedrock:StartIngestionJob'], resources: [ `arn:aws:bedrock:${props.region}:*:knowledge-base/${props.knowledgeBaseId}`, ], }), ); // EventBridge rule — fires daily at 02:00 UTC const dailyRule = new events.Rule(this, 'DailySyncRule', { ruleName: 'seahaven-notion-daily-sync', description: 'Daily Notion → KB sync at 02:00 UTC', schedule: events.Schedule.cron({ minute: '0', hour: '2' }), }); dailyRule.addTarget(new targets.LambdaFunction(this.syncLambda)); // Surface the secret name so operators know where to put the token new cdk.CfnOutput(scope, 'NotionSecretName', { value: this.notionSecret.secretName, description: 'Set your Notion integration token here after deploy (apiKey field)', }); } }