@stacksjs/ts-cloud-core 0.1.6 → 0.1.7
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/advanced-features.test.d.ts +0 -0
- package/dist/aws/cloudformation.d.ts +69 -0
- package/dist/aws/cloudfront.d.ts +21 -0
- package/dist/aws/credentials.d.ts +66 -0
- package/dist/aws/credentials.test.d.ts +0 -0
- package/dist/aws/index.d.ts +73 -0
- package/dist/aws/s3.d.ts +130 -0
- package/dist/aws/s3.test.d.ts +0 -0
- package/dist/aws/signature.d.ts +101 -0
- package/dist/aws/signature.test.d.ts +0 -0
- package/dist/backup/disaster-recovery.d.ts +98 -0
- package/dist/backup/disaster-recovery.test.d.ts +0 -0
- package/dist/backup/index.d.ts +24 -0
- package/dist/backup/manager.d.ts +112 -0
- package/dist/backup/manager.test.d.ts +0 -0
- package/dist/cicd/circleci.d.ts +47 -0
- package/dist/cicd/github-actions.d.ts +55 -0
- package/dist/cicd/gitlab-ci.d.ts +46 -0
- package/dist/cicd/index.d.ts +3 -0
- package/dist/cli/history.d.ts +66 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/progress.d.ts +97 -0
- package/dist/cli/repl.d.ts +76 -0
- package/dist/cli/suggestions.d.ts +67 -0
- package/dist/cli/table.d.ts +70 -0
- package/dist/cli/table.test.d.ts +0 -0
- package/dist/cloudformation/builder.d.ts +59 -0
- package/dist/cloudformation/builder.test.d.ts +0 -0
- package/dist/cloudformation/builders/api-gateway.d.ts +30 -0
- package/dist/cloudformation/builders/cache.d.ts +35 -0
- package/dist/cloudformation/builders/cdn.d.ts +34 -0
- package/dist/cloudformation/builders/compute.d.ts +66 -0
- package/dist/cloudformation/builders/database.d.ts +61 -0
- package/dist/cloudformation/builders/functions.d.ts +32 -0
- package/dist/cloudformation/builders/messaging.d.ts +17 -0
- package/dist/cloudformation/builders/monitoring.d.ts +36 -0
- package/dist/cloudformation/builders/network.d.ts +14 -0
- package/dist/cloudformation/builders/queue.d.ts +8 -0
- package/dist/cloudformation/builders/security.d.ts +31 -0
- package/dist/cloudformation/builders/storage.d.ts +8 -0
- package/dist/cloudformation/index.d.ts +24 -0
- package/dist/cloudformation/types.d.ts +132 -0
- package/dist/compliance/aws-config.d.ts +88 -0
- package/dist/compliance/cloudtrail.d.ts +96 -0
- package/dist/compliance/compliance.test.d.ts +0 -0
- package/dist/compliance/guardduty.d.ts +110 -0
- package/dist/compliance/index.d.ts +50 -0
- package/dist/compliance/security-hub.d.ts +110 -0
- package/dist/containers/build-optimization.d.ts +110 -0
- package/dist/containers/containers.test.d.ts +0 -0
- package/dist/containers/image-scanning.d.ts +96 -0
- package/dist/containers/index.d.ts +4 -0
- package/dist/containers/registry.d.ts +99 -0
- package/dist/containers/service-mesh.d.ts +206 -0
- package/dist/database/database.test.d.ts +0 -0
- package/dist/database/index.d.ts +4 -0
- package/dist/database/migrations.d.ts +102 -0
- package/dist/database/performance.d.ts +168 -0
- package/dist/database/replicas.d.ts +146 -0
- package/dist/database/users.d.ts +102 -0
- package/dist/dependency-graph.d.ts +19 -0
- package/dist/deployment/ab-testing.d.ts +114 -0
- package/dist/deployment/blue-green.d.ts +98 -0
- package/dist/deployment/canary.d.ts +103 -0
- package/dist/deployment/deployment.test.d.ts +0 -0
- package/dist/deployment/index.d.ts +45 -0
- package/dist/deployment/progressive.d.ts +34 -0
- package/dist/dns/dns.test.d.ts +0 -0
- package/dist/dns/dnssec.d.ts +75 -0
- package/dist/dns/index.d.ts +3 -0
- package/dist/dns/resolver.d.ts +150 -0
- package/dist/dns/routing.d.ts +217 -0
- package/dist/email/advanced/analytics.d.ts +78 -0
- package/dist/email/advanced/index.d.ts +7 -0
- package/dist/email/advanced/rules.d.ts +60 -0
- package/dist/email/advanced/scheduling.d.ts +63 -0
- package/dist/email/advanced/search.d.ts +76 -0
- package/dist/email/advanced/shared-mailboxes.d.ts +66 -0
- package/dist/email/advanced/templates.d.ts +39 -0
- package/dist/email/advanced/threading.d.ts +53 -0
- package/dist/email/analytics.d.ts +144 -0
- package/dist/email/bounce-handling.d.ts +120 -0
- package/dist/email/email.test.d.ts +0 -0
- package/dist/email/handlers/__tests__/inbound.test.d.ts +0 -0
- package/dist/email/handlers/__tests__/outbound.test.d.ts +0 -0
- package/dist/email/handlers/converter.d.ts +225 -0
- package/dist/email/handlers/feedback.d.ts +226 -0
- package/dist/email/handlers/inbound.d.ts +167 -0
- package/dist/email/handlers/outbound.d.ts +176 -0
- package/dist/email/index.d.ts +6 -0
- package/dist/email/reputation.d.ts +97 -0
- package/dist/email/templates.d.ts +82 -0
- package/dist/errors/index.d.ts +186 -0
- package/dist/errors/index.test.d.ts +0 -0
- package/dist/health-checks/index.d.ts +35 -0
- package/dist/index.d.ts +256 -0
- package/dist/index.js +63499 -0
- package/dist/intrinsic-functions.d.ts +37 -0
- package/dist/lambda/concurrency.d.ts +98 -0
- package/dist/lambda/destinations.d.ts +99 -0
- package/dist/lambda/dlq.d.ts +109 -0
- package/dist/lambda/index.d.ts +6 -0
- package/dist/lambda/lambda.test.d.ts +0 -0
- package/dist/lambda/layers.d.ts +81 -0
- package/dist/lambda/versions.d.ts +91 -0
- package/dist/lambda/vpc.d.ts +116 -0
- package/dist/local/config.d.ts +44 -0
- package/dist/local/index.d.ts +2 -0
- package/dist/local/mock-aws.d.ts +60 -0
- package/dist/modules/ai.d.ts +47 -0
- package/dist/modules/api.d.ts +98 -0
- package/dist/modules/auth.d.ts +165 -0
- package/dist/modules/cache.d.ts +73 -0
- package/dist/modules/cdn.d.ts +125 -0
- package/dist/modules/communication.d.ts +98 -0
- package/dist/modules/compute.d.ts +309 -0
- package/dist/modules/database.d.ts +105 -0
- package/dist/modules/deployment.d.ts +181 -0
- package/dist/modules/dns.d.ts +45 -0
- package/dist/modules/email.d.ts +217 -0
- package/dist/modules/filesystem.d.ts +94 -0
- package/dist/modules/index.d.ts +27 -0
- package/dist/modules/messaging.d.ts +108 -0
- package/dist/modules/monitoring.d.ts +127 -0
- package/dist/modules/network.d.ts +102 -0
- package/dist/modules/parameter-store.d.ts +33 -0
- package/dist/modules/permissions.d.ts +132 -0
- package/dist/modules/phone.d.ts +80 -0
- package/dist/modules/queue.d.ts +210 -0
- package/dist/modules/redirects.d.ts +59 -0
- package/dist/modules/registry.d.ts +73 -0
- package/dist/modules/search.d.ts +56 -0
- package/dist/modules/secrets.d.ts +80 -0
- package/dist/modules/security.d.ts +100 -0
- package/dist/modules/sms.d.ts +52 -0
- package/dist/modules/storage.d.ts +160 -0
- package/dist/modules/workflow.d.ts +205 -0
- package/dist/multi-account/config.d.ts +315 -0
- package/dist/multi-account/index.d.ts +2 -0
- package/dist/multi-account/manager.d.ts +100 -0
- package/dist/multi-region/cross-region.d.ts +114 -0
- package/dist/multi-region/index.d.ts +3 -0
- package/dist/multi-region/manager.d.ts +72 -0
- package/dist/multi-region/regions.d.ts +98 -0
- package/dist/network-security/index.d.ts +39 -0
- package/dist/observability/index.d.ts +4 -0
- package/dist/observability/logs.d.ts +129 -0
- package/dist/observability/metrics.d.ts +153 -0
- package/dist/observability/observability.test.d.ts +0 -0
- package/dist/observability/synthetics.d.ts +146 -0
- package/dist/observability/xray.d.ts +129 -0
- package/dist/phone/advanced/analytics.d.ts +66 -0
- package/dist/phone/advanced/callbacks.d.ts +50 -0
- package/dist/phone/advanced/index.d.ts +4 -0
- package/dist/phone/advanced/ivr-builder.d.ts +83 -0
- package/dist/phone/advanced/recording.d.ts +48 -0
- package/dist/phone/handlers/__tests__/incoming-call.test.d.ts +0 -0
- package/dist/phone/handlers/incoming-call.d.ts +115 -0
- package/dist/phone/handlers/missed-call.d.ts +114 -0
- package/dist/phone/handlers/voicemail.d.ts +177 -0
- package/dist/phone/index.d.ts +2 -0
- package/dist/presets/api-backend.d.ts +11 -0
- package/dist/presets/data-pipeline.d.ts +11 -0
- package/dist/presets/extend.d.ts +194 -0
- package/dist/presets/extend.test.d.ts +0 -0
- package/dist/presets/fullstack-app.d.ts +12 -0
- package/dist/presets/index.d.ts +24 -0
- package/dist/presets/jamstack.d.ts +12 -0
- package/dist/presets/microservices.d.ts +18 -0
- package/dist/presets/ml-api.d.ts +13 -0
- package/dist/presets/nodejs-server.d.ts +14 -0
- package/dist/presets/nodejs-serverless.d.ts +14 -0
- package/dist/presets/realtime-app.d.ts +11 -0
- package/dist/presets/static-site.d.ts +12 -0
- package/dist/presets/traditional-web-app.d.ts +16 -0
- package/dist/presets/wordpress.d.ts +12 -0
- package/dist/preview/github.d.ts +32 -0
- package/dist/preview/github.test.d.ts +0 -0
- package/dist/preview/index.d.ts +27 -0
- package/dist/preview/manager.d.ts +58 -0
- package/dist/preview/manager.test.d.ts +0 -0
- package/dist/preview/notifications.d.ts +55 -0
- package/dist/preview/notifications.test.d.ts +0 -0
- package/dist/queue/batch-processing.d.ts +87 -0
- package/dist/queue/dlq-monitoring.d.ts +95 -0
- package/dist/queue/fifo.d.ts +90 -0
- package/dist/queue/index.d.ts +4 -0
- package/dist/queue/management.d.ts +105 -0
- package/dist/queue/queue.test.d.ts +0 -0
- package/dist/resource-mgmt/index.d.ts +29 -0
- package/dist/resource-naming.d.ts +26 -0
- package/dist/s3/index.d.ts +173 -0
- package/dist/schema/index.d.ts +9 -0
- package/dist/security/certificate-manager.d.ts +121 -0
- package/dist/security/index.d.ts +4 -0
- package/dist/security/scanning.d.ts +147 -0
- package/dist/security/secrets-manager.d.ts +144 -0
- package/dist/security/secrets-rotation.d.ts +115 -0
- package/dist/security/security.test.d.ts +0 -0
- package/dist/sms/advanced/ab-testing.d.ts +54 -0
- package/dist/sms/advanced/analytics.d.ts +56 -0
- package/dist/sms/advanced/campaigns.d.ts +82 -0
- package/dist/sms/advanced/chatbot.d.ts +48 -0
- package/dist/sms/advanced/index.d.ts +6 -0
- package/dist/sms/advanced/link-tracking.d.ts +42 -0
- package/dist/sms/advanced/mms.d.ts +35 -0
- package/dist/sms/handlers/__tests__/send.test.d.ts +0 -0
- package/dist/sms/handlers/delivery-status.d.ts +131 -0
- package/dist/sms/handlers/receive.d.ts +160 -0
- package/dist/sms/handlers/send.d.ts +172 -0
- package/dist/sms/index.d.ts +2 -0
- package/dist/stack-diff.d.ts +34 -0
- package/dist/static-site/index.d.ts +49 -0
- package/dist/template-builder.d.ts +14 -0
- package/dist/template-validator.d.ts +24 -0
- package/dist/utils/cache.d.ts +55 -0
- package/dist/utils/diff.d.ts +48 -0
- package/dist/utils/hash.d.ts +58 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/parallel.d.ts +60 -0
- package/dist/validators/credentials.d.ts +23 -0
- package/dist/validators/credentials.test.d.ts +0 -0
- package/dist/validators/quotas.d.ts +60 -0
- package/dist/validators/quotas.test.d.ts +0 -0
- package/package.json +4 -4
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MMS Support (Images, Media)
|
|
3
|
+
*
|
|
4
|
+
* Provides multimedia messaging capabilities
|
|
5
|
+
*/
|
|
6
|
+
export declare interface MmsMessage {
|
|
7
|
+
to: string
|
|
8
|
+
body?: string
|
|
9
|
+
mediaUrls: string[]
|
|
10
|
+
mediaType?: 'image' | 'video' | 'audio' | 'document'
|
|
11
|
+
fallbackSms?: string
|
|
12
|
+
}
|
|
13
|
+
export declare interface MmsMedia {
|
|
14
|
+
url: string
|
|
15
|
+
contentType: string
|
|
16
|
+
size: number
|
|
17
|
+
filename?: string
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* MMS Module
|
|
21
|
+
*/
|
|
22
|
+
export declare class MmsSupport {
|
|
23
|
+
static MmsSenderCode: any;
|
|
24
|
+
static createMediaBucket(config: { slug: string }): Record<string, any>;
|
|
25
|
+
static createMmsSenderLambda(config: {
|
|
26
|
+
slug: string
|
|
27
|
+
roleArn: string
|
|
28
|
+
mediaBucket: string
|
|
29
|
+
messageLogTable: string
|
|
30
|
+
originationNumber?: string
|
|
31
|
+
}): Record<string, any>;
|
|
32
|
+
static readonly SupportedMediaTypes: any;
|
|
33
|
+
static readonly MediaSizeLimits: { image: number, video: number, audio: number };
|
|
34
|
+
}
|
|
35
|
+
export default MmsSupport;
|
|
File without changes
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMS Delivery Status Lambda Handler
|
|
3
|
+
*
|
|
4
|
+
* Processes delivery status updates:
|
|
5
|
+
* - Process delivery receipts
|
|
6
|
+
* - Update message status
|
|
7
|
+
* - Handle failures
|
|
8
|
+
*/
|
|
9
|
+
export declare const handler: `
|
|
10
|
+
const { DynamoDBClient, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
|
|
11
|
+
const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');
|
|
12
|
+
|
|
13
|
+
const dynamodb = new DynamoDBClient({});
|
|
14
|
+
const sns = new SNSClient({});
|
|
15
|
+
|
|
16
|
+
exports.handler = async (event) => {
|
|
17
|
+
console.log('SMS delivery status event:', JSON.stringify(event, null, 2));
|
|
18
|
+
|
|
19
|
+
const messageLogTable = process.env.MESSAGE_LOG_TABLE;
|
|
20
|
+
const notificationTopicArn = process.env.NOTIFICATION_TOPIC_ARN;
|
|
21
|
+
const webhookUrl = process.env.WEBHOOK_URL;
|
|
22
|
+
|
|
23
|
+
for (const record of event.Records) {
|
|
24
|
+
try {
|
|
25
|
+
// Parse delivery status from SNS/Pinpoint
|
|
26
|
+
const message = JSON.parse(record.Sns?.Message || record.body || '{}');
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
eventType,
|
|
30
|
+
messageId,
|
|
31
|
+
destinationPhoneNumber,
|
|
32
|
+
messageStatus,
|
|
33
|
+
messageStatusDescription,
|
|
34
|
+
isoCountryCode,
|
|
35
|
+
mcc,
|
|
36
|
+
mnc,
|
|
37
|
+
priceInMillicentsUSD,
|
|
38
|
+
} = message;
|
|
39
|
+
|
|
40
|
+
if (!messageId) {
|
|
41
|
+
console.log('No messageId in delivery status');
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const timestamp = new Date().toISOString();
|
|
46
|
+
const status = messageStatus || eventType || 'UNKNOWN';
|
|
47
|
+
|
|
48
|
+
console.log(\`Delivery status for \${messageId}: \${status}\`);
|
|
49
|
+
|
|
50
|
+
// Update message log
|
|
51
|
+
if (messageLogTable) {
|
|
52
|
+
await dynamodb.send(new UpdateItemCommand({
|
|
53
|
+
TableName: messageLogTable,
|
|
54
|
+
Key: {
|
|
55
|
+
messageId: { S: messageId },
|
|
56
|
+
},
|
|
57
|
+
UpdateExpression: 'SET deliveryStatus = :status, statusDescription = :desc, deliveredAt = :at, priceMillicents = :price, countryCode = :country',
|
|
58
|
+
ExpressionAttributeValues: {
|
|
59
|
+
':status': { S: status },
|
|
60
|
+
':desc': { S: messageStatusDescription || '' },
|
|
61
|
+
':at': { S: timestamp },
|
|
62
|
+
':price': { N: String(priceInMillicentsUSD || 0) },
|
|
63
|
+
':country': { S: isoCountryCode || '' },
|
|
64
|
+
},
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Handle failures - notify admin
|
|
69
|
+
const isFailure = ['FAILED', 'UNREACHABLE', 'UNKNOWN', 'CARRIER_UNREACHABLE', 'BLOCKED', 'CARRIER_BLOCKED', 'INVALID', 'INVALID_MESSAGE', 'OPTED_OUT'].includes(status);
|
|
70
|
+
|
|
71
|
+
if (isFailure) {
|
|
72
|
+
console.log(\`SMS delivery failed: \${status} - \${messageStatusDescription}\`);
|
|
73
|
+
|
|
74
|
+
// Send failure notification
|
|
75
|
+
if (notificationTopicArn) {
|
|
76
|
+
await sns.send(new PublishCommand({
|
|
77
|
+
TopicArn: notificationTopicArn,
|
|
78
|
+
Subject: \`SMS Delivery Failed: \${status}\`,
|
|
79
|
+
Message: JSON.stringify({
|
|
80
|
+
type: 'sms_delivery_failed',
|
|
81
|
+
messageId,
|
|
82
|
+
to: destinationPhoneNumber,
|
|
83
|
+
status,
|
|
84
|
+
description: messageStatusDescription,
|
|
85
|
+
countryCode: isoCountryCode,
|
|
86
|
+
timestamp,
|
|
87
|
+
}, null, 2),
|
|
88
|
+
MessageAttributes: {
|
|
89
|
+
eventType: {
|
|
90
|
+
DataType: 'String',
|
|
91
|
+
StringValue: 'sms_delivery_failed',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Forward to webhook for all status updates
|
|
99
|
+
if (webhookUrl) {
|
|
100
|
+
try {
|
|
101
|
+
await fetch(webhookUrl, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: { 'Content-Type': 'application/json' },
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
event: 'sms_delivery_status',
|
|
106
|
+
data: {
|
|
107
|
+
messageId,
|
|
108
|
+
to: destinationPhoneNumber,
|
|
109
|
+
status,
|
|
110
|
+
description: messageStatusDescription,
|
|
111
|
+
countryCode: isoCountryCode,
|
|
112
|
+
carrier: { mcc, mnc },
|
|
113
|
+
priceMillicents: priceInMillicentsUSD,
|
|
114
|
+
timestamp,
|
|
115
|
+
},
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.error('Webhook failed:', err.message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('Error processing delivery status:', error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { statusCode: 200, body: 'OK' };
|
|
129
|
+
};
|
|
130
|
+
`;
|
|
131
|
+
export default handler;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMS Receive Lambda Handler
|
|
3
|
+
*
|
|
4
|
+
* Processes inbound SMS messages:
|
|
5
|
+
* - Process inbound SMS (two-way)
|
|
6
|
+
* - Handle opt-out keywords
|
|
7
|
+
* - Forward to webhook
|
|
8
|
+
*/
|
|
9
|
+
export declare const handler: `
|
|
10
|
+
const { DynamoDBClient, PutItemCommand, GetItemCommand, DeleteItemCommand } = require('@aws-sdk/client-dynamodb');
|
|
11
|
+
const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');
|
|
12
|
+
|
|
13
|
+
const dynamodb = new DynamoDBClient({});
|
|
14
|
+
const sns = new SNSClient({});
|
|
15
|
+
|
|
16
|
+
const OPT_OUT_KEYWORDS = ['STOP', 'UNSUBSCRIBE', 'CANCEL', 'END', 'QUIT', 'OPTOUT', 'OPT OUT'];
|
|
17
|
+
const OPT_IN_KEYWORDS = ['START', 'SUBSCRIBE', 'OPTIN', 'OPT IN', 'YES'];
|
|
18
|
+
|
|
19
|
+
exports.handler = async (event) => {
|
|
20
|
+
console.log('SMS receive event:', JSON.stringify(event, null, 2));
|
|
21
|
+
|
|
22
|
+
const optOutTable = process.env.OPT_OUT_TABLE;
|
|
23
|
+
const messageLogTable = process.env.MESSAGE_LOG_TABLE;
|
|
24
|
+
const notificationTopicArn = process.env.NOTIFICATION_TOPIC_ARN;
|
|
25
|
+
const webhookUrl = process.env.WEBHOOK_URL;
|
|
26
|
+
|
|
27
|
+
for (const record of event.Records) {
|
|
28
|
+
try {
|
|
29
|
+
// Parse SNS message from Pinpoint
|
|
30
|
+
const message = JSON.parse(record.Sns?.Message || record.body || '{}');
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
originationNumber,
|
|
34
|
+
destinationNumber,
|
|
35
|
+
messageBody,
|
|
36
|
+
messageKeyword,
|
|
37
|
+
inboundMessageId,
|
|
38
|
+
} = message;
|
|
39
|
+
|
|
40
|
+
if (!originationNumber || !messageBody) {
|
|
41
|
+
console.log('Missing required fields');
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const timestamp = new Date().toISOString();
|
|
46
|
+
const normalizedBody = messageBody.trim().toUpperCase();
|
|
47
|
+
|
|
48
|
+
// Check for opt-out keywords
|
|
49
|
+
if (OPT_OUT_KEYWORDS.some(kw => normalizedBody === kw || normalizedBody.startsWith(kw + ' '))) {
|
|
50
|
+
console.log(\`Opt-out request from \${originationNumber}\`);
|
|
51
|
+
|
|
52
|
+
if (optOutTable) {
|
|
53
|
+
await dynamodb.send(new PutItemCommand({
|
|
54
|
+
TableName: optOutTable,
|
|
55
|
+
Item: {
|
|
56
|
+
phoneNumber: { S: originationNumber },
|
|
57
|
+
optedOutAt: { S: timestamp },
|
|
58
|
+
keyword: { S: normalizedBody.split(' ')[0] },
|
|
59
|
+
originalMessage: { S: messageBody },
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Send confirmation (optional - check carrier requirements)
|
|
65
|
+
// await sendOptOutConfirmation(originationNumber, destinationNumber);
|
|
66
|
+
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check for opt-in keywords
|
|
71
|
+
if (OPT_IN_KEYWORDS.some(kw => normalizedBody === kw || normalizedBody.startsWith(kw + ' '))) {
|
|
72
|
+
console.log(\`Opt-in request from \${originationNumber}\`);
|
|
73
|
+
|
|
74
|
+
if (optOutTable) {
|
|
75
|
+
await dynamodb.send(new DeleteItemCommand({
|
|
76
|
+
TableName: optOutTable,
|
|
77
|
+
Key: {
|
|
78
|
+
phoneNumber: { S: originationNumber },
|
|
79
|
+
},
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Log inbound message
|
|
87
|
+
if (messageLogTable) {
|
|
88
|
+
await dynamodb.send(new PutItemCommand({
|
|
89
|
+
TableName: messageLogTable,
|
|
90
|
+
Item: {
|
|
91
|
+
messageId: { S: inboundMessageId || \`inbound-\${Date.now()}\` },
|
|
92
|
+
direction: { S: 'inbound' },
|
|
93
|
+
from: { S: originationNumber },
|
|
94
|
+
to: { S: destinationNumber },
|
|
95
|
+
body: { S: messageBody },
|
|
96
|
+
keyword: { S: messageKeyword || '' },
|
|
97
|
+
receivedAt: { S: timestamp },
|
|
98
|
+
ttl: { N: String(Math.floor(Date.now() / 1000) + 90 * 24 * 60 * 60) },
|
|
99
|
+
},
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Send SNS notification
|
|
104
|
+
if (notificationTopicArn) {
|
|
105
|
+
await sns.send(new PublishCommand({
|
|
106
|
+
TopicArn: notificationTopicArn,
|
|
107
|
+
Subject: 'Inbound SMS',
|
|
108
|
+
Message: JSON.stringify({
|
|
109
|
+
type: 'inbound_sms',
|
|
110
|
+
from: originationNumber,
|
|
111
|
+
to: destinationNumber,
|
|
112
|
+
body: messageBody,
|
|
113
|
+
keyword: messageKeyword,
|
|
114
|
+
timestamp,
|
|
115
|
+
}, null, 2),
|
|
116
|
+
MessageAttributes: {
|
|
117
|
+
eventType: {
|
|
118
|
+
DataType: 'String',
|
|
119
|
+
StringValue: 'inbound_sms',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Forward to webhook
|
|
126
|
+
if (webhookUrl) {
|
|
127
|
+
try {
|
|
128
|
+
const response = await fetch(webhookUrl, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: { 'Content-Type': 'application/json' },
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
event: 'inbound_sms',
|
|
133
|
+
data: {
|
|
134
|
+
from: originationNumber,
|
|
135
|
+
to: destinationNumber,
|
|
136
|
+
body: messageBody,
|
|
137
|
+
keyword: messageKeyword,
|
|
138
|
+
messageId: inboundMessageId,
|
|
139
|
+
timestamp,
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
console.log(\`Webhook response: \${response.status}\`);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error('Webhook failed:', err.message);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log(\`Processed inbound SMS from \${originationNumber}\`);
|
|
151
|
+
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error('Error processing inbound SMS:', error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { statusCode: 200, body: 'OK' };
|
|
158
|
+
};
|
|
159
|
+
`;
|
|
160
|
+
export default handler;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMS Send Lambda Handler
|
|
3
|
+
*
|
|
4
|
+
* Sends SMS messages:
|
|
5
|
+
* - Send SMS via Pinpoint/SNS
|
|
6
|
+
* - Handle templated messages
|
|
7
|
+
* - Track delivery status
|
|
8
|
+
*/
|
|
9
|
+
export declare const handler: `
|
|
10
|
+
const { PinpointClient, SendMessagesCommand } = require('@aws-sdk/client-pinpoint');
|
|
11
|
+
const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');
|
|
12
|
+
const { DynamoDBClient, PutItemCommand } = require('@aws-sdk/client-dynamodb');
|
|
13
|
+
|
|
14
|
+
const pinpoint = new PinpointClient({});
|
|
15
|
+
const sns = new SNSClient({});
|
|
16
|
+
const dynamodb = new DynamoDBClient({});
|
|
17
|
+
|
|
18
|
+
exports.handler = async (event) => {
|
|
19
|
+
console.log('SMS send event:', JSON.stringify(event, null, 2));
|
|
20
|
+
|
|
21
|
+
const applicationId = process.env.PINPOINT_APP_ID;
|
|
22
|
+
const messageLogTable = process.env.MESSAGE_LOG_TABLE;
|
|
23
|
+
const senderId = process.env.SMS_SENDER_ID;
|
|
24
|
+
const originationNumber = process.env.SMS_ORIGINATION_NUMBER;
|
|
25
|
+
|
|
26
|
+
// Handle both direct invocation and SQS/SNS events
|
|
27
|
+
const messages = event.Records
|
|
28
|
+
? event.Records.map(r => JSON.parse(r.body || r.Sns?.Message || '{}'))
|
|
29
|
+
: [event];
|
|
30
|
+
|
|
31
|
+
const results = [];
|
|
32
|
+
|
|
33
|
+
for (const message of messages) {
|
|
34
|
+
try {
|
|
35
|
+
const {
|
|
36
|
+
to,
|
|
37
|
+
body,
|
|
38
|
+
template,
|
|
39
|
+
templateData,
|
|
40
|
+
messageType = 'TRANSACTIONAL',
|
|
41
|
+
} = message;
|
|
42
|
+
|
|
43
|
+
if (!to || (!body && !template)) {
|
|
44
|
+
console.log('Missing required fields (to, body/template)');
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Resolve template if provided
|
|
49
|
+
let messageBody = body;
|
|
50
|
+
if (template && templateData) {
|
|
51
|
+
messageBody = resolveTemplate(template, templateData);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const messageId = \`sms-\${Date.now()}-\${Math.random().toString(36).substr(2)}\`;
|
|
55
|
+
|
|
56
|
+
// Send via Pinpoint if app ID is configured
|
|
57
|
+
if (applicationId) {
|
|
58
|
+
const sendResult = await pinpoint.send(new SendMessagesCommand({
|
|
59
|
+
ApplicationId: applicationId,
|
|
60
|
+
MessageRequest: {
|
|
61
|
+
Addresses: {
|
|
62
|
+
[to]: {
|
|
63
|
+
ChannelType: 'SMS',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
MessageConfiguration: {
|
|
67
|
+
SMSMessage: {
|
|
68
|
+
Body: messageBody,
|
|
69
|
+
MessageType: messageType,
|
|
70
|
+
SenderId: senderId,
|
|
71
|
+
OriginationNumber: originationNumber,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
const result = sendResult.MessageResponse?.Result?.[to] || {};
|
|
78
|
+
|
|
79
|
+
// Log message
|
|
80
|
+
if (messageLogTable) {
|
|
81
|
+
await dynamodb.send(new PutItemCommand({
|
|
82
|
+
TableName: messageLogTable,
|
|
83
|
+
Item: {
|
|
84
|
+
messageId: { S: result.MessageId || messageId },
|
|
85
|
+
to: { S: to },
|
|
86
|
+
body: { S: messageBody },
|
|
87
|
+
messageType: { S: messageType },
|
|
88
|
+
deliveryStatus: { S: result.DeliveryStatus || 'UNKNOWN' },
|
|
89
|
+
statusCode: { N: String(result.StatusCode || 0) },
|
|
90
|
+
statusMessage: { S: result.StatusMessage || '' },
|
|
91
|
+
sentAt: { S: new Date().toISOString() },
|
|
92
|
+
ttl: { N: String(Math.floor(Date.now() / 1000) + 90 * 24 * 60 * 60) },
|
|
93
|
+
},
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
results.push({
|
|
98
|
+
to,
|
|
99
|
+
messageId: result.MessageId || messageId,
|
|
100
|
+
status: result.DeliveryStatus,
|
|
101
|
+
statusCode: result.StatusCode,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
} else {
|
|
105
|
+
// Fallback to SNS
|
|
106
|
+
const snsResult = await sns.send(new PublishCommand({
|
|
107
|
+
PhoneNumber: to,
|
|
108
|
+
Message: messageBody,
|
|
109
|
+
MessageAttributes: {
|
|
110
|
+
'AWS.SNS.SMS.SMSType': {
|
|
111
|
+
DataType: 'String',
|
|
112
|
+
StringValue: messageType === 'PROMOTIONAL' ? 'Promotional' : 'Transactional',
|
|
113
|
+
},
|
|
114
|
+
...(senderId && {
|
|
115
|
+
'AWS.SNS.SMS.SenderID': {
|
|
116
|
+
DataType: 'String',
|
|
117
|
+
StringValue: senderId,
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
// Log message
|
|
124
|
+
if (messageLogTable) {
|
|
125
|
+
await dynamodb.send(new PutItemCommand({
|
|
126
|
+
TableName: messageLogTable,
|
|
127
|
+
Item: {
|
|
128
|
+
messageId: { S: snsResult.MessageId || messageId },
|
|
129
|
+
to: { S: to },
|
|
130
|
+
body: { S: messageBody },
|
|
131
|
+
messageType: { S: messageType },
|
|
132
|
+
deliveryStatus: { S: 'SENT' },
|
|
133
|
+
sentAt: { S: new Date().toISOString() },
|
|
134
|
+
ttl: { N: String(Math.floor(Date.now() / 1000) + 90 * 24 * 60 * 60) },
|
|
135
|
+
},
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
results.push({
|
|
140
|
+
to,
|
|
141
|
+
messageId: snsResult.MessageId || messageId,
|
|
142
|
+
status: 'SENT',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log(\`SMS sent to \${to}: \${results[results.length - 1].status}\`);
|
|
147
|
+
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error('Error sending SMS:', error);
|
|
150
|
+
results.push({
|
|
151
|
+
to: message.to,
|
|
152
|
+
error: error.message,
|
|
153
|
+
status: 'FAILED',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
statusCode: 200,
|
|
160
|
+
body: JSON.stringify({ results }),
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
function resolveTemplate(template, data) {
|
|
165
|
+
let result = template;
|
|
166
|
+
for (const [key, value] of Object.entries(data)) {
|
|
167
|
+
result = result.replace(new RegExp(\`{{\\\\s*\${key}\\\\s*}}\`, 'g'), value);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
export default handler;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CloudFormationTemplate } from '@stacksjs/ts-cloud-aws-types';
|
|
2
|
+
/**
|
|
3
|
+
* Analyze differences between two CloudFormation templates
|
|
4
|
+
*/
|
|
5
|
+
export declare function analyzeStackDiff(oldTemplate: CloudFormationTemplate, newTemplate: CloudFormationTemplate): StackDiff;
|
|
6
|
+
/**
|
|
7
|
+
* Format diff for display
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatDiff(diff: StackDiff): string;
|
|
10
|
+
export declare interface ResourceDiff {
|
|
11
|
+
logicalId: string
|
|
12
|
+
action: 'add' | 'remove' | 'update' | 'replace'
|
|
13
|
+
resourceType: string
|
|
14
|
+
changes?: PropertyChange[]
|
|
15
|
+
reason?: string
|
|
16
|
+
}
|
|
17
|
+
export declare interface PropertyChange {
|
|
18
|
+
path: string
|
|
19
|
+
oldValue: any
|
|
20
|
+
newValue: any
|
|
21
|
+
requiresReplacement?: boolean
|
|
22
|
+
}
|
|
23
|
+
export declare interface StackDiff {
|
|
24
|
+
added: ResourceDiff[]
|
|
25
|
+
removed: ResourceDiff[]
|
|
26
|
+
updated: ResourceDiff[]
|
|
27
|
+
replaced: ResourceDiff[]
|
|
28
|
+
unchanged: string[]
|
|
29
|
+
summary: {
|
|
30
|
+
totalChanges: number
|
|
31
|
+
requiresReplacement: boolean
|
|
32
|
+
dangerousChanges: string[]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const staticSiteManager: StaticSiteManager;
|
|
2
|
+
/**
|
|
3
|
+
* Static Site Advanced Features
|
|
4
|
+
* Asset optimization, image optimization, SSG support, and prerendering
|
|
5
|
+
*/
|
|
6
|
+
export declare interface AssetOptimization {
|
|
7
|
+
id: string
|
|
8
|
+
name: string
|
|
9
|
+
minify: boolean
|
|
10
|
+
compress: boolean
|
|
11
|
+
compressionType: 'gzip' | 'brotli' | 'both'
|
|
12
|
+
sourceMaps: boolean
|
|
13
|
+
cacheControl: string
|
|
14
|
+
}
|
|
15
|
+
export declare interface ImageOptimization {
|
|
16
|
+
id: string
|
|
17
|
+
formats: Array<'webp' | 'avif' | 'jpeg' | 'png'>
|
|
18
|
+
quality: number
|
|
19
|
+
responsive: boolean
|
|
20
|
+
lazy: boolean
|
|
21
|
+
sizes: number[]
|
|
22
|
+
}
|
|
23
|
+
export declare interface SSGConfig {
|
|
24
|
+
id: string
|
|
25
|
+
framework: 'next' | 'gatsby' | 'astro' | 'hugo' | 'eleventy'
|
|
26
|
+
outputDir: string
|
|
27
|
+
buildCommand: string
|
|
28
|
+
routes: string[]
|
|
29
|
+
}
|
|
30
|
+
export declare interface PrerenderConfig {
|
|
31
|
+
id: string
|
|
32
|
+
routes: string[]
|
|
33
|
+
fallback: 'blocking' | 'static' | false
|
|
34
|
+
revalidate?: number
|
|
35
|
+
}
|
|
36
|
+
export declare class StaticSiteManager {
|
|
37
|
+
private optimizations: Map<string, AssetOptimization>;
|
|
38
|
+
private imageConfigs: Map<string, ImageOptimization>;
|
|
39
|
+
private ssgConfigs: Map<string, SSGConfig>;
|
|
40
|
+
private prerenderConfigs: Map<string, PrerenderConfig>;
|
|
41
|
+
private counter: any;
|
|
42
|
+
createAssetOptimization(config: Omit<AssetOptimization, 'id'>): AssetOptimization;
|
|
43
|
+
createImageOptimization(config: Omit<ImageOptimization, 'id'>): ImageOptimization;
|
|
44
|
+
createSSGConfig(config: Omit<SSGConfig, 'id'>): SSGConfig;
|
|
45
|
+
createPrerenderConfig(config: Omit<PrerenderConfig, 'id'>): PrerenderConfig;
|
|
46
|
+
listOptimizations(): AssetOptimization[];
|
|
47
|
+
listImageConfigs(): ImageOptimization[];
|
|
48
|
+
clear(): void;
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CloudFormationResource, CloudFormationTemplate } from '@stacksjs/ts-cloud-aws-types';
|
|
2
|
+
export declare class TemplateBuilder {
|
|
3
|
+
private template: CloudFormationTemplate;
|
|
4
|
+
constructor(description?: string);
|
|
5
|
+
addResource(logicalId: string, resource: CloudFormationResource): this;
|
|
6
|
+
addResources(resources: Record<string, CloudFormationResource>): this;
|
|
7
|
+
addParameter(name: string, parameter: NonNullable<CloudFormationTemplate['Parameters']>[string]): this;
|
|
8
|
+
addOutput(name: string, output: NonNullable<CloudFormationTemplate['Outputs']>[string]): this;
|
|
9
|
+
getResources(): Record<string, CloudFormationResource>;
|
|
10
|
+
build(): CloudFormationTemplate;
|
|
11
|
+
toJSON(pretty?: any): string;
|
|
12
|
+
toYAML(): string;
|
|
13
|
+
private convertToYAML(obj: any, indent?: any): string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CloudFormationTemplate } from '@stacksjs/ts-cloud-aws-types';
|
|
2
|
+
/**
|
|
3
|
+
* Validate a CloudFormation template
|
|
4
|
+
*/
|
|
5
|
+
export declare function validateTemplate(template: CloudFormationTemplate): ValidationResult;
|
|
6
|
+
/**
|
|
7
|
+
* Validate template size
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateTemplateSize(templateJson: string): ValidationResult;
|
|
10
|
+
/**
|
|
11
|
+
* Validate resource limits
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateResourceLimits(template: CloudFormationTemplate): ValidationResult;
|
|
14
|
+
export declare interface ValidationError {
|
|
15
|
+
path: string
|
|
16
|
+
message: string
|
|
17
|
+
severity: 'error' | 'warning' | 'info'
|
|
18
|
+
}
|
|
19
|
+
export declare interface ValidationResult {
|
|
20
|
+
valid: boolean
|
|
21
|
+
errors: ValidationError[]
|
|
22
|
+
warnings: ValidationError[]
|
|
23
|
+
info: ValidationError[]
|
|
24
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global template cache instance
|
|
3
|
+
*/
|
|
4
|
+
export declare const templateCache: TemplateCache;
|
|
5
|
+
export declare interface CacheOptions {
|
|
6
|
+
ttl?: number
|
|
7
|
+
maxSize?: number
|
|
8
|
+
}
|
|
9
|
+
export declare interface CacheEntry<T> {
|
|
10
|
+
value: T
|
|
11
|
+
timestamp: number
|
|
12
|
+
hash?: string
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Simple in-memory cache with TTL support
|
|
16
|
+
*/
|
|
17
|
+
export declare class Cache<T = any> {
|
|
18
|
+
private cache: Map<string, CacheEntry<T>>;
|
|
19
|
+
private ttl: number;
|
|
20
|
+
private maxSize: number;
|
|
21
|
+
constructor(options?: CacheOptions);
|
|
22
|
+
get(key: string): T | undefined;
|
|
23
|
+
set(key: string, value: T, hash?: string): void;
|
|
24
|
+
has(key: string): boolean;
|
|
25
|
+
clear(): void;
|
|
26
|
+
prune(): void;
|
|
27
|
+
stats(): { size: number, ttl: number, maxSize: number };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* File-based cache for persistent caching
|
|
31
|
+
*/
|
|
32
|
+
export declare class FileCache<T = any> {
|
|
33
|
+
private cacheDir: string;
|
|
34
|
+
private ttl: number;
|
|
35
|
+
constructor(cacheDir: string, options?: CacheOptions);
|
|
36
|
+
private getCachePath(key: string): string;
|
|
37
|
+
get(key: string): T | undefined;
|
|
38
|
+
set(key: string, value: T, hash?: string): void;
|
|
39
|
+
has(key: string): boolean;
|
|
40
|
+
clear(): void;
|
|
41
|
+
prune(): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Template cache for CloudFormation templates
|
|
45
|
+
*/
|
|
46
|
+
export declare class TemplateCache {
|
|
47
|
+
private cache: FileCache<string>;
|
|
48
|
+
constructor(cacheDir?: string);
|
|
49
|
+
getTemplate(stackName: string): string | undefined;
|
|
50
|
+
setTemplate(stackName: string, template: string): void;
|
|
51
|
+
hasChanged(stackName: string, newTemplate: string): boolean;
|
|
52
|
+
private hashTemplate(template: string): string;
|
|
53
|
+
clear(): void;
|
|
54
|
+
prune(): void;
|
|
55
|
+
}
|