@stacksjs/cloud 0.70.23 → 0.70.25

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/queue.d.ts DELETED
@@ -1,144 +0,0 @@
1
- import type { Cluster, TaskDefinition } from 'aws-cdk-lib/aws-ecs';
2
- import type { Construct } from 'constructs';
3
- import type { NestedCloudProps } from '../types';
4
-
5
- export declare interface QueueStackProps extends NestedCloudProps {
6
- cluster: Cluster
7
- taskDefinition: TaskDefinition
8
- }
9
- export declare class QueueStack {
10
- scope: Construct
11
- props: QueueStackProps
12
-
13
- constructor(scope: Construct, props: QueueStackProps) {
14
- this.scope = scope
15
- this.props = props
16
- }
17
-
18
- async init(): Promise<void> {
19
- const jobsDir = path.jobsPath()
20
- const actionsDir = path.appPath('Actions')
21
- const ormActionDir = path.builtUserActionsPath('src')
22
-
23
- const jobFiles = await fs.readdir(jobsDir)
24
- const actionFiles = await fs.readdir(actionsDir)
25
- const ormActionFiles = await fs.readdir(ormActionDir)
26
- const jobs = []
27
-
28
- for (const file of jobFiles) {
29
- if (!file.endsWith('.ts'))
30
- continue
31
-
32
- const jobPath = path.jobsPath(file)
33
-
34
- const job = await this.loadModule(jobPath)
35
- this.createQueueRule(job, file)
36
- jobs.push(job)
37
- }
38
-
39
- for (const ormFile of ormActionFiles) {
40
- if (!ormFile.endsWith('.ts'))
41
- continue
42
-
43
- const ormActionPath = path.builtUserActionsPath(`src/${ormFile}`)
44
-
45
- const ormAction = await this.loadModule(ormActionPath)
46
- this.createQueueRule(ormAction, ormFile)
47
- jobs.push(ormAction)
48
- }
49
-
50
- for (const file of actionFiles) {
51
- if (!file.endsWith('.ts'))
52
- continue
53
-
54
- const actionPath = path.appPath(`Actions/${file}`)
55
-
56
- const action = await this.loadModule(actionPath)
57
- this.createQueueRule(action, file)
58
- jobs.push(action)
59
- }
60
- }
61
-
62
- cronScheduleFromRate(rate: string): {
63
- minute?: string
64
- hour?: string
65
- month?: string
66
- weekDay?: string
67
- year?: string
68
- } {
69
- const parts = rate.split(' ')
70
- return {
71
- minute: parts[0],
72
- hour: parts[1],
73
- month: parts[2],
74
- weekDay: parts[3],
75
- year: parts[4],
76
- }
77
- }
78
-
79
- async loadModule(filePath: string): Promise<{ default: any }> {
80
- const jobModule = await import(filePath)
81
-
82
- return jobModule
83
- }
84
-
85
- createQueueRule(module: { default: any }, file: string): void {
86
- const rate = module.default?.rate
87
-
88
- if (!rate || module.default?.enabled === false)
89
- return
90
-
91
- const schedule = Schedule.cron(this.cronScheduleFromRate(rate))
92
-
93
- const id = `QueueRule${pascalCase(file.replace('.ts', ''))}`
94
-
95
- const rule = new Rule(this.scope, id, {
96
- ruleName: `${this.props.appName}-${this.props.appEnv}-queue-rule-${slug(file.replace('.ts', ''))}`,
97
- schedule,
98
- })
99
-
100
- rule.addTarget(
101
- new EcsTask({
102
- cluster: this.props.cluster,
103
- taskDefinition: this.props.taskDefinition,
104
- containerOverrides: [
105
- {
106
- containerName: `${this.props.appName}-${this.props.appEnv}-api`,
107
- environment: [
108
- {
109
- name: 'QUEUE_WORKER',
110
- value: 'true',
111
- },
112
- {
113
- name: 'JOB',
114
- value: file,
115
- },
116
- {
117
- name: 'JOB_BACKOFF_FACTOR',
118
- value: module.default?.backoffFactor,
119
- },
120
- {
121
- name: 'JOB_RETRIES',
122
- value: module.default?.tries,
123
- },
124
- {
125
- name: 'JOB_INITIAL_DELAY',
126
- value: module.default?.initialDelay,
127
- },
128
- {
129
- name: 'JOB_JITTER',
130
- value: module.default?.jitter,
131
- },
132
- ],
133
- },
134
- ],
135
-
136
- retryAttempts: 1,
137
-
138
- subnetSelection: {
139
- subnetType: ec2.SubnetType.PUBLIC,
140
- },
141
- }),
142
- )
143
- }
144
- }
@@ -1,45 +0,0 @@
1
- import type { Construct } from 'constructs';
2
- import type { NestedCloudProps } from '../types';
3
-
4
- export declare interface RedirectsStackProps extends NestedCloudProps {
5
- }
6
- export declare class RedirectsStack {
7
- redirectZones: route53.IHostedZone[] = []
8
-
9
- constructor(scope: Construct, props: RedirectsStackProps) {
10
- config.dns.redirects?.forEach((redirect: string) => {
11
- const slug = redirect
12
- .split('.')
13
- .map((part: string, index: number) => (index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)))
14
- .join('')
15
- const hostedZone = route53.HostedZone.fromLookup(scope, 'HostedZone', {
16
- domainName: redirect,
17
- })
18
-
19
- const redirectBucket = new s3.Bucket(scope, `RedirectBucket${slug}`, {
20
- bucketName: `${redirect}-redirect`,
21
- websiteRedirect: {
22
- hostName: props.domain,
23
- protocol: s3.RedirectProtocol.HTTPS,
24
- },
25
- removalPolicy: RemovalPolicy.DESTROY,
26
- autoDeleteObjects: true,
27
- })
28
-
29
- new route53.CnameRecord(scope, `RedirectRecord${slug}`, {
30
- zone: hostedZone,
31
- recordName: 'redirect',
32
- domainName: redirectBucket.bucketWebsiteDomainName,
33
- })
34
- })
35
-
36
- config.dns.redirects?.forEach((redirect: string) => {
37
- const slug = redirect
38
- .split('.')
39
- .map((part: string, index: number) => (index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)))
40
- .join('')
41
- const hostedZone = route53.HostedZone.fromLookup(scope, `RedirectHostedZone${slug}`, { domainName: redirect })
42
- this.redirectZones.push(hostedZone)
43
- })
44
- }
45
- }
@@ -1,149 +0,0 @@
1
- import type { Construct } from 'constructs';
2
- import type { NestedCloudProps } from '../types';
3
-
4
- export declare interface StorageStackProps extends NestedCloudProps {
5
- zone: route53.IHostedZone
6
- }
7
- export declare class SecurityStack {
8
- firewall: wafv2.CfnWebACL
9
- kmsKey: kms.Key
10
- certificate: acm.Certificate
11
- originAccessIdentity: cloudfront.OriginAccessIdentity
12
-
13
- constructor(scope: Construct, props: StorageStackProps) {
14
- const firewallOptions = config.cloud.firewall
15
-
16
- if (!firewallOptions)
17
- throw new Error('No firewall options found in config')
18
-
19
- const options = {
20
- defaultAction: { allow: {} },
21
- scope: 'CLOUDFRONT',
22
- visibilityConfig: {
23
- sampledRequestsEnabled: true,
24
- cloudWatchMetricsEnabled: true,
25
- metricName: 'firewallMetric',
26
- },
27
- rules: this.getFirewallRules(scope),
28
- }
29
-
30
- this.firewall = new wafv2.CfnWebACL(scope, 'StacksWebFirewall', options)
31
- Tags.of(this.firewall).add('Name', 'waf-cloudfront', { priority: 300 })
32
- Tags.of(this.firewall).add('Purpose', 'CloudFront', { priority: 300 })
33
- Tags.of(this.firewall).add('CreatedBy', 'CloudFormation', {
34
- priority: 300,
35
- })
36
-
37
- this.kmsKey = new kms.Key(scope, 'EncryptionKey', {
38
- alias: 'stacks-encryption-key',
39
- description: 'KMS key for Stacks Cloud',
40
- enableKeyRotation: true,
41
- removalPolicy: RemovalPolicy.DESTROY,
42
- pendingWindow: Duration.days(30),
43
- })
44
-
45
- this.certificate = new acm.Certificate(scope, 'Certificate', {
46
- domainName: props.domain,
47
- validation: acm.CertificateValidation.fromDns(props.zone),
48
- subjectAlternativeNames: [`www.${props.domain}`, `api.${props.domain}`, `docs.${props.domain}`],
49
- })
50
-
51
- this.originAccessIdentity = new cloudfront.OriginAccessIdentity(scope, 'OAI')
52
- }
53
-
54
- getFirewallRules(scope: Construct): wafv2.CfnWebACL.RuleProperty[] {
55
- const rules: wafv2.CfnWebACL.RuleProperty[] = []
56
- const priorities = []
57
-
58
- if (config.security.firewall?.countryCodes?.length) {
59
- priorities.push(1)
60
- rules.push({
61
- name: 'CountryRule',
62
- priority: priorities.length,
63
- statement: {
64
- geoMatchStatement: {
65
- countryCodes: config.security.firewall.countryCodes as string[],
66
- },
67
- },
68
- action: {
69
- block: {},
70
- },
71
- visibilityConfig: {
72
- sampledRequestsEnabled: true,
73
- cloudWatchMetricsEnabled: true,
74
- metricName: 'CountryRule',
75
- },
76
- })
77
- }
78
-
79
- if (config.security.firewall?.ipAddresses?.length) {
80
- const ipSet = new wafv2.CfnIPSet(scope, 'IpSet', {
81
- name: 'IpSet',
82
- description: 'IP Set',
83
- scope: 'CLOUDFRONT',
84
- addresses: config.security.firewall.ipAddresses as string[],
85
- ipAddressVersion: 'IPV4',
86
- })
87
-
88
- priorities.push(1)
89
- rules.push({
90
- name: 'IpAddressRule',
91
- priority: priorities.length,
92
- statement: {
93
- ipSetReferenceStatement: {
94
- arn: ipSet.attrArn,
95
- },
96
- },
97
- action: {
98
- block: {},
99
- },
100
- visibilityConfig: {
101
- sampledRequestsEnabled: true,
102
- cloudWatchMetricsEnabled: true,
103
- metricName: 'IpAddressRule',
104
- },
105
- })
106
- }
107
-
108
- if (config.security.firewall?.httpHeaders?.length) {
109
- config.security.firewall.httpHeaders.forEach((header: string | undefined, index: number) => {
110
- priorities.push(1)
111
- rules.push({
112
- name: `HttpHeaderRule${index}`,
113
- priority: priorities.length,
114
- statement: {
115
- byteMatchStatement: {
116
- fieldToMatch: {
117
- singleHeader: {
118
- name: header,
119
- },
120
- },
121
- positionalConstraint: 'EXACTLY',
122
- searchString: 'true',
123
- textTransformations: [
124
- {
125
- priority: 0,
126
- type: 'NONE',
127
- },
128
- ],
129
- },
130
- },
131
- action: {
132
- block: {},
133
- },
134
- visibilityConfig: {
135
- sampledRequestsEnabled: true,
136
- cloudWatchMetricsEnabled: true,
137
- metricName: `HttpHeaderRule${index}`,
138
- },
139
- })
140
- })
141
- }
142
-
143
-
144
-
145
-
146
-
147
- return rules
148
- }
149
- }
package/dist/storage.d.ts DELETED
@@ -1,189 +0,0 @@
1
- import type { Construct } from 'constructs';
2
- import type { NestedCloudProps } from '../types';
3
-
4
- export declare interface StorageStackProps extends NestedCloudProps {
5
- kmsKey: kms.Key
6
- originAccessIdentity: cloudfront.OriginAccessIdentity
7
- }
8
- export declare class StorageStack {
9
- publicBucket: s3.Bucket
10
- privateBucket: s3.Bucket
11
- docsBucket?: s3.Bucket
12
- logBucket: s3.Bucket
13
- bucketPrefix: string
14
- vault: backup.BackupVault
15
- backupPlan: backup.BackupPlan
16
- backupRole: iam.Role
17
-
18
- constructor(scope: Construct, props: StorageStackProps) {
19
- this.bucketPrefix = `${props.slug}-${props.appEnv}`
20
-
21
- this.publicBucket = new s3.Bucket(scope, 'PublicBucket', {
22
- bucketName: `${this.bucketPrefix}-public-${props.timestamp}`,
23
- versioned: true,
24
- autoDeleteObjects: true,
25
- removalPolicy: RemovalPolicy.DESTROY,
26
- encryption: s3.BucketEncryption.S3_MANAGED,
27
- websiteIndexDocument: 'index.html',
28
- websiteErrorDocument: 'index.html',
29
- publicReadAccess: true,
30
- blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS,
31
- })
32
-
33
- Tags.of(this.publicBucket).add('daily-backup', 'true')
34
-
35
- if (this.shouldDeployDocs()) {
36
- this.docsBucket = new s3.Bucket(scope, 'DocsBucket', {
37
- bucketName: `${this.bucketPrefix}-docs-${props.timestamp}`,
38
- versioned: true,
39
- autoDeleteObjects: true,
40
- removalPolicy: RemovalPolicy.DESTROY,
41
- encryption: s3.BucketEncryption.S3_MANAGED,
42
- websiteIndexDocument: 'index.html',
43
- websiteErrorDocument: 'index.html',
44
- publicReadAccess: true,
45
- blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS,
46
- })
47
-
48
- Tags.of(this.docsBucket).add('weekly-backup', 'true')
49
- }
50
-
51
- this.privateBucket = new s3.Bucket(scope, 'PrivateBucket', {
52
- bucketName: `${this.bucketPrefix}-private-${props.timestamp}`,
53
- versioned: true,
54
- removalPolicy: RemovalPolicy.DESTROY,
55
- autoDeleteObjects: true,
56
- encryption: s3.BucketEncryption.S3_MANAGED,
57
- enforceSSL: true,
58
- publicReadAccess: false,
59
- blockPublicAccess: {
60
- blockPublicAcls: true,
61
- blockPublicPolicy: true,
62
- ignorePublicAcls: true,
63
- restrictPublicBuckets: true,
64
- },
65
- })
66
-
67
- Tags.of(this.privateBucket).add('daily-backup', 'true')
68
-
69
- this.logBucket = new s3.Bucket(scope, 'LogsBucket', {
70
- bucketName: `${this.bucketPrefix}-logs-${props.timestamp}`,
71
- removalPolicy: RemovalPolicy.RETAIN,
72
- blockPublicAccess: new s3.BlockPublicAccess({
73
- blockPublicAcls: false,
74
- ignorePublicAcls: true,
75
- blockPublicPolicy: true,
76
- restrictPublicBuckets: true,
77
- }),
78
- objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_PREFERRED,
79
- })
80
-
81
- Tags.of(this.logBucket).add('daily-backup', 'true')
82
-
83
- this.backupRole = this.createBackupRole(scope)
84
-
85
- this.vault = new backup.BackupVault(scope, 'BackupVault', {
86
- backupVaultName: `${props.slug}-${props.appEnv}-daily-backup-vault`,
87
- encryptionKey: props.kmsKey,
88
- removalPolicy: RemovalPolicy.DESTROY,
89
- })
90
-
91
- this.backupPlan = backup.BackupPlan.daily35DayRetention(scope, 'BackupPlan', this.vault)
92
-
93
- this.backupPlan.addSelection('Selection', {
94
- role: this.backupRole,
95
- resources: [backup.BackupResource.fromTag('daily-backup', 'true')],
96
- })
97
- }
98
-
99
- createBackupRole(scope: Construct): iam.Role {
100
- const backupRole = new iam.Role(scope, 'BackupRole', {
101
- assumedBy: new iam.ServicePrincipal('backup.amazonaws.com'),
102
- })
103
-
104
- backupRole.addToPolicy(
105
- new iam.PolicyStatement({
106
- actions: [
107
- 's3:GetInventoryConfiguration',
108
- 's3:PutInventoryConfiguration',
109
- 's3:ListBucketVersions',
110
- 's3:ListBucket',
111
- 's3:GetBucketVersioning',
112
- 's3:GetBucketNotification',
113
- 's3:PutBucketNotification',
114
- 's3:GetBucketLocation',
115
- 's3:GetBucketTagging',
116
- ],
117
- resources: ['arn:aws:s3:::*'],
118
- sid: 'S3BucketBackupPermissions',
119
- }),
120
- )
121
-
122
- backupRole.addToPolicy(
123
- new iam.PolicyStatement({
124
- actions: [
125
- 's3:GetObjectAcl',
126
- 's3:GetObject',
127
- 's3:GetObjectVersionTagging',
128
- 's3:GetObjectVersionAcl',
129
- 's3:GetObjectTagging',
130
- 's3:GetObjectVersion',
131
- ],
132
- resources: ['arn:aws:s3:::*/*'],
133
- sid: 'S3ObjectBackupPermissions',
134
- }),
135
- )
136
-
137
- backupRole.addToPolicy(
138
- new iam.PolicyStatement({
139
- actions: ['s3:ListAllMyBuckets'],
140
- resources: ['*'],
141
- sid: 'S3GlobalPermissions',
142
- }),
143
- )
144
-
145
- backupRole.addToPolicy(
146
- new iam.PolicyStatement({
147
- actions: ['kms:Decrypt', 'kms:DescribeKey'],
148
- resources: ['*'],
149
- sid: 'KMSBackupPermissions',
150
- conditions: {
151
- StringLike: {
152
- 'kms:ViaService': 's3.*.amazonaws.com',
153
- },
154
- },
155
- }),
156
- )
157
-
158
- backupRole.addToPolicy(
159
- new iam.PolicyStatement({
160
- actions: [
161
- 'events:DescribeRule',
162
- 'events:EnableRule',
163
- 'events:PutRule',
164
- 'events:DeleteRule',
165
- 'events:PutTargets',
166
- 'events:RemoveTargets',
167
- 'events:ListTargetsByRule',
168
- 'events:DisableRule',
169
- ],
170
- resources: ['arn:aws:events:*:*:rule/AwsBackupManagedRule*'],
171
- sid: 'EventsPermissions',
172
- }),
173
- )
174
-
175
- backupRole.addToPolicy(
176
- new iam.PolicyStatement({
177
- actions: ['cloudwatch:GetMetricData', 'events:ListRules'],
178
- resources: ['*'],
179
- sid: 'EventsMetricsGlobalPermissions',
180
- }),
181
- )
182
-
183
- return backupRole
184
- }
185
-
186
- shouldDeployDocs(): boolean {
187
- return (hasFiles(p.projectPath('docs')) || config.app.docMode) ?? false
188
- }
189
- }