@stacksjs/ts-cloud-core 0.1.2 → 0.1.6
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/README.md +98 -13
- package/package.json +12 -3
- package/src/advanced-features.test.ts +0 -465
- package/src/aws/cloudformation.ts +0 -421
- package/src/aws/cloudfront.ts +0 -158
- package/src/aws/credentials.test.ts +0 -132
- package/src/aws/credentials.ts +0 -545
- package/src/aws/index.ts +0 -87
- package/src/aws/s3.test.ts +0 -188
- package/src/aws/s3.ts +0 -1088
- package/src/aws/signature.test.ts +0 -670
- package/src/aws/signature.ts +0 -1155
- package/src/backup/disaster-recovery.test.ts +0 -726
- package/src/backup/disaster-recovery.ts +0 -500
- package/src/backup/index.ts +0 -34
- package/src/backup/manager.test.ts +0 -498
- package/src/backup/manager.ts +0 -432
- package/src/cicd/circleci.ts +0 -430
- package/src/cicd/github-actions.ts +0 -424
- package/src/cicd/gitlab-ci.ts +0 -255
- package/src/cicd/index.ts +0 -8
- package/src/cli/history.ts +0 -396
- package/src/cli/index.ts +0 -10
- package/src/cli/progress.ts +0 -458
- package/src/cli/repl.ts +0 -454
- package/src/cli/suggestions.ts +0 -327
- package/src/cli/table.test.ts +0 -319
- package/src/cli/table.ts +0 -332
- package/src/cloudformation/builder.test.ts +0 -327
- package/src/cloudformation/builder.ts +0 -378
- package/src/cloudformation/builders/api-gateway.ts +0 -449
- package/src/cloudformation/builders/cache.ts +0 -334
- package/src/cloudformation/builders/cdn.ts +0 -278
- package/src/cloudformation/builders/compute.ts +0 -485
- package/src/cloudformation/builders/database.ts +0 -392
- package/src/cloudformation/builders/functions.ts +0 -343
- package/src/cloudformation/builders/messaging.ts +0 -140
- package/src/cloudformation/builders/monitoring.ts +0 -300
- package/src/cloudformation/builders/network.ts +0 -264
- package/src/cloudformation/builders/queue.ts +0 -147
- package/src/cloudformation/builders/security.ts +0 -399
- package/src/cloudformation/builders/storage.ts +0 -285
- package/src/cloudformation/index.ts +0 -30
- package/src/cloudformation/types.ts +0 -173
- package/src/compliance/aws-config.ts +0 -543
- package/src/compliance/cloudtrail.ts +0 -376
- package/src/compliance/compliance.test.ts +0 -423
- package/src/compliance/guardduty.ts +0 -446
- package/src/compliance/index.ts +0 -66
- package/src/compliance/security-hub.ts +0 -456
- package/src/containers/build-optimization.ts +0 -416
- package/src/containers/containers.test.ts +0 -508
- package/src/containers/image-scanning.ts +0 -360
- package/src/containers/index.ts +0 -9
- package/src/containers/registry.ts +0 -293
- package/src/containers/service-mesh.ts +0 -520
- package/src/database/database.test.ts +0 -762
- package/src/database/index.ts +0 -9
- package/src/database/migrations.ts +0 -444
- package/src/database/performance.ts +0 -528
- package/src/database/replicas.ts +0 -534
- package/src/database/users.ts +0 -494
- package/src/dependency-graph.ts +0 -143
- package/src/deployment/ab-testing.ts +0 -582
- package/src/deployment/blue-green.ts +0 -452
- package/src/deployment/canary.ts +0 -500
- package/src/deployment/deployment.test.ts +0 -526
- package/src/deployment/index.ts +0 -61
- package/src/deployment/progressive.ts +0 -62
- package/src/dns/dns.test.ts +0 -641
- package/src/dns/dnssec.ts +0 -315
- package/src/dns/index.ts +0 -8
- package/src/dns/resolver.ts +0 -496
- package/src/dns/routing.ts +0 -593
- package/src/email/advanced/analytics.ts +0 -445
- package/src/email/advanced/index.ts +0 -11
- package/src/email/advanced/rules.ts +0 -465
- package/src/email/advanced/scheduling.ts +0 -352
- package/src/email/advanced/search.ts +0 -412
- package/src/email/advanced/shared-mailboxes.ts +0 -404
- package/src/email/advanced/templates.ts +0 -455
- package/src/email/advanced/threading.ts +0 -281
- package/src/email/analytics.ts +0 -467
- package/src/email/bounce-handling.ts +0 -425
- package/src/email/email.test.ts +0 -431
- package/src/email/handlers/__tests__/inbound.test.ts +0 -38
- package/src/email/handlers/__tests__/outbound.test.ts +0 -37
- package/src/email/handlers/converter.ts +0 -227
- package/src/email/handlers/feedback.ts +0 -228
- package/src/email/handlers/inbound.ts +0 -169
- package/src/email/handlers/outbound.ts +0 -178
- package/src/email/index.ts +0 -15
- package/src/email/reputation.ts +0 -303
- package/src/email/templates.ts +0 -352
- package/src/errors/index.test.ts +0 -434
- package/src/errors/index.ts +0 -416
- package/src/health-checks/index.ts +0 -40
- package/src/index.ts +0 -360
- package/src/intrinsic-functions.ts +0 -118
- package/src/lambda/concurrency.ts +0 -330
- package/src/lambda/destinations.ts +0 -345
- package/src/lambda/dlq.ts +0 -425
- package/src/lambda/index.ts +0 -11
- package/src/lambda/lambda.test.ts +0 -840
- package/src/lambda/layers.ts +0 -263
- package/src/lambda/versions.ts +0 -376
- package/src/lambda/vpc.ts +0 -399
- package/src/local/config.ts +0 -114
- package/src/local/index.ts +0 -6
- package/src/local/mock-aws.ts +0 -351
- package/src/modules/ai.ts +0 -340
- package/src/modules/api.ts +0 -478
- package/src/modules/auth.ts +0 -805
- package/src/modules/cache.ts +0 -417
- package/src/modules/cdn.ts +0 -1062
- package/src/modules/communication.ts +0 -1094
- package/src/modules/compute.ts +0 -3348
- package/src/modules/database.ts +0 -554
- package/src/modules/deployment.ts +0 -1079
- package/src/modules/dns.ts +0 -337
- package/src/modules/email.ts +0 -1538
- package/src/modules/filesystem.ts +0 -515
- package/src/modules/index.ts +0 -32
- package/src/modules/messaging.ts +0 -486
- package/src/modules/monitoring.ts +0 -2086
- package/src/modules/network.ts +0 -664
- package/src/modules/parameter-store.ts +0 -325
- package/src/modules/permissions.ts +0 -1081
- package/src/modules/phone.ts +0 -494
- package/src/modules/queue.ts +0 -1260
- package/src/modules/redirects.ts +0 -464
- package/src/modules/registry.ts +0 -699
- package/src/modules/search.ts +0 -401
- package/src/modules/secrets.ts +0 -416
- package/src/modules/security.ts +0 -731
- package/src/modules/sms.ts +0 -389
- package/src/modules/storage.ts +0 -1120
- package/src/modules/workflow.ts +0 -680
- package/src/multi-account/config.ts +0 -521
- package/src/multi-account/index.ts +0 -7
- package/src/multi-account/manager.ts +0 -427
- package/src/multi-region/cross-region.ts +0 -410
- package/src/multi-region/index.ts +0 -8
- package/src/multi-region/manager.ts +0 -483
- package/src/multi-region/regions.ts +0 -435
- package/src/network-security/index.ts +0 -48
- package/src/observability/index.ts +0 -9
- package/src/observability/logs.ts +0 -522
- package/src/observability/metrics.ts +0 -460
- package/src/observability/observability.test.ts +0 -782
- package/src/observability/synthetics.ts +0 -568
- package/src/observability/xray.ts +0 -358
- package/src/phone/advanced/analytics.ts +0 -349
- package/src/phone/advanced/callbacks.ts +0 -428
- package/src/phone/advanced/index.ts +0 -8
- package/src/phone/advanced/ivr-builder.ts +0 -504
- package/src/phone/advanced/recording.ts +0 -310
- package/src/phone/handlers/__tests__/incoming-call.test.ts +0 -40
- package/src/phone/handlers/incoming-call.ts +0 -117
- package/src/phone/handlers/missed-call.ts +0 -116
- package/src/phone/handlers/voicemail.ts +0 -179
- package/src/phone/index.ts +0 -9
- package/src/presets/api-backend.ts +0 -134
- package/src/presets/data-pipeline.ts +0 -204
- package/src/presets/extend.test.ts +0 -295
- package/src/presets/extend.ts +0 -297
- package/src/presets/fullstack-app.ts +0 -144
- package/src/presets/index.ts +0 -27
- package/src/presets/jamstack.ts +0 -135
- package/src/presets/microservices.ts +0 -167
- package/src/presets/ml-api.ts +0 -208
- package/src/presets/nodejs-server.ts +0 -104
- package/src/presets/nodejs-serverless.ts +0 -114
- package/src/presets/realtime-app.ts +0 -184
- package/src/presets/static-site.ts +0 -64
- package/src/presets/traditional-web-app.ts +0 -339
- package/src/presets/wordpress.ts +0 -138
- package/src/preview/github.test.ts +0 -249
- package/src/preview/github.ts +0 -297
- package/src/preview/index.ts +0 -37
- package/src/preview/manager.test.ts +0 -440
- package/src/preview/manager.ts +0 -326
- package/src/preview/notifications.test.ts +0 -582
- package/src/preview/notifications.ts +0 -341
- package/src/queue/batch-processing.ts +0 -402
- package/src/queue/dlq-monitoring.ts +0 -402
- package/src/queue/fifo.ts +0 -342
- package/src/queue/index.ts +0 -9
- package/src/queue/management.ts +0 -428
- package/src/queue/queue.test.ts +0 -429
- package/src/resource-mgmt/index.ts +0 -39
- package/src/resource-naming.ts +0 -62
- package/src/s3/index.ts +0 -523
- package/src/schema/cloud-config.schema.json +0 -554
- package/src/schema/index.ts +0 -68
- package/src/security/certificate-manager.ts +0 -492
- package/src/security/index.ts +0 -9
- package/src/security/scanning.ts +0 -545
- package/src/security/secrets-manager.ts +0 -476
- package/src/security/secrets-rotation.ts +0 -456
- package/src/security/security.test.ts +0 -738
- package/src/sms/advanced/ab-testing.ts +0 -389
- package/src/sms/advanced/analytics.ts +0 -336
- package/src/sms/advanced/campaigns.ts +0 -523
- package/src/sms/advanced/chatbot.ts +0 -224
- package/src/sms/advanced/index.ts +0 -10
- package/src/sms/advanced/link-tracking.ts +0 -248
- package/src/sms/advanced/mms.ts +0 -308
- package/src/sms/handlers/__tests__/send.test.ts +0 -40
- package/src/sms/handlers/delivery-status.ts +0 -133
- package/src/sms/handlers/receive.ts +0 -162
- package/src/sms/handlers/send.ts +0 -174
- package/src/sms/index.ts +0 -9
- package/src/stack-diff.ts +0 -389
- package/src/static-site/index.ts +0 -85
- package/src/template-builder.ts +0 -110
- package/src/template-validator.ts +0 -574
- package/src/utils/cache.ts +0 -291
- package/src/utils/diff.ts +0 -269
- package/src/utils/hash.ts +0 -227
- package/src/utils/index.ts +0 -8
- package/src/utils/parallel.ts +0 -294
- package/src/validators/credentials.test.ts +0 -274
- package/src/validators/credentials.ts +0 -233
- package/src/validators/quotas.test.ts +0 -434
- package/src/validators/quotas.ts +0 -217
- package/test/ai.test.ts +0 -327
- package/test/api.test.ts +0 -511
- package/test/auth.test.ts +0 -632
- package/test/cache.test.ts +0 -406
- package/test/cdn.test.ts +0 -247
- package/test/compute.test.ts +0 -861
- package/test/database.test.ts +0 -523
- package/test/deployment.test.ts +0 -499
- package/test/dns.test.ts +0 -270
- package/test/email.test.ts +0 -439
- package/test/filesystem.test.ts +0 -382
- package/test/integration.test.ts +0 -350
- package/test/messaging.test.ts +0 -514
- package/test/monitoring.test.ts +0 -634
- package/test/network.test.ts +0 -425
- package/test/permissions.test.ts +0 -488
- package/test/queue.test.ts +0 -484
- package/test/registry.test.ts +0 -306
- package/test/security.test.ts +0 -462
- package/test/storage.test.ts +0 -463
- package/test/template-validator.test.ts +0 -559
- package/test/workflow.test.ts +0 -592
- package/tsconfig.json +0 -16
- package/tsconfig.tsbuildinfo +0 -1
package/src/modules/redirects.ts
DELETED
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Redirects Module - URL Redirect Management
|
|
3
|
-
* Provides clean API for creating domain and path-based redirects
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { CloudFrontFunction, S3Bucket, S3BucketPolicy } from '@stacksjs/ts-cloud-aws-types'
|
|
7
|
-
import type { EnvironmentType } from '@stacksjs/ts-cloud-types'
|
|
8
|
-
import { Fn } from '../intrinsic-functions'
|
|
9
|
-
import { generateLogicalId, generateResourceName } from '../resource-naming'
|
|
10
|
-
|
|
11
|
-
export interface RedirectRule {
|
|
12
|
-
source: string
|
|
13
|
-
target: string
|
|
14
|
-
statusCode?: 301 | 302 | 307 | 308
|
|
15
|
-
preserveQueryString?: boolean
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface DomainRedirectOptions {
|
|
19
|
-
slug: string
|
|
20
|
-
environment: EnvironmentType
|
|
21
|
-
sourceDomain: string
|
|
22
|
-
targetDomain: string
|
|
23
|
-
protocol?: 'http' | 'https'
|
|
24
|
-
preservePath?: boolean
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface PathRedirectOptions {
|
|
28
|
-
slug: string
|
|
29
|
-
environment: EnvironmentType
|
|
30
|
-
rules: RedirectRule[]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Redirects Module - Domain and Path-based Redirects
|
|
35
|
-
* Provides clean API for URL redirects using S3 and CloudFront Functions
|
|
36
|
-
*/
|
|
37
|
-
export class Redirects {
|
|
38
|
-
/**
|
|
39
|
-
* Create an S3 bucket configured for domain redirect
|
|
40
|
-
* Redirects all requests from one domain to another
|
|
41
|
-
*/
|
|
42
|
-
static createDomainRedirectBucket(options: DomainRedirectOptions): {
|
|
43
|
-
bucket: S3Bucket
|
|
44
|
-
bucketPolicy: S3BucketPolicy
|
|
45
|
-
logicalId: string
|
|
46
|
-
policyLogicalId: string
|
|
47
|
-
} {
|
|
48
|
-
const {
|
|
49
|
-
slug,
|
|
50
|
-
environment,
|
|
51
|
-
sourceDomain,
|
|
52
|
-
targetDomain,
|
|
53
|
-
protocol = 'https',
|
|
54
|
-
} = options
|
|
55
|
-
|
|
56
|
-
const resourceName = generateResourceName({
|
|
57
|
-
slug,
|
|
58
|
-
environment,
|
|
59
|
-
resourceType: 's3',
|
|
60
|
-
suffix: 'redirect',
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
const logicalId = generateLogicalId(resourceName)
|
|
64
|
-
const policyLogicalId = generateLogicalId(`${resourceName}-policy`)
|
|
65
|
-
|
|
66
|
-
const bucket: S3Bucket = {
|
|
67
|
-
Type: 'AWS::S3::Bucket',
|
|
68
|
-
Properties: {
|
|
69
|
-
BucketName: sourceDomain,
|
|
70
|
-
WebsiteConfiguration: {
|
|
71
|
-
RedirectAllRequestsTo: {
|
|
72
|
-
HostName: targetDomain,
|
|
73
|
-
Protocol: protocol,
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
PublicAccessBlockConfiguration: {
|
|
77
|
-
BlockPublicAcls: false,
|
|
78
|
-
BlockPublicPolicy: false,
|
|
79
|
-
IgnorePublicAcls: false,
|
|
80
|
-
RestrictPublicBuckets: false,
|
|
81
|
-
},
|
|
82
|
-
Tags: [
|
|
83
|
-
{ Key: 'Name', Value: resourceName },
|
|
84
|
-
{ Key: 'Environment', Value: environment },
|
|
85
|
-
{ Key: 'Purpose', Value: 'Domain Redirect' },
|
|
86
|
-
],
|
|
87
|
-
},
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const bucketPolicy: S3BucketPolicy = {
|
|
91
|
-
Type: 'AWS::S3::BucketPolicy',
|
|
92
|
-
Properties: {
|
|
93
|
-
Bucket: Fn.Ref(logicalId),
|
|
94
|
-
PolicyDocument: {
|
|
95
|
-
Version: '2012-10-17',
|
|
96
|
-
Statement: [{
|
|
97
|
-
Sid: 'PublicReadForRedirect',
|
|
98
|
-
Effect: 'Allow',
|
|
99
|
-
Principal: '*',
|
|
100
|
-
Action: ['s3:GetObject'],
|
|
101
|
-
Resource: [Fn.Join('', [Fn.GetAtt(logicalId, 'Arn'), '/*']) as any],
|
|
102
|
-
}],
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
bucket,
|
|
109
|
-
bucketPolicy,
|
|
110
|
-
logicalId,
|
|
111
|
-
policyLogicalId,
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Create a CloudFront Function for path-based redirects
|
|
117
|
-
*/
|
|
118
|
-
static createPathRedirectFunction(options: PathRedirectOptions): {
|
|
119
|
-
function: CloudFrontFunction
|
|
120
|
-
logicalId: string
|
|
121
|
-
functionCode: string
|
|
122
|
-
} {
|
|
123
|
-
const { slug, environment, rules } = options
|
|
124
|
-
|
|
125
|
-
const resourceName = generateResourceName({
|
|
126
|
-
slug,
|
|
127
|
-
environment,
|
|
128
|
-
resourceType: 'cf-redirect',
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
const logicalId = generateLogicalId(resourceName)
|
|
132
|
-
|
|
133
|
-
// Generate the CloudFront Function code
|
|
134
|
-
const functionCode = Redirects.generateRedirectFunctionCode(rules)
|
|
135
|
-
|
|
136
|
-
const cloudFrontFunction: CloudFrontFunction = {
|
|
137
|
-
Type: 'AWS::CloudFront::Function',
|
|
138
|
-
Properties: {
|
|
139
|
-
Name: resourceName,
|
|
140
|
-
AutoPublish: true,
|
|
141
|
-
FunctionConfig: {
|
|
142
|
-
Comment: `Path redirect function for ${slug} (${environment})`,
|
|
143
|
-
Runtime: 'cloudfront-js-2.0',
|
|
144
|
-
},
|
|
145
|
-
FunctionCode: functionCode,
|
|
146
|
-
},
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
function: cloudFrontFunction,
|
|
151
|
-
logicalId,
|
|
152
|
-
functionCode,
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Generate CloudFront Function code for path redirects
|
|
158
|
-
*/
|
|
159
|
-
static generateRedirectFunctionCode(rules: RedirectRule[]): string {
|
|
160
|
-
const redirectMap = rules.map((rule) => {
|
|
161
|
-
const preserveQs = rule.preserveQueryString !== false
|
|
162
|
-
const statusCode = rule.statusCode || 301
|
|
163
|
-
return ` '${rule.source}': { target: '${rule.target}', statusCode: ${statusCode}, preserveQs: ${preserveQs} }`
|
|
164
|
-
}).join(',\n')
|
|
165
|
-
|
|
166
|
-
return `function handler(event) {
|
|
167
|
-
var request = event.request;
|
|
168
|
-
var uri = request.uri;
|
|
169
|
-
var querystring = request.querystring;
|
|
170
|
-
|
|
171
|
-
var redirects = {
|
|
172
|
-
${redirectMap}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Check for exact match
|
|
176
|
-
var redirect = redirects[uri];
|
|
177
|
-
|
|
178
|
-
// Check for pattern matches (trailing slash variations)
|
|
179
|
-
if (!redirect) {
|
|
180
|
-
// Try without trailing slash
|
|
181
|
-
if (uri.endsWith('/') && uri !== '/') {
|
|
182
|
-
redirect = redirects[uri.slice(0, -1)];
|
|
183
|
-
}
|
|
184
|
-
// Try with trailing slash
|
|
185
|
-
else {
|
|
186
|
-
redirect = redirects[uri + '/'];
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (redirect) {
|
|
191
|
-
var targetUrl = redirect.target;
|
|
192
|
-
|
|
193
|
-
// Preserve query string if configured
|
|
194
|
-
if (redirect.preserveQs && Object.keys(querystring).length > 0) {
|
|
195
|
-
var qs = Object.keys(querystring).map(function(key) {
|
|
196
|
-
var val = querystring[key];
|
|
197
|
-
if (val.value) {
|
|
198
|
-
return key + '=' + val.value;
|
|
199
|
-
}
|
|
200
|
-
return key;
|
|
201
|
-
}).join('&');
|
|
202
|
-
|
|
203
|
-
if (qs) {
|
|
204
|
-
targetUrl += (targetUrl.indexOf('?') >= 0 ? '&' : '?') + qs;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
statusCode: redirect.statusCode,
|
|
210
|
-
statusDescription: redirect.statusCode === 301 ? 'Moved Permanently' : 'Found',
|
|
211
|
-
headers: {
|
|
212
|
-
'location': { value: targetUrl },
|
|
213
|
-
'cache-control': { value: 'max-age=3600' }
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return request;
|
|
219
|
-
}`
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Create common redirect patterns
|
|
224
|
-
*/
|
|
225
|
-
static readonly CommonRedirects = {
|
|
226
|
-
/**
|
|
227
|
-
* www to non-www redirect
|
|
228
|
-
*/
|
|
229
|
-
wwwToApex: (domain: string, protocol: 'http' | 'https' = 'https'): DomainRedirectOptions => ({
|
|
230
|
-
slug: domain.replace(/\./g, '-'),
|
|
231
|
-
environment: 'production',
|
|
232
|
-
sourceDomain: `www.${domain}`,
|
|
233
|
-
targetDomain: domain,
|
|
234
|
-
protocol,
|
|
235
|
-
preservePath: true,
|
|
236
|
-
}),
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* non-www to www redirect
|
|
240
|
-
*/
|
|
241
|
-
apexToWww: (domain: string, protocol: 'http' | 'https' = 'https'): DomainRedirectOptions => ({
|
|
242
|
-
slug: domain.replace(/\./g, '-'),
|
|
243
|
-
environment: 'production',
|
|
244
|
-
sourceDomain: domain,
|
|
245
|
-
targetDomain: `www.${domain}`,
|
|
246
|
-
protocol,
|
|
247
|
-
preservePath: true,
|
|
248
|
-
}),
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* HTTP to HTTPS redirect (handled at CloudFront/ALB level typically)
|
|
252
|
-
*/
|
|
253
|
-
httpToHttps: (path: string = '/'): RedirectRule => ({
|
|
254
|
-
source: path,
|
|
255
|
-
target: path,
|
|
256
|
-
statusCode: 301,
|
|
257
|
-
}),
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Trailing slash normalization (add trailing slash)
|
|
261
|
-
*/
|
|
262
|
-
addTrailingSlash: (paths: string[]): RedirectRule[] =>
|
|
263
|
-
paths.map(path => ({
|
|
264
|
-
source: path.endsWith('/') ? path.slice(0, -1) : path,
|
|
265
|
-
target: path.endsWith('/') ? path : `${path}/`,
|
|
266
|
-
statusCode: 301,
|
|
267
|
-
})),
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Trailing slash normalization (remove trailing slash)
|
|
271
|
-
*/
|
|
272
|
-
removeTrailingSlash: (paths: string[]): RedirectRule[] =>
|
|
273
|
-
paths.map(path => ({
|
|
274
|
-
source: path.endsWith('/') && path !== '/' ? path : `${path}/`,
|
|
275
|
-
target: path.endsWith('/') && path !== '/' ? path.slice(0, -1) : path,
|
|
276
|
-
statusCode: 301,
|
|
277
|
-
})),
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Create redirect rules for common URL patterns
|
|
282
|
-
*/
|
|
283
|
-
static readonly Patterns = {
|
|
284
|
-
/**
|
|
285
|
-
* Old blog URL pattern to new pattern
|
|
286
|
-
* /blog/2023/01/my-post -> /blog/my-post
|
|
287
|
-
*/
|
|
288
|
-
flattenBlogUrls: (posts: Array<{ oldPath: string, newPath: string }>): RedirectRule[] =>
|
|
289
|
-
posts.map(({ oldPath, newPath }) => ({
|
|
290
|
-
source: oldPath,
|
|
291
|
-
target: newPath,
|
|
292
|
-
statusCode: 301,
|
|
293
|
-
preserveQueryString: true,
|
|
294
|
-
})),
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Category URL changes
|
|
298
|
-
*/
|
|
299
|
-
categoryRename: (oldCategory: string, newCategory: string): RedirectRule[] => [
|
|
300
|
-
{
|
|
301
|
-
source: `/${oldCategory}`,
|
|
302
|
-
target: `/${newCategory}`,
|
|
303
|
-
statusCode: 301,
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
source: `/${oldCategory}/`,
|
|
307
|
-
target: `/${newCategory}/`,
|
|
308
|
-
statusCode: 301,
|
|
309
|
-
},
|
|
310
|
-
],
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Product page URL pattern
|
|
314
|
-
*/
|
|
315
|
-
productSlugChange: (products: Array<{ oldSlug: string, newSlug: string }>): RedirectRule[] =>
|
|
316
|
-
products.map(({ oldSlug, newSlug }) => ({
|
|
317
|
-
source: `/products/${oldSlug}`,
|
|
318
|
-
target: `/products/${newSlug}`,
|
|
319
|
-
statusCode: 301,
|
|
320
|
-
})),
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Deprecated API version redirects
|
|
324
|
-
*/
|
|
325
|
-
apiVersionRedirect: (oldVersion: string, newVersion: string, endpoints: string[]): RedirectRule[] =>
|
|
326
|
-
endpoints.map(endpoint => ({
|
|
327
|
-
source: `/api/${oldVersion}/${endpoint}`,
|
|
328
|
-
target: `/api/${newVersion}/${endpoint}`,
|
|
329
|
-
statusCode: 307, // Temporary, preserves method
|
|
330
|
-
preserveQueryString: true,
|
|
331
|
-
})),
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Gone (410) redirects for deleted content
|
|
335
|
-
*/
|
|
336
|
-
gonePages: (paths: string[]): RedirectRule[] =>
|
|
337
|
-
paths.map(path => ({
|
|
338
|
-
source: path,
|
|
339
|
-
target: '/410',
|
|
340
|
-
statusCode: 301, // CloudFront Functions don't support 410, redirect to a 410 page
|
|
341
|
-
})),
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Create a complete redirect setup with multiple rules
|
|
346
|
-
*/
|
|
347
|
-
static createRedirectSetup(options: {
|
|
348
|
-
slug: string
|
|
349
|
-
environment: EnvironmentType
|
|
350
|
-
domainRedirects?: DomainRedirectOptions[]
|
|
351
|
-
pathRedirects?: RedirectRule[]
|
|
352
|
-
}): {
|
|
353
|
-
resources: Record<string, any>
|
|
354
|
-
outputs: {
|
|
355
|
-
domainRedirectBuckets: string[]
|
|
356
|
-
pathRedirectFunctionLogicalId: string | null
|
|
357
|
-
}
|
|
358
|
-
} {
|
|
359
|
-
const { slug, environment, domainRedirects = [], pathRedirects = [] } = options
|
|
360
|
-
const resources: Record<string, any> = {}
|
|
361
|
-
const domainRedirectBuckets: string[] = []
|
|
362
|
-
|
|
363
|
-
// Create domain redirect buckets
|
|
364
|
-
for (const domainRedirect of domainRedirects) {
|
|
365
|
-
const { bucket, bucketPolicy, logicalId, policyLogicalId } = Redirects.createDomainRedirectBucket({
|
|
366
|
-
...domainRedirect,
|
|
367
|
-
slug,
|
|
368
|
-
environment,
|
|
369
|
-
})
|
|
370
|
-
resources[logicalId] = bucket
|
|
371
|
-
resources[policyLogicalId] = bucketPolicy
|
|
372
|
-
domainRedirectBuckets.push(logicalId)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Create path redirect function if there are path redirects
|
|
376
|
-
let pathRedirectFunctionLogicalId: string | null = null
|
|
377
|
-
if (pathRedirects.length > 0) {
|
|
378
|
-
const { function: redirectFunction, logicalId } = Redirects.createPathRedirectFunction({
|
|
379
|
-
slug,
|
|
380
|
-
environment,
|
|
381
|
-
rules: pathRedirects,
|
|
382
|
-
})
|
|
383
|
-
resources[logicalId] = redirectFunction
|
|
384
|
-
pathRedirectFunctionLogicalId = logicalId
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return {
|
|
388
|
-
resources,
|
|
389
|
-
outputs: {
|
|
390
|
-
domainRedirectBuckets,
|
|
391
|
-
pathRedirectFunctionLogicalId,
|
|
392
|
-
},
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Generate redirect rules from a simple mapping object
|
|
398
|
-
*/
|
|
399
|
-
static fromMapping(
|
|
400
|
-
mapping: Record<string, string>,
|
|
401
|
-
options?: { statusCode?: 301 | 302 | 307 | 308, preserveQueryString?: boolean },
|
|
402
|
-
): RedirectRule[] {
|
|
403
|
-
return Object.entries(mapping).map(([source, target]) => ({
|
|
404
|
-
source,
|
|
405
|
-
target,
|
|
406
|
-
statusCode: options?.statusCode || 301,
|
|
407
|
-
preserveQueryString: options?.preserveQueryString ?? true,
|
|
408
|
-
}))
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Validate redirect rules for common issues
|
|
413
|
-
*/
|
|
414
|
-
static validateRules(rules: RedirectRule[]): {
|
|
415
|
-
valid: boolean
|
|
416
|
-
errors: string[]
|
|
417
|
-
warnings: string[]
|
|
418
|
-
} {
|
|
419
|
-
const errors: string[] = []
|
|
420
|
-
const warnings: string[] = []
|
|
421
|
-
const sources = new Set<string>()
|
|
422
|
-
|
|
423
|
-
for (const rule of rules) {
|
|
424
|
-
// Check for duplicate sources
|
|
425
|
-
if (sources.has(rule.source)) {
|
|
426
|
-
errors.push(`Duplicate source path: ${rule.source}`)
|
|
427
|
-
}
|
|
428
|
-
sources.add(rule.source)
|
|
429
|
-
|
|
430
|
-
// Check for redirect loops
|
|
431
|
-
if (rule.source === rule.target) {
|
|
432
|
-
errors.push(`Redirect loop detected: ${rule.source} -> ${rule.target}`)
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Check for relative URLs in target
|
|
436
|
-
if (!rule.target.startsWith('/') && !rule.target.startsWith('http')) {
|
|
437
|
-
warnings.push(`Target URL should start with / or http: ${rule.target}`)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Check for empty paths
|
|
441
|
-
if (!rule.source || rule.source.trim() === '') {
|
|
442
|
-
errors.push('Source path cannot be empty')
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (!rule.target || rule.target.trim() === '') {
|
|
446
|
-
errors.push('Target path cannot be empty')
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Check for potential redirect chains
|
|
451
|
-
for (const rule of rules) {
|
|
452
|
-
const chainTarget = rules.find(r => r.source === rule.target)
|
|
453
|
-
if (chainTarget) {
|
|
454
|
-
warnings.push(`Potential redirect chain: ${rule.source} -> ${rule.target} -> ${chainTarget.target}`)
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return {
|
|
459
|
-
valid: errors.length === 0,
|
|
460
|
-
errors,
|
|
461
|
-
warnings,
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|