@stacksjs/cloud 0.58.50 → 0.58.52

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/cloud",
3
3
  "type": "module",
4
- "version": "0.58.50",
4
+ "version": "0.58.52",
5
5
  "description": "The Stacks cloud/serverless integration & implementation.",
6
6
  "author": "Chris Breuer",
7
7
  "license": "MIT",
@@ -68,7 +68,6 @@
68
68
  "@aws-sdk/client-ses": "^3.504.0",
69
69
  "@aws-sdk/client-sesv2": "^3.504.0",
70
70
  "@aws-sdk/client-ssm": "^3.504.0",
71
- "@aws-sdk/lib-dynamodb": "^3.506.0",
72
71
  "@stacksjs/config": "latest",
73
72
  "@stacksjs/env": "latest",
74
73
  "@stacksjs/logging": "latest",
@@ -114,7 +113,7 @@
114
113
  "@stacksjs/development": "latest",
115
114
  "@stacksjs/env": "latest",
116
115
  "jszip": "^3.10.1",
117
- "oclif": "^4.4.2",
116
+ "oclif": "^4.4.4",
118
117
  "source-map-support": "^0.5.21"
119
118
  }
120
119
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-sdk-layer",
3
- "version": "0.58.50",
3
+ "version": "0.58.52",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "license": "ISC",
package/src/cloud/cdn.ts CHANGED
@@ -6,6 +6,8 @@ import { config } from '@stacksjs/config'
6
6
  import { hasFiles } from '@stacksjs/storage'
7
7
  import { path as p } from '@stacksjs/path'
8
8
  import { env } from '@stacksjs/env'
