@stacksjs/ts-cloud 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.
Files changed (117) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +321 -0
  3. package/bin/cli.ts +133 -0
  4. package/bin/commands/analytics.ts +328 -0
  5. package/bin/commands/api.ts +379 -0
  6. package/bin/commands/assets.ts +221 -0
  7. package/bin/commands/audit.ts +501 -0
  8. package/bin/commands/backup.ts +682 -0
  9. package/bin/commands/cache.ts +294 -0
  10. package/bin/commands/cdn.ts +281 -0
  11. package/bin/commands/config.ts +202 -0
  12. package/bin/commands/container.ts +105 -0
  13. package/bin/commands/cost.ts +208 -0
  14. package/bin/commands/database.ts +401 -0
  15. package/bin/commands/deploy.ts +674 -0
  16. package/bin/commands/domain.ts +397 -0
  17. package/bin/commands/email.ts +423 -0
  18. package/bin/commands/environment.ts +285 -0
  19. package/bin/commands/events.ts +424 -0
  20. package/bin/commands/firewall.ts +145 -0
  21. package/bin/commands/function.ts +116 -0
  22. package/bin/commands/generate.ts +280 -0
  23. package/bin/commands/git.ts +139 -0
  24. package/bin/commands/iam.ts +464 -0
  25. package/bin/commands/index.ts +48 -0
  26. package/bin/commands/init.ts +120 -0
  27. package/bin/commands/logs.ts +148 -0
  28. package/bin/commands/network.ts +579 -0
  29. package/bin/commands/notify.ts +489 -0
  30. package/bin/commands/queue.ts +407 -0
  31. package/bin/commands/scheduler.ts +370 -0
  32. package/bin/commands/secrets.ts +54 -0
  33. package/bin/commands/server.ts +629 -0
  34. package/bin/commands/shared.ts +97 -0
  35. package/bin/commands/ssl.ts +138 -0
  36. package/bin/commands/stack.ts +325 -0
  37. package/bin/commands/status.ts +385 -0
  38. package/bin/commands/storage.ts +450 -0
  39. package/bin/commands/team.ts +96 -0
  40. package/bin/commands/tunnel.ts +489 -0
  41. package/bin/commands/utils.ts +202 -0
  42. package/build.ts +15 -0
  43. package/cloud +2 -0
  44. package/package.json +99 -0
  45. package/src/aws/acm.ts +768 -0
  46. package/src/aws/application-autoscaling.ts +845 -0
  47. package/src/aws/bedrock.ts +4074 -0
  48. package/src/aws/client.ts +878 -0
  49. package/src/aws/cloudformation.ts +896 -0
  50. package/src/aws/cloudfront.ts +1531 -0
  51. package/src/aws/cloudwatch-logs.ts +154 -0
  52. package/src/aws/comprehend.ts +839 -0
  53. package/src/aws/connect.ts +1056 -0
  54. package/src/aws/deploy-imap.ts +384 -0
  55. package/src/aws/dynamodb.ts +340 -0
  56. package/src/aws/ec2.ts +1385 -0
  57. package/src/aws/ecr.ts +621 -0
  58. package/src/aws/ecs.ts +615 -0
  59. package/src/aws/elasticache.ts +301 -0
  60. package/src/aws/elbv2.ts +942 -0
  61. package/src/aws/email.ts +928 -0
  62. package/src/aws/eventbridge.ts +248 -0
  63. package/src/aws/iam.ts +1689 -0
  64. package/src/aws/imap-server.ts +2100 -0
  65. package/src/aws/index.ts +213 -0
  66. package/src/aws/kendra.ts +1097 -0
  67. package/src/aws/lambda.ts +786 -0
  68. package/src/aws/opensearch.ts +158 -0
  69. package/src/aws/personalize.ts +977 -0
  70. package/src/aws/polly.ts +559 -0
  71. package/src/aws/rds.ts +888 -0
  72. package/src/aws/rekognition.ts +846 -0
  73. package/src/aws/route53-domains.ts +359 -0
  74. package/src/aws/route53.ts +1046 -0
  75. package/src/aws/s3.ts +2318 -0
  76. package/src/aws/scheduler.ts +571 -0
  77. package/src/aws/secrets-manager.ts +769 -0
  78. package/src/aws/ses.ts +1081 -0
  79. package/src/aws/setup-phone.ts +104 -0
  80. package/src/aws/setup-sms.ts +580 -0
  81. package/src/aws/sms.ts +1735 -0
  82. package/src/aws/smtp-server.ts +531 -0
  83. package/src/aws/sns.ts +758 -0
  84. package/src/aws/sqs.ts +382 -0
  85. package/src/aws/ssm.ts +807 -0
  86. package/src/aws/sts.ts +92 -0
  87. package/src/aws/support.ts +391 -0
  88. package/src/aws/test-imap.ts +86 -0
  89. package/src/aws/textract.ts +780 -0
  90. package/src/aws/transcribe.ts +108 -0
  91. package/src/aws/translate.ts +641 -0
  92. package/src/aws/voice.ts +1379 -0
  93. package/src/config.ts +35 -0
  94. package/src/deploy/index.ts +7 -0
  95. package/src/deploy/static-site-external-dns.ts +906 -0
  96. package/src/deploy/static-site.ts +1125 -0
  97. package/src/dns/godaddy.ts +412 -0
  98. package/src/dns/index.ts +183 -0
  99. package/src/dns/porkbun.ts +362 -0
  100. package/src/dns/route53-adapter.ts +414 -0
  101. package/src/dns/types.ts +114 -0
  102. package/src/dns/validator.ts +369 -0
  103. package/src/generators/index.ts +5 -0
  104. package/src/generators/infrastructure.ts +1660 -0
  105. package/src/index.ts +163 -0
  106. package/src/push/apns.ts +452 -0
  107. package/src/push/fcm.ts +506 -0
  108. package/src/push/index.ts +58 -0
  109. package/src/ssl/acme-client.ts +478 -0
  110. package/src/ssl/index.ts +7 -0
  111. package/src/ssl/letsencrypt.ts +747 -0
  112. package/src/types.ts +2 -0
  113. package/src/utils/cli.ts +398 -0
  114. package/src/validation/index.ts +5 -0
  115. package/src/validation/template.ts +405 -0
  116. package/test/index.test.ts +128 -0
  117. package/tsconfig.json +18 -0
