@friggframework/devtools 2.0.0--canary.454.25d396a.0 → 2.0.0--canary.463.62579dd.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
3
  const { AWSDiscovery } = require('./aws-discovery');
4
- const { buildPrismaLayer } = require('./scripts/build-prisma-layer');
5
4
 
6
5
  const shouldRunDiscovery = (AppDefinition) => {
7
6
  console.log(
@@ -18,8 +17,7 @@ const shouldRunDiscovery = (AppDefinition) => {
18
17
  return (
19
18
  AppDefinition.vpc?.enable === true ||
20
19
  AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
21
- AppDefinition.ssm?.enable === true ||
22
- AppDefinition.database?.postgres?.enable === true
20
+ AppDefinition.ssm?.enable === true
23
21
  );
24
22
  };
25
23
 
@@ -70,7 +68,8 @@ const getAppEnvironmentVars = (AppDefinition) => {
70
68
  }
71
69
  if (skippedKeys.length > 0) {
72
70
  console.log(
73
- ` ⚠️ Skipped ${skippedKeys.length
71
+ ` ⚠️ Skipped ${
72
+ skippedKeys.length
74
73
  } reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
75
74
  );
76
75
  }
@@ -545,17 +544,10 @@ const gatherDiscoveredResources = async (AppDefinition) => {
545
544
  try {
546
545
  const region = process.env.AWS_REGION || 'us-east-1';
547
546
  const discovery = new AWSDiscovery(region);
548
- // Use Serverless Framework's stage resolution (opt:stage with 'dev' as default)
549
- // This matches how serverless.yml resolves ${opt:stage, "dev"}
550
- // IMPORTANT: Use SLS_STAGE (not STAGE) to match actual deployment stage
551
- const stage = process.env.SLS_STAGE || 'dev';
552
-
553
547
  const config = {
554
548
  vpc: AppDefinition.vpc || {},
555
549
  encryption: AppDefinition.encryption || {},
556
550
  ssm: AppDefinition.ssm || {},
557
- serviceName: AppDefinition.name || 'create-frigg-app',
558
- stage: stage,
559
551
  };
560
552
 
561
553
  const discoveredResources = await discovery.discoverResources(config);
@@ -611,22 +603,6 @@ const buildEnvironment = (appEnvironmentVars, discoveredResources) => {
611
603
  }
612
604
  }
613
605
 
614
- // Add Aurora discovery mappings
615
- if (discoveredResources.aurora) {
616
- if (discoveredResources.aurora.clusterIdentifier) {
617
- environment.AWS_DISCOVERY_AURORA_CLUSTER_ID = discoveredResources.aurora.clusterIdentifier;
618
- }
619
- if (discoveredResources.aurora.endpoint) {
620
- environment.AWS_DISCOVERY_AURORA_ENDPOINT = discoveredResources.aurora.endpoint;
621
- }
622
- if (discoveredResources.aurora.port) {
623
- environment.AWS_DISCOVERY_AURORA_PORT = discoveredResources.aurora.port.toString();
624
- }
625
- if (discoveredResources.aurora.secretArn) {
626
- environment.AWS_DISCOVERY_AURORA_SECRET_ARN = discoveredResources.aurora.secretArn;
627
- }
628
- }
629
-
630
606
  return environment;
631
607
  };
632
608
 
@@ -642,27 +618,15 @@ const createBaseDefinition = (
642
618
  service: AppDefinition.name || 'create-frigg-app',
643
619
  package: {
644
620
  individually: true,
645
- patterns: [
646
- // Existing AWS SDK exclusions
621
+ exclude: [
647
622
  '!**/node_modules/aws-sdk/**',
648
623
  '!**/node_modules/@aws-sdk/**',
649
624
  '!package.json',
650
-
651
- // GLOBAL Prisma exclusions - all Prisma packages moved to Lambda Layer
652
- // This reduces each function from ~120MB to ~45MB (60% reduction)
653
- '!node_modules/@prisma/**',
654
- '!node_modules/.prisma/**',
655
- '!node_modules/@prisma-mongodb/**',
656
- '!node_modules/@prisma-postgresql/**',
657
- '!node_modules/prisma/**',
658
- // Prisma packages will be provided at runtime via Lambda Layer
659
- // See: LAMBDA-LAYER-PRISMA.md for complete documentation
660
625
  ],
661
626
  },
662
627
  useDotenv: true,
663
628
  provider: {
664
629
  name: AppDefinition.provider || 'aws',
665
- ...(process.env.AWS_PROFILE && { profile: process.env.AWS_PROFILE }),
666
630
  runtime: 'nodejs20.x',
667
631
  timeout: 30,
668
632
  region,
@@ -711,8 +675,7 @@ const createBaseDefinition = (
711
675
  },
712
676
  },
713
677
  plugins: [
714
- // Temporarily disabled Jetpack - it ignores package.patterns in dependency mode
715
- // 'serverless-jetpack',
678
+ 'serverless-jetpack',
716
679
  'serverless-dotenv-plugin',
717
680
  'serverless-offline-sqs',
718
681
  'serverless-offline',
@@ -733,15 +696,14 @@ const createBaseDefinition = (
733
696
  secretAccessKey: 'root',
734
697
  skipCacheInvalidation: false,
735
698
  },
736
- // Jetpack config removed - testing with standard Serverless packaging
737
- // jetpack: {
738
- // base: '..',
739
- // },
699
+ jetpack: {
700
+ base: '..',
701
+ },
740
702
  },
741
703
  functions: {
742
704
  auth: {
743
- handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
744
- layers: [{ Ref: 'PrismaLambdaLayer' }],
705
+ handler:
706
+ 'node_modules/@friggframework/core/handlers/routers/auth.handler',
745
707
  events: [
746
708
  { httpApi: { path: '/api/integrations', method: 'ANY' } },
747
709
  {
@@ -754,57 +716,20 @@ const createBaseDefinition = (
754
716
  ],
755
717
  },
756
718
  user: {
757
- handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
758
- layers: [{ Ref: 'PrismaLambdaLayer' }],
759
- events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
719
+ handler:
720
+ 'node_modules/@friggframework/core/handlers/routers/user.handler',
721
+ events: [
722
+ { httpApi: { path: '/user/{proxy+}', method: 'ANY' } },
723
+ ],
760
724
  },
761
725
  health: {
762
- handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
763
- layers: [{ Ref: 'PrismaLambdaLayer' }],
726
+ handler:
727
+ 'node_modules/@friggframework/core/handlers/routers/health.handler',
764
728
  events: [
765
729
  { httpApi: { path: '/health', method: 'GET' } },
766
730
  { httpApi: { path: '/health/{proxy+}', method: 'GET' } },
767
731
  ],
768
732
  },
769
- dbMigrate: {
770
- handler: 'node_modules/@friggframework/core/handlers/workers/db-migration.handler',
771
- layers: [{ Ref: 'PrismaLambdaLayer' }],
772
- timeout: 300, // 5 minutes for long-running migrations
773
- memorySize: 512, // Extra memory for Prisma CLI operations
774
- reservedConcurrency: 1, // Prevent concurrent migrations
775
- description: 'Runs database migrations via Prisma (invoke manually from CI/CD)',
776
- // No events - this function is invoked manually via AWS CLI
777
- maximumEventAge: 60, // Don't retry old migration requests (60 seconds)
778
- maximumRetryAttempts: 0, // Don't auto-retry failed migrations
779
- tags: {
780
- Purpose: 'DatabaseMigration',
781
- ManagedBy: 'Frigg',
782
- },
783
- // Environment variables for non-interactive Prisma CLI operation
784
- environment: {
785
- CI: '1', // Forces Prisma to non-interactive mode
786
- PRISMA_HIDE_UPDATE_MESSAGE: '1', // Suppress update messages
787
- PRISMA_MIGRATE_SKIP_SEED: '1', // Skip seeding during migrations
788
- },
789
- // Function-specific packaging: Include Prisma schemas (CLI from layer)
790
- package: {
791
- patterns: [
792
- // Include Prisma schemas from @friggframework/core
793
- // Note: Prisma CLI and clients come from Lambda Layer
794
- 'node_modules/@friggframework/core/prisma-mongodb/**',
795
- 'node_modules/@friggframework/core/prisma-postgresql/**',
796
- ],
797
- },
798
- },
799
- },
800
- layers: {
801
- prisma: {
802
- path: 'layers/prisma',
803
- name: '${self:service}-prisma-${sls:stage}',
804
- description: 'Prisma ORM clients for MongoDB and PostgreSQL with rhel-openssl-3.0.x binaries. Reduces function sizes by ~60% (120MB → 45MB). See LAMBDA-LAYER-PRISMA.md for details.',
805
- compatibleRuntimes: ['nodejs18.x', 'nodejs20.x'],
806
- retain: false, // Don't retain old layer versions
807
- },
808
733
  },
809
734
  resources: {
810
735
  Resources: {
@@ -903,22 +828,18 @@ const applyKmsConfiguration = (
903
828
  }
904
829
 
905
830
  if (discoveredResources.defaultKmsKeyId) {
906
- console.log(`Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`);
907
-
908
- // Only create alias if it doesn't already exist
909
- if (!discoveredResources.kmsAliasExists) {
910
- console.log('Creating KMS alias for discovered key...');
911
- definition.resources.Resources.FriggKMSKeyAlias = {
912
- Type: 'AWS::KMS::Alias',
913
- DeletionPolicy: 'Retain',
914
- Properties: {
915
- AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
916
- TargetKeyId: discoveredResources.defaultKmsKeyId,
917
- },
918
- };
919
- } else {
920
- console.log('KMS alias already exists, skipping alias creation');
921
- }
831
+ console.log(
832
+ `Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`
833
+ );
834
+ definition.resources.Resources.FriggKMSKeyAlias = {
835
+ Type: 'AWS::KMS::Alias',
836
+ DeletionPolicy: 'Retain',
837
+ Properties: {
838
+ AliasName:
839
+ 'alias/${self:service}-${self:provider.stage}-frigg-kms',
840
+ TargetKeyId: discoveredResources.defaultKmsKeyId,
841
+ },
842
+ };
922
843
 
923
844
  definition.provider.iamRoleStatements.push({
924
845
  Effect: 'Allow',
@@ -929,7 +850,7 @@ const applyKmsConfiguration = (
929
850
  if (AppDefinition.encryption?.createResourceIfNoneFound !== true) {
930
851
  throw new Error(
931
852
  'KMS field-level encryption is enabled but no KMS key was found. ' +
932
- 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
853
+ 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
933
854
  );
934
855
  }
935
856
 
@@ -968,8 +889,9 @@ const applyKmsConfiguration = (
968
889
  Resource: '*',
969
890
  Condition: {
970
891
  StringEquals: {
971
- 'kms:ViaService': `lambda.${process.env.AWS_REGION || 'us-east-1'
972
- }.amazonaws.com`,
892
+ 'kms:ViaService': `lambda.${
893
+ process.env.AWS_REGION || 'us-east-1'
894
+ }.amazonaws.com`,
973
895
  },
974
896
  },
975
897
  },
@@ -1371,13 +1293,13 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1371
1293
  };
1372
1294
 
1373
1295
  definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
1374
- {
1375
- Type: 'AWS::EC2::SubnetRouteTableAssociation',
1376
- Properties: {
1377
- SubnetId: { Ref: 'FriggPublicSubnet' },
1378
- RouteTableId: { Ref: 'FriggPublicRouteTable' },
1379
- },
1380
- };
1296
+ {
1297
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
1298
+ Properties: {
1299
+ SubnetId: { Ref: 'FriggPublicSubnet' },
1300
+ RouteTableId: { Ref: 'FriggPublicRouteTable' },
1301
+ },
1302
+ };
1381
1303
 
1382
1304
  definition.resources.Resources.FriggLambdaRouteTable = {
1383
1305
  Type: 'AWS::EC2::RouteTable',
@@ -1394,22 +1316,22 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1394
1316
  };
1395
1317
 
1396
1318
  definition.resources.Resources.FriggPrivateSubnet1RouteTableAssociation =
1397
- {
1398
- Type: 'AWS::EC2::SubnetRouteTableAssociation',
1399
- Properties: {
1400
- SubnetId: { Ref: 'FriggPrivateSubnet1' },
1401
- RouteTableId: { Ref: 'FriggLambdaRouteTable' },
1402
- },
1403
- };
1319
+ {
1320
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
1321
+ Properties: {
1322
+ SubnetId: { Ref: 'FriggPrivateSubnet1' },
1323
+ RouteTableId: { Ref: 'FriggLambdaRouteTable' },
1324
+ },
1325
+ };
1404
1326
 
1405
1327
  definition.resources.Resources.FriggPrivateSubnet2RouteTableAssociation =
1406
- {
1407
- Type: 'AWS::EC2::SubnetRouteTableAssociation',
1408
- Properties: {
1409
- SubnetId: { Ref: 'FriggPrivateSubnet2' },
1410
- RouteTableId: { Ref: 'FriggLambdaRouteTable' },
1411
- },
1412
- };
1328
+ {
1329
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
1330
+ Properties: {
1331
+ SubnetId: { Ref: 'FriggPrivateSubnet2' },
1332
+ RouteTableId: { Ref: 'FriggLambdaRouteTable' },
1333
+ },
1334
+ };
1413
1335
  }
1414
1336
  } else if (subnetManagement === 'use-existing') {
1415
1337
  if (
@@ -1426,12 +1348,12 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1426
1348
  AppDefinition.vpc.subnets?.ids?.length > 0
1427
1349
  ? AppDefinition.vpc.subnets.ids
1428
1350
  : discoveredResources.privateSubnetId1 &&
1429
- discoveredResources.privateSubnetId2
1430
- ? [
1431
- discoveredResources.privateSubnetId1,
1432
- discoveredResources.privateSubnetId2,
1433
- ]
1434
- : [];
1351
+ discoveredResources.privateSubnetId2
1352
+ ? [
1353
+ discoveredResources.privateSubnetId1,
1354
+ discoveredResources.privateSubnetId2,
1355
+ ]
1356
+ : [];
1435
1357
 
1436
1358
  if (vpcConfig.subnetIds.length < 2) {
1437
1359
  if (AppDefinition.vpc.selfHeal) {
@@ -1638,13 +1560,13 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1638
1560
  };
1639
1561
 
1640
1562
  definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
1641
- {
1642
- Type: 'AWS::EC2::SubnetRouteTableAssociation',
1643
- Properties: {
1644
- SubnetId: { Ref: 'FriggPublicSubnet' },
1645
- RouteTableId: { Ref: 'FriggPublicRouteTable' },
1646
- },
1647
- };
1563
+ {
1564
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
1565
+ Properties: {
1566
+ SubnetId: { Ref: 'FriggPublicSubnet' },
1567
+ RouteTableId: { Ref: 'FriggPublicRouteTable' },
1568
+ },
1569
+ };
1648
1570
  }
1649
1571
 
1650
1572
  definition.resources.Resources.FriggNATGateway = {
@@ -1655,11 +1577,11 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1655
1577
  AllocationId: useExistingEip
1656
1578
  ? discoveredResources.existingElasticIpAllocationId
1657
1579
  : {
1658
- 'Fn::GetAtt': [
1659
- 'FriggNATGatewayEIP',
1660
- 'AllocationId',
1661
- ],
1662
- },
1580
+ 'Fn::GetAtt': [
1581
+ 'FriggNATGatewayEIP',
1582
+ 'AllocationId',
1583
+ ],
1584
+ },
1663
1585
  SubnetId: discoveredResources.publicSubnetId || {
1664
1586
  Ref: 'FriggPublicSubnet',
1665
1587
  },
@@ -1972,9 +1894,7 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1972
1894
  },
1973
1895
  };
1974
1896
 
1975
- // Create Secrets Manager VPC Endpoint if explicitly enabled OR if Aurora is enabled
1976
- // (Aurora requires Secrets Manager access for credential retrieval)
1977
- if (AppDefinition.secretsManager?.enable === true || AppDefinition.database?.postgres?.enable === true) {
1897
+ if (AppDefinition.secretsManager?.enable === true) {
1978
1898
  definition.resources.Resources.VPCEndpointSecretsManager = {
1979
1899
  Type: 'AWS::EC2::VPCEndpoint',
1980
1900
  Properties: {
@@ -1992,283 +1912,6 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1992
1912
  }
1993
1913
  };
1994
1914
 
1995
- const createAuroraInfrastructure = (definition, AppDefinition, discoveredResources) => {
1996
- const dbConfig = AppDefinition.database.postgres;
1997
-
1998
- console.log('🔧 Creating Aurora Serverless v2 infrastructure...');
1999
-
2000
- // 1. DB Subnet Group (using Lambda private subnets)
2001
- definition.resources.Resources.FriggDBSubnetGroup = {
2002
- Type: 'AWS::RDS::DBSubnetGroup',
2003
- Properties: {
2004
- DBSubnetGroupDescription: 'Subnet group for Frigg Aurora cluster',
2005
- SubnetIds: [
2006
- discoveredResources.privateSubnetId1,
2007
- discoveredResources.privateSubnetId2
2008
- ],
2009
- Tags: [
2010
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-db-subnet-group' },
2011
- { Key: 'ManagedBy', Value: 'Frigg' },
2012
- { Key: 'Service', Value: '${self:service}' },
2013
- { Key: 'Stage', Value: '${self:provider.stage}' },
2014
- ]
2015
- }
2016
- };
2017
-
2018
- // 2. Security Group (allow Lambda SG to access 5432)
2019
- // In create-new VPC mode, Lambda uses FriggLambdaSecurityGroup
2020
- // In other modes, use discovered default security group
2021
- const lambdaSecurityGroupId = AppDefinition.vpc?.management === 'create-new'
2022
- ? { Ref: 'FriggLambdaSecurityGroup' }
2023
- : discoveredResources.defaultSecurityGroupId;
2024
-
2025
- definition.resources.Resources.FriggAuroraSecurityGroup = {
2026
- Type: 'AWS::EC2::SecurityGroup',
2027
- Properties: {
2028
- GroupDescription: 'Security group for Frigg Aurora PostgreSQL',
2029
- VpcId: discoveredResources.defaultVpcId,
2030
- SecurityGroupIngress: [
2031
- {
2032
- IpProtocol: 'tcp',
2033
- FromPort: 5432,
2034
- ToPort: 5432,
2035
- SourceSecurityGroupId: lambdaSecurityGroupId,
2036
- Description: 'PostgreSQL access from Lambda functions'
2037
- }
2038
- ],
2039
- Tags: [
2040
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-aurora-sg' },
2041
- { Key: 'ManagedBy', Value: 'Frigg' },
2042
- { Key: 'Service', Value: '${self:service}' },
2043
- { Key: 'Stage', Value: '${self:provider.stage}' },
2044
- ]
2045
- }
2046
- };
2047
-
2048
- // 3. Secrets Manager Secret (database credentials)
2049
- definition.resources.Resources.FriggDatabaseSecret = {
2050
- Type: 'AWS::SecretsManager::Secret',
2051
- Properties: {
2052
- Name: '${self:service}-${self:provider.stage}-aurora-credentials',
2053
- Description: 'Aurora PostgreSQL credentials for Frigg application',
2054
- GenerateSecretString: {
2055
- SecretStringTemplate: JSON.stringify({
2056
- username: dbConfig.masterUsername || 'frigg_admin'
2057
- }),
2058
- GenerateStringKey: 'password',
2059
- PasswordLength: 32,
2060
- ExcludeCharacters: '"@/\\'
2061
- },
2062
- Tags: [
2063
- { Key: 'ManagedBy', Value: 'Frigg' },
2064
- { Key: 'Service', Value: '${self:service}' },
2065
- { Key: 'Stage', Value: '${self:provider.stage}' },
2066
- ]
2067
- }
2068
- };
2069
-
2070
- // 4. Aurora Serverless v2 Cluster
2071
- definition.resources.Resources.FriggAuroraCluster = {
2072
- Type: 'AWS::RDS::DBCluster',
2073
- DeletionPolicy: 'Snapshot',
2074
- UpdateReplacePolicy: 'Snapshot',
2075
- Properties: {
2076
- Engine: 'aurora-postgresql',
2077
- EngineVersion: dbConfig.engineVersion || '15.3',
2078
- EngineMode: 'provisioned', // Required for Serverless v2
2079
- DatabaseName: dbConfig.databaseName || 'frigg_db',
2080
- MasterUsername: { 'Fn::Sub': '{{resolve:secretsmanager:${FriggDatabaseSecret}:SecretString:username}}' },
2081
- MasterUserPassword: { 'Fn::Sub': '{{resolve:secretsmanager:${FriggDatabaseSecret}:SecretString:password}}' },
2082
- DBSubnetGroupName: { Ref: 'FriggDBSubnetGroup' },
2083
- VpcSecurityGroupIds: [{ Ref: 'FriggAuroraSecurityGroup' }],
2084
- ServerlessV2ScalingConfiguration: {
2085
- MinCapacity: dbConfig.scaling?.minCapacity || 0.5,
2086
- MaxCapacity: dbConfig.scaling?.maxCapacity || 1.0
2087
- },
2088
- BackupRetentionPeriod: dbConfig.backupRetentionDays || 7,
2089
- PreferredBackupWindow: dbConfig.preferredBackupWindow || '03:00-04:00',
2090
- DeletionProtection: dbConfig.deletionProtection !== false,
2091
- EnableCloudwatchLogsExports: ['postgresql'],
2092
- Tags: [
2093
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-aurora-cluster' },
2094
- { Key: 'ManagedBy', Value: 'Frigg' },
2095
- { Key: 'Service', Value: '${self:service}' },
2096
- { Key: 'Stage', Value: '${self:provider.stage}' },
2097
- ]
2098
- }
2099
- };
2100
-
2101
- // 5. Aurora Serverless v2 Instance
2102
- definition.resources.Resources.FriggAuroraInstance = {
2103
- Type: 'AWS::RDS::DBInstance',
2104
- Properties: {
2105
- Engine: 'aurora-postgresql',
2106
- DBInstanceClass: 'db.serverless',
2107
- DBClusterIdentifier: { Ref: 'FriggAuroraCluster' },
2108
- PubliclyAccessible: false,
2109
- EnablePerformanceInsights: dbConfig.enablePerformanceInsights || false,
2110
- Tags: [
2111
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-aurora-instance' },
2112
- { Key: 'ManagedBy', Value: 'Frigg' },
2113
- { Key: 'Service', Value: '${self:service}' },
2114
- { Key: 'Stage', Value: '${self:provider.stage}' },
2115
- ]
2116
- }
2117
- };
2118
-
2119
- // 6. Secret Attachment (links cluster to secret)
2120
- definition.resources.Resources.FriggSecretAttachment = {
2121
- Type: 'AWS::SecretsManager::SecretTargetAttachment',
2122
- Properties: {
2123
- SecretId: { Ref: 'FriggDatabaseSecret' },
2124
- TargetId: { Ref: 'FriggAuroraCluster' },
2125
- TargetType: 'AWS::RDS::DBCluster'
2126
- }
2127
- };
2128
-
2129
- // 7. Add IAM permissions for Secrets Manager
2130
- definition.provider.iamRoleStatements.push({
2131
- Effect: 'Allow',
2132
- Action: [
2133
- 'secretsmanager:GetSecretValue',
2134
- 'secretsmanager:DescribeSecret'
2135
- ],
2136
- Resource: { Ref: 'FriggDatabaseSecret' }
2137
- });
2138
-
2139
- // 8. Set DATABASE_URL environment variable
2140
- definition.provider.environment.DATABASE_URL = {
2141
- 'Fn::Sub': [
2142
- 'postgresql://${Username}:${Password}@${Endpoint}:${Port}/${DatabaseName}',
2143
- {
2144
- Username: { 'Fn::Sub': '{{resolve:secretsmanager:${FriggDatabaseSecret}:SecretString:username}}' },
2145
- Password: { 'Fn::Sub': '{{resolve:secretsmanager:${FriggDatabaseSecret}:SecretString:password}}' },
2146
- Endpoint: { 'Fn::GetAtt': ['FriggAuroraCluster', 'Endpoint'] },
2147
- Port: { 'Fn::GetAtt': ['FriggAuroraCluster', 'Port'] },
2148
- DatabaseName: dbConfig.databaseName || 'frigg_db'
2149
- }
2150
- ]
2151
- };
2152
-
2153
- // 9. Set DB_TYPE for Prisma client selection
2154
- definition.provider.environment.DB_TYPE = 'postgresql';
2155
-
2156
- console.log('✅ Aurora infrastructure resources created');
2157
- };
2158
-
2159
- const useExistingAurora = (definition, AppDefinition, discoveredResources) => {
2160
- const dbConfig = AppDefinition.database.postgres;
2161
-
2162
- console.log(`🔗 Using existing Aurora cluster: ${discoveredResources.aurora.clusterIdentifier}`);
2163
-
2164
- // Add IAM permissions for Secrets Manager if secret exists
2165
- if (discoveredResources.aurora.secretArn) {
2166
- definition.provider.iamRoleStatements.push({
2167
- Effect: 'Allow',
2168
- Action: [
2169
- 'secretsmanager:GetSecretValue',
2170
- 'secretsmanager:DescribeSecret'
2171
- ],
2172
- Resource: discoveredResources.aurora.secretArn
2173
- });
2174
-
2175
- // Set DATABASE_URL from discovered secret
2176
- definition.provider.environment.DATABASE_URL = {
2177
- 'Fn::Sub': [
2178
- 'postgresql://${Username}:${Password}@${Endpoint}:${Port}/${DatabaseName}',
2179
- {
2180
- Username: { 'Fn::Sub': `{{resolve:secretsmanager:${discoveredResources.aurora.secretArn}:SecretString:username}}` },
2181
- Password: { 'Fn::Sub': `{{resolve:secretsmanager:${discoveredResources.aurora.secretArn}:SecretString:password}}` },
2182
- Endpoint: discoveredResources.aurora.endpoint,
2183
- Port: discoveredResources.aurora.port,
2184
- DatabaseName: dbConfig.databaseName || 'frigg_db'
2185
- }
2186
- ]
2187
- };
2188
- } else if (dbConfig.secretArn) {
2189
- // Use user-provided secret ARN
2190
- definition.provider.iamRoleStatements.push({
2191
- Effect: 'Allow',
2192
- Action: [
2193
- 'secretsmanager:GetSecretValue',
2194
- 'secretsmanager:DescribeSecret'
2195
- ],
2196
- Resource: dbConfig.secretArn
2197
- });
2198
-
2199
- definition.provider.environment.DATABASE_URL = {
2200
- 'Fn::Sub': [
2201
- 'postgresql://${Username}:${Password}@${Endpoint}:${Port}/${DatabaseName}',
2202
- {
2203
- Username: { 'Fn::Sub': `{{resolve:secretsmanager:${dbConfig.secretArn}:SecretString:username}}` },
2204
- Password: { 'Fn::Sub': `{{resolve:secretsmanager:${dbConfig.secretArn}:SecretString:password}}` },
2205
- Endpoint: discoveredResources.aurora.endpoint,
2206
- Port: discoveredResources.aurora.port,
2207
- DatabaseName: dbConfig.databaseName || 'frigg_db'
2208
- }
2209
- ]
2210
- };
2211
- } else {
2212
- throw new Error('No database secret found. Provide secretArn in database.postgres configuration or ensure Secrets Manager secret exists.');
2213
- }
2214
-
2215
- // Set DB_TYPE for Prisma client selection
2216
- definition.provider.environment.DB_TYPE = 'postgresql';
2217
-
2218
- console.log('✅ Existing Aurora cluster configured');
2219
- };
2220
-
2221
- const useDiscoveredAurora = (definition, AppDefinition, discoveredResources) => {
2222
- console.log(`🔍 Using discovered Aurora cluster: ${discoveredResources.aurora.clusterIdentifier}`);
2223
- useExistingAurora(definition, AppDefinition, discoveredResources);
2224
- };
2225
-
2226
- const configurePostgres = (definition, AppDefinition, discoveredResources) => {
2227
- if (!AppDefinition.database?.postgres?.enable) {
2228
- return;
2229
- }
2230
-
2231
- // Validate VPC is enabled (required for Aurora deployment)
2232
- if (!AppDefinition.vpc?.enable) {
2233
- throw new Error(
2234
- 'Aurora PostgreSQL requires VPC deployment. ' +
2235
- 'Set vpc.enable to true in your app definition.'
2236
- );
2237
- }
2238
-
2239
- // Validate private subnets exist (Aurora requires at least 2 subnets in different AZs)
2240
- // Skip validation if VPC management is 'create-new' (subnets will be created)
2241
- const vpcManagement = AppDefinition.vpc?.management || 'discover';
2242
- if (vpcManagement !== 'create-new' && (!discoveredResources.privateSubnetId1 || !discoveredResources.privateSubnetId2)) {
2243
- throw new Error(
2244
- 'Aurora PostgreSQL requires at least 2 private subnets in different availability zones. ' +
2245
- 'No private subnets were discovered in your VPC. ' +
2246
- 'Please create private subnets or use VPC management mode "create-new".'
2247
- );
2248
- }
2249
-
2250
- const dbConfig = AppDefinition.database.postgres;
2251
- const management = dbConfig.management || 'discover';
2252
-
2253
- console.log(`\n🐘 PostgreSQL Management Mode: ${management}`);
2254
-
2255
- if (management === 'create-new' || discoveredResources.aurora?.needsCreation) {
2256
- createAuroraInfrastructure(definition, AppDefinition, discoveredResources);
2257
- } else if (management === 'use-existing') {
2258
- if (!discoveredResources.aurora?.clusterIdentifier && !dbConfig.clusterIdentifier) {
2259
- throw new Error('PostgreSQL management is set to "use-existing" but no clusterIdentifier was found or provided');
2260
- }
2261
- useExistingAurora(definition, AppDefinition, discoveredResources);
2262
- } else {
2263
- // discover mode
2264
- if (discoveredResources.aurora?.clusterIdentifier) {
2265
- useDiscoveredAurora(definition, AppDefinition, discoveredResources);
2266
- } else {
2267
- throw new Error('No Aurora cluster found in discovery mode. Set management to "create-new" or provide clusterIdentifier with "use-existing".');
2268
- }
2269
- }
2270
- };
2271
-
2272
1915
  const configureSsm = (definition, AppDefinition) => {
2273
1916
  if (AppDefinition.ssm?.enable !== true) {
2274
1917
  return;
@@ -2312,8 +1955,9 @@ const attachIntegrations = (definition, AppDefinition) => {
2312
1955
  }
2313
1956
 
2314
1957
  const integrationName = integration.Definition.name;
2315
- const queueReference = `${integrationName.charAt(0).toUpperCase() + integrationName.slice(1)
2316
- }Queue`;
1958
+ const queueReference = `${
1959
+ integrationName.charAt(0).toUpperCase() + integrationName.slice(1)
1960
+ }Queue`;
2317
1961
  const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
2318
1962
 
2319
1963
  definition.functions[integrationName] = {
@@ -2369,7 +2013,10 @@ const attachIntegrations = (definition, AppDefinition) => {
2369
2013
 
2370
2014
  // Add webhook handler if enabled
2371
2015
  const webhookConfig = integration.Definition.webhooks;
2372
- if (webhookConfig && (webhookConfig === true || webhookConfig.enabled === true)) {
2016
+ if (
2017
+ webhookConfig &&
2018
+ (webhookConfig === true || webhookConfig.enabled === true)
2019
+ ) {
2373
2020
  const webhookFunctionName = `${integrationName}Webhook`;
2374
2021
 
2375
2022
  definition.functions[webhookFunctionName] = {
@@ -2409,40 +2056,9 @@ const configureWebsockets = (definition, AppDefinition) => {
2409
2056
  };
2410
2057
  };
2411
2058
 
2412
- /**
2413
- * Ensure Prisma Lambda Layer exists
2414
- * Automatically builds the layer if it doesn't exist in the project root
2415
- */
2416
- async function ensurePrismaLayerExists() {
2417
- const projectRoot = process.cwd();
2418
- const layerPath = path.join(projectRoot, 'layers/prisma');
2419
-
2420
- // Check if layer already exists
2421
- if (fs.existsSync(layerPath)) {
2422
- console.log('✓ Prisma Lambda Layer already exists at', layerPath);
2423
- return;
2424
- }
2425
-
2426
- // Layer doesn't exist - build it automatically
2427
- console.log('📦 Prisma Lambda Layer not found - building automatically...');
2428
- console.log(' This may take a minute on first deployment.\n');
2429
-
2430
- try {
2431
- await buildPrismaLayer();
2432
- console.log('✓ Prisma Lambda Layer built successfully\n');
2433
- } catch (error) {
2434
- console.error('✗ Failed to build Prisma Lambda Layer:', error.message);
2435
- console.error(' You may need to run: npm install @friggframework/core\n');
2436
- throw error;
2437
- }
2438
- }
2439
-
2440
2059
  const composeServerlessDefinition = async (AppDefinition) => {
2441
2060
  console.log('composeServerlessDefinition', AppDefinition);
2442
2061
 
2443
- // Ensure Prisma layer exists before generating serverless config
2444
- await ensurePrismaLayerExists();
2445
-
2446
2062
  const discoveredResources = await gatherDiscoveredResources(AppDefinition);
2447
2063
  const appEnvironmentVars = getAppEnvironmentVars(AppDefinition);
2448
2064
  const definition = createBaseDefinition(
@@ -2464,7 +2080,6 @@ const composeServerlessDefinition = async (AppDefinition) => {
2464
2080
  if (!isLocalBuild) {
2465
2081
  applyKmsConfiguration(definition, AppDefinition, discoveredResources);
2466
2082
  configureVpc(definition, AppDefinition, discoveredResources);
2467
- configurePostgres(definition, AppDefinition, discoveredResources);
2468
2083
  configureSsm(definition, AppDefinition);
2469
2084
  } else {
2470
2085
  console.log(