@stacksjs/ts-cloud 0.1.2 → 0.1.5

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 (187) hide show
  1. package/README.md +98 -13
  2. package/dist/aws/acm.d.ts +129 -0
  3. package/dist/aws/application-autoscaling.d.ts +282 -0
  4. package/dist/aws/bedrock.d.ts +2292 -0
  5. package/dist/aws/client.d.ts +79 -0
  6. package/dist/aws/cloudformation.d.ts +105 -0
  7. package/dist/aws/cloudfront.d.ts +265 -0
  8. package/dist/aws/cloudwatch-logs.d.ts +48 -0
  9. package/dist/aws/comprehend.d.ts +505 -0
  10. package/dist/aws/connect.d.ts +377 -0
  11. package/dist/aws/deploy-imap.d.ts +14 -0
  12. package/dist/aws/dynamodb.d.ts +176 -0
  13. package/dist/aws/ec2.d.ts +272 -0
  14. package/dist/aws/ecr.d.ts +149 -0
  15. package/dist/aws/ecs.d.ts +162 -0
  16. package/dist/aws/elasticache.d.ts +71 -0
  17. package/dist/aws/elbv2.d.ts +248 -0
  18. package/dist/aws/email.d.ts +175 -0
  19. package/dist/aws/eventbridge.d.ts +142 -0
  20. package/dist/aws/iam.d.ts +638 -0
  21. package/dist/aws/imap-server.d.ts +119 -0
  22. package/{src/aws/index.ts → dist/aws/index.d.ts} +62 -83
  23. package/{src/aws/kendra.ts → dist/aws/kendra.d.ts} +71 -386
  24. package/dist/aws/lambda.d.ts +232 -0
  25. package/dist/aws/opensearch.d.ts +87 -0
  26. package/dist/aws/personalize.d.ts +516 -0
  27. package/dist/aws/polly.d.ts +214 -0
  28. package/dist/aws/rds.d.ts +240 -0
  29. package/dist/aws/rekognition.d.ts +543 -0
  30. package/dist/aws/route53-domains.d.ts +113 -0
  31. package/dist/aws/route53.d.ts +215 -0
  32. package/dist/aws/s3.d.ts +212 -0
  33. package/dist/aws/scheduler.d.ts +140 -0
  34. package/dist/aws/secrets-manager.d.ts +170 -0
  35. package/dist/aws/ses.d.ts +288 -0
  36. package/dist/aws/setup-phone.d.ts +0 -0
  37. package/dist/aws/setup-sms.d.ts +115 -0
  38. package/dist/aws/sms.d.ts +304 -0
  39. package/dist/aws/smtp-server.d.ts +61 -0
  40. package/dist/aws/sns.d.ts +117 -0
  41. package/dist/aws/sqs.d.ts +65 -0
  42. package/dist/aws/ssm.d.ts +179 -0
  43. package/dist/aws/sts.d.ts +15 -0
  44. package/dist/aws/support.d.ts +104 -0
  45. package/dist/aws/test-imap.d.ts +0 -0
  46. package/dist/aws/textract.d.ts +403 -0
  47. package/dist/aws/transcribe.d.ts +60 -0
  48. package/dist/aws/translate.d.ts +358 -0
  49. package/dist/aws/voice.d.ts +219 -0
  50. package/dist/bin/cli.js +1724 -0
  51. package/dist/config.d.ts +7 -0
  52. package/dist/deploy/index.d.ts +2 -0
  53. package/dist/deploy/static-site-external-dns.d.ts +51 -0
  54. package/dist/deploy/static-site.d.ts +71 -0
  55. package/dist/dns/cloudflare.d.ts +52 -0
  56. package/dist/dns/godaddy.d.ts +38 -0
  57. package/dist/dns/index.d.ts +45 -0
  58. package/dist/dns/porkbun.d.ts +18 -0
  59. package/dist/dns/route53-adapter.d.ts +38 -0
  60. package/{src/dns/types.ts → dist/dns/types.d.ts} +26 -63
  61. package/dist/dns/validator.d.ts +78 -0
  62. package/dist/generators/index.d.ts +1 -0
  63. package/dist/generators/infrastructure.d.ts +30 -0
  64. package/{src/index.ts → dist/index.d.ts} +70 -93
  65. package/dist/index.js +7881 -0
  66. package/dist/push/apns.d.ts +60 -0
  67. package/dist/push/fcm.d.ts +117 -0
  68. package/dist/push/index.d.ts +14 -0
  69. package/dist/security/pre-deploy-scanner.d.ts +69 -0
  70. package/dist/ssl/acme-client.d.ts +67 -0
  71. package/dist/ssl/index.d.ts +2 -0
  72. package/dist/ssl/letsencrypt.d.ts +48 -0
  73. package/dist/types.d.ts +1 -0
  74. package/dist/utils/cli.d.ts +123 -0
  75. package/dist/validation/index.d.ts +1 -0
  76. package/dist/validation/template.d.ts +23 -0
  77. package/package.json +8 -8
  78. package/bin/cli.ts +0 -133
  79. package/bin/commands/analytics.ts +0 -328
  80. package/bin/commands/api.ts +0 -379
  81. package/bin/commands/assets.ts +0 -221
  82. package/bin/commands/audit.ts +0 -501
  83. package/bin/commands/backup.ts +0 -682
  84. package/bin/commands/cache.ts +0 -294
  85. package/bin/commands/cdn.ts +0 -281
  86. package/bin/commands/config.ts +0 -202
  87. package/bin/commands/container.ts +0 -105
  88. package/bin/commands/cost.ts +0 -208
  89. package/bin/commands/database.ts +0 -401
  90. package/bin/commands/deploy.ts +0 -674
  91. package/bin/commands/domain.ts +0 -397
  92. package/bin/commands/email.ts +0 -423
  93. package/bin/commands/environment.ts +0 -285
  94. package/bin/commands/events.ts +0 -424
  95. package/bin/commands/firewall.ts +0 -145
  96. package/bin/commands/function.ts +0 -116
  97. package/bin/commands/generate.ts +0 -280
  98. package/bin/commands/git.ts +0 -139
  99. package/bin/commands/iam.ts +0 -464
  100. package/bin/commands/index.ts +0 -48
  101. package/bin/commands/init.ts +0 -120
  102. package/bin/commands/logs.ts +0 -148
  103. package/bin/commands/network.ts +0 -579
  104. package/bin/commands/notify.ts +0 -489
  105. package/bin/commands/queue.ts +0 -407
  106. package/bin/commands/scheduler.ts +0 -370
  107. package/bin/commands/secrets.ts +0 -54
  108. package/bin/commands/server.ts +0 -629
  109. package/bin/commands/shared.ts +0 -97
  110. package/bin/commands/ssl.ts +0 -138
  111. package/bin/commands/stack.ts +0 -325
  112. package/bin/commands/status.ts +0 -385
  113. package/bin/commands/storage.ts +0 -450
  114. package/bin/commands/team.ts +0 -96
  115. package/bin/commands/tunnel.ts +0 -489
  116. package/bin/commands/utils.ts +0 -202
  117. package/build.ts +0 -15
  118. package/cloud +0 -2
  119. package/src/aws/acm.ts +0 -768
  120. package/src/aws/application-autoscaling.ts +0 -845
  121. package/src/aws/bedrock.ts +0 -4074
  122. package/src/aws/client.ts +0 -878
  123. package/src/aws/cloudformation.ts +0 -896
  124. package/src/aws/cloudfront.ts +0 -1531
  125. package/src/aws/cloudwatch-logs.ts +0 -154
  126. package/src/aws/comprehend.ts +0 -839
  127. package/src/aws/connect.ts +0 -1056
  128. package/src/aws/deploy-imap.ts +0 -384
  129. package/src/aws/dynamodb.ts +0 -340
  130. package/src/aws/ec2.ts +0 -1385
  131. package/src/aws/ecr.ts +0 -621
  132. package/src/aws/ecs.ts +0 -615
  133. package/src/aws/elasticache.ts +0 -301
  134. package/src/aws/elbv2.ts +0 -942
  135. package/src/aws/email.ts +0 -928
  136. package/src/aws/eventbridge.ts +0 -248
  137. package/src/aws/iam.ts +0 -1689
  138. package/src/aws/imap-server.ts +0 -2100
  139. package/src/aws/lambda.ts +0 -786
  140. package/src/aws/opensearch.ts +0 -158
  141. package/src/aws/personalize.ts +0 -977
  142. package/src/aws/polly.ts +0 -559
  143. package/src/aws/rds.ts +0 -888
  144. package/src/aws/rekognition.ts +0 -846
  145. package/src/aws/route53-domains.ts +0 -359
  146. package/src/aws/route53.ts +0 -1046
  147. package/src/aws/s3.ts +0 -2318
  148. package/src/aws/scheduler.ts +0 -571
  149. package/src/aws/secrets-manager.ts +0 -769
  150. package/src/aws/ses.ts +0 -1081
  151. package/src/aws/setup-phone.ts +0 -104
  152. package/src/aws/setup-sms.ts +0 -580
  153. package/src/aws/sms.ts +0 -1735
  154. package/src/aws/smtp-server.ts +0 -531
  155. package/src/aws/sns.ts +0 -758
  156. package/src/aws/sqs.ts +0 -382
  157. package/src/aws/ssm.ts +0 -807
  158. package/src/aws/sts.ts +0 -92
  159. package/src/aws/support.ts +0 -391
  160. package/src/aws/test-imap.ts +0 -86
  161. package/src/aws/textract.ts +0 -780
  162. package/src/aws/transcribe.ts +0 -108
  163. package/src/aws/translate.ts +0 -641
  164. package/src/aws/voice.ts +0 -1379
  165. package/src/config.ts +0 -35
  166. package/src/deploy/index.ts +0 -7
  167. package/src/deploy/static-site-external-dns.ts +0 -906
  168. package/src/deploy/static-site.ts +0 -1125
  169. package/src/dns/godaddy.ts +0 -412
  170. package/src/dns/index.ts +0 -183
  171. package/src/dns/porkbun.ts +0 -362
  172. package/src/dns/route53-adapter.ts +0 -414
  173. package/src/dns/validator.ts +0 -369
  174. package/src/generators/index.ts +0 -5
  175. package/src/generators/infrastructure.ts +0 -1660
  176. package/src/push/apns.ts +0 -452
  177. package/src/push/fcm.ts +0 -506
  178. package/src/push/index.ts +0 -58
  179. package/src/ssl/acme-client.ts +0 -478
  180. package/src/ssl/index.ts +0 -7
  181. package/src/ssl/letsencrypt.ts +0 -747
  182. package/src/types.ts +0 -2
  183. package/src/utils/cli.ts +0 -398
  184. package/src/validation/index.ts +0 -5
  185. package/src/validation/template.ts +0 -405
  186. package/test/index.test.ts +0 -128
  187. package/tsconfig.json +0 -18