9
+ import type { ApplicationLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancingv2'
10
+ import * as kinesis from 'aws-cdk-lib/aws-kinesis'
9
11
  import type { NestedCloudProps } from '../types'
10
12
  import type { EnvKey } from '../../../../env'
11
13
 
@@ -21,6 +23,7 @@ export interface CdnStackProps extends NestedCloudProps {
21
23
  cliSetupUrl: lambda.FunctionUrl
22
24
  askAiUrl: lambda.FunctionUrl
23
25
  summarizeAiUrl: lambda.FunctionUrl
26
+ lb?: ApplicationLoadBalancer
24
27
  }
25
28
 
26
29
  export class CdnStack {
@@ -29,6 +32,7 @@ export class CdnStack {
29
32
  cdnCachePolicy: cloudfront.CachePolicy
30
33
  apiCachePolicy: cloudfront.CachePolicy | undefined
31
34
  vanityUrl: string
35
+ realtimeLogConfig: cloudfront.RealtimeLogConfig
32
36
  props: CdnStackProps
33
37
 
34
38
  constructor(scope: Construct, props: CdnStackProps) {
@@ -45,6 +49,47 @@ export class CdnStack {
45
49
  cookieBehavior: this.getCookieBehavior(config.cloud.cdn?.cookieBehavior),
46
50
  })
47
51
 
52
+ // Step 1: Create a Kinesis Firehose delivery stream for the logs
53
+ const logStream = new kinesis.Stream(scope, 'StacksCdnRealtimeLogStream', {
54
+ streamName: 'StacksCdnRealtimeLogStream',
55
+ retentionPeriod: Duration.days(1),
56
+ shardCount: 1,
57
+ encryption: kinesis.StreamEncryption.UNENCRYPTED,
58
+ })
59
+
60
+ // Create an IAM role for CloudFront to write logs to Kinesis Firehose
61
+ // new iam.Role(scope, 'LoggingRole', {
62
+ // assumedBy: new iam.ServicePrincipal('cloudfront.amazonaws.com'),
63
+ // inlinePolicies: {
64
+ // loggingPolicy: new iam.PolicyDocument({
65
+ // statements: [
66
+ // new iam.PolicyStatement({
67
+ // actions: ['kinesis:PutRecord', 'kinesis:PutRecordBatch'],
68
+ // resources: [logStream.streamArn],
69
+ // }),
70
+ // ],
71
+ // }),
72
+ // },
73
+ // })
74
+
75
+ // TODO: make this configurable
76
+ this.realtimeLogConfig = new cloudfront.RealtimeLogConfig(scope, 'StacksRealTimeLogConfig', {
77
+ endPoints: [
78
+ cloudfront.Endpoint.fromKinesisStream(logStream),
79
+ ],
80
+ fields: [
81
+ 'timestamp',
82
+ 'c-ip',
83
+ 'cs-method',
84
+ 'cs-uri-stem',
85
+ 'cs-uri-query',
86
+ 'cs-referer',
87
+ 'cs-user-agent',
88
+ 'sc-status',
89
+ ],
90
+ samplingRate: 100, // Adjust the sampling rate as needed
91
+ })
92
+
48
93
  // the actual CDN distribution
49
94
  this.distribution = new cloudfront.Distribution(scope, 'Cdn', {
50
95
  domainNames: [props.domain],
@@ -75,6 +120,7 @@ export class CdnStack {
75
120
  cachedMethods: this.cachedMethods(),
76
121
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
77
122
  cachePolicy: this.cdnCachePolicy,
123
+ realtimeLogConfig: this.realtimeLogConfig,
78
124
  },
79
125
 
80
126
  additionalBehaviors: this.additionalBehaviors(scope, props),
@@ -188,10 +234,11 @@ export class CdnStack {
188
234
  }
189
235
 
190
236
  apiBehaviorOptions(scope: Construct, props: CdnStackProps): Record<string, cloudfront.BehaviorOptions> {
191
- const hostname = Fn.select(2, Fn.split('/', props.webServerUrl!.url))
192
- const origin = (path: '/api' | '/api/*' = '/api') => {
237
+ const hostname = `api.${props.domain}`
238
+
239
+ const origin = () => {
193
240
  return new origins.HttpOrigin(hostname, {
194
- originPath: path,
241
+ originPath: '/',
195
242
  protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
196
243
  })
197
244
  }
@@ -204,14 +251,16 @@ export class CdnStack {
204
251
  cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
205
252
  cachePolicy: this.setApiCachePolicy(scope),
206
253
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
254
+ realtimeLogConfig: this.realtimeLogConfig,
207
255
  },
208
256
  '/api/*': {
209
- origin: origin('/api/*'),
257
+ origin: origin(),
210
258
  compress: true,
211
259
  allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
212
260
  cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
213
261
  cachePolicy: this.apiCachePolicy,
214
262
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
263
+ realtimeLogConfig: this.realtimeLogConfig,
215
264
  },
216
265
  }
217
266
  }
@@ -228,6 +277,7 @@ export class CdnStack {
228
277
  cachedMethods: this.cachedMethodsFromString(config.cloud.cdn?.cachedMethods),
229
278
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
230
279
  cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
280
+ realtimeLogConfig: this.realtimeLogConfig,
231
281
  },
232
282
  '/docs/*': {
233
283
  origin: new origins.S3Origin(props.publicBucket, {
@@ -239,6 +289,7 @@ export class CdnStack {
239
289
  cachedMethods: this.cachedMethodsFromString(config.cloud.cdn?.cachedMethods),
240
290
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
241
291
  cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
292
+ realtimeLogConfig: this.realtimeLogConfig,
242
293
  },
243
294
  }
244
295
  }
@@ -267,6 +318,7 @@ export class CdnStack {
267
318
  allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
268
319
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
269
320
  cachePolicy: aiCachePolicy,
321
+ realtimeLogConfig: this.realtimeLogConfig,
270
322
  },
271
323
  '/ai/summary': {
272
324
  origin: new origins.HttpOrigin(summaryHostname, {
@@ -277,6 +329,7 @@ export class CdnStack {
277
329
  allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
278
330
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
279
331
  cachePolicy: aiCachePolicy,
332
+ realtimeLogConfig: this.realtimeLogConfig,
280
333
  },
281
334
  }
282
335
  }
@@ -287,7 +340,6 @@ export class CdnStack {
287
340
  return {
288
341
  '/install': {
289
342
  origin: new origins.HttpOrigin(hostname, {
290
- // origin: new origins.HttpOrigin('tipevv3dfx35fb7ptyq7nrxtga0qkcgc.lambda-url.us-east-1.on.aws', {
291
343
  originPath: '/cli-setup',
292
344
  protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
293
345
  }),
@@ -303,6 +355,7 @@ export class CdnStack {
303
355
  headerBehavior: cloudfront.CacheHeaderBehavior.none(),
304
356
  queryStringBehavior: cloudfront.CacheQueryStringBehavior.none(),
305
357
  }),
358
+ realtimeLogConfig: this.realtimeLogConfig, // we potentially want to allow for tracking 100% of the traffic here?
306
359
  },
307
360
  }
308
361
  }
@@ -326,7 +379,7 @@ export class CdnStack {
326
379
  const keysToRemove = ['_HANDLER', '_X_AMZN_TRACE_ID', 'AWS_REGION', 'AWS_EXECUTION_ENV', 'AWS_LAMBDA_FUNCTION_NAME', 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE', 'AWS_LAMBDA_FUNCTION_VERSION', 'AWS_LAMBDA_INITIALIZATION_TYPE', 'AWS_LAMBDA_LOG_GROUP_NAME', 'AWS_LAMBDA_LOG_STREAM_NAME', 'AWS_ACCESS_KEY', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN', 'AWS_LAMBDA_RUNTIME_API', 'LAMBDA_TASK_ROOT', 'LAMBDA_RUNTIME_DIR', '_']
327
380
  keysToRemove.forEach(key => delete env[key as EnvKey])
328
381
 
329
- // behaviorOptions = this.apiBehaviorOptions(scope, props)
382
+ behaviorOptions = this.apiBehaviorOptions(scope, props)
330
383
  }
331
384
 
332
385
  // if docMode is used, we don't need to add a behavior for the docs
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-new */
2
- import type { aws_certificatemanager as acm, aws_efs as efs, aws_lambda as lambda, aws_route53 as route53 } from 'aws-cdk-lib'
3
- import { Duration, CfnOutput as Output, RemovalPolicy, aws_ec2 as ec2, aws_ecs as ecs, aws_secretsmanager as secretsmanager } from 'aws-cdk-lib'
2
+ import type { aws_certificatemanager as acm, aws_efs as efs } from 'aws-cdk-lib'
3
+ import { Duration, CfnOutput as Output, RemovalPolicy, aws_ec2 as ec2, aws_ecs as ecs, aws_route53 as route53, aws_route53_targets as route53Targets, aws_secretsmanager as secretsmanager } from 'aws-cdk-lib'
4
4
  import type { Construct } from 'constructs'
5
5
  import { path as p } from '@stacksjs/path'
6
6
  import { env } from '@stacksjs/env'
@@ -18,8 +18,8 @@ export interface ComputeStackProps extends NestedCloudProps {
18
18
 
19
19
  export class ComputeStack {
20
20
  lb: elbv2.ApplicationLoadBalancer
21
- apiServer: lambda.Function
22
- apiServerUrl: lambda.FunctionUrl
21
+ cluster: ecs.Cluster
22
+ taskDefinition: ecs.FargateTaskDefinition
23
23
 
24
24
  constructor(scope: Construct, props: ComputeStackProps) {
25
25
  const vpc = props.vpc
@@ -28,12 +28,12 @@ export class ComputeStack {
28
28
  if (!fileSystem)
29
29
  throw new Error('The file system is missing. Please make sure it was created properly.')
30
30
 
31
- const cluster = new ecs.Cluster(scope, 'StacksCluster', {
31
+ this.cluster = new ecs.Cluster(scope, 'StacksCluster', {
32
32
  clusterName: `${props.slug}-${props.appEnv}-web-server-cluster`,
33
33
  vpc,
34
34
  })
35
35
 
36
- const taskDefinition = new ecs.FargateTaskDefinition(scope, 'TaskDefinition', {
36
+ this.taskDefinition = new ecs.FargateTaskDefinition(scope, 'TaskDefinition', {
37
37
  family: `${props.appName}-${props.appEnv}-api`,
38
38
  memoryLimitMiB: 512, // Match your Lambda memory size
39
39
  cpu: 256, // Choose an appropriate value
@@ -42,18 +42,18 @@ export class ComputeStack {
42
42
  },
43
43
  })
44
44
 
45
- const container = taskDefinition.addContainer('WebServerContainer', {
45
+ const container = this.taskDefinition.addContainer('WebServerContainer', {
46
46
  containerName: `${props.appName}-${props.appEnv}-api`,
47
47
  image: ecs.ContainerImage.fromAsset(p.frameworkPath('server')),
48
48
  logging: new ecs.AwsLogDriver({
49
- streamPrefix: `${props.appName}-${props.appEnv}-web`,
49
+ streamPrefix: `${props.appName}-${props.appEnv}-web-server-logs`,
50
50
  logGroup: new LogGroup(scope, 'StacksApiLogs', {
51
- logGroupName: '/ecs/stacks-api',
51
+ logGroupName: '/aws/ecs/stacks-api',
52
52
  removalPolicy: RemovalPolicy.DESTROY, // Automatically remove logs on stack deletion
53
53
  }),
54
54
  }),
55
55
  healthCheck: {
56
- command: ['CMD-SHELL', 'curl -f http://localhost:3000/health || exit 1'],
56
+ command: ['CMD-SHELL', 'curl -f http://localhost:3000/api/health || exit 1'], // requires curl inside the container which isn't available in the base image. I wonder if there is a better way
57
57
  interval: Duration.seconds(10),
58
58
  timeout: Duration.seconds(5),
59
59
  retries: 3,
@@ -85,10 +85,8 @@ export class ComputeStack {
85
85
  'Ingress from the public ALB',
86
86
  )
87
87
 
88
- // Assuming serviceSecurityGroup and publicLoadBalancerSG are already defined
89
- serviceSecurityGroup.addIngressRule(publicLoadBalancerSG, ec2.Port.allTraffic(), 'Ingress from the public ALB')
90
-
91
88
  this.lb = new elbv2.ApplicationLoadBalancer(scope, 'ApplicationLoadBalancer', {
89
+ http2Enabled: true,
92
90
  loadBalancerName: `${props.appName}-${props.appEnv}-alb`,
93
91
  vpc,
94
92
  vpcSubnets: {
@@ -102,15 +100,21 @@ export class ComputeStack {
102
100
  securityGroup: publicLoadBalancerSG,
103
101
  })
104
102
 
103
+ new route53.ARecord(scope, 'ApiDomainAliasRecord', {
104
+ zone: props.zone,
105
+ recordName: 'api',
106
+ target: route53.RecordTarget.fromAlias(new route53Targets.LoadBalancerTarget(this.lb)),
107
+ })
108
+
105
109
  const serviceTargetGroup = new elbv2.ApplicationTargetGroup(scope, 'ServiceTargetGroup', {
106
- // targetGroupName: `${props.appName}-${props.appEnv}-service-tg`,
110
+ targetGroupName: `${props.appName}-${props.appEnv}-api-tg`,
107
111
  vpc,
108
112
  targetType: elbv2.TargetType.IP,
109
113
  protocol: elbv2.ApplicationProtocol.HTTP,
110
114
  port: 3000,
111
115
  healthCheck: {
112
116
  interval: Duration.seconds(6),
113
- path: '/',
117
+ path: '/api/health',
114
118
  protocol: elbv2.Protocol.HTTP,
115
119
  timeout: Duration.seconds(5),
116
120
  healthyThresholdCount: 2,
@@ -120,9 +124,9 @@ export class ComputeStack {
120
124
 
121
125
  const service = new ecs.FargateService(scope, 'StacksApiService', {
122
126
  serviceName: `${props.appName}-${props.appEnv}-api-service`,
123
- cluster,
124
- taskDefinition,
125
- desiredCount: 2,
127
+ cluster: this.cluster,
128
+ taskDefinition: this.taskDefinition,
129
+ desiredCount: 1,
126
130
  assignPublicIp: true,
127
131
  maxHealthyPercent: 200,
128
132
  vpcSubnets: vpc.selectSubnets({
@@ -136,6 +140,12 @@ export class ComputeStack {
136
140
  service.attachToApplicationTargetGroup(serviceTargetGroup)
137
141
  publicLoadBalancerSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.allTraffic())
138
142
 
143
+ this.lb.addListener('HttpsListener', {
144
+ port: 443,
145
+ certificates: [props.certificate],
146
+ defaultAction: elbv2.ListenerAction.forward([serviceTargetGroup]),
147
+ })
148
+
139
149
  this.lb.addListener('HttpListener', {
140
150
  port: 80,
141
151
  defaultAction: elbv2.ListenerAction.forward([serviceTargetGroup]),
@@ -144,7 +154,7 @@ export class ComputeStack {
144
154
  props.fileSystem.connections.allowFromAnyIpv4(ec2.Port.tcp(2049)) // port 2049 (NFS) for EFS
145
155
 
146
156
  const volumeName = `${props.slug}-${props.appEnv}-efs`
147
- taskDefinition.addVolume({
157
+ this.taskDefinition.addVolume({
148
158
  name: volumeName,
149
159
  efsVolumeConfiguration: {
150
160
  fileSystemId: props.fileSystem.fileSystemId,
package/src/cloud/dns.ts CHANGED
@@ -30,5 +30,13 @@ export class DnsStack {
30
30
  zone: this.zone,
31
31
  target: route53.RecordTarget.fromAlias(new targets.BucketWebsiteTarget(wwwBucket)),
32
32
  })
33
+
34
+ // TODO: this only needs to be created if Lemon Squeezy is being used
35
+ // Create a Route53 record for www.yourdomain.com
36
+ new route53.ARecord(scope, 'StoreAliasRecord', {
37
+ recordName: `store.${props.domain}`,
38
+ zone: this.zone,
39
+ target: route53.RecordTarget.fromIpAddresses('137.66.37.136'),
40
+ })
33
41
  }
34
42
  }
@@ -18,6 +18,7 @@ import { RedirectsStack } from './redirects'
18
18
  import { EmailStack } from './email'
19
19
  import { PermissionsStack } from './permissions'
20
20
  import { ComputeStack } from './compute'
21
+ import { QueueStack } from './queue'
21
22
 
22
23
  // import { DashboardStack } from './dashboard'
23
24
 
@@ -72,6 +73,12 @@ export class Cloud extends Stack {
72
73
  zone: dns.zone,
73
74
  certificate: security.certificate,
74
75
  })
76
+
77
+ new QueueStack(this, {
78
+ ...props,
79
+ cluster: api.cluster,
80
+ taskDefinition: api.taskDefinition,
81
+ })
75
82
  }
76
83
 
77
84
  const ai = new AiStack(this, props)
@@ -89,8 +96,7 @@ export class Cloud extends Stack {
89
96
  cliSetupUrl: cli.cliSetupUrl,
90
97
  askAiUrl: ai.askAiUrl,
91
98
  summarizeAiUrl: ai.summarizeAiUrl,
92
- webServer: api?.apiServer,
93
- webServerUrl: api?.apiServerUrl,
99
+ lb: api?.lb,
94
100
  })
95
101
 
96
102
  new DeploymentStack(this, {
@@ -0,0 +1,46 @@
1
+ import { aws_ec2 as ec2 } from 'aws-cdk-lib'
2
+ import { Rule, Schedule } from 'aws-cdk-lib/aws-events'
3
+ import { EcsTask } from 'aws-cdk-lib/aws-events-targets'
4
+ import type { Cluster, TaskDefinition } from 'aws-cdk-lib/aws-ecs'
5
+ import type { Construct } from 'constructs'
6
+ import type { NestedCloudProps } from '../types'
7
+
8
+ export interface QueueStackProps extends NestedCloudProps {
9
+ cluster: Cluster
10
+ taskDefinition: TaskDefinition
11
+ }
12
+
13
+ export class QueueStack {
14
+ constructor(scope: Construct, props: QueueStackProps) {
15
+ const rule = new Rule(scope, 'Rule', {
16
+ // schedule to run every second
17
+ ruleName: `${props.appName}-${props.appEnv}-queue`,
18
+ schedule: Schedule.cron({ minute: '*', hour: '*', month: '*', weekDay: '*', year: '*' }),
19
+ // schedule: Schedule.cron({ minute: '0', hour: '0' }), // For example, every day at midnight
20
+ })
21
+
22
+ rule.addTarget(new EcsTask({
23
+ cluster: props.cluster,
24
+ taskDefinition: props.taskDefinition,
25
+ containerOverrides: [
26
+ {
27
+ containerName: `${props.appName}-${props.appEnv}-api`,
28
+ environment: [
29
+ {
30
+ name: 'QUEUE_WORKER',
31
+ value: 'true',
32
+ },
33
+ {
34
+ name: 'JOB',
35
+ value: 'DummyJob.ts',
36
+ },
37
+ ],
38
+ },
39
+ ],
40
+ retryAttempts: 3,
41
+ subnetSelection: {
42
+ subnetType: ec2.SubnetType.PUBLIC,
43
+ },
44
+ }))
45
+ }
46
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stacks-router-layer",
3
- "version": "0.58.50",
3
+ "version": "0.58.52",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -10,6 +10,6 @@
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
11
11
  },
12
12
  "dependencies": {
13
- "@stacksjs/router": "^0.58.49"
13
+ "@stacksjs/router": "^0.58.51"
14
14
  }
15
15
  }
@@ -31,7 +31,7 @@ export class SecurityStack {
31
31
  rules: this.getFirewallRules(),
32
32
  }
33
33
 
34
- this.firewall = new wafv2.CfnWebACL(scope, 'WebFirewall', options)
34
+ this.firewall = new wafv2.CfnWebACL(scope, 'StacksWebFirewall', options)
35
35
  Tags.of(this.firewall).add('Name', 'waf-cloudfront', { priority: 300 })
36
36
  Tags.of(this.firewall).add('Purpose', 'CloudFront', { priority: 300 })
37
37
  Tags.of(this.firewall).add('CreatedBy', 'CloudFormation', { priority: 300 })
package/src/helpers.ts CHANGED
@@ -186,6 +186,8 @@ export async function deleteJumpBox(stackName?: string) {
186
186
  if (!jumpBoxId)
187
187
  return err('Jump-box not found')
188
188
 
189
+ log.info(`Deleting jump-box ${jumpBoxId}...`)
190
+
189
191
  return await deleteEc2Instance(jumpBoxId, stackName)
190
192
  }
191
193
 
@@ -203,6 +205,7 @@ export async function deleteIamUsers() {
203
205
 
204
206
  const promises = users.map(async (user) => {
205
207
  const userName = user.UserName || ''
208
+
206
209
  log.info(`Deleting IAM user: ${userName}`)
207
210
 
208
211
  // Get the list of policies attached to the user
@@ -251,12 +254,15 @@ export async function deleteStacksBuckets() {
251
254
  // List all objects in the bucket
252
255
  const objects = await s3.listObjectsV2({ Bucket: bucketName })
253
256
  log.info(`Finished listing bucket ${bucketName} objects`)
257
+
254
258
  // Delete all objects
255
259
  if (objects.Contents) {
256
260
  log.info('Deleting bucket objects...')
261
+
257
262
  await Promise.all(objects.Contents.map(object =>
258
263
  s3.deleteObject({ Bucket: bucketName, Key: object.Key || '' }).catch(error => handleError(error)),
259
264
  ))
265
+
260
266
  log.info(`Finished deleting objects from bucket ${bucketName}`)
261
267
  }
262
268
 
@@ -273,10 +279,12 @@ export async function deleteStacksBuckets() {
273
279
 
274
280
  // Delete all delete markers
275
281
  log.info(`Deleting bucket ${bucketName} delete markers...`)
282
+
276
283
  if (versions.DeleteMarkers) {
277
284
  await Promise.all(versions.DeleteMarkers.map(marker =>
278
285
  s3.deleteObject({ Bucket: bucketName, Key: marker.Key || '', VersionId: marker.VersionId }),
279
286
  )).catch(error => handleError(error))
287
+
280
288
  log.info(`Finished deleting delete markers from bucket ${bucketName}`)
281
289
  }
282
290
 
@@ -284,13 +292,16 @@ export async function deleteStacksBuckets() {
284
292
  const uploads = await s3.listMultipartUploads({ Bucket: bucketName })
285
293
  if (uploads.Uploads) {
286
294
  log.info('Aborting bucket multipart uploads...')
295
+
287
296
  await Promise.all(uploads.Uploads.map(upload =>
288
297
  s3.abortMultipartUpload({ Bucket: bucketName, Key: upload.Key || '', UploadId: upload.UploadId }),
289
298
  )).catch(error => handleError(error))
299
+
290
300
  log.info(`Finished aborting multipart uploads from bucket ${bucketName}`)
291
301
  }
292
302
 
293
303
  await s3.deleteBucket({ Bucket: bucketName }).catch(error => handleError(error))
304
+
294
305
  log.info(`Bucket ${bucketName} deleted`)
295
306
  }
296
307
  catch (error) {
@@ -322,6 +333,7 @@ export async function deleteStacksFunctions() {
322
333
  await Promise.all(promises).catch((error: Error) => {
323
334
  if (error.message.includes('it is a replicated function')) {
324
335
  log.info('Function is replicated, skipping...')
336
+
325
337
  return ok('CloudFront is still deleting the some functions. Try again later.')
326
338
  }
327
339
 
@@ -334,7 +346,6 @@ export async function deleteStacksFunctions() {
334
346
  export async function deleteLogGroups() {
335
347
  try {
336
348
  const client = new CloudWatchLogsClient({ region: 'us-east-1' })
337
-
338
349
  const logGroups: DescribeLogGroupsCommandOutput = await client.send(new DescribeLogGroupsCommand({}))
339
350
 
340
351
  if (!logGroups?.logGroups)
@@ -391,6 +402,7 @@ export async function hasBeenDeployed() {
391
402
 
392
403
  try {
393
404
  const response = await s3.send(new ListBucketsCommand({}))
405
+
394
406
  return ok(response.Buckets?.some(bucket => bucket.Name?.includes(config.app.name?.toLocaleLowerCase() || 'stacks')) || false)
395
407
  }
396
408
  catch (error) {
@@ -1,59 +0,0 @@
1
- # Stacks Router
2
-
3
- This package contains the Stacks Router.
4
-
5
- ## ☘️ Features
6
-
7
- wip
8
-
9
- - ⚡️
10
-
11
- wip
12
-
13
- ## 🤖 Usage
14
-
15
- wip
16
-
17
- ```bash
18
- bun install -d @stacksjs/actions
19
- ```
20
-
21
- Now, you can use it in your project:
22
-
23
- ```js
24
- import * as router from '@stacksjs/router'
25
-
26
- // wip
27
- ```
28
-
29
- Learn more in the docs.
30
-
31
- ## 🧪 Testing
32
-
33
- ```bash
34
- bun test
35
- ```
36
-
37
- ## 📈 Changelog
38
-
39
- Please see our [releases](https://github.com/stacksjs/stacks/releases) page for more information on what has changed recently.
40
-
41
- ## 🚜 Contributing
42
-
43
- Please review the [Contributing Guide](https://github.com/stacksjs/contributing) for details.
44
-
45
- ## 🏝 Community
46
-
47
- For help, discussion about best practices, or any other conversation that would benefit from being searchable:
48
-
49
- [Discussions on GitHub](https://github.com/stacksjs/stacks/discussions)
50
-
51
- For casual chit-chat with others using this package:
52
-
53
- [Join the Stacks Discord Server](https://discord.gg/stacksjs)
54
-
55
- ## 📄 License
56
-
57
- The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stacks/tree/main/LICENSE.md) for more information.
58
-
59
- Made with 💙
@@ -1,63 +0,0 @@
1
- {
2
- "name": "@stacksjs/router",
3
- "type": "module",
4
- "version": "0.58.50",
5
- "description": "The Stacks framework router.",
6
- "author": "Chris Breuer",
7
- "license": "MIT",
8
- "funding": "https://github.com/sponsors/chrisbbreuer",
9
- "homepage": "https://github.com/stacksjs/stacks/tree/main/storage/framework/core/router#readme",
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/stacksjs/stacks.git",
13
- "directory": "./storage/framework/core/router"
14
- },
15
- "bugs": {
16
- "url": "https://github.com/stacksjs/stacks/issues"
17
- },
18
- "keywords": [
19
- "router",
20
- "stacks",
21
- "framework",
22
- "typescript",
23
- "javascript"
24
- ],
25
- "exports": {
26
- ".": {
27
- "bun": "./src/index.ts",
28
- "import": "./dist/index.js"
29
- },
30
- "./*": {
31
- "bun": "./src/*",
32
- "import": "./dist/*"
33
- }
34
- },
35
- "module": "dist/index.js",
36
- "types": "dist/index.d.ts",
37
- "contributors": [
38
- "Chris Breuer <chris@stacksjs.org>"
39
- ],
40
- "files": [
41
- "README.md",
42
- "dist",
43
- "src"
44
- ],
45
- "scripts": {
46
- "build": "bun --bun build.ts",
47
- "typecheck": "bun --bun tsc --noEmit",
48
- "prepublishOnly": "bun --bun run build"
49
- },
50
- "peerDependencies": {
51
- "@stacksjs/config": "latest",
52
- "unplugin-vue-router": "^0.7.0",
53
- "vue-router": "^4.2.5"
54
- },
55
- "dependencies": {
56
- "@stacksjs/config": "latest",
57
- "unplugin-vue-router": "^0.7.0",
58
- "vue-router": "^4.2.5"
59
- },
60
- "devDependencies": {
61
- "@stacksjs/development": "latest"
62
- }
63
- }