@@ -0,0 +1,407 @@
1
+ import type { CLI } from '@stacksjs/clapp'
2
+ import * as cli from '../../src/utils/cli'
3
+ import { SQSClient } from '../../src/aws/sqs'
4
+ import { loadValidatedConfig } from './shared'
5
+
6
+ export function registerQueueCommands(app: CLI): void {
7
+ app
8
+ .command('queue:list', 'List all SQS queues')
9
+ .option('--region <region>', 'AWS region')
10
+ .action(async (options: { region?: string }) => {
11
+ cli.header('SQS Queues')
12
+
13
+ try {
14
+ const config = await loadValidatedConfig()
15
+ const region = options.region || config.project.region || 'us-east-1'
16
+ const sqs = new SQSClient(region)
17
+
18
+ const spinner = new cli.Spinner('Fetching queues...')
19
+ spinner.start()
20
+
21
+ const result = await sqs.listQueues()
22
+ const queues = result.QueueUrls || []
23
+
24
+ spinner.succeed(`Found ${queues.length} queue(s)`)
25
+
26
+ if (queues.length === 0) {
27
+ cli.info('No SQS queues found')
28
+ cli.info('Use `cloud queue:create` to create a new queue')
29
+ return
30
+ }
31
+
32
+ // Get attributes for each queue
33
+ const queueData: { url: string; name: string; messages: string; type: string }[] = []
34
+
35
+ for (const queueUrl of queues) {
36
+ try {
37
+ const attrs = await sqs.getQueueAttributes(queueUrl)
38
+
39
+ const name = queueUrl.split('/').pop() || queueUrl
40
+ queueData.push({
41
+ url: queueUrl,
42
+ name,
43
+ messages: attrs.Attributes?.ApproximateNumberOfMessages || '0',
44
+ type: attrs.Attributes?.FifoQueue === 'true' ? 'FIFO' : 'Standard',
45
+ })
46
+ }
47
+ catch {
48
+ const name = queueUrl.split('/').pop() || queueUrl
49
+ queueData.push({
50
+ url: queueUrl,
51
+ name,
52
+ messages: 'N/A',
53
+ type: 'Unknown',
54
+ })
55
+ }
56
+ }
57
+
58
+ cli.table(
59
+ ['Queue Name', 'Messages', 'Type'],
60
+ queueData.map(q => [q.name, q.messages, q.type]),
61
+ )
62
+ }
63
+ catch (error: any) {
64
+ cli.error(`Failed to list queues: ${error.message}`)
65
+ process.exit(1)
66
+ }
67
+ })
68
+
69
+ app
70
+ .command('queue:create <name>', 'Create a new SQS queue')
71
+ .option('--region <region>', 'AWS region', { default: 'us-east-1' })
72
+ .option('--fifo', 'Create a FIFO queue')
73
+ .option('--dlq <queueArn>', 'Dead letter queue ARN')
74
+ .option('--max-retries <number>', 'Max receive count before DLQ', { default: '3' })
75
+ .option('--visibility <seconds>', 'Visibility timeout in seconds', { default: '30' })
76
+ .option('--retention <days>', 'Message retention in days', { default: '4' })
77
+ .action(async (name: string, options: {
78
+ region: string
79
+ fifo?: boolean
80
+ dlq?: string
81
+ maxRetries: string
82
+ visibility: string
83
+ retention: string
84
+ }) => {
85
+ cli.header('Create SQS Queue')
86
+
87
+ try {
88
+ const sqs = new SQSClient(options.region)
89
+
90
+ // FIFO queues must end with .fifo
91
+ const queueName = options.fifo && !name.endsWith('.fifo') ? `${name}.fifo` : name
92
+
93
+ cli.info(`Queue name: ${queueName}`)
94
+ cli.info(`Type: ${options.fifo ? 'FIFO' : 'Standard'}`)
95
+ cli.info(`Visibility timeout: ${options.visibility} seconds`)
96
+ cli.info(`Message retention: ${options.retention} days`)
97
+ if (options.dlq) {
98
+ cli.info(`Dead letter queue: ${options.dlq}`)
99
+ cli.info(`Max retries: ${options.maxRetries}`)
100
+ }
101
+
102
+ const confirmed = await cli.confirm('\nCreate this queue?', true)
103
+ if (!confirmed) {
104
+ cli.info('Operation cancelled')
105
+ return
106
+ }
107
+
108
+ const spinner = new cli.Spinner('Creating queue...')
109
+ spinner.start()
110
+
111
+ const result = await sqs.createQueue({
112
+ queueName,
113
+ fifo: options.fifo,
114
+ visibilityTimeout: Number.parseInt(options.visibility),
115
+ messageRetentionPeriod: Number.parseInt(options.retention) * 24 * 60 * 60,
116
+ contentBasedDeduplication: options.fifo ? true : undefined,
117
+ deadLetterTargetArn: options.dlq,
118
+ maxReceiveCount: options.dlq ? Number.parseInt(options.maxRetries) : undefined,
119
+ })
120
+
121
+ spinner.succeed('Queue created')
122
+
123
+ cli.success(`\nQueue URL: ${result.QueueUrl}`)
124
+ cli.info('\nTo send a message:')
125
+ cli.info(` cloud queue:send ${queueName} --message "Hello World"`)
126
+ }
127
+ catch (error: any) {
128
+ cli.error(`Failed to create queue: ${error.message}`)
129
+ process.exit(1)
130
+ }
131
+ })
132
+
133
+ app
134
+ .command('queue:delete <name>', 'Delete an SQS queue')
135
+ .option('--region <region>', 'AWS region', { default: 'us-east-1' })
136
+ .action(async (name: string, options: { region: string }) => {
137
+ cli.header('Delete SQS Queue')
138
+
139
+ try {
140
+ const sqs = new SQSClient(options.region)
141
+
142
+ cli.warn(`This will permanently delete queue: ${name}`)
143
+ cli.warn('All messages in the queue will be lost!')
144
+
145
+ const confirmed = await cli.confirm('\nDelete this queue?', false)
146
+ if (!confirmed) {
147
+ cli.info('Operation cancelled')
148
+ return
149
+ }
150
+
151
+ const spinner = new cli.Spinner('Getting queue URL...')
152
+ spinner.start()
153
+
154
+ const urlResult = await sqs.getQueueUrl(name)
155
+
156
+ if (!urlResult.QueueUrl) {
157
+ spinner.fail('Queue not found')
158
+ return
159
+ }
160
+
161
+ spinner.text = 'Deleting queue...'
162
+
163
+ await sqs.deleteQueue(urlResult.QueueUrl)
164
+
165
+ spinner.succeed('Queue deleted')
166
+ }
167
+ catch (error: any) {
168
+ cli.error(`Failed to delete queue: ${error.message}`)
169
+ process.exit(1)
170
+ }
171
+ })
172
+
173
+ app
174
+ .command('queue:send <name>', 'Send a message to an SQS queue')
175
+ .option('--region <region>', 'AWS region', { default: 'us-east-1' })
176
+ .option('--message <body>', 'Message body')
177
+ .option('--file <path>', 'Read message body from file')
178
+ .option('--group <id>', 'Message group ID (for FIFO queues)')
179
+ .option('--dedup <id>', 'Deduplication ID (for FIFO queues)')
180
+ .option('--delay <seconds>', 'Delay delivery in seconds', { default: '0' })
181
+ .action(async (name: string, options: {
182
+ region: string
183
+ message?: string
184
+ file?: string
185
+ group?: string
186
+ dedup?: string
187
+ delay: string
188
+ }) => {
189
+ cli.header('Send SQS Message')
190
+
191
+ try {
192
+ const sqs = new SQSClient(options.region)
193
+
194
+ // Get message body
195
+ let messageBody: string
196
+
197
+ if (options.file) {
198
+ const file = Bun.file(options.file)
199
+ messageBody = await file.text()
200
+ }
201
+ else if (options.message) {
202
+ messageBody = options.message
203
+ }
204
+ else {
205
+ messageBody = await cli.prompt('Message body')
206
+ }
207
+
208
+ if (!messageBody) {
209
+ cli.error('Message body is required')
210
+ return
211
+ }
212
+
213
+ const spinner = new cli.Spinner('Getting queue URL...')
214
+ spinner.start()
215
+
216
+ const urlResult = await sqs.getQueueUrl(name)
217
+
218
+ if (!urlResult.QueueUrl) {
219
+ spinner.fail('Queue not found')
220
+ return
221
+ }
222
+
223
+ spinner.text = 'Sending message...'
224
+
225
+ const result = await sqs.sendMessage({
226
+ queueUrl: urlResult.QueueUrl,
227
+ messageBody,
228
+ delaySeconds: Number.parseInt(options.delay),
229
+ messageGroupId: options.group,
230
+ messageDeduplicationId: options.dedup,
231
+ })
232
+
233
+ spinner.succeed('Message sent')
234
+
235
+ cli.success(`\nMessage ID: ${result.MessageId}`)
236
+ }
237
+ catch (error: any) {
238
+ cli.error(`Failed to send message: ${error.message}`)
239
+ process.exit(1)
240
+ }
241
+ })
242
+
243
+ app
244
+ .command('queue:receive <name>', 'Receive messages from an SQS queue')
245
+ .option('--region <region>', 'AWS region', { default: 'us-east-1' })
246
+ .option('--max <number>', 'Maximum number of messages', { default: '1' })
247
+ .option('--wait <seconds>', 'Long polling wait time', { default: '0' })
248
+ .option('--delete', 'Delete messages after receiving')
249
+ .action(async (name: string, options: {
250
+ region: string
251
+ max: string
252
+ wait: string
253
+ delete?: boolean
254
+ }) => {
255
+ cli.header('Receive SQS Messages')
256
+
257
+ try {
258
+ const sqs = new SQSClient(options.region)
259
+
260
+ const spinner = new cli.Spinner('Getting queue URL...')
261
+ spinner.start()
262
+
263
+ const urlResult = await sqs.getQueueUrl(name)
264
+
265
+ if (!urlResult.QueueUrl) {
266
+ spinner.fail('Queue not found')
267
+ return
268
+ }
269
+
270
+ spinner.text = 'Receiving messages...'
271
+
272
+ const result = await sqs.receiveMessages({
273
+ queueUrl: urlResult.QueueUrl,
274
+ maxMessages: Number.parseInt(options.max),
275
+ waitTimeSeconds: Number.parseInt(options.wait),
276
+ })
277
+
278
+ const messages = result.Messages || []
279
+
280
+ spinner.succeed(`Received ${messages.length} message(s)`)
281
+
282
+ if (messages.length === 0) {
283
+ cli.info('No messages available')
284
+ return
285
+ }
286
+
287
+ for (const msg of messages) {
288
+ cli.info(`\n--- Message: ${msg.MessageId} ---`)
289
+ cli.info(`Body: ${msg.Body}`)
290
+
291
+ if (msg.Attributes) {
292
+ cli.info(`Sent: ${msg.Attributes.SentTimestamp ? new Date(Number.parseInt(msg.Attributes.SentTimestamp)).toISOString() : 'N/A'}`)
293
+ cli.info(`Receive Count: ${msg.Attributes.ApproximateReceiveCount || 'N/A'}`)
294
+ }
295
+
296
+ if (options.delete && msg.ReceiptHandle) {
297
+ await sqs.deleteMessage(urlResult.QueueUrl, msg.ReceiptHandle)
298
+ cli.info('(Deleted)')
299
+ }
300
+ }
301
+ }
302
+ catch (error: any) {
303
+ cli.error(`Failed to receive messages: ${error.message}`)
304
+ process.exit(1)
305
+ }
306
+ })
307
+
308
+ app
309
+ .command('queue:purge <name>', 'Purge all messages from an SQS queue')
310
+ .option('--region <region>', 'AWS region', { default: 'us-east-1' })
311
+ .action(async (name: string, options: { region: string }) => {
312
+ cli.header('Purge SQS Queue')
313
+
314
+ try {
315
+ const sqs = new SQSClient(options.region)
316
+
317
+ cli.warn(`This will delete ALL messages in queue: ${name}`)
318
+ cli.warn('This action cannot be undone!')
319
+
320
+ const confirmed = await cli.confirm('\nPurge this queue?', false)
321
+ if (!confirmed) {
322
+ cli.info('Operation cancelled')
323
+ return
324
+ }
325
+
326
+ const spinner = new cli.Spinner('Getting queue URL...')
327
+ spinner.start()
328
+
329
+ const urlResult = await sqs.getQueueUrl(name)
330
+
331
+ if (!urlResult.QueueUrl) {
332
+ spinner.fail('Queue not found')
333
+ return
334
+ }
335
+
336
+ spinner.text = 'Purging queue...'
337
+
338
+ await sqs.purgeQueue(urlResult.QueueUrl)
339
+
340
+ spinner.succeed('Queue purged')
341
+
342
+ cli.info('\nNote: It may take up to 60 seconds for the purge to complete.')
343
+ }
344
+ catch (error: any) {
345
+ cli.error(`Failed to purge queue: ${error.message}`)
346
+ process.exit(1)
347
+ }
348
+ })
349
+
350
+ app
351
+ .command('queue:stats <name>', 'Show SQS queue statistics')
352
+ .option('--region <region>', 'AWS region', { default: 'us-east-1' })
353
+ .action(async (name: string, options: { region: string }) => {
354
+ cli.header(`SQS Queue Stats: ${name}`)
355
+
356
+ try {
357
+ const sqs = new SQSClient(options.region)
358
+
359
+ const spinner = new cli.Spinner('Fetching queue stats...')
360
+ spinner.start()
361
+
362
+ const urlResult = await sqs.getQueueUrl(name)
363
+
364
+ if (!urlResult.QueueUrl) {
365
+ spinner.fail('Queue not found')
366
+ return
367
+ }
368
+
369
+ const attrs = await sqs.getQueueAttributes(urlResult.QueueUrl)
370
+
371
+ spinner.succeed('Stats loaded')
372
+
373
+ const a = attrs.Attributes || {}
374
+
375
+ cli.info('\nQueue Information:')
376
+ cli.info(` URL: ${urlResult.QueueUrl}`)
377
+ cli.info(` ARN: ${a.QueueArn || 'N/A'}`)
378
+ cli.info(` Type: ${a.FifoQueue === 'true' ? 'FIFO' : 'Standard'}`)
379
+
380
+ cli.info('\nMessages:')
381
+ cli.info(` Available: ${a.ApproximateNumberOfMessages || '0'}`)
382
+ cli.info(` In Flight: ${a.ApproximateNumberOfMessagesNotVisible || '0'}`)
383
+ cli.info(` Delayed: ${a.ApproximateNumberOfMessagesDelayed || '0'}`)
384
+
385
+ cli.info('\nConfiguration:')
386
+ cli.info(` Visibility Timeout: ${a.VisibilityTimeout || '30'} seconds`)
387
+ cli.info(` Message Retention: ${Number.parseInt(a.MessageRetentionPeriod || '345600') / 86400} days`)
388
+ cli.info(` Max Message Size: ${Number.parseInt(a.MaximumMessageSize || '262144') / 1024} KB`)
389
+ cli.info(` Receive Wait Time: ${a.ReceiveMessageWaitTimeSeconds || '0'} seconds`)
390
+
391
+ if (a.RedrivePolicy) {
392
+ const dlqPolicy = JSON.parse(a.RedrivePolicy)
393
+ cli.info('\nDead Letter Queue:')
394
+ cli.info(` Target ARN: ${dlqPolicy.deadLetterTargetArn}`)
395
+ cli.info(` Max Receives: ${dlqPolicy.maxReceiveCount}`)
396
+ }
397
+
398
+ cli.info('\nTimestamps:')
399
+ cli.info(` Created: ${a.CreatedTimestamp ? new Date(Number.parseInt(a.CreatedTimestamp) * 1000).toISOString() : 'N/A'}`)
400
+ cli.info(` Last Modified: ${a.LastModifiedTimestamp ? new Date(Number.parseInt(a.LastModifiedTimestamp) * 1000).toISOString() : 'N/A'}`)
401
+ }
402
+ catch (error: any) {
403
+ cli.error(`Failed to get queue stats: ${error.message}`)
404
+ process.exit(1)
405
+ }
406
+ })
407
+ }