@@ -1,423 +0,0 @@
1
- import type { CLI } from '@stacksjs/clapp'
2
- import * as cli from '../../src/utils/cli'
3
- import { SESClient } from '../../src/aws/ses'
4
- import { loadValidatedConfig } from './shared'
5
-
6
- export function registerEmailCommands(app: CLI): void {
7
- app
8
- .command('email:identities', 'List verified email identities')
9
- .option('--region <region>', 'AWS region')
10
- .action(async (options: { region?: string }) => {
11
- cli.header('Email Identities')
12
-
13
- try {
14
- const config = await loadValidatedConfig()
15
- const region = options.region || config.project.region || 'us-east-1'
16
- const ses = new SESClient(region)
17
-
18
- const spinner = new cli.Spinner('Fetching identities...')
19
- spinner.start()
20
-
21
- const result = await ses.listEmailIdentities()
22
- const identities = result.EmailIdentities || []
23
-
24
- spinner.succeed(`Found ${identities.length} identity(s)`)
25
-
26
- if (identities.length === 0) {
27
- cli.info('No email identities found')
28
- cli.info('Use `cloud email:verify` to verify an email or domain')
29
- return
30
- }
31
-
32
- // Get verification status for each identity
33
- const rows: string[][] = []
34
- for (const identity of identities) {
35
- const name = identity.IdentityName || 'Unknown'
36
- let verificationStatus = 'Unknown'
37
- try {
38
- const detail = await ses.getEmailIdentity(name)
39
- verificationStatus = detail.VerificationStatus || 'Unknown'
40
- }
41
- catch {
42
- // If we can't fetch details, just show what we have
43
- }
44
- const type = identity.IdentityType === 'EMAIL_ADDRESS' ? 'Email' : identity.IdentityType === 'DOMAIN' ? 'Domain' : (identity.IdentityType || 'Unknown')
45
- rows.push([name, type, verificationStatus])
46
- }
47
-
48
- cli.table(
49
- ['Identity', 'Type', 'Verification Status'],
50
- rows,
51
- )
52
- }
53
- catch (error: unknown) {
54
- const message = error instanceof Error ? error.message : String(error)
55
- cli.error(`Failed to list identities: ${message}`)
56
- process.exit(1)
57
- }
58
- })
59
-
60
- app
61
- .command('email:verify <identity>', 'Verify an email address or domain')
62
- .option('--region <region>', 'AWS region', { default: 'us-east-1' })
63
- .action(async (identity: string, options: { region: string }) => {
64
- cli.header('Verify Email Identity')
65
-
66
- try {
67
- const ses = new SESClient(options.region)
68
-
69
- const isEmail = identity.includes('@')
70
-
71
- cli.info(`Identity: ${identity}`)
72
- cli.info(`Type: ${isEmail ? 'Email Address' : 'Domain'}`)
73
-
74
- const confirmed = await cli.confirm('\nSend verification?', true)
75
- if (!confirmed) {
76
- cli.info('Operation cancelled')
77
- return
78
- }
79
-
80
- const spinner = new cli.Spinner('Initiating verification...')
81
- spinner.start()
82
-
83
- if (isEmail) {
84
- await ses.createEmailIdentity({ EmailIdentity: identity })
85
- spinner.succeed('Verification email sent')
86
-
87
- cli.info(`\nA verification email has been sent to ${identity}`)
88
- cli.info('Click the link in the email to complete verification.')
89
- }
90
- else {
91
- const result = await ses.verifyDomain(identity)
92
- spinner.succeed('Domain verification initiated')
93
-
94
- const dkimTokens = result.dkimTokens || []
95
-
96
- if (dkimTokens.length > 0) {
97
- cli.info('\nDKIM Records (for email authentication):')
98
- for (const token of dkimTokens) {
99
- cli.info(`\n Name: ${token}._domainkey.${identity}`)
100
- cli.info(` Type: CNAME`)
101
- cli.info(` Value: ${token}.dkim.amazonses.com`)
102
- }
103
- }
104
-
105
- cli.info(`\nVerification Status: ${result.verificationStatus || 'PENDING'}`)
106
- }
107
- }
108
- catch (error: unknown) {
109
- const message = error instanceof Error ? error.message : String(error)
110
- cli.error(`Failed to verify identity: ${message}`)
111
- process.exit(1)
112
- }
113
- })
114
-
115
- app
116
- .command('email:delete <identity>', 'Delete a verified email identity')
117
- .option('--region <region>', 'AWS region', { default: 'us-east-1' })
118
- .action(async (identity: string, options: { region: string }) => {
119
- cli.header('Delete Email Identity')
120
-
121
- try {
122
- const ses = new SESClient(options.region)
123
-
124
- cli.warn(`This will remove identity: ${identity}`)
125
-
126
- const confirmed = await cli.confirm('\nDelete this identity?', false)
127
- if (!confirmed) {
128
- cli.info('Operation cancelled')
129
- return
130
- }
131
-
132
- const spinner = new cli.Spinner('Deleting identity...')
133
- spinner.start()
134
-
135
- await ses.deleteEmailIdentity(identity)
136
-
137
- spinner.succeed('Identity deleted')
138
- }
139
- catch (error: unknown) {
140
- const message = error instanceof Error ? error.message : String(error)
141
- cli.error(`Failed to delete identity: ${message}`)
142
- process.exit(1)
143
- }
144
- })
145
-
146
- app
147
- .command('email:send', 'Send a test email')
148
- .option('--region <region>', 'AWS region', { default: 'us-east-1' })
149
- .option('--from <email>', 'From email address (must be verified)')
150
- .option('--to <email>', 'To email address')
151
- .option('--subject <text>', 'Email subject')
152
- .option('--body <text>', 'Email body (text)')
153
- .option('--html <html>', 'Email body (HTML)')
154
- .action(async (options: {
155
- region: string
156
- from?: string
157
- to?: string
158
- subject?: string
159
- body?: string
160
- html?: string
161
- }) => {
162
- cli.header('Send Email')
163
-
164
- try {
165
- const ses = new SESClient(options.region)
166
-
167
- const from = options.from || await cli.prompt('From (verified email)')
168
- const to = options.to || await cli.prompt('To')
169
- const subject = options.subject || await cli.prompt('Subject', 'Test Email from ts-cloud')
170
- const body = options.body || await cli.prompt('Body', 'This is a test email sent from ts-cloud CLI.')
171
-
172
- cli.info(`\nFrom: ${from}`)
173
- cli.info(`To: ${to}`)
174
- cli.info(`Subject: ${subject}`)
175
-
176
- const confirmed = await cli.confirm('\nSend this email?', true)
177
- if (!confirmed) {
178
- cli.info('Operation cancelled')
179
- return
180
- }
181
-
182
- const spinner = new cli.Spinner('Sending email...')
183
- spinner.start()
184
-
185
- const result = await ses.sendEmail({
186
- FromEmailAddress: from,
187
- Destination: {
188
- ToAddresses: [to],
189
- },
190
- Content: {
191
- Simple: {
192
- Subject: {
193
- Data: subject,
194
- },
195
- Body: {
196
- Text: {
197
- Data: body,
198
- },
199
- ...(options.html && {
200
- Html: {
201
- Data: options.html,
202
- },
203
- }),
204
- },
205
- },
206
- },
207
- })
208
-
209
- spinner.succeed('Email sent')
210
-
211
- cli.success(`\nMessage ID: ${result.MessageId}`)
212
- }
213
- catch (error: unknown) {
214
- const message = error instanceof Error ? error.message : String(error)
215
- cli.error(`Failed to send email: ${message}`)
216
- process.exit(1)
217
- }
218
- })
219
-
220
- app
221
- .command('email:templates', 'List email templates')
222
- .option('--region <region>', 'AWS region')
223
- .action(async (options: { region?: string }) => {
224
- cli.header('Email Templates')
225
-
226
- try {
227
- const config = await loadValidatedConfig()
228
- const region = options.region || config.project.region || 'us-east-1'
229
- const ses = new SESClient(region)
230
-
231
- const spinner = new cli.Spinner('Fetching templates...')
232
- spinner.start()
233
-
234
- const result = await ses.listEmailTemplates()
235
- const templates = result.TemplatesMetadata || []
236
-
237
- spinner.succeed(`Found ${templates.length} template(s)`)
238
-
239
- if (templates.length === 0) {
240
- cli.info('No email templates found')
241
- cli.info('Use `cloud email:template:create` to create a template')
242
- return
243
- }
244
-
245
- cli.table(
246
- ['Name', 'Created'],
247
- templates.map((t: { TemplateName?: string, CreatedTimestamp?: string }) => [
248
- t.TemplateName || 'N/A',
249
- t.CreatedTimestamp ? new Date(t.CreatedTimestamp).toLocaleString() : 'N/A',
250
- ]),
251
- )
252
- }
253
- catch (error: unknown) {
254
- const message = error instanceof Error ? error.message : String(error)
255
- cli.error(`Failed to list templates: ${message}`)
256
- process.exit(1)
257
- }
258
- })
259
-
260
- app
261
- .command('email:template:create <name>', 'Create an email template')
262
- .option('--region <region>', 'AWS region', { default: 'us-east-1' })
263
- .option('--subject <text>', 'Email subject (supports {{variable}} placeholders)')
264
- .option('--text <text>', 'Text body')
265
- .option('--html <html>', 'HTML body')
266
- .option('--html-file <path>', 'HTML body from file')
267
- .action(async (name: string, options: {
268
- region: string
269
- subject?: string
270
- text?: string
271
- html?: string
272
- htmlFile?: string
273
- }) => {
274
- cli.header('Create Email Template')
275
-
276
- try {
277
- const ses = new SESClient(options.region)
278
-
279
- const subject = options.subject || await cli.prompt('Subject template', 'Hello {{name}}')
280
- const textBody = options.text || await cli.prompt('Text body', 'Hello {{name}}, this is a test.')
281
-
282
- let htmlBody = options.html
283
- if (options.htmlFile) {
284
- const file = Bun.file(options.htmlFile)
285
- htmlBody = await file.text()
286
- }
287
-
288
- cli.info(`\nTemplate Name: ${name}`)
289
- cli.info(`Subject: ${subject}`)
290
-
291
- const confirmed = await cli.confirm('\nCreate this template?', true)
292
- if (!confirmed) {
293
- cli.info('Operation cancelled')
294
- return
295
- }
296
-
297
- const spinner = new cli.Spinner('Creating template...')
298
- spinner.start()
299
-
300
- await ses.createEmailTemplate({
301
- TemplateName: name,
302
- TemplateContent: {
303
- Subject: subject,
304
- Text: textBody,
305
- Html: htmlBody,
306
- },
307
- })
308
-
309
- spinner.succeed('Template created')
310
-
311
- cli.info('\nTo send using this template:')
312
- cli.info(` cloud email:send:template --template ${name} --to user@example.com --data '{"name":"John"}'`)
313
- }
314
- catch (error: unknown) {
315
- const message = error instanceof Error ? error.message : String(error)
316
- cli.error(`Failed to create template: ${message}`)
317
- process.exit(1)
318
- }
319
- })
320
-
321
- app
322
- .command('email:template:delete <name>', 'Delete an email template')
323
- .option('--region <region>', 'AWS region', { default: 'us-east-1' })
324
- .action(async (name: string, options: { region: string }) => {
325
- cli.header('Delete Email Template')
326
-
327
- try {
328
- const ses = new SESClient(options.region)
329
-
330
- cli.warn(`This will delete template: ${name}`)
331
-
332
- const confirmed = await cli.confirm('\nDelete this template?', false)
333
- if (!confirmed) {
334
- cli.info('Operation cancelled')
335
- return
336
- }
337
-
338
- const spinner = new cli.Spinner('Deleting template...')
339
- spinner.start()
340
-
341
- await ses.deleteEmailTemplate(name)
342
-
343
- spinner.succeed('Template deleted')
344
- }
345
- catch (error: unknown) {
346
- const message = error instanceof Error ? error.message : String(error)
347
- cli.error(`Failed to delete template: ${message}`)
348
- process.exit(1)
349
- }
350
- })
351
-
352
- app
353
- .command('email:stats', 'Show SES sending statistics')
354
- .option('--region <region>', 'AWS region')
355
- .action(async (options: { region?: string }) => {
356
- cli.header('Email Statistics')
357
-
358
- try {
359
- const config = await loadValidatedConfig()
360
- const region = options.region || config.project.region || 'us-east-1'
361
- const ses = new SESClient(region)
362
-
363
- const spinner = new cli.Spinner('Fetching statistics...')
364
- spinner.start()
365
-
366
- const [quota, stats] = await Promise.all([
367
- ses.getSendQuota(),
368
- ses.getSendStatistics(),
369
- ])
370
-
371
- spinner.succeed('Statistics loaded')
372
-
373
- cli.info('\nSending Quota:')
374
- cli.info(` Max 24-hour Send: ${quota.Max24HourSend || 0}`)
375
- cli.info(` Max Send Rate: ${quota.MaxSendRate || 0} emails/second`)
376
- cli.info(` Sent Last 24h: ${quota.SentLast24Hours || 0}`)
377
-
378
- const remaining = (quota.Max24HourSend || 0) - (quota.SentLast24Hours || 0)
379
- cli.info(` Remaining: ${remaining}`)
380
-
381
- if (stats.SendDataPoints && stats.SendDataPoints.length > 0) {
382
- cli.info('\nRecent Statistics:')
383
-
384
- // Aggregate stats
385
- let totalDelivered = 0
386
- let totalBounces = 0
387
- let totalComplaints = 0
388
- let totalRejects = 0
389
-
390
- for (const point of stats.SendDataPoints) {
391
- totalDelivered += point.DeliveryAttempts || 0
392
- totalBounces += point.Bounces || 0
393
- totalComplaints += point.Complaints || 0
394
- totalRejects += point.Rejects || 0
395
- }
396
-
397
- cli.info(` Total Attempts: ${totalDelivered}`)
398
- cli.info(` Bounces: ${totalBounces}`)
399
- cli.info(` Complaints: ${totalComplaints}`)
400
- cli.info(` Rejects: ${totalRejects}`)
401
-
402
- if (totalDelivered > 0) {
403
- const bounceRate = ((totalBounces / totalDelivered) * 100).toFixed(2)
404
- const complaintRate = ((totalComplaints / totalDelivered) * 100).toFixed(2)
405
- cli.info(`\n Bounce Rate: ${bounceRate}%`)
406
- cli.info(` Complaint Rate: ${complaintRate}%`)
407
-
408
- if (Number.parseFloat(bounceRate) > 5) {
409
- cli.warn('\n Warning: Bounce rate is high (>5%). This may affect your sender reputation.')
410
- }
411
- if (Number.parseFloat(complaintRate) > 0.1) {
412
- cli.warn('\n Warning: Complaint rate is high (>0.1%). This may affect your sender reputation.')
413
- }
414
- }
415
- }
416
- }
417
- catch (error: unknown) {
418
- const message = error instanceof Error ? error.message : String(error)
419
- cli.error(`Failed to get statistics: ${message}`)
420
- process.exit(1)
421
- }
422
- })
423
- }
@@ -1,285 +0,0 @@
1
- import type { CLI } from '@stacksjs/clapp'
2
- import * as cli from '../../src/utils/cli'
3
-
4
- export function registerEnvironmentCommands(app: CLI): void {
5
- app
6
- .command('env:create <name>', 'Create new environment')
7
- .option('--clone <source>', 'Clone from existing environment')
8
- .action(async (name: string, options?: { clone?: string }) => {
9
- cli.header(`Creating Environment: ${name}`)
10
-
11
- const validEnvs = ['production', 'staging', 'development', 'preview', 'test']
12
- if (!validEnvs.includes(name.toLowerCase())) {
13
- cli.warn(`Warning: Creating non-standard environment name`)
14
- cli.info(`Standard names: ${validEnvs.join(', ')}`)
15
- }
16
-
17
- if (options?.clone) {
18
- cli.info(`Cloning from: ${options.clone}`)
19
- }
20
-
21
- const confirm = await cli.confirm('\nCreate this environment?', true)
22
- if (!confirm) {
23
- cli.info('Operation cancelled')
24
- return
25
- }
26
-
27
- const spinner = new cli.Spinner('Creating environment infrastructure...')
28
- spinner.start()
29
-
30
- // TODO: Create CloudFormation stack for new environment
31
- // TODO: If cloning, copy configuration from source environment
32
- await new Promise(resolve => setTimeout(resolve, 3000))
33
-
34
- spinner.succeed('Environment created successfully')
35
-
36
- cli.success('\nEnvironment created!')
37
- cli.info(`Environment ${name} is now available`)
38
-
39
- cli.info('\nNext steps:')
40
- cli.info(` - Deploy to environment: cloud deploy --env ${name}`)
41
- cli.info(` - Switch to environment: cloud env:switch ${name}`)
42
- })
43
-
44
- app
45
- .command('env:list', 'List environments')
46
- .action(async () => {
47
- cli.header('Environments')
48
-
49
- const spinner = new cli.Spinner('Fetching environments...')
50
- spinner.start()
51
-
52
- // TODO: Fetch from CloudFormation stacks or config
53
- await new Promise(resolve => setTimeout(resolve, 1500))
54
-
55
- spinner.stop()
56
-
57
- cli.table(
58
- ['Environment', 'Status', 'Region', 'Last Deployed', 'Active'],
59
- [
60
- ['production', 'Active', 'us-east-1', '2 hours ago', ''],
61
- ['staging', 'Active', 'us-east-1', '1 day ago', '*'],
62
- ['development', 'Active', 'us-west-2', '3 days ago', ''],
63
- ['preview-pr-123', 'Active', 'us-east-1', '5 hours ago', ''],
64
- ],
65
- )
66
-
67
- cli.info('\nTip: Use `cloud env:switch NAME` to switch active environment')
68
- cli.info('Tip: Use `cloud env:create NAME` to create new environment')
69
- })
70
-
71
- app
72
- .command('env:switch <name>', 'Switch active environment')
73
- .action(async (name: string) => {
74
- cli.header(`Switching to Environment: ${name}`)
75
-
76
- cli.info(`Switching to: ${name}`)
77
-
78
- const spinner = new cli.Spinner('Updating environment configuration...')
79
- spinner.start()
80
-
81
- // TODO: Update config to set active environment
82
- await new Promise(resolve => setTimeout(resolve, 1000))
83
-
84
- spinner.succeed('Environment switched successfully')
85
-
86
- cli.success(`\nNow using environment: ${name}`)
87
- cli.info(`All commands will now target the ${name} environment`)
88
-
89
- cli.info('\nEnvironment details:')
90
- cli.info(` - Region: us-east-1`)
91
- cli.info(` - Status: Active`)
92
- cli.info(` - Last deployed: 1 day ago`)
93
- })
94
-
95
- app
96
- .command('env:clone <source> <target>', 'Clone environment')
97
- .action(async (source: string, target: string) => {
98
- cli.header('Cloning Environment')
99
-
100
- cli.info(`Source: ${source}`)
101
- cli.info(`Target: ${target}`)
102
-
103
- cli.warn('\nThis will copy:')
104
- cli.info(' - Infrastructure configuration')
105
- cli.info(' - Environment variables')
106
- cli.info(' - Database schema (not data)')
107
-
108
- const confirm = await cli.confirm('\nClone environment?', true)
109
- if (!confirm) {
110
- cli.info('Operation cancelled')
111
- return
112
- }
113
-
114
- const spinner = new cli.Spinner('Cloning environment...')
115
- spinner.start()
116
-
117
- // TODO: Copy CloudFormation stack and config
118
- await new Promise(resolve => setTimeout(resolve, 5000))
119
-
120
- spinner.succeed('Environment cloned')
121
-
122
- cli.success(`\nEnvironment ${target} created from ${source}!`)
123
- cli.info('Deploy with: cloud deploy --env ' + target)
124
- })
125
-
126
- app
127
- .command('env:promote <source> <target>', 'Promote environment')
128
- .action(async (source: string, target: string) => {
129
- cli.header('Promoting Environment')
130
-
131
- cli.info(`From: ${source}`)
132
- cli.info(`To: ${target}`)
133
-
134
- cli.warn('\nThis will:')
135
- cli.info(' - Deploy code from source to target')
136
- cli.info(' - Update target configuration')
137
- cli.info(' - Run database migrations if any')
138
-
139
- const confirm = await cli.confirm('\nPromote to ' + target + '?', false)
140
- if (!confirm) {
141
- cli.info('Operation cancelled')
142
- return
143
- }
144
-
145
- const spinner = new cli.Spinner('Promoting environment...')
146
- spinner.start()
147
-
148
- // TODO: Deploy source to target
149
- await new Promise(resolve => setTimeout(resolve, 6000))
150
-
151
- spinner.succeed('Promotion complete')
152
-
153
- cli.success(`\n${source} promoted to ${target}!`)
154
- })
155
-
156
- app
157
- .command('env:compare <env1> <env2>', 'Compare configurations')
158
- .action(async (env1: string, env2: string) => {
159
- cli.header('Comparing Environments')
160
-
161
- cli.info(`Environment 1: ${env1}`)
162
- cli.info(`Environment 2: ${env2}`)
163
-
164
- const spinner = new cli.Spinner('Analyzing configurations...')
165
- spinner.start()
166
-
167
- // TODO: Compare CloudFormation stacks and config
168
- await new Promise(resolve => setTimeout(resolve, 2000))
169
-
170
- spinner.stop()
171
-
172
- cli.info('\nConfiguration Differences:\n')
173
-
174
- cli.table(
175
- ['Setting', env1, env2, 'Match'],
176
- [
177
- ['Instance Type', 't3.medium', 't3.small', 'X'],
178
- ['Database Size', 'db.t3.medium', 'db.t3.micro', 'X'],
179
- ['Auto Scaling', 'Enabled', 'Disabled', 'X'],
180
- ['Region', 'us-east-1', 'us-east-1', '*'],
181
- ['Node Version', '20.x', '20.x', '*'],
182
- ],
183
- )
184
-
185
- cli.info('\nFound 3 differences')
186
- })
187
-
188
- app
189
- .command('env:sync <source> <target>', 'Sync configuration')
190
- .action(async (source: string, target: string) => {
191
- cli.header('Syncing Configuration')
192
-
193
- cli.info(`Source: ${source}`)
194
- cli.info(`Target: ${target}`)
195
-
196
- cli.warn('\nThis will sync configuration (not resources or data)')
197
-
198
- const confirm = await cli.confirm('\nSync configuration?', true)
199
- if (!confirm) {
200
- cli.info('Operation cancelled')
201
- return
202
- }
203
-
204
- const spinner = new cli.Spinner('Syncing configuration...')
205
- spinner.start()
206
-
207
- // TODO: Sync config files
208
- await new Promise(resolve => setTimeout(resolve, 2000))
209
-
210
- spinner.succeed('Configuration synced')
211
-
212
- cli.success('\nConfiguration synchronized!')
213
- })
214
-
215
- app
216
- .command('env:preview <branch>', 'Create preview environment from branch')
217
- .action(async (branch: string) => {
218
- cli.header(`Creating Preview Environment for ${branch}`)
219
-
220
- const envName = `preview-${branch.replace(/[^a-z0-9]/gi, '-').toLowerCase()}`
221
-
222
- cli.info(`Environment name: ${envName}`)
223
- cli.info(`Branch: ${branch}`)
224
-
225
- const confirm = await cli.confirm('\nCreate preview environment?', true)
226
- if (!confirm) {
227
- cli.info('Operation cancelled')
228
- return
229
- }
230
-
231
- const spinner = new cli.Spinner('Creating preview environment...')
232
- spinner.start()
233
-
234
- // TODO: Create temporary CloudFormation stack
235
- await new Promise(resolve => setTimeout(resolve, 8000))
236
-
237
- spinner.succeed('Preview environment created')
238
-
239
- cli.success('\nPreview environment ready!')
240
- cli.info(`URL: https://${envName}.preview.example.com`)
241
- cli.info('\nThis environment will auto-delete after 7 days')
242
- })
243
-
244
- app
245
- .command('env:cleanup', 'Remove stale preview environments')
246
- .action(async () => {
247
- cli.header('Cleaning Up Preview Environments')
248
-
249
- const spinner = new cli.Spinner('Finding stale preview environments...')
250
- spinner.start()
251
-
252
- // TODO: Find old preview stacks
253
- await new Promise(resolve => setTimeout(resolve, 2000))
254
-
255
- spinner.stop()
256
-
257
- cli.info('\nFound 3 stale preview environments:\n')
258
-
259
- cli.table(
260
- ['Environment', 'Created', 'Age', 'Status'],
261
- [
262
- ['preview-feature-123', '2024-10-15', '30 days', 'Inactive'],
263
- ['preview-bugfix-456', '2024-10-20', '25 days', 'Inactive'],
264
- ['preview-test-789', '2024-11-01', '14 days', 'Inactive'],
265
- ],
266
- )
267
-
268
- const confirm = await cli.confirm('\nDelete these environments?', true)
269
- if (!confirm) {
270
- cli.info('Operation cancelled')
271
- return
272
- }
273
-
274
- const cleanupSpinner = new cli.Spinner('Deleting stale environments...')
275
- cleanupSpinner.start()
276
-
277
- // TODO: Delete CloudFormation stacks
278
- await new Promise(resolve => setTimeout(resolve, 4000))
279
-
280
- cleanupSpinner.succeed('Cleanup complete')
281
-
282
- cli.success('\n3 preview environments deleted!')
283
- cli.info('Estimated monthly savings: $87')
284
- })
285
- }