diff --git a/.gitignore b/.gitignore index 33ca726..fc2ead2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ yarn-error.log # CDK asset staging directory .cdk.staging cdk.out +n8n.yaml diff --git a/bin/n8n.ts b/bin/n8n.ts index 93382dd..3d6d41a 100644 --- a/bin/n8n.ts +++ b/bin/n8n.ts @@ -4,4 +4,4 @@ import * as cdk from 'aws-cdk-lib' import { N8NStack } from '../lib/n8n-stack' const app = new cdk.App() -new N8NStack(app, 'N8N') +new N8NStack(app, 'N8N', {}) diff --git a/cdk.context.json b/cdk.context.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/cdk.context.json @@ -0,0 +1 @@ +{} diff --git a/cdk.json b/cdk.json index 2874cce..913f07b 100644 --- a/cdk.json +++ b/cdk.json @@ -1,5 +1,6 @@ { "app": "npx ts-node --prefer-ts-exts bin/n8n.ts", + "versionReporting": false, "watch": { "include": [ "**" diff --git a/lib/n8n-stack.ts b/lib/n8n-stack.ts index 80f5f4f..fc23f3b 100644 --- a/lib/n8n-stack.ts +++ b/lib/n8n-stack.ts @@ -1,112 +1,400 @@ +import { CfnParameter, Duration, RemovalPolicy, Stack } from 'aws-cdk-lib' import { - aws_ec2, aws_ecs, aws_iam, aws_logs, aws_rds, - RemovalPolicy, Stack -} from 'aws-cdk-lib' + Certificate, + CertificateValidation, +} from 'aws-cdk-lib/aws-certificatemanager' +import { + ISecurityGroup, + InstanceClass, + InstanceSize, + InstanceType, + Port, + SecurityGroup, + SubnetType, + Vpc, +} from 'aws-cdk-lib/aws-ec2' +import { + AwsLogDriver, + Cluster, + ContainerImage, + FargateService, + FargateTaskDefinition, + ICluster, +} from 'aws-cdk-lib/aws-ecs' +import { FileSystem } from 'aws-cdk-lib/aws-efs' +import { CfnCacheCluster, CfnSubnetGroup } from 'aws-cdk-lib/aws-elasticache' +import { + ApplicationLoadBalancer, + ApplicationProtocol, + IApplicationListener, + ListenerAction, + ListenerCondition, + Protocol, +} from 'aws-cdk-lib/aws-elasticloadbalancingv2' +import { IRole, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam' +import { ILogGroup, LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs' +import { + Credentials, + DatabaseInstance, + DatabaseInstanceEngine, + DatabaseSecret, + IDatabaseInstance, + PostgresEngineVersion, +} from 'aws-cdk-lib/aws-rds' +import { ARecord, HostedZone } from 'aws-cdk-lib/aws-route53' +import { LoadBalancerTarget } from 'aws-cdk-lib/aws-route53-targets' +import { ISecret, Secret } from 'aws-cdk-lib/aws-secretsmanager' import { Construct } from 'constructs' const databaseUser = 'n8n' const databaseName = 'n8n' const port = 5678 +interface N8NStackProps { + region?: string; +} + export class N8NStack extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id) + private readonly vpc: Vpc + private readonly domainName: string + private readonly hostedZoneId: string + private readonly ecsCluster: ICluster + private readonly database: IDatabaseInstance + private readonly redis: CfnCacheCluster + private readonly lbListener: IApplicationListener + private readonly taskRole: IRole + private readonly logGroup: ILogGroup + + private readonly secrets: { + database: ISecret; + encryption: ISecret; + jwt: ISecret; + } + + private readonly securityGroups: { + database: ISecurityGroup; + redis: ISecurityGroup; + app: ISecurityGroup; + } + + constructor(scope: Construct, id: string, props: N8NStackProps) { + super(scope, id, { + env: { + region: props.region || 'eu-central-1', + }, + }) + + const domainName = new CfnParameter(this, 'DomainName', { + type: 'String', + description: 'The domain name that would point to the n8n instance', + }) - const vpc = new aws_ec2.Vpc(this, 'VPC', { + const hostedZoneId = new CfnParameter(this, 'HostedZoneId', { + type: 'String', + description: 'The Route53 hosted zone to manage DNS entries', + }) + + this.domainName = domainName.valueAsString + this.hostedZoneId = hostedZoneId.valueAsString + + const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZone', { + zoneName: this.domainName, + hostedZoneId: this.hostedZoneId, + }) + + const certificate = new Certificate(this, 'Certificate', { + certificateName: 'n8n', + domainName: this.domainName, + validation: CertificateValidation.fromDns(hostedZone), + }) + + const vpc = this.vpc = new Vpc(this, 'VPC', { + vpcName: 'n8n', maxAzs: 2, natGateways: 2, subnetConfiguration: [ - { name: 'Public', subnetType: aws_ec2.SubnetType.PUBLIC }, - ] + { name: 'Public', subnetType: SubnetType.PUBLIC }, + { name: 'App', subnetType: SubnetType.PRIVATE_WITH_EGRESS }, + { name: 'Internal', subnetType: SubnetType.PRIVATE_ISOLATED }, + ], }) - const dbSecret = new aws_rds.DatabaseSecret(this, 'DBSecret', { - username: databaseUser, - secretName: 'N8NDatabaseSecret' - }) + this.secrets = { + database: new DatabaseSecret(this, 'DatabaseSecret', { + username: 'n8n', + secretName: 'n8n-DatabaseSecret', + }), + encryption: new Secret(this, 'EncryptionKey', { + secretName: 'n8n-EncryptionKey', + generateSecretString: { + excludePunctuation: true, + } + }), + jwt: new Secret(this, 'JWTSigningSecret', { + secretName: 'n8n-JWTSigningSecret', + generateSecretString: { + excludePunctuation: true, + } + }) + } - const dbSecurityGroup = new aws_ec2.SecurityGroup(this, 'DBSecurityGroup', { - vpc, - securityGroupName: 'N8NDatabase' - }) + this.securityGroups = { + database: new SecurityGroup(this, 'DatabaseSecurityGroup', { + vpc, + securityGroupName: 'n8n-Database', + allowAllOutbound: false, + }), + redis: new SecurityGroup(this, 'RedisSecurityGroup', { + vpc, + securityGroupName: 'n8n-Redis', + allowAllOutbound: false, + }), + app: new SecurityGroup(this, 'AppSecurityGroup', { + vpc, + securityGroupName: 'n8n-App', + }), + } - const database = new aws_rds.DatabaseInstance(this, 'Instance', { + this.database = new DatabaseInstance(this, 'DatabaseInstance', { databaseName, instanceIdentifier: databaseName, vpc, vpcSubnets: { onePerAz: true, - subnetType: aws_ec2.SubnetType.PUBLIC + subnetType: SubnetType.PRIVATE_ISOLATED, }, - securityGroups: [ dbSecurityGroup ], - engine: aws_rds.DatabaseInstanceEngine.postgres({ - version: aws_rds.PostgresEngineVersion.VER_11_15 + securityGroups: [this.securityGroups.database], + engine: DatabaseInstanceEngine.postgres({ + version: PostgresEngineVersion.VER_13, }), - credentials: aws_rds.Credentials.fromSecret(dbSecret), + credentials: Credentials.fromSecret(this.secrets.database), removalPolicy: RemovalPolicy.DESTROY, - instanceType: aws_ec2.InstanceType.of( - aws_ec2.InstanceClass.BURSTABLE3, - aws_ec2.InstanceSize.SMALL, + instanceType: InstanceType.of( + InstanceClass.T4G, + InstanceSize.MEDIUM ), }) - const ecsCluster = new aws_ecs.Cluster(this, 'Cluster', { vpc }) - const appSecurityGroup = new aws_ec2.SecurityGroup(this, 'AppSecurityGroup', { vpc }) + const redisSubnetGroup = new CfnSubnetGroup(this, 'RedisSubnetGroup', { + cacheSubnetGroupName: 'n8n', + description: 'Cache SubnetGroup for n8n', + subnetIds: vpc.isolatedSubnets.map((s) => s.subnetId), + }) - const taskRole = new aws_iam.Role(this, 'AppTaskRole', { - roleName: 'n8nTask', - assumedBy: new aws_iam.ServicePrincipal('ecs-tasks.amazonaws.com'), + this.redis = new CfnCacheCluster(this, 'RedisCluster', { + clusterName: 'n8n', + engine: 'redis', + engineVersion: '7.0', + autoMinorVersionUpgrade: false, + cacheNodeType: 'cache.t4g.medium', + numCacheNodes: 1, + cacheSubnetGroupName: redisSubnetGroup.ref, + vpcSecurityGroupIds: [this.securityGroups.redis.securityGroupId], }) + this.redis.node.addDependency(redisSubnetGroup) - const taskDefinition = new aws_ecs.FargateTaskDefinition(this, 'AppTaskDefinition', { - taskRole, - executionRole: taskRole, + const loadBalancer = new ApplicationLoadBalancer(this, 'LoadBalancer', { + loadBalancerName: 'n8n', + internetFacing: true, + vpc, + vpcSubnets: { + onePerAz: true, + subnetType: SubnetType.PUBLIC, + }, + securityGroup: new SecurityGroup(this, 'LoadBalancerSecurityGroup', { + vpc, + securityGroupName: 'n8n-LoadBalancer', + }), }) + loadBalancer + .addListener('Redirector', { protocol: ApplicationProtocol.HTTP }) + .addAction('RedirectToHTTPS', { + action: ListenerAction.redirect({ + port: '443', + protocol: 'HTTPS', + permanent: true, + }), + }) - const logGroup = new aws_logs.LogGroup(this, 'AppLogs', { - logGroupName: '/n8n/logs' + new ARecord(this, 'ALBRecord', { + recordName: this.domainName, + zone: hostedZone, + target: { + aliasTarget: new LoadBalancerTarget(loadBalancer), + }, }) - taskDefinition.addContainer('n8n', { - image: aws_ecs.ContainerImage.fromRegistry('n8nio/n8n'), - command: ['n8n', 'start', '--tunnel'], + this.lbListener = loadBalancer.addListener('Listener', { + protocol: ApplicationProtocol.HTTPS, + certificates: [certificate], + defaultAction: ListenerAction.fixedResponse(404), + }) + + // TODO: enable capacity-providers to use spot-instances for workers + this.ecsCluster = new Cluster(this, 'Cluster', { vpc, clusterName: 'n8n', containerInsights: true }) + + this.taskRole = new Role(this, 'AppTaskRole', { + roleName: 'n8n-ECSTask', + assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'), + }) + + this.logGroup = new LogGroup(this, 'AppLogs', { + logGroupName: '/n8n/logs', + retention: RetentionDays.ONE_MONTH, + removalPolicy: RemovalPolicy.DESTROY, + }) + + this.createService('webhook') + this.createService('main') + this.createService('worker') + } + + private createService(serviceName: 'main' | 'worker' | 'webhook') { + const taskDefinition = new FargateTaskDefinition( + this, + `TaskDefinition-${serviceName}`, + { + family: `n8n-${serviceName}`, + taskRole: this.taskRole, + executionRole: this.taskRole, + cpu: 512, + memoryLimitMiB: serviceName === 'main' ? 1024 : 2048, + } + ) + + const container = taskDefinition.addContainer(`n8n-${serviceName}`, { + image: ContainerImage.fromRegistry('n8nio/n8n:1.0.4'), + command: [...(serviceName === 'main' ? ['start'] : serviceName === 'worker' ? ['worker', '--concurrency=20'] : [serviceName])], environment: { + N8N_DIAGNOSTICS_ENABLED: 'true', DB_TYPE: 'postgresdb', - DB_POSTGRESDB_HOST: database.dbInstanceEndpointAddress, - DB_POSTGRESDB_PORT: database.dbInstanceEndpointPort, + DB_POSTGRESDB_HOST: this.database.dbInstanceEndpointAddress, + DB_POSTGRESDB_PORT: this.database.dbInstanceEndpointPort, DB_POSTGRESDB_DATABASE: databaseName, DB_POSTGRESDB_USER: databaseUser, - DB_POSTGRESDB_PASSWORD: dbSecret.secretValueFromJson('password').unsafeUnwrap(), - N8N_BASIC_AUTH_ACTIVE: 'false', - N8N_DIAGNOSTICS_ENABLED: 'true', + DB_POSTGRESDB_PASSWORD: this.secrets.database + .secretValueFromJson('password') + .unsafeUnwrap(), + N8N_ENCRYPTION_KEY: this.secrets.encryption.secretValue.unsafeUnwrap(), + N8N_USER_MANAGEMENT_JWT_SECRET: this.secrets.jwt.secretValue.unsafeUnwrap(), + EXECUTIONS_PROCESS: 'main', + EXECUTIONS_MODE: 'queue', + N8N_DISABLE_PRODUCTION_MAIN_PROCESS: 'true', + QUEUE_BULL_REDIS_HOST: this.redis.attrRedisEndpointAddress, + QUEUE_HEALTH_CHECK_ACTIVE: 'true', + N8N_PUSH_BACKEND: 'websocket', + WEBHOOK_URL: `https://${this.domainName}`, + N8N_BLOCK_ENV_ACCESS_IN_NODE: 'true', }, - logging: new aws_ecs.AwsLogDriver({ - logGroup, - streamPrefix: '/' + logging: new AwsLogDriver({ + logGroup: this.logGroup, + streamPrefix: `/${serviceName}`, }), healthCheck: { command: [ 'CMD-SHELL', - `curl --fail http://localhost:${port}/healthz || exit 1` + // `curl --fail http://localhost:${port}/healthz || exit 1`, + 'exit 0', ], - retries: 10 + retries: 10, }, - portMappings: [{ - containerPort: port, - hostPort: port - }] + portMappings: [ + { + containerPort: port, + hostPort: port, + }, + ], }) - const service = new aws_ecs.FargateService(this, 'Service', { - serviceName: 'n8n', - cluster: ecsCluster, - desiredCount: 1, - assignPublicIp: true, + // let fileSystem: FileSystem | undefined = undefined + // if (serviceName === 'main') { + // fileSystem = new FileSystem(this, 'FileSystem', { vpc: this.vpc }) + // const efsAccessPoint = fileSystem.addAccessPoint('AccessPoint', { + // createAcl: { + // ownerUid: '1000', + // ownerGid: '1000', + // permissions: '755', + // }, + // posixUser: { + // uid: '1000', + // gid: '1000' + // } + // }) + // efsAccessPoint.node.addDependency(fileSystem) + + // this.taskRole.addToPrincipalPolicy(new PolicyStatement({ + // actions: [ + // 'elasticfilesystem:ClientMount', + // 'elasticfilesystem:ClientWrite', + // 'elasticfilesystem:ClientRootAccess' + // ], + // resources: [ + // efsAccessPoint.accessPointArn, + // fileSystem.fileSystemArn + // ] + // })) + + // taskDefinition.addVolume({ + // name: 'Persistance', + // efsVolumeConfiguration: { + // fileSystemId: fileSystem.fileSystemId, + // transitEncryption: 'ENABLED', + // authorizationConfig: { + // accessPointId: efsAccessPoint.accessPointId, + // } + // }, + // }) + + // container.addMountPoints({ + // readOnly: false, + // containerPath: '/home/node/.n8n', + // sourceVolume: 'Persistance' + // }) + // } + + const service = new FargateService(this, `Service-${serviceName}`, { + serviceName: `n8n-${serviceName}`, + cluster: this.ecsCluster, + desiredCount: serviceName === 'main' ? 1 : serviceName === 'webhook' ? 1 : 2, // TODO: auto-scale workers taskDefinition, - securityGroups: [appSecurityGroup], + securityGroups: [this.securityGroups.app], + vpcSubnets: { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }, }) - service.connections.allowTo(dbSecurityGroup, aws_ec2.Port.tcp(5432)) - service.connections.allowFromAnyIpv4(aws_ec2.Port.tcp(port)) + service.connections.allowTo(this.securityGroups.redis, Port.tcp(6379)) + service.connections.allowTo(this.securityGroups.database, Port.tcp(5432)) + service.connections.allowFromAnyIpv4(Port.tcp(port)) + // fileSystem?.connections.allowDefaultPortFrom(service) + + if (serviceName !== 'worker') { + this.lbListener.addTargets(`n8n-${serviceName}`, { + targetGroupName: serviceName, + protocol: ApplicationProtocol.HTTP, + port, + priority: serviceName === 'webhook' ? 10 : 20, + conditions: [ + ListenerCondition.hostHeaders([this.domainName]), + ...(serviceName === 'webhook' + ? [ + ListenerCondition.pathPatterns([ + '/webhook/*', + '/webhook-waiting/*', + ]), + ] + : []), + ], + targets: [service], + healthCheck: { + protocol: Protocol.HTTP, + path: '/healthz', + healthyThresholdCount: 3, + unhealthyThresholdCount: 10, + interval: Duration.seconds(30), + }, + deregistrationDelay: Duration.seconds(10), + }) + } } } diff --git a/package.json b/package.json index 50ac382..a62e8cd 100644 --- a/package.json +++ b/package.json @@ -5,24 +5,25 @@ "scripts": { "build": "tsc", "watch": "tsc -w", - "cdk": "cdk" + "cdk": "cdk", + "synthesize": "cdk synthesize --version-reporting false --path-metadata false" }, "devDependencies": { "@types/node": "18.16.13", - "@types/source-map-support": "0.5.6", + "@types/source-map-support": "0.5.7", "@typescript-eslint/eslint-plugin": "5.59.6", "@typescript-eslint/eslint-plugin-tslint": "5.59.6", "@typescript-eslint/parser": "5.59.6", - "aws-cdk": "2.79.1", + "aws-cdk": "2.96.1", "eslint": "8.40.0", "eslint-config-standard": "17.0.0", "ts-node": "10.9.1", "tslint": "6.1.3", - "typescript": "5.0.4" + "typescript": "5.2.2" }, "dependencies": { - "aws-cdk-lib": "2.79.1", - "constructs": "10.2.28", + "aws-cdk-lib": "2.96.1", + "constructs": "10.2.70", "source-map-support": "0.5.21" }, "packageManager": "yarn@3.5.1" diff --git a/tsconfig.json b/tsconfig.json index 4a1fb06..84cadde 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "strictNullChecks": true, "noImplicitThis": true, "alwaysStrict": true, - "noUnusedLocals": true, + // "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": false, diff --git a/yarn.lock b/yarn.lock index 97b1cc9..b24612c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,24 +5,24 @@ __metadata: version: 6 cacheKey: 8 -"@aws-cdk/asset-awscli-v1@npm:^2.2.165": - version: 2.2.179 - resolution: "@aws-cdk/asset-awscli-v1@npm:2.2.179" - checksum: f5c54d818264af6b68e6f424c5543d697d20c80364f0bc78801072d1ef1759448dbc6fbc6f118893c143cad4093f0dd61b9bc131efe8172f810e1d7b463a5ed2 +"@aws-cdk/asset-awscli-v1@npm:^2.2.200": + version: 2.2.200 + resolution: "@aws-cdk/asset-awscli-v1@npm:2.2.200" + checksum: 0de92336887957e188092dc62a9b8fa87b8e602b8678faa361cc12680cedde46a2592dc1c73b33a0d2067e1a000d2afa762b2a14c07fb47b3fcad288a24ce7ed languageName: node linkType: hard -"@aws-cdk/asset-kubectl-v20@npm:^2.1.1": - version: 2.1.1 - resolution: "@aws-cdk/asset-kubectl-v20@npm:2.1.1" - checksum: f5883fae7b6d3d17e5125560a6efa5065d8b319c11ea6639caa68e315427c4fa266433db04b1a776cf12ccc8c8f8e9a3dfc0ac8cc3c0e73545f80b07808281e2 +"@aws-cdk/asset-kubectl-v20@npm:^2.1.2": + version: 2.1.2 + resolution: "@aws-cdk/asset-kubectl-v20@npm:2.1.2" + checksum: 987bce26f54ba64596b7d15adf0c09603814ed56f06b498d17dc4b8859f4708662c9c48f88bba2810d8fac04cf84b8f3e51806c93cf65aeb6148ad76bc250f84 languageName: node linkType: hard -"@aws-cdk/asset-node-proxy-agent-v5@npm:^2.0.139": - version: 2.0.150 - resolution: "@aws-cdk/asset-node-proxy-agent-v5@npm:2.0.150" - checksum: e44f8ffed4973e8b14ceddc45f2d8f29416475a6f18843697df6cbe9246acd4ebecccc70e6c3cb322685a664102fe0d730bcb47f15486643deb47f29f7bebd4b +"@aws-cdk/asset-node-proxy-agent-v6@npm:^2.0.1": + version: 2.0.1 + resolution: "@aws-cdk/asset-node-proxy-agent-v6@npm:2.0.1" + checksum: 5d011a554e71212662e32ff5d9187a1733a07d5424bdedfef2f43e7a6075d4b5f316d696867cc5dcd9ed1a11731b0d6b3ae41872c454796c326983ab382e05d0 languageName: node linkType: hard @@ -270,12 +270,12 @@ __metadata: languageName: node linkType: hard -"@types/source-map-support@npm:0.5.6": - version: 0.5.6 - resolution: "@types/source-map-support@npm:0.5.6" +"@types/source-map-support@npm:0.5.7": + version: 0.5.7 + resolution: "@types/source-map-support@npm:0.5.7" dependencies: source-map: ^0.6.0 - checksum: b2b52b3e4901e52df30ebd23056885a352462c0d6fd73418e793684665a7602168e7c337241f0391562e8c1051ff37cbcb9b7d775990ee1f2a0d8a6ffa2cb22a + checksum: 6da844f0b8333a1789dd27ad7bb33b9bcb41159939a94451738a8f7a1e1fd69169aa6e2dda51ba8f61753c57ead159331330c996b1d672abc2e33d6991b38bf1 languageName: node linkType: hard @@ -587,13 +587,13 @@ __metadata: languageName: node linkType: hard -"aws-cdk-lib@npm:2.79.1": - version: 2.79.1 - resolution: "aws-cdk-lib@npm:2.79.1" +"aws-cdk-lib@npm:2.96.1": + version: 2.96.1 + resolution: "aws-cdk-lib@npm:2.96.1" dependencies: - "@aws-cdk/asset-awscli-v1": ^2.2.165 - "@aws-cdk/asset-kubectl-v20": ^2.1.1 - "@aws-cdk/asset-node-proxy-agent-v5": ^2.0.139 + "@aws-cdk/asset-awscli-v1": ^2.2.200 + "@aws-cdk/asset-kubectl-v20": ^2.1.2 + "@aws-cdk/asset-node-proxy-agent-v6": ^2.0.1 "@balena/dockerignore": ^1.0.2 case: 1.6.3 fs-extra: ^11.1.1 @@ -601,18 +601,18 @@ __metadata: jsonschema: ^1.4.1 minimatch: ^3.1.2 punycode: ^2.3.0 - semver: ^7.5.0 + semver: ^7.5.4 table: ^6.8.1 yaml: 1.10.2 peerDependencies: constructs: ^10.0.0 - checksum: 76c6fc9b95e3b460073c119d9db99dd48526333be7505b75d5b9831a44afdf2c23d2acb3d026ad3eafdd8967637f246dded305e60e4e844e1c7a63f74382a299 + checksum: 2fac4404192d7c2f97ccfc51194d0b9008d8886817cd20cac268012963e33ebdb7e788c686a58e9b2c1fed0009b3e593458fa27ae500c7ad0f04831e2baab8e8 languageName: node linkType: hard -"aws-cdk@npm:2.79.1": - version: 2.79.1 - resolution: "aws-cdk@npm:2.79.1" +"aws-cdk@npm:2.96.1": + version: 2.96.1 + resolution: "aws-cdk@npm:2.96.1" dependencies: fsevents: 2.3.2 dependenciesMeta: @@ -620,7 +620,7 @@ __metadata: optional: true bin: cdk: bin/cdk - checksum: d35964d5ffabd9b10fbb8cc82bbd9fc2b18dd5157a9309d4560c73f25a81db61f5f2871964d72b4e2d16ec04904f4dc426915ac3cda0131ca1516bf436d214d3 + checksum: 72a614d2340b5f020861d1d2a9aef6aad75c21043d2cfc44092424db6f4792e3bdfd5b51a6727b04cda0ddaeeac135578b3dd314ee9d0876587e00e9c34f475a languageName: node linkType: hard @@ -718,19 +718,19 @@ __metadata: resolution: "cdk-fargate-n8n@workspace:." dependencies: "@types/node": 18.16.13 - "@types/source-map-support": 0.5.6 + "@types/source-map-support": 0.5.7 "@typescript-eslint/eslint-plugin": 5.59.6 "@typescript-eslint/eslint-plugin-tslint": 5.59.6 "@typescript-eslint/parser": 5.59.6 - aws-cdk: 2.79.1 - aws-cdk-lib: 2.79.1 - constructs: 10.2.28 + aws-cdk: 2.96.1 + aws-cdk-lib: 2.96.1 + constructs: 10.2.70 eslint: 8.40.0 eslint-config-standard: 17.0.0 source-map-support: 0.5.21 ts-node: 10.9.1 tslint: 6.1.3 - typescript: 5.0.4 + typescript: 5.2.2 bin: cdk-fargate-n8n: bin/n8n.js languageName: unknown @@ -833,10 +833,10 @@ __metadata: languageName: node linkType: hard -"constructs@npm:10.2.28": - version: 10.2.28 - resolution: "constructs@npm:10.2.28" - checksum: 04045a17752a3eb6398ec3ce3229c58dcb3beb9f9c3df0d4ea585b473737dea2b192f076399905878ff13a3fd093b8b54fc20617eb90492eee6515c056ed4575 +"constructs@npm:10.2.70": + version: 10.2.70 + resolution: "constructs@npm:10.2.70" + checksum: 8649ed2c95cb0107d389249285229a2d6594cd36a24af2c106b25630cc4f649c2e628f237ce8da1f26454742ed23cc7f1fcfeab9c3a6592180a48848aa5bf82d languageName: node linkType: hard @@ -2193,14 +2193,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.5.0": - version: 7.5.1 - resolution: "semver@npm:7.5.1" +"semver@npm:^7.5.4": + version: 7.5.4 + resolution: "semver@npm:7.5.4" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: d16dbedad53c65b086f79524b9ef766bf38670b2395bdad5c957f824dcc566b624988013564f4812bcace3f9d405355c3635e2007396a39d1bffc71cfec4a2fc + checksum: 12d8ad952fa353b0995bf180cdac205a4068b759a140e5d3c608317098b3575ac2f1e09182206bf2eb26120e1c0ed8fb92c48c592f6099680de56bb071423ca3 languageName: node linkType: hard @@ -2525,23 +2525,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.0.4": - version: 5.0.4 - resolution: "typescript@npm:5.0.4" +"typescript@npm:5.2.2": + version: 5.2.2 + resolution: "typescript@npm:5.2.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 82b94da3f4604a8946da585f7d6c3025fff8410779e5bde2855ab130d05e4fd08938b9e593b6ebed165bda6ad9292b230984f10952cf82f0a0ca07bbeaa08172 + checksum: 7912821dac4d962d315c36800fe387cdc0a6298dba7ec171b350b4a6e988b51d7b8f051317786db1094bd7431d526b648aba7da8236607febb26cf5b871d2d3c languageName: node linkType: hard -"typescript@patch:typescript@5.0.4#~builtin": - version: 5.0.4 - resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=7ad353" +"typescript@patch:typescript@5.2.2#~builtin": + version: 5.2.2 + resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=7ad353" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 6a1fe9a77bb9c5176ead919cc4a1499ee63e46b4e05bf667079f11bf3a8f7887f135aa72460a4c3b016e6e6bb65a822cb8689a6d86cbfe92d22cc9f501f09213 + checksum: 07106822b4305de3f22835cbba949a2b35451cad50888759b6818421290ff95d522b38ef7919e70fb381c5fe9c1c643d7dea22c8b31652a717ddbd57b7f4d554 languageName: node linkType: hard