@stacksjs/ts-cloud-core 0.1.1
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/LICENSE.md +21 -0
- package/README.md +321 -0
- package/package.json +31 -0
- package/src/advanced-features.test.ts +465 -0
- package/src/aws/cloudformation.ts +421 -0
- package/src/aws/cloudfront.ts +158 -0
- package/src/aws/credentials.test.ts +132 -0
- package/src/aws/credentials.ts +545 -0
- package/src/aws/index.ts +87 -0
- package/src/aws/s3.test.ts +188 -0
- package/src/aws/s3.ts +1088 -0
- package/src/aws/signature.test.ts +670 -0
- package/src/aws/signature.ts +1155 -0
- package/src/backup/disaster-recovery.test.ts +726 -0
- package/src/backup/disaster-recovery.ts +500 -0
- package/src/backup/index.ts +34 -0
- package/src/backup/manager.test.ts +498 -0
- package/src/backup/manager.ts +432 -0
- package/src/cicd/circleci.ts +430 -0
- package/src/cicd/github-actions.ts +424 -0
- package/src/cicd/gitlab-ci.ts +255 -0
- package/src/cicd/index.ts +8 -0
- package/src/cli/history.ts +396 -0
- package/src/cli/index.ts +10 -0
- package/src/cli/progress.ts +458 -0
- package/src/cli/repl.ts +454 -0
- package/src/cli/suggestions.ts +327 -0
- package/src/cli/table.test.ts +319 -0
- package/src/cli/table.ts +332 -0
- package/src/cloudformation/builder.test.ts +327 -0
- package/src/cloudformation/builder.ts +378 -0
- package/src/cloudformation/builders/api-gateway.ts +449 -0
- package/src/cloudformation/builders/cache.ts +334 -0
- package/src/cloudformation/builders/cdn.ts +278 -0
- package/src/cloudformation/builders/compute.ts +485 -0
- package/src/cloudformation/builders/database.ts +392 -0
- package/src/cloudformation/builders/functions.ts +343 -0
- package/src/cloudformation/builders/messaging.ts +140 -0
- package/src/cloudformation/builders/monitoring.ts +300 -0
- package/src/cloudformation/builders/network.ts +264 -0
- package/src/cloudformation/builders/queue.ts +147 -0
- package/src/cloudformation/builders/security.ts +399 -0
- package/src/cloudformation/builders/storage.ts +285 -0
- package/src/cloudformation/index.ts +30 -0
- package/src/cloudformation/types.ts +173 -0
- package/src/compliance/aws-config.ts +543 -0
- package/src/compliance/cloudtrail.ts +376 -0
- package/src/compliance/compliance.test.ts +423 -0
- package/src/compliance/guardduty.ts +446 -0
- package/src/compliance/index.ts +66 -0
- package/src/compliance/security-hub.ts +456 -0
- package/src/containers/build-optimization.ts +416 -0
- package/src/containers/containers.test.ts +508 -0
- package/src/containers/image-scanning.ts +360 -0
- package/src/containers/index.ts +9 -0
- package/src/containers/registry.ts +293 -0
- package/src/containers/service-mesh.ts +520 -0
- package/src/database/database.test.ts +762 -0
- package/src/database/index.ts +9 -0
- package/src/database/migrations.ts +444 -0
- package/src/database/performance.ts +528 -0
- package/src/database/replicas.ts +534 -0
- package/src/database/users.ts +494 -0
- package/src/dependency-graph.ts +143 -0
- package/src/deployment/ab-testing.ts +582 -0
- package/src/deployment/blue-green.ts +452 -0
- package/src/deployment/canary.ts +500 -0
- package/src/deployment/deployment.test.ts +526 -0
- package/src/deployment/index.ts +61 -0
- package/src/deployment/progressive.ts +62 -0
- package/src/dns/dns.test.ts +641 -0
- package/src/dns/dnssec.ts +315 -0
- package/src/dns/index.ts +8 -0
- package/src/dns/resolver.ts +496 -0
- package/src/dns/routing.ts +593 -0
- package/src/email/advanced/analytics.ts +445 -0
- package/src/email/advanced/index.ts +11 -0
- package/src/email/advanced/rules.ts +465 -0
- package/src/email/advanced/scheduling.ts +352 -0
- package/src/email/advanced/search.ts +412 -0
- package/src/email/advanced/shared-mailboxes.ts +404 -0
- package/src/email/advanced/templates.ts +455 -0
- package/src/email/advanced/threading.ts +281 -0
- package/src/email/analytics.ts +467 -0
- package/src/email/bounce-handling.ts +425 -0
- package/src/email/email.test.ts +431 -0
- package/src/email/handlers/__tests__/inbound.test.ts +38 -0
- package/src/email/handlers/__tests__/outbound.test.ts +37 -0
- package/src/email/handlers/converter.ts +227 -0
- package/src/email/handlers/feedback.ts +228 -0
- package/src/email/handlers/inbound.ts +169 -0
- package/src/email/handlers/outbound.ts +178 -0
- package/src/email/index.ts +15 -0
- package/src/email/reputation.ts +303 -0
- package/src/email/templates.ts +352 -0
- package/src/errors/index.test.ts +434 -0
- package/src/errors/index.ts +416 -0
- package/src/health-checks/index.ts +40 -0
- package/src/index.ts +360 -0
- package/src/intrinsic-functions.ts +118 -0
- package/src/lambda/concurrency.ts +330 -0
- package/src/lambda/destinations.ts +345 -0
- package/src/lambda/dlq.ts +425 -0
- package/src/lambda/index.ts +11 -0
- package/src/lambda/lambda.test.ts +840 -0
- package/src/lambda/layers.ts +263 -0
- package/src/lambda/versions.ts +376 -0
- package/src/lambda/vpc.ts +399 -0
- package/src/local/config.ts +114 -0
- package/src/local/index.ts +6 -0
- package/src/local/mock-aws.ts +351 -0
- package/src/modules/ai.ts +340 -0
- package/src/modules/api.ts +478 -0
- package/src/modules/auth.ts +805 -0
- package/src/modules/cache.ts +417 -0
- package/src/modules/cdn.ts +1062 -0
- package/src/modules/communication.ts +1094 -0
- package/src/modules/compute.ts +3348 -0
- package/src/modules/database.ts +554 -0
- package/src/modules/deployment.ts +1079 -0
- package/src/modules/dns.ts +337 -0
- package/src/modules/email.ts +1538 -0
- package/src/modules/filesystem.ts +515 -0
- package/src/modules/index.ts +32 -0
- package/src/modules/messaging.ts +486 -0
- package/src/modules/monitoring.ts +2086 -0
- package/src/modules/network.ts +664 -0
- package/src/modules/parameter-store.ts +325 -0
- package/src/modules/permissions.ts +1081 -0
- package/src/modules/phone.ts +494 -0
- package/src/modules/queue.ts +1260 -0
- package/src/modules/redirects.ts +464 -0
- package/src/modules/registry.ts +699 -0
- package/src/modules/search.ts +401 -0
- package/src/modules/secrets.ts +416 -0
- package/src/modules/security.ts +731 -0
- package/src/modules/sms.ts +389 -0
- package/src/modules/storage.ts +1120 -0
- package/src/modules/workflow.ts +680 -0
- package/src/multi-account/config.ts +521 -0
- package/src/multi-account/index.ts +7 -0
- package/src/multi-account/manager.ts +427 -0
- package/src/multi-region/cross-region.ts +410 -0
- package/src/multi-region/index.ts +8 -0
- package/src/multi-region/manager.ts +483 -0
- package/src/multi-region/regions.ts +435 -0
- package/src/network-security/index.ts +48 -0
- package/src/observability/index.ts +9 -0
- package/src/observability/logs.ts +522 -0
- package/src/observability/metrics.ts +460 -0
- package/src/observability/observability.test.ts +782 -0
- package/src/observability/synthetics.ts +568 -0
- package/src/observability/xray.ts +358 -0
- package/src/phone/advanced/analytics.ts +349 -0
- package/src/phone/advanced/callbacks.ts +428 -0
- package/src/phone/advanced/index.ts +8 -0
- package/src/phone/advanced/ivr-builder.ts +504 -0
- package/src/phone/advanced/recording.ts +310 -0
- package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
- package/src/phone/handlers/incoming-call.ts +117 -0
- package/src/phone/handlers/missed-call.ts +116 -0
- package/src/phone/handlers/voicemail.ts +179 -0
- package/src/phone/index.ts +9 -0
- package/src/presets/api-backend.ts +134 -0
- package/src/presets/data-pipeline.ts +204 -0
- package/src/presets/extend.test.ts +295 -0
- package/src/presets/extend.ts +297 -0
- package/src/presets/fullstack-app.ts +144 -0
- package/src/presets/index.ts +27 -0
- package/src/presets/jamstack.ts +135 -0
- package/src/presets/microservices.ts +167 -0
- package/src/presets/ml-api.ts +208 -0
- package/src/presets/nodejs-server.ts +104 -0
- package/src/presets/nodejs-serverless.ts +114 -0
- package/src/presets/realtime-app.ts +184 -0
- package/src/presets/static-site.ts +64 -0
- package/src/presets/traditional-web-app.ts +339 -0
- package/src/presets/wordpress.ts +138 -0
- package/src/preview/github.test.ts +249 -0
- package/src/preview/github.ts +297 -0
- package/src/preview/index.ts +37 -0
- package/src/preview/manager.test.ts +440 -0
- package/src/preview/manager.ts +326 -0
- package/src/preview/notifications.test.ts +582 -0
- package/src/preview/notifications.ts +341 -0
- package/src/queue/batch-processing.ts +402 -0
- package/src/queue/dlq-monitoring.ts +402 -0
- package/src/queue/fifo.ts +342 -0
- package/src/queue/index.ts +9 -0
- package/src/queue/management.ts +428 -0
- package/src/queue/queue.test.ts +429 -0
- package/src/resource-mgmt/index.ts +39 -0
- package/src/resource-naming.ts +62 -0
- package/src/s3/index.ts +523 -0
- package/src/schema/cloud-config.schema.json +554 -0
- package/src/schema/index.ts +68 -0
- package/src/security/certificate-manager.ts +492 -0
- package/src/security/index.ts +9 -0
- package/src/security/scanning.ts +545 -0
- package/src/security/secrets-manager.ts +476 -0
- package/src/security/secrets-rotation.ts +456 -0
- package/src/security/security.test.ts +738 -0
- package/src/sms/advanced/ab-testing.ts +389 -0
- package/src/sms/advanced/analytics.ts +336 -0
- package/src/sms/advanced/campaigns.ts +523 -0
- package/src/sms/advanced/chatbot.ts +224 -0
- package/src/sms/advanced/index.ts +10 -0
- package/src/sms/advanced/link-tracking.ts +248 -0
- package/src/sms/advanced/mms.ts +308 -0
- package/src/sms/handlers/__tests__/send.test.ts +40 -0
- package/src/sms/handlers/delivery-status.ts +133 -0
- package/src/sms/handlers/receive.ts +162 -0
- package/src/sms/handlers/send.ts +174 -0
- package/src/sms/index.ts +9 -0
- package/src/stack-diff.ts +389 -0
- package/src/static-site/index.ts +85 -0
- package/src/template-builder.ts +110 -0
- package/src/template-validator.ts +574 -0
- package/src/utils/cache.ts +291 -0
- package/src/utils/diff.ts +269 -0
- package/src/utils/hash.ts +227 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/parallel.ts +294 -0
- package/src/validators/credentials.test.ts +274 -0
- package/src/validators/credentials.ts +233 -0
- package/src/validators/quotas.test.ts +434 -0
- package/src/validators/quotas.ts +217 -0
- package/test/ai.test.ts +327 -0
- package/test/api.test.ts +511 -0
- package/test/auth.test.ts +632 -0
- package/test/cache.test.ts +406 -0
- package/test/cdn.test.ts +247 -0
- package/test/compute.test.ts +861 -0
- package/test/database.test.ts +523 -0
- package/test/deployment.test.ts +499 -0
- package/test/dns.test.ts +270 -0
- package/test/email.test.ts +439 -0
- package/test/filesystem.test.ts +382 -0
- package/test/integration.test.ts +350 -0
- package/test/messaging.test.ts +514 -0
- package/test/monitoring.test.ts +634 -0
- package/test/network.test.ts +425 -0
- package/test/permissions.test.ts +488 -0
- package/test/queue.test.ts +484 -0
- package/test/registry.test.ts +306 -0
- package/test/security.test.ts +462 -0
- package/test/storage.test.ts +463 -0
- package/test/template-validator.test.ts +559 -0
- package/test/workflow.test.ts +592 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback Requests and Queue Management
|
|
3
|
+
*
|
|
4
|
+
* Provides callback scheduling and queue management
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface CallbackRequest {
|
|
8
|
+
id: string
|
|
9
|
+
phoneNumber: string
|
|
10
|
+
customerName?: string
|
|
11
|
+
reason?: string
|
|
12
|
+
preferredTime?: string
|
|
13
|
+
queueId?: string
|
|
14
|
+
priority: number
|
|
15
|
+
status: 'pending' | 'scheduled' | 'in-progress' | 'completed' | 'failed' | 'cancelled'
|
|
16
|
+
attempts: number
|
|
17
|
+
maxAttempts: number
|
|
18
|
+
createdAt: string
|
|
19
|
+
scheduledFor?: string
|
|
20
|
+
completedAt?: string
|
|
21
|
+
notes?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface QueuePosition {
|
|
25
|
+
position: number
|
|
26
|
+
estimatedWaitTime: number
|
|
27
|
+
callersAhead: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Callback Module
|
|
32
|
+
*/
|
|
33
|
+
export class Callbacks {
|
|
34
|
+
/**
|
|
35
|
+
* Lambda code for callback request handling
|
|
36
|
+
*/
|
|
37
|
+
static CallbackRequestCode = `
|
|
38
|
+
const { DynamoDBClient, PutItemCommand, QueryCommand, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
|
|
39
|
+
const { ConnectClient, StartOutboundVoiceContactCommand } = require('@aws-sdk/client-connect');
|
|
40
|
+
const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');
|
|
41
|
+
|
|
42
|
+
const dynamodb = new DynamoDBClient({});
|
|
43
|
+
const connect = new ConnectClient({});
|
|
44
|
+
const sns = new SNSClient({});
|
|
45
|
+
|
|
46
|
+
const CALLBACKS_TABLE = process.env.CALLBACKS_TABLE;
|
|
47
|
+
const CONNECT_INSTANCE_ID = process.env.CONNECT_INSTANCE_ID;
|
|
48
|
+
const CONTACT_FLOW_ID = process.env.CONTACT_FLOW_ID;
|
|
49
|
+
const SOURCE_PHONE_NUMBER = process.env.SOURCE_PHONE_NUMBER;
|
|
50
|
+
const NOTIFICATION_TOPIC_ARN = process.env.NOTIFICATION_TOPIC_ARN;
|
|
51
|
+
|
|
52
|
+
exports.handler = async (event) => {
|
|
53
|
+
console.log('Callback request event:', JSON.stringify(event, null, 2));
|
|
54
|
+
|
|
55
|
+
const { httpMethod, body, pathParameters } = event;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
switch (httpMethod) {
|
|
59
|
+
case 'POST':
|
|
60
|
+
return await createCallbackRequest(JSON.parse(body || '{}'));
|
|
61
|
+
case 'GET':
|
|
62
|
+
if (pathParameters?.id) {
|
|
63
|
+
return await getCallbackRequest(pathParameters.id);
|
|
64
|
+
}
|
|
65
|
+
return await listCallbackRequests(event.queryStringParameters);
|
|
66
|
+
case 'DELETE':
|
|
67
|
+
return await cancelCallbackRequest(pathParameters?.id);
|
|
68
|
+
default:
|
|
69
|
+
return { statusCode: 405, body: JSON.stringify({ error: 'Method not allowed' }) };
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error('Error:', error);
|
|
73
|
+
return { statusCode: 500, body: JSON.stringify({ error: error.message }) };
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
async function createCallbackRequest(data) {
|
|
78
|
+
const id = \`cb-\${Date.now()}-\${Math.random().toString(36).substr(2, 9)}\`;
|
|
79
|
+
const now = new Date().toISOString();
|
|
80
|
+
|
|
81
|
+
const callback = {
|
|
82
|
+
id: { S: id },
|
|
83
|
+
phoneNumber: { S: data.phoneNumber },
|
|
84
|
+
customerName: { S: data.customerName || '' },
|
|
85
|
+
reason: { S: data.reason || '' },
|
|
86
|
+
preferredTime: { S: data.preferredTime || '' },
|
|
87
|
+
queueId: { S: data.queueId || '' },
|
|
88
|
+
priority: { N: String(data.priority || 5) },
|
|
89
|
+
status: { S: 'pending' },
|
|
90
|
+
attempts: { N: '0' },
|
|
91
|
+
maxAttempts: { N: String(data.maxAttempts || 3) },
|
|
92
|
+
createdAt: { S: now },
|
|
93
|
+
ttl: { N: String(Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60) },
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
await dynamodb.send(new PutItemCommand({
|
|
97
|
+
TableName: CALLBACKS_TABLE,
|
|
98
|
+
Item: callback,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
// Send notification
|
|
102
|
+
if (NOTIFICATION_TOPIC_ARN) {
|
|
103
|
+
await sns.send(new PublishCommand({
|
|
104
|
+
TopicArn: NOTIFICATION_TOPIC_ARN,
|
|
105
|
+
Subject: 'New Callback Request',
|
|
106
|
+
Message: JSON.stringify({
|
|
107
|
+
type: 'callback_request',
|
|
108
|
+
id,
|
|
109
|
+
phoneNumber: data.phoneNumber,
|
|
110
|
+
customerName: data.customerName,
|
|
111
|
+
reason: data.reason,
|
|
112
|
+
timestamp: now,
|
|
113
|
+
}, null, 2),
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
statusCode: 201,
|
|
119
|
+
headers: { 'Content-Type': 'application/json' },
|
|
120
|
+
body: JSON.stringify({ id, status: 'pending', createdAt: now }),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function getCallbackRequest(id) {
|
|
125
|
+
const result = await dynamodb.send(new GetItemCommand({
|
|
126
|
+
TableName: CALLBACKS_TABLE,
|
|
127
|
+
Key: { id: { S: id } },
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
if (!result.Item) {
|
|
131
|
+
return { statusCode: 404, body: JSON.stringify({ error: 'Not found' }) };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
statusCode: 200,
|
|
136
|
+
headers: { 'Content-Type': 'application/json' },
|
|
137
|
+
body: JSON.stringify(unmarshallCallback(result.Item)),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function listCallbackRequests(params) {
|
|
142
|
+
const result = await dynamodb.send(new ScanCommand({
|
|
143
|
+
TableName: CALLBACKS_TABLE,
|
|
144
|
+
FilterExpression: '#status = :status',
|
|
145
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
146
|
+
ExpressionAttributeValues: {
|
|
147
|
+
':status': { S: params?.status || 'pending' },
|
|
148
|
+
},
|
|
149
|
+
}));
|
|
150
|
+
|
|
151
|
+
const callbacks = (result.Items || []).map(unmarshallCallback);
|
|
152
|
+
callbacks.sort((a, b) => a.priority - b.priority);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
statusCode: 200,
|
|
156
|
+
headers: { 'Content-Type': 'application/json' },
|
|
157
|
+
body: JSON.stringify(callbacks),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function cancelCallbackRequest(id) {
|
|
162
|
+
await dynamodb.send(new UpdateItemCommand({
|
|
163
|
+
TableName: CALLBACKS_TABLE,
|
|
164
|
+
Key: { id: { S: id } },
|
|
165
|
+
UpdateExpression: 'SET #status = :status',
|
|
166
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
167
|
+
ExpressionAttributeValues: {
|
|
168
|
+
':status': { S: 'cancelled' },
|
|
169
|
+
},
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
return { statusCode: 200, body: JSON.stringify({ id, status: 'cancelled' }) };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function unmarshallCallback(item) {
|
|
176
|
+
return {
|
|
177
|
+
id: item.id.S,
|
|
178
|
+
phoneNumber: item.phoneNumber.S,
|
|
179
|
+
customerName: item.customerName?.S,
|
|
180
|
+
reason: item.reason?.S,
|
|
181
|
+
preferredTime: item.preferredTime?.S,
|
|
182
|
+
queueId: item.queueId?.S,
|
|
183
|
+
priority: parseInt(item.priority?.N || '5'),
|
|
184
|
+
status: item.status.S,
|
|
185
|
+
attempts: parseInt(item.attempts?.N || '0'),
|
|
186
|
+
maxAttempts: parseInt(item.maxAttempts?.N || '3'),
|
|
187
|
+
createdAt: item.createdAt.S,
|
|
188
|
+
scheduledFor: item.scheduledFor?.S,
|
|
189
|
+
completedAt: item.completedAt?.S,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
`
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Lambda code for processing callbacks
|
|
196
|
+
*/
|
|
197
|
+
static CallbackProcessorCode = `
|
|
198
|
+
const { DynamoDBClient, ScanCommand, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
|
|
199
|
+
const { ConnectClient, StartOutboundVoiceContactCommand } = require('@aws-sdk/client-connect');
|
|
200
|
+
|
|
201
|
+
const dynamodb = new DynamoDBClient({});
|
|
202
|
+
const connect = new ConnectClient({});
|
|
203
|
+
|
|
204
|
+
const CALLBACKS_TABLE = process.env.CALLBACKS_TABLE;
|
|
205
|
+
const CONNECT_INSTANCE_ID = process.env.CONNECT_INSTANCE_ID;
|
|
206
|
+
const CONTACT_FLOW_ID = process.env.CONTACT_FLOW_ID;
|
|
207
|
+
const SOURCE_PHONE_NUMBER = process.env.SOURCE_PHONE_NUMBER;
|
|
208
|
+
const QUEUE_ID = process.env.QUEUE_ID;
|
|
209
|
+
|
|
210
|
+
exports.handler = async (event) => {
|
|
211
|
+
console.log('Callback processor event:', JSON.stringify(event, null, 2));
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
// Get pending callbacks
|
|
215
|
+
const result = await dynamodb.send(new ScanCommand({
|
|
216
|
+
TableName: CALLBACKS_TABLE,
|
|
217
|
+
FilterExpression: '#status = :pending',
|
|
218
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
219
|
+
ExpressionAttributeValues: {
|
|
220
|
+
':pending': { S: 'pending' },
|
|
221
|
+
},
|
|
222
|
+
}));
|
|
223
|
+
|
|
224
|
+
const callbacks = result.Items || [];
|
|
225
|
+
|
|
226
|
+
// Sort by priority and creation time
|
|
227
|
+
callbacks.sort((a, b) => {
|
|
228
|
+
const priorityDiff = parseInt(a.priority?.N || '5') - parseInt(b.priority?.N || '5');
|
|
229
|
+
if (priorityDiff !== 0) return priorityDiff;
|
|
230
|
+
return new Date(a.createdAt.S) - new Date(b.createdAt.S);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Process up to 5 callbacks at a time
|
|
234
|
+
const toProcess = callbacks.slice(0, 5);
|
|
235
|
+
|
|
236
|
+
for (const callback of toProcess) {
|
|
237
|
+
const id = callback.id.S;
|
|
238
|
+
const phoneNumber = callback.phoneNumber.S;
|
|
239
|
+
const attempts = parseInt(callback.attempts?.N || '0');
|
|
240
|
+
const maxAttempts = parseInt(callback.maxAttempts?.N || '3');
|
|
241
|
+
|
|
242
|
+
// Check if preferred time has passed
|
|
243
|
+
if (callback.preferredTime?.S) {
|
|
244
|
+
const preferredTime = new Date(callback.preferredTime.S);
|
|
245
|
+
if (preferredTime > new Date()) {
|
|
246
|
+
console.log(\`Skipping callback \${id} - preferred time not reached\`);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
// Update status to in-progress
|
|
253
|
+
await dynamodb.send(new UpdateItemCommand({
|
|
254
|
+
TableName: CALLBACKS_TABLE,
|
|
255
|
+
Key: { id: { S: id } },
|
|
256
|
+
UpdateExpression: 'SET #status = :status, attempts = :attempts',
|
|
257
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
258
|
+
ExpressionAttributeValues: {
|
|
259
|
+
':status': { S: 'in-progress' },
|
|
260
|
+
':attempts': { N: String(attempts + 1) },
|
|
261
|
+
},
|
|
262
|
+
}));
|
|
263
|
+
|
|
264
|
+
// Initiate outbound call
|
|
265
|
+
await connect.send(new StartOutboundVoiceContactCommand({
|
|
266
|
+
InstanceId: CONNECT_INSTANCE_ID,
|
|
267
|
+
ContactFlowId: CONTACT_FLOW_ID,
|
|
268
|
+
DestinationPhoneNumber: phoneNumber,
|
|
269
|
+
SourcePhoneNumber: SOURCE_PHONE_NUMBER,
|
|
270
|
+
QueueId: QUEUE_ID,
|
|
271
|
+
Attributes: {
|
|
272
|
+
callbackId: id,
|
|
273
|
+
customerName: callback.customerName?.S || '',
|
|
274
|
+
reason: callback.reason?.S || '',
|
|
275
|
+
},
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
console.log(\`Initiated callback to \${phoneNumber}\`);
|
|
279
|
+
|
|
280
|
+
// Mark as completed (will be updated by call result handler)
|
|
281
|
+
await dynamodb.send(new UpdateItemCommand({
|
|
282
|
+
TableName: CALLBACKS_TABLE,
|
|
283
|
+
Key: { id: { S: id } },
|
|
284
|
+
UpdateExpression: 'SET #status = :status, completedAt = :now',
|
|
285
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
286
|
+
ExpressionAttributeValues: {
|
|
287
|
+
':status': { S: 'completed' },
|
|
288
|
+
':now': { S: new Date().toISOString() },
|
|
289
|
+
},
|
|
290
|
+
}));
|
|
291
|
+
|
|
292
|
+
} catch (callError) {
|
|
293
|
+
console.error(\`Failed to call \${phoneNumber}:\`, callError);
|
|
294
|
+
|
|
295
|
+
// Check if max attempts reached
|
|
296
|
+
if (attempts + 1 >= maxAttempts) {
|
|
297
|
+
await dynamodb.send(new UpdateItemCommand({
|
|
298
|
+
TableName: CALLBACKS_TABLE,
|
|
299
|
+
Key: { id: { S: id } },
|
|
300
|
+
UpdateExpression: 'SET #status = :status, error = :error',
|
|
301
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
302
|
+
ExpressionAttributeValues: {
|
|
303
|
+
':status': { S: 'failed' },
|
|
304
|
+
':error': { S: callError.message },
|
|
305
|
+
},
|
|
306
|
+
}));
|
|
307
|
+
} else {
|
|
308
|
+
// Reset to pending for retry
|
|
309
|
+
await dynamodb.send(new UpdateItemCommand({
|
|
310
|
+
TableName: CALLBACKS_TABLE,
|
|
311
|
+
Key: { id: { S: id } },
|
|
312
|
+
UpdateExpression: 'SET #status = :status',
|
|
313
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
314
|
+
ExpressionAttributeValues: {
|
|
315
|
+
':status': { S: 'pending' },
|
|
316
|
+
},
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return { statusCode: 200, processed: toProcess.length };
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error('Error processing callbacks:', error);
|
|
325
|
+
return { statusCode: 500, error: error.message };
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
`
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Create callbacks DynamoDB table
|
|
332
|
+
*/
|
|
333
|
+
static createCallbacksTable(config: { slug: string }): Record<string, any> {
|
|
334
|
+
return {
|
|
335
|
+
[`${config.slug}CallbacksTable`]: {
|
|
336
|
+
Type: 'AWS::DynamoDB::Table',
|
|
337
|
+
Properties: {
|
|
338
|
+
TableName: `${config.slug}-callbacks`,
|
|
339
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
340
|
+
AttributeDefinitions: [
|
|
341
|
+
{ AttributeName: 'id', AttributeType: 'S' },
|
|
342
|
+
],
|
|
343
|
+
KeySchema: [
|
|
344
|
+
{ AttributeName: 'id', KeyType: 'HASH' },
|
|
345
|
+
],
|
|
346
|
+
TimeToLiveSpecification: {
|
|
347
|
+
AttributeName: 'ttl',
|
|
348
|
+
Enabled: true,
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Create callback request Lambda
|
|
357
|
+
*/
|
|
358
|
+
static createCallbackRequestLambda(config: {
|
|
359
|
+
slug: string
|
|
360
|
+
roleArn: string
|
|
361
|
+
callbacksTable: string
|
|
362
|
+
notificationTopicArn?: string
|
|
363
|
+
}): Record<string, any> {
|
|
364
|
+
return {
|
|
365
|
+
[`${config.slug}CallbackRequestLambda`]: {
|
|
366
|
+
Type: 'AWS::Lambda::Function',
|
|
367
|
+
Properties: {
|
|
368
|
+
FunctionName: `${config.slug}-callback-request`,
|
|
369
|
+
Runtime: 'nodejs20.x',
|
|
370
|
+
Handler: 'index.handler',
|
|
371
|
+
Role: config.roleArn,
|
|
372
|
+
Timeout: 30,
|
|
373
|
+
MemorySize: 256,
|
|
374
|
+
Code: {
|
|
375
|
+
ZipFile: Callbacks.CallbackRequestCode,
|
|
376
|
+
},
|
|
377
|
+
Environment: {
|
|
378
|
+
Variables: {
|
|
379
|
+
CALLBACKS_TABLE: config.callbacksTable,
|
|
380
|
+
NOTIFICATION_TOPIC_ARN: config.notificationTopicArn || '',
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Create callback processor Lambda
|
|
390
|
+
*/
|
|
391
|
+
static createCallbackProcessorLambda(config: {
|
|
392
|
+
slug: string
|
|
393
|
+
roleArn: string
|
|
394
|
+
callbacksTable: string
|
|
395
|
+
connectInstanceId: string
|
|
396
|
+
contactFlowId: string
|
|
397
|
+
sourcePhoneNumber: string
|
|
398
|
+
queueId?: string
|
|
399
|
+
}): Record<string, any> {
|
|
400
|
+
return {
|
|
401
|
+
[`${config.slug}CallbackProcessorLambda`]: {
|
|
402
|
+
Type: 'AWS::Lambda::Function',
|
|
403
|
+
Properties: {
|
|
404
|
+
FunctionName: `${config.slug}-callback-processor`,
|
|
405
|
+
Runtime: 'nodejs20.x',
|
|
406
|
+
Handler: 'index.handler',
|
|
407
|
+
Role: config.roleArn,
|
|
408
|
+
Timeout: 300,
|
|
409
|
+
MemorySize: 256,
|
|
410
|
+
Code: {
|
|
411
|
+
ZipFile: Callbacks.CallbackProcessorCode,
|
|
412
|
+
},
|
|
413
|
+
Environment: {
|
|
414
|
+
Variables: {
|
|
415
|
+
CALLBACKS_TABLE: config.callbacksTable,
|
|
416
|
+
CONNECT_INSTANCE_ID: config.connectInstanceId,
|
|
417
|
+
CONTACT_FLOW_ID: config.contactFlowId,
|
|
418
|
+
SOURCE_PHONE_NUMBER: config.sourcePhoneNumber,
|
|
419
|
+
QUEUE_ID: config.queueId || '',
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export default Callbacks
|