@stacksjs/cloud 0.62.0 → 0.63.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.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {ContactType, Route53Domains} from "@aws-sdk/client-route-53-domains";
10
10
  import {ListBucketsCommand, S3} from "@aws-sdk/client-s3";
11
11
  import {SSM} from "@aws-sdk/client-ssm";
12
12
  import {runCommand} from "@stacksjs/cli";
13
- import {config as config2} from "@stacksjs/config";
13
+ import {config as config3} from "@stacksjs/config";
14
14
  import {err, handleError, ok} from "@stacksjs/error-handling";
15
15
  import {log} from "@stacksjs/logging";
16
16
  import {path as p} from "@stacksjs/path";
@@ -97,7 +97,7 @@ async function getJumpBoxInstanceId(name) {
97
97
  }
98
98
  ]
99
99
  });
100
- if (data.Reservations?.[0].Instances?.[0])
100
+ if (data.Reservations?.[0]?.Instances?.[0])
101
101
  return data.Reservations[0].Instances[0].InstanceId;
102
102
  return;
103
103
  }
@@ -122,7 +122,7 @@ async function deleteJumpBox(stackName) {
122
122
  async function deleteIamUsers() {
123
123
  const iam = new IAM({ region: "us-east-1" });
124
124
  const data = await iam.listUsers({});
125
- const teamName = slug(config2.team.name);
125
+ const teamName = slug(config3.team.name);
126
126
  const users = data.Users?.filter((user) => {
127
127
  const userNameLower = user.UserName?.toLowerCase();
128
128
  return userNameLower !== "stacks" && userNameLower !== teamName.toLowerCase() && userNameLower?.includes(teamName.toLowerCase());
@@ -273,7 +273,7 @@ async function hasBeenDeployed() {
273
273
  const s3 = new S3({ region: "us-east-1" });
274
274
  try {
275
275
  const response = await s3.send(new ListBucketsCommand({}));
276
- return ok(response.Buckets?.some((bucket) => bucket.Name?.includes(config2.app.name?.toLocaleLowerCase() || "stacks")) || false);
276
+ return ok(response.Buckets?.some((bucket) => bucket.Name?.includes(config3.app.name?.toLocaleLowerCase() || "stacks")) || false);
277
277
  } catch (error) {
278
278
  console.error(error);
279
279
  return err(handleError("Error checking if the app has been deployed"));
@@ -307,7 +307,7 @@ async function addJumpBox(stackName) {
307
307
  const client = new EFSClient({ region: "us-east-1" });
308
308
  const command = new DescribeFileSystemsCommand({});
309
309
  const data = await client.send(command);
310
- const fileSystemName = `stacks-${config2.app.env}-efs`;
310
+ const fileSystemName = `stacks-${config3.app.env}-efs`;
311
311
  const fileSystem = data.FileSystems?.find((fs) => fs.Name === fileSystemName);
312
312
  const fileSystemId = fileSystem?.FileSystemId;
313
313
  if (!fileSystem || !fileSystemId)
@@ -360,7 +360,7 @@ async function getJumpBoxSecurityGroupName() {
360
360
  return err("Jump-box not found");
361
361
  const ec2 = new EC2({ region: "us-east-1" });
362
362
  const data = await ec2.describeInstances({ InstanceIds: [jumpBoxId] });
363
- if (data.Reservations?.[0].Instances?.[0]) {
363
+ if (data.Reservations?.[0]?.Instances?.[0]) {
364
364
  const instance = data.Reservations[0].Instances[0];
365
365
  const securityGroups = instance.SecurityGroups;
366
366
  if (securityGroups?.[0])
@@ -371,7 +371,7 @@ async function getJumpBoxSecurityGroupName() {
371
371
  async function getSecurityGroupFromInstanceId(instanceId) {
372
372
  const ec2 = new EC2({ region: "us-east-1" });
373
373
  const data = await ec2.describeInstances({ InstanceIds: [instanceId] });
374
- if (data.Reservations?.[0].Instances?.[0]) {
374
+ if (data.Reservations?.[0]?.Instances?.[0]) {
375
375
  const instance = data.Reservations[0].Instances[0];
376
376
  const securityGroups = instance.SecurityGroups;
377
377
  if (securityGroups?.[0])
@@ -416,14 +416,17 @@ async function getOrCreateTimestamp() {
416
416
  return timestamp;
417
417
  }
418
418
  }
419
- var appEnv = config2.app.env === "local" ? "dev" : config2.app.env;
419
+ async function getCloudFrontDistributionId() {
420
+ return "";
421
+ }
422
+ var appEnv = config3.app.env === "local" ? "dev" : config3.app.env;
420
423
  var cloudName = `stacks-cloud-${appEnv}`;
421
424
  // src/cloud/index.ts
422
- import {config as config20} from "@stacksjs/config";
425
+ import {config as config21} from "@stacksjs/config";
423
426
  import {Stack as Stack2} from "aws-cdk-lib";
424
427
 
425
428
  // src/cloud/ai.ts
426
- import {config as config4} from "@stacksjs/config";
429
+ import {config as config5} from "@stacksjs/config";
427
430
  import {Duration, CfnOutput as Output, aws_iam as iam, aws_lambda as lambda} from "aws-cdk-lib";
428
431
 
429
432
  class AiStack {
@@ -438,7 +441,7 @@ class AiStack {
438
441
  const bedrockAccessPolicy = new iam.PolicyStatement({
439
442
  effect: iam.Effect.ALLOW,
440
443
  actions: ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
441
- resources: config4.ai.models?.map((model) => `arn:aws:bedrock:us-east-1::foundation-model/${model}`)
444
+ resources: config5.ai.models?.map((model) => `arn:aws:bedrock:us-east-1::foundation-model/${model}`)
442
445
  });
443
446
  const bedrockAccessRole = new iam.Role(scope, "BedrockAccessRole", {
444
447
  assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
@@ -495,7 +498,7 @@ class AiStack {
495
498
  }
496
499
 
497
500
  // src/cloud/cdn.ts
498
- import {config as config6} from "@stacksjs/config";
501
+ import {config as config7} from "@stacksjs/config";
499
502
  import {env as env2} from "@stacksjs/env";
500
503
  import {path as p2} from "@stacksjs/path";
501
504
  import {hasFiles} from "@stacksjs/storage";
@@ -508,7 +511,6 @@ aws_cloudfront_origins as origins,
508
511
  aws_route53 as route53,
509
512
  aws_route53_targets as targets
510
513
  } from "aws-cdk-lib";
511
- import * as kinesis from "aws-cdk-lib/aws-kinesis";
512
514
 
513
515
  class CdnStack {
514
516
  distribution;
@@ -524,35 +526,15 @@ class CdnStack {
524
526
  this.cdnCachePolicy = new cloudfront.CachePolicy(scope, "CdnCachePolicy", {
525
527
  comment: "Stacks CDN Cache Policy",
526
528
  cachePolicyName: `${props.slug}-${props.appEnv}-cdn-cache-policy`,
527
- minTtl: config6.cloud.cdn?.minTtl ? Duration2.seconds(config6.cloud.cdn.minTtl) : undefined,
528
- defaultTtl: config6.cloud.cdn?.defaultTtl ? Duration2.seconds(config6.cloud.cdn.defaultTtl) : undefined,
529
- maxTtl: config6.cloud.cdn?.maxTtl ? Duration2.seconds(config6.cloud.cdn.maxTtl) : undefined,
530
- cookieBehavior: this.getCookieBehavior(config6.cloud.cdn?.cookieBehavior)
531
- });
532
- const logStream = new kinesis.Stream(scope, "StacksCdnRealtimeLogStream", {
533
- streamName: "StacksCdnRealtimeLogStream",
534
- retentionPeriod: Duration2.days(1),
535
- shardCount: 1,
536
- encryption: kinesis.StreamEncryption.UNENCRYPTED
537
- });
538
- this.realtimeLogConfig = new cloudfront.RealtimeLogConfig(scope, "StacksRealTimeLogConfig", {
539
- endPoints: [cloudfront.Endpoint.fromKinesisStream(logStream)],
540
- fields: [
541
- "timestamp",
542
- "c-ip",
543
- "cs-method",
544
- "cs-uri-stem",
545
- "cs-uri-query",
546
- "cs-referer",
547
- "cs-user-agent",
548
- "sc-status"
549
- ],
550
- samplingRate: 100
529
+ minTtl: config7.cloud.cdn?.minTtl ? Duration2.seconds(config7.cloud.cdn.minTtl) : undefined,
530
+ defaultTtl: config7.cloud.cdn?.defaultTtl ? Duration2.seconds(config7.cloud.cdn.defaultTtl) : undefined,
531
+ maxTtl: config7.cloud.cdn?.maxTtl ? Duration2.seconds(config7.cloud.cdn.maxTtl) : undefined,
532
+ cookieBehavior: this.getCookieBehavior(config7.cloud.cdn?.cookieBehavior)
551
533
  });
552
534
  this.distribution = new cloudfront.Distribution(scope, "Cdn", {
553
535
  domainNames: [props.domain],
554
536
  defaultRootObject: "index.html",
555
- comment: `CDN for ${config6.app.url}`,
537
+ comment: `CDN for ${config7.app.url}`,
556
538
  certificate: props.certificate,
557
539
  enableLogging: true,
558
540
  logBucket: props.logBucket,
@@ -563,7 +545,7 @@ class CdnStack {
563
545
  webAclId: props.firewall.attrArn,
564
546
  enableIpv6: true,
565
547
  defaultBehavior: {
566
- origin: new origins.S3Origin(props.publicBucket, {
548
+ origin: new origins.S3Origin(config7.app.docMode ? props.docsBucket : props.publicBucket, {
567
549
  originAccessIdentity: this.originAccessIdentity
568
550
  }),
569
551
  edgeLambdas: [
@@ -572,12 +554,11 @@ class CdnStack {
572
554
  functionVersion: props.originRequestFunction.currentVersion
573
555
  }
574
556
  ],
575
- compress: config6.cloud.cdn?.compress,
557
+ compress: config7.cloud.cdn?.compress,
576
558
  allowedMethods: this.allowedMethods(),
577
559
  cachedMethods: this.cachedMethods(),
578
560
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
579
- cachePolicy: this.cdnCachePolicy,
580
- realtimeLogConfig: this.realtimeLogConfig
561
+ cachePolicy: this.cdnCachePolicy
581
562
  },
582
563
  additionalBehaviors: this.additionalBehaviors(scope, props),
583
564
  errorResponses: [
@@ -614,13 +595,13 @@ class CdnStack {
614
595
  case "none":
615
596
  return cloudfront.CacheCookieBehavior.none();
616
597
  case "allowList":
617
- return cloudfront.CacheCookieBehavior.allowList(...config6.cloud.cdn?.allowList.cookies || []);
598
+ return cloudfront.CacheCookieBehavior.allowList(...config7.cloud.cdn?.allowList.cookies || []);
618
599
  default:
619
600
  return;
620
601
  }
621
602
  }
622
603
  allowedMethods() {
623
- switch (config6.cloud.cdn?.allowedMethods) {
604
+ switch (config7.cloud.cdn?.allowedMethods) {
624
605
  case "ALL":
625
606
  return cloudfront.AllowedMethods.ALLOW_ALL;
626
607
  case "GET_HEAD":
@@ -632,7 +613,7 @@ class CdnStack {
632
613
  }
633
614
  }
634
615
  cachedMethods() {
635
- switch (config6.cloud.cdn?.cachedMethods) {
616
+ switch (config7.cloud.cdn?.cachedMethods) {
636
617
  case "GET_HEAD":
637
618
  return cloudfront.CachedMethods.CACHE_GET_HEAD;
638
619
  case "GET_HEAD_OPTIONS":
@@ -668,19 +649,17 @@ class CdnStack {
668
649
  }
669
650
  }
670
651
  shouldDeployApi() {
671
- return config6.api?.deploy;
652
+ return config7.cloud.api?.deploy;
672
653
  }
673
654
  apiBehaviorOptions(scope, props) {
674
655
  const hostname = `api.${props.domain}`;
675
- const origin = () => {
676
- return new origins.HttpOrigin(hostname, {
677
- originPath: "/",
678
- protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY
679
- });
680
- };
656
+ const origin = new origins.HttpOrigin(hostname, {
657
+ originPath: "/",
658
+ protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY
659
+ });
681
660
  return {
682
661
  "/api": {
683
- origin: origin(),
662
+ origin,
684
663
  compress: true,
685
664
  allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
686
665
  cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
@@ -689,7 +668,7 @@ class CdnStack {
689
668
  realtimeLogConfig: this.realtimeLogConfig
690
669
  },
691
670
  "/api/*": {
692
- origin: origin(),
671
+ origin,
693
672
  compress: true,
694
673
  allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
695
674
  cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
@@ -699,28 +678,28 @@ class CdnStack {
699
678
  }
700
679
  };
701
680
  }
702
- docsBehaviorOptions(props) {
681
+ docsBehaviorOptions(docsBucket) {
682
+ if (!docsBucket)
683
+ return {};
684
+ const origin = new origins.S3Origin(docsBucket, {
685
+ originAccessIdentity: this.originAccessIdentity,
686
+ originPath: "/"
687
+ });
703
688
  return {
704
689
  "/docs": {
705
- origin: new origins.S3Origin(props.publicBucket, {
706
- originAccessIdentity: this.originAccessIdentity,
707
- originPath: "/docs"
708
- }),
690
+ origin,
709
691
  compress: true,
710
- allowedMethods: this.allowedMethodsFromString(config6.cloud.cdn?.allowedMethods),
711
- cachedMethods: this.cachedMethodsFromString(config6.cloud.cdn?.cachedMethods),
692
+ allowedMethods: this.allowedMethodsFromString(config7.cloud.cdn?.allowedMethods),
693
+ cachedMethods: this.cachedMethodsFromString(config7.cloud.cdn?.cachedMethods),
712
694
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
713
695
  cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
714
696
  realtimeLogConfig: this.realtimeLogConfig
715
697
  },
716
698
  "/docs/*": {
717
- origin: new origins.S3Origin(props.publicBucket, {
718
- originAccessIdentity: this.originAccessIdentity,
719
- originPath: "/docs"
720
- }),
699
+ origin,
721
700
  compress: true,
722
- allowedMethods: this.allowedMethodsFromString(config6.cloud.cdn?.allowedMethods),
723
- cachedMethods: this.cachedMethodsFromString(config6.cloud.cdn?.cachedMethods),
701
+ allowedMethods: this.allowedMethodsFromString(config7.cloud.cdn?.allowedMethods),
702
+ cachedMethods: this.cachedMethodsFromString(config7.cloud.cdn?.cachedMethods),
724
703
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
725
704
  cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
726
705
  realtimeLogConfig: this.realtimeLogConfig
@@ -787,13 +766,13 @@ class CdnStack {
787
766
  };
788
767
  }
789
768
  shouldDeployAiEndpoints() {
790
- return config6.cloud.ai;
769
+ return config7.cloud.ai;
791
770
  }
792
771
  shouldDeployCliSetup() {
793
- return config6.cloud.cli;
772
+ return config7.cloud.cli;
794
773
  }
795
774
  shouldDeployDocs() {
796
- return hasFiles(p2.projectPath("docs"));
775
+ return hasFiles(p2.projectPath("docs")) && !config7.app.docMode;
797
776
  }
798
777
  additionalBehaviors(scope, props) {
799
778
  let behaviorOptions = {};
@@ -821,9 +800,9 @@ class CdnStack {
821
800
  keysToRemove.forEach((key) => delete env2[key]);
822
801
  behaviorOptions = this.apiBehaviorOptions(scope, props);
823
802
  }
824
- if (this.shouldDeployDocs() && !config6.app.docMode) {
803
+ if (this.shouldDeployDocs()) {
825
804
  behaviorOptions = {
826
- ...this.docsBehaviorOptions(props),
805
+ ...this.docsBehaviorOptions(props.docsBucket),
827
806
  ...behaviorOptions
828
807
  };
829
808
  }
@@ -926,7 +905,7 @@ class ComputeStack {
926
905
  });
927
906
  const container = this.taskDefinition.addContainer("WebServerContainer", {
928
907
  containerName: `${props.appName}-${props.appEnv}-api`,
929
- image: ecs.ContainerImage.fromAsset(p3.frameworkPath("server")),
908
+ image: ecs.ContainerImage.fromAsset(p3.frameworkCloudPath()),
930
909
  logging: new ecs.AwsLogDriver({
931
910
  streamPrefix: `${props.appName}-${props.appEnv}-web-server-logs`,
932
911
  logGroup: new LogGroup(scope, "StacksApiLogs", {
@@ -1069,8 +1048,12 @@ class ComputeStack {
1069
1048
  generateStringKey: Object.keys(env4).join(",").length.toString()
1070
1049
  }
1071
1050
  });
1072
- secrets.grantRead(service.taskDefinition.executionRole);
1073
- container.addEnvironment("SECRETS_ARN", secrets.secretArn);
1051
+ if (service.taskDefinition.executionRole) {
1052
+ secrets.grantRead(service.taskDefinition.executionRole);
1053
+ container.addEnvironment("SECRETS_ARN", secrets.secretArn);
1054
+ } else {
1055
+ throw new Error("Service task execution role is undefined.");
1056
+ }
1074
1057
  const apiPrefix = "api";
1075
1058
  new Output4(scope, "ApiUrl", {
1076
1059
  value: `https://${props.domain}/${apiPrefix}`,
@@ -1084,21 +1067,23 @@ class ComputeStack {
1084
1067
  }
1085
1068
 
1086
1069
  // src/cloud/deployment.ts
1087
- import {config as config8} from "@stacksjs/config";
1088
- import {websiteSourceHash} from "@stacksjs/utils";
1070
+ import {config as config9} from "@stacksjs/config";
1071
+ import {path as p4} from "@stacksjs/path";
1072
+ import {hasFiles as hasFiles2} from "@stacksjs/storage";
1073
+ import {docsSourceHash, websiteSourceHash} from "@stacksjs/utils";
1089
1074
  import {AssetHashType, aws_s3_deployment as s3deploy} from "aws-cdk-lib";
1090
1075
 
1091
1076
  class DeploymentStack {
1092
1077
  privateSource;
1093
1078
  docsSource;
1094
- websiteSource;
1079
+ publicSource;
1095
1080
  constructor(scope, props) {
1096
1081
  this.privateSource = "../../private";
1097
1082
  this.docsSource = "../docs/dist/";
1098
- this.websiteSource = config8.app.docMode === true ? this.docsSource : "../views/dist/";
1083
+ this.publicSource = config9.app.docMode === true ? this.docsSource : "../views/web/dist/";
1099
1084
  new s3deploy.BucketDeployment(scope, "Website", {
1100
1085
  sources: [
1101
- s3deploy.Source.asset(this.websiteSource, {
1086
+ s3deploy.Source.asset(this.publicSource, {
1102
1087
  assetHash: websiteSourceHash(),
1103
1088
  assetHashType: AssetHashType.CUSTOM
1104
1089
  })
@@ -1107,10 +1092,30 @@ class DeploymentStack {
1107
1092
  distribution: props.cdn,
1108
1093
  distributionPaths: ["/*"]
1109
1094
  });
1110
- new s3deploy.BucketDeployment(scope, "PrivateFiles", {
1111
- sources: [s3deploy.Source.asset(this.privateSource)],
1112
- destinationBucket: props.privateBucket
1113
- });
1095
+ if (this.shouldDeployDocs()) {
1096
+ new s3deploy.BucketDeployment(scope, "Docs", {
1097
+ sources: [
1098
+ s3deploy.Source.asset(this.docsSource, {
1099
+ assetHash: docsSourceHash(),
1100
+ assetHashType: AssetHashType.CUSTOM
1101
+ })
1102
+ ],
1103
+ destinationBucket: props.docsBucket,
1104
+ distribution: props.cdn,
1105
+ distributionPaths: ["/docs/*"]
1106
+ });
1107
+ }
1108
+ if (hasFiles2(this.privateSource)) {
1109
+ new s3deploy.BucketDeployment(scope, "PrivateFiles", {
1110
+ sources: [s3deploy.Source.asset(this.privateSource)],
1111
+ destinationBucket: props.privateBucket
1112
+ });
1113
+ } else {
1114
+ console.error(`The path ${this.privateSource} does not have any files`);
1115
+ }
1116
+ }
1117
+ shouldDeployDocs() {
1118
+ return hasFiles2(p4.projectPath("docs")) && !config9.app.docMode;
1114
1119
  }
1115
1120
  }
1116
1121
 
@@ -1146,29 +1151,29 @@ class DnsStack {
1146
1151
  }
1147
1152
 
1148
1153
  // src/cloud/docs.ts
1149
- import {config as config10} from "@stacksjs/config";
1150
- import {path as p4} from "@stacksjs/path";
1151
- import {storage as storage3} from "@stacksjs/storage";
1154
+ import {config as config11} from "@stacksjs/config";
1155
+ import {path as p5} from "@stacksjs/path";
1156
+ import {storage as storage4} from "@stacksjs/storage";
1152
1157
  import {originRequestFunctionHash} from "@stacksjs/utils";
1153
1158
  import {AssetHashType as AssetHashType2, CfnOutput as Output5, RemovalPolicy as RemovalPolicy3, aws_lambda as lambda3} from "aws-cdk-lib";
1154
1159
 
1155
1160
  class DocsStack {
1156
1161
  originRequestFunction;
1157
1162
  constructor(scope, props) {
1158
- const docsPrefix = config10.app.docMode ? "" : config10.docs.base;
1163
+ const docsPrefix = "docs";
1159
1164
  this.originRequestFunction = new lambda3.Function(scope, "OriginRequestFunction", {
1160
1165
  functionName: `${props.slug}-${props.appEnv}-origin-request-${props.timestamp}`,
1161
1166
  description: "The Stacks Origin Request function that prettifies URLs",
1162
1167
  runtime: lambda3.Runtime.NODEJS_18_X,
1163
1168
  handler: "dist/origin-request.handler",
1164
- code: lambda3.Code.fromAsset(p4.frameworkCloudPath("dist.zip"), {
1169
+ code: lambda3.Code.fromAsset(p5.frameworkCloudPath("dist.zip"), {
1165
1170
  assetHash: originRequestFunctionHash(),
1166
1171
  assetHashType: AssetHashType2.CUSTOM
1167
1172
  })
1168
1173
  });
1169
1174
  const cfnOriginRequestFunction = this.originRequestFunction.node.defaultChild;
1170
1175
  cfnOriginRequestFunction.applyRemovalPolicy(RemovalPolicy3.RETAIN);
1171
- if (!config10.app.docMode && storage3.hasFiles(p4.projectPath("docs"))) {
1176
+ if (!config11.app.docMode && storage4.hasFiles(p5.projectPath("docs"))) {
1172
1177
  new Output5(scope, "DocsUrl", {
1173
1178
  value: `https://${props.domain}/${docsPrefix}`,
1174
1179
  description: "The URL of the deployed documentation"
@@ -1178,7 +1183,7 @@ class DocsStack {
1178
1183
  }
1179
1184
 
1180
1185
  // src/cloud/email.ts
1181
- import {config as config12} from "@stacksjs/config";
1186
+ import {config as config13} from "@stacksjs/config";
1182
1187
  import {
1183
1188
  Duration as Duration5,
1184
1189
  RemovalPolicy as RemovalPolicy4,
@@ -1269,8 +1274,8 @@ class EmailStack {
1269
1274
  }
1270
1275
  }
1271
1276
  ],
1272
- recipients: config12.email.mailboxes || [],
1273
- scanEnabled: config12.email.server?.scan || true,
1277
+ recipients: config13.email.mailboxes || [],
1278
+ scanEnabled: config13.email.server?.scan || true,
1274
1279
  tlsPolicy: "Require"
1275
1280
  }
1276
1281
  });
@@ -1548,15 +1553,15 @@ class NetworkStack {
1548
1553
  }
1549
1554
 
1550
1555
  // src/cloud/permissions.ts
1551
- import {config as config14} from "@stacksjs/config";
1556
+ import {config as config15} from "@stacksjs/config";
1552
1557
  import {env as env6} from "@stacksjs/env";
1553
1558
  import {string} from "@stacksjs/strings";
1554
1559
  import {SecretValue, aws_iam as iam4} from "aws-cdk-lib";
1555
1560
 
1556
1561
  class PermissionsStack {
1557
1562
  constructor(scope) {
1558
- const teamName = config14.team.name;
1559
- const users = config14.team.members;
1563
+ const teamName = config15.team.name;
1564
+ const users = config15.team.members;
1560
1565
  const password = env6.AWS_DEFAULT_PASSWORD || string.random();
1561
1566
  for (const name in users) {
1562
1567
  const id = `User${string.pascalCase(teamName)}${string.pascalCase(name)}`;
@@ -1572,7 +1577,7 @@ class PermissionsStack {
1572
1577
  }
1573
1578
 
1574
1579
  // src/cloud/queue.ts
1575
- import {path as path6} from "@stacksjs/path";
1580
+ import {path as path7} from "@stacksjs/path";
1576
1581
  import {fs} from "@stacksjs/storage";
1577
1582
  import {pascalCase, slug as slug2} from "@stacksjs/strings";
1578
1583
  import {aws_ec2 as ec24} from "aws-cdk-lib";
@@ -1587,9 +1592,9 @@ class QueueStack {
1587
1592
  this.props = props;
1588
1593
  }
1589
1594
  async init() {
1590
- const jobsDir = path6.jobsPath();
1591
- const actionsDir = path6.appPath("Actions");
1592
- const ormActionDir = path6.projectStoragePath("framework/orm/Actions");
1595
+ const jobsDir = path7.jobsPath();
1596
+ const actionsDir = path7.appPath("Actions");
1597
+ const ormActionDir = path7.builtUserActionsPath("src");
1593
1598
  const jobFiles = await fs.readdir(jobsDir);
1594
1599
  const actionFiles = await fs.readdir(actionsDir);
1595
1600
  const ormActionFiles = await fs.readdir(ormActionDir);
@@ -1597,7 +1602,7 @@ class QueueStack {
1597
1602
  for (const file of jobFiles) {
1598
1603
  if (!file.endsWith(".ts"))
1599
1604
  continue;
1600
- const jobPath = path6.jobsPath(file);
1605
+ const jobPath = path7.jobsPath(file);
1601
1606
  const job = await this.loadModule(jobPath);
1602
1607
  this.createQueueRule(job, file);
1603
1608
  jobs.push(job);
@@ -1605,7 +1610,7 @@ class QueueStack {
1605
1610
  for (const ormFile of ormActionFiles) {
1606
1611
  if (!ormFile.endsWith(".ts"))
1607
1612
  continue;
1608
- const ormActionPath = path6.projectStoragePath(`framework/orm/Actions/${ormFile}`);
1613
+ const ormActionPath = path7.builtUserActionsPath(ormFile);
1609
1614
  const ormAction = await this.loadModule(ormActionPath);
1610
1615
  this.createQueueRule(ormAction, ormFile);
1611
1616
  jobs.push(ormAction);
@@ -1613,8 +1618,7 @@ class QueueStack {
1613
1618
  for (const file of actionFiles) {
1614
1619
  if (!file.endsWith(".ts"))
1615
1620
  continue;
1616
- const actionPath = path6.appPath(`Actions/${file}`);
1617
- console.log(actionPath);
1621
+ const actionPath = path7.appPath(`Actions/${file}`);
1618
1622
  const action = await this.loadModule(actionPath);
1619
1623
  this.createQueueRule(action, file);
1620
1624
  jobs.push(action);
@@ -1687,13 +1691,13 @@ class QueueStack {
1687
1691
  }
1688
1692
 
1689
1693
  // src/cloud/redirects.ts
1690
- import {config as config16} from "@stacksjs/config";
1694
+ import {config as config17} from "@stacksjs/config";
1691
1695
  import {RemovalPolicy as RemovalPolicy6, aws_route53 as route535, aws_s3 as s33} from "aws-cdk-lib";
1692
1696
 
1693
1697
  class RedirectsStack {
1694
1698
  redirectZones = [];
1695
1699
  constructor(scope, props) {
1696
- config16.dns.redirects?.forEach((redirect) => {
1700
+ config17.dns.redirects?.forEach((redirect) => {
1697
1701
  const slug3 = redirect.split(".").map((part, index) => index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
1698
1702
  const hostedZone = route535.HostedZone.fromLookup(scope, "HostedZone", {
1699
1703
  domainName: redirect
@@ -1713,7 +1717,7 @@ class RedirectsStack {
1713
1717
  domainName: redirectBucket.bucketWebsiteDomainName
1714
1718
  });
1715
1719
  });
1716
- config16.dns.redirects?.forEach((redirect) => {
1720
+ config17.dns.redirects?.forEach((redirect) => {
1717
1721
  const slug3 = redirect.split(".").map((part, index) => index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)).join("");
1718
1722
  const hostedZone = route535.HostedZone.fromLookup(scope, `RedirectHostedZone${slug3}`, { domainName: redirect });
1719
1723
  this.redirectZones.push(hostedZone);
@@ -1722,7 +1726,7 @@ class RedirectsStack {
1722
1726
  }
1723
1727
 
1724
1728
  // src/cloud/security.ts
1725
- import {config as config18} from "@stacksjs/config";
1729
+ import {config as config19} from "@stacksjs/config";
1726
1730
  import {
1727
1731
  Duration as Duration6,
1728
1732
  RemovalPolicy as RemovalPolicy7,
@@ -1737,7 +1741,7 @@ class SecurityStack {
1737
1741
  kmsKey;
1738
1742
  certificate;
1739
1743
  constructor(scope, props) {
1740
- const firewallOptions = config18.cloud.firewall;
1744
+ const firewallOptions = config19.cloud.firewall;
1741
1745
  if (!firewallOptions)
1742
1746
  throw new Error("No firewall options found in config");
1743
1747
  const options = {
@@ -1772,12 +1776,16 @@ class SecurityStack {
1772
1776
  getFirewallRules(scope) {
1773
1777
  const rules = [];
1774
1778
  const priorities = [];
1775
- if (config18.security.firewall?.countryCodes?.length) {
1779
+ if (config19.security.firewall?.countryCodes?.length) {
1776
1780
  priorities.push(1);
1777
1781
  rules.push({
1778
1782
  name: "CountryRule",
1779
1783
  priority: priorities.length,
1780
- statement: {},
1784
+ statement: {
1785
+ geoMatchStatement: {
1786
+ countryCodes: config19.security.firewall.countryCodes
1787
+ }
1788
+ },
1781
1789
  action: {
1782
1790
  block: {}
1783
1791
  },
@@ -1788,12 +1796,12 @@ class SecurityStack {
1788
1796
  }
1789
1797
  });
1790
1798
  }
1791
- if (config18.security.firewall?.ipAddresses?.length) {
1799
+ if (config19.security.firewall?.ipAddresses?.length) {
1792
1800
  const ipSet = new wafv2.CfnIPSet(scope, "IpSet", {
1793
1801
  name: "IpSet",
1794
1802
  description: "IP Set",
1795
1803
  scope: "CLOUDFRONT",
1796
- addresses: config18.security.firewall.ipAddresses,
1804
+ addresses: config19.security.firewall.ipAddresses,
1797
1805
  ipAddressVersion: "IPV4"
1798
1806
  });
1799
1807
  priorities.push(1);
@@ -1815,8 +1823,8 @@ class SecurityStack {
1815
1823
  }
1816
1824
  });
1817
1825
  }
1818
- if (config18.security.firewall?.httpHeaders?.length) {
1819
- config18.security.firewall.httpHeaders.forEach((header, index) => {
1826
+ if (config19.security.firewall?.httpHeaders?.length) {
1827
+ config19.security.firewall.httpHeaders.forEach((header, index) => {
1820
1828
  priorities.push(1);
1821
1829
  rules.push({
1822
1830
  name: `HttpHeaderRule${index}`,
@@ -1832,7 +1840,7 @@ class SecurityStack {
1832
1840
  searchString: "true",
1833
1841
  textTransformations: [
1834
1842
  {
1835
- priority: index,
1843
+ priority: 0,
1836
1844
  type: "NONE"
1837
1845
  }
1838
1846
  ]
@@ -1854,11 +1862,14 @@ class SecurityStack {
1854
1862
  }
1855
1863
 
1856
1864
  // src/cloud/storage.ts
1865
+ import {path as p6} from "@stacksjs/path";
1866
+ import {hasFiles as hasFiles3} from "@stacksjs/storage";
1857
1867
  import {RemovalPolicy as RemovalPolicy8, Tags as Tags3, aws_backup as backup, aws_iam as iam5, aws_s3 as s34} from "aws-cdk-lib";
1858
1868
 
1859
1869
  class StorageStack {
1860
1870
  publicBucket;
1861
1871
  privateBucket;
1872
+ docsBucket;
1862
1873
  logBucket;
1863
1874
  bucketPrefix;
1864
1875
  vault;
@@ -1874,6 +1885,16 @@ class StorageStack {
1874
1885
  encryption: s34.BucketEncryption.S3_MANAGED
1875
1886
  });
1876
1887
  Tags3.of(this.publicBucket).add("daily-backup", "true");
1888
+ if (this.shouldDeployDocs()) {
1889
+ this.docsBucket = new s34.Bucket(scope, "DocsBucket", {
1890
+ bucketName: `${this.bucketPrefix}-docs-${props.timestamp}`,
1891
+ versioned: true,
1892
+ autoDeleteObjects: true,
1893
+ removalPolicy: RemovalPolicy8.DESTROY,
1894
+ encryption: s34.BucketEncryption.S3_MANAGED
1895
+ });
1896
+ Tags3.of(this.docsBucket).add("weekly-backup", "true");
1897
+ }
1877
1898
  this.privateBucket = new s34.Bucket(scope, "PrivateBucket", {
1878
1899
  bucketName: `${this.bucketPrefix}-private-${props.timestamp}`,
1879
1900
  versioned: true,
@@ -1981,6 +2002,9 @@ class StorageStack {
1981
2002
  }));
1982
2003
  return backupRole;
1983
2004
  }
2005
+ shouldDeployDocs() {
2006
+ return hasFiles3(p6.projectPath("docs")) || config.app.docMode;
2007
+ }
1984
2008
  }
1985
2009
 
1986
2010
  // src/cloud/index.ts
@@ -2037,8 +2061,8 @@ class Cloud extends Stack2 {
2037
2061
  this.cli = new CliStack(this, props);
2038
2062
  }
2039
2063
  async init() {
2040
- if (config20.api?.deploy) {
2041
- const props = this.props;
2064
+ const props = this.props;
2065
+ if (this.shouldDeployApi()) {
2042
2066
  this.api = new ComputeStack(this, {
2043
2067
  ...props,
2044
2068
  vpc: this.network.vpc,
@@ -2052,26 +2076,31 @@ class Cloud extends Stack2 {
2052
2076
  taskDefinition: this.api.taskDefinition
2053
2077
  });
2054
2078
  await this.queue.init();
2055
- this.cdn = new CdnStack(this, {
2056
- ...props,
2057
- publicBucket: this.storage.publicBucket,
2058
- logBucket: this.storage.logBucket,
2059
- certificate: this.security.certificate,
2060
- firewall: this.security.firewall,
2061
- originRequestFunction: this.docs.originRequestFunction,
2062
- zone: this.dns.zone,
2063
- cliSetupUrl: this.cli.cliSetupUrl,
2064
- askAiUrl: this.ai.askAiUrl,
2065
- summarizeAiUrl: this.ai.summarizeAiUrl,
2066
- lb: this.api?.lb
2067
- });
2068
- this.deployment = new DeploymentStack(this, {
2069
- ...props,
2070
- publicBucket: this.storage.publicBucket,
2071
- privateBucket: this.storage.privateBucket,
2072
- cdn: this.cdn.distribution
2073
- });
2074
2079
  }
2080
+ this.cdn = new CdnStack(this, {
2081
+ ...props,
2082
+ publicBucket: this.storage.publicBucket,
2083
+ docsBucket: this.storage.docsBucket,
2084
+ logBucket: this.storage.logBucket,
2085
+ certificate: this.security.certificate,
2086
+ firewall: this.security.firewall,
2087
+ originRequestFunction: this.docs.originRequestFunction,
2088
+ zone: this.dns.zone,
2089
+ cliSetupUrl: this.cli.cliSetupUrl,
2090
+ askAiUrl: this.ai.askAiUrl,
2091
+ summarizeAiUrl: this.ai.summarizeAiUrl,
2092
+ lb: this.api?.lb
2093
+ });
2094
+ this.deployment = new DeploymentStack(this, {
2095
+ ...props,
2096
+ publicBucket: this.storage.publicBucket,
2097
+ privateBucket: this.storage.privateBucket,
2098
+ docsBucket: this.storage.docsBucket,
2099
+ cdn: this.cdn.distribution
2100
+ });
2101
+ }
2102
+ shouldDeployApi() {
2103
+ return config21.cloud.api?.deploy;
2075
2104
  }
2076
2105
  }
2077
2106
  export {
@@ -2085,6 +2114,7 @@ export {
2085
2114
  getJumpBoxSecurityGroupName,
2086
2115
  getJumpBoxInstanceProfileName,
2087
2116
  getJumpBoxInstanceId,
2117
+ getCloudFrontDistributionId,
2088
2118
  deleteStacksFunctions,
2089
2119
  deleteStacksBuckets,
2090
2120
  deleteParameterStore,
@@ -2097,3 +2127,6 @@ export {
2097
2127
  InstanceType,
2098
2128
  Cloud
2099
2129
  };
2130
+
2131
+ //# debugId=B17D4D8836F8DA4664756E2164756E21
2132
+ //# sourceMappingURL=index.js.map