@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,280 @@
1
+ import type { CLI } from '@stacksjs/clapp'
2
+ import { existsSync } from 'node:fs'
3
+ import { mkdir, writeFile } from 'node:fs/promises'
4
+ import { join } from 'node:path'
5
+ import * as cli from '../../src/utils/cli'
6
+ import { InfrastructureGenerator } from '../../src/generators/infrastructure'
7
+ import { CloudFormationClient } from '../../src/aws/cloudformation'
8
+ import { validateTemplate, validateTemplateSize, validateResourceLimits } from '../../src/validation/template'
9
+ import { loadValidatedConfig } from './shared'
10
+
11
+ export function registerGenerateCommands(app: CLI): void {
12
+ app
13
+ .command('generate', 'Generate CloudFormation templates')
14
+ .alias('gen')
15
+ .option('--output <path>', 'Output directory for templates', { default: 'cloudformation' })
16
+ .option('--format <format>', 'Output format: json or yaml', { default: 'json' })
17
+ .option('--module <module>', 'Generate specific module only')
18
+ .action(async (options?: { output?: string, format?: string, module?: string }) => {
19
+ cli.header('Generating CloudFormation Templates')
20
+
21
+ const spinner = new cli.Spinner('Loading configuration...')
22
+ spinner.start()
23
+
24
+ try {
25
+ const config = await loadValidatedConfig()
26
+ spinner.succeed('Configuration loaded')
27
+
28
+ const outputDir = options?.output || 'cloudformation'
29
+ const format = options?.format || 'json'
30
+ const environment = (options as any)?.env || 'production'
31
+
32
+ // Create output directory
33
+ if (!existsSync(outputDir)) {
34
+ await mkdir(outputDir, { recursive: true })
35
+ }
36
+
37
+ // Generate infrastructure using all Phase 2 modules
38
+ cli.step('Generating infrastructure...')
39
+ const generator = new InfrastructureGenerator({
40
+ config,
41
+ environment,
42
+ modules: options?.module ? [options.module] : undefined,
43
+ })
44
+
45
+ const generationSpinner = new cli.Spinner('Generating CloudFormation template...')
46
+ generationSpinner.start()
47
+
48
+ // Generate the template
49
+ generator.generate()
50
+ const output = format === 'yaml' ? generator.toYAML() : generator.toJSON()
51
+ generationSpinner.succeed('Template generated')
52
+
53
+ // Validate template
54
+ cli.step('Validating template...')
55
+ const template = JSON.parse(generator.toJSON())
56
+ const validation = validateTemplate(template)
57
+ const sizeValidation = validateTemplateSize(output)
58
+ const limitsValidation = validateResourceLimits(template)
59
+
60
+ // Show errors
61
+ const allErrors = [
62
+ ...validation.errors,
63
+ ...sizeValidation.errors,
64
+ ...limitsValidation.errors,
65
+ ]
66
+
67
+ if (allErrors.length > 0) {
68
+ cli.error('Template validation failed:')
69
+ for (const error of allErrors) {
70
+ cli.error(` - ${error.path}: ${error.message}`)
71
+ }
72
+ }
73
+ else {
74
+ cli.success('Template validated successfully')
75
+ }
76
+
77
+ // Show warnings
78
+ const allWarnings = [
79
+ ...validation.warnings,
80
+ ...sizeValidation.warnings,
81
+ ...limitsValidation.warnings,
82
+ ]
83
+
84
+ if (allWarnings.length > 0) {
85
+ for (const warning of allWarnings) {
86
+ cli.warn(` - ${warning.path}: ${warning.message}`)
87
+ }
88
+ }
89
+
90
+ // Write to file
91
+ const filename = join(outputDir, `${environment}.${format}`)
92
+ await writeFile(filename, output)
93
+ cli.success(`Generated ${filename}`)
94
+
95
+ // Show summary
96
+ const builder = generator.getBuilder()
97
+ const resourceCount = Object.keys(builder.getResources()).length
98
+ cli.info(`\nGenerated ${resourceCount} resources:`)
99
+
100
+ // Count resource types
101
+ const resources = builder.getResources()
102
+ const typeCounts: Record<string, number> = {}
103
+ for (const resource of Object.values(resources)) {
104
+ const type = (resource as any).Type
105
+ typeCounts[type] = (typeCounts[type] || 0) + 1
106
+ }
107
+
108
+ // Display resource types
109
+ const types = Object.entries(typeCounts).sort((a, b) => b[1] - a[1])
110
+ for (const [type, count] of types) {
111
+ cli.info(` - ${type}: ${count}`)
112
+ }
113
+
114
+ cli.info(`\nNext steps:
115
+ 1. Review the generated templates in ${outputDir}/
116
+ 2. Run 'cloud deploy' to deploy your infrastructure`)
117
+ }
118
+ catch (error) {
119
+ spinner.fail('Failed to generate templates')
120
+ cli.error(error instanceof Error ? error.message : 'Unknown error')
121
+ }
122
+ })
123
+
124
+ app
125
+ .command('generate:preview', 'Preview what will be generated')
126
+ .action(async () => {
127
+ cli.header('Template Preview')
128
+ cli.info('This command will show a preview of generated templates')
129
+ // TODO: Implement preview logic
130
+ })
131
+
132
+ app
133
+ .command('diff', 'Show diff between local config and deployed stack')
134
+ .alias('generate:diff')
135
+ .option('--stack <name>', 'Stack name to compare against')
136
+ .option('--env <environment>', 'Environment (production, staging, development)')
137
+ .action(async (options?: { stack?: string, env?: string }) => {
138
+ cli.header('Infrastructure Diff')
139
+
140
+ try {
141
+ const config = await loadValidatedConfig()
142
+ const environment = (options?.env || 'production') as 'production' | 'staging' | 'development'
143
+ const stackName = options?.stack || `${config.project.slug}-${environment}`
144
+ const region = config.project.region || 'us-east-1'
145
+
146
+ cli.info(`Stack: ${stackName}`)
147
+ cli.info(`Region: ${region}`)
148
+ cli.info(`Environment: ${environment}`)
149
+
150
+ // Generate new template from config
151
+ cli.step('Generating template from configuration...')
152
+ const generator = new InfrastructureGenerator({
153
+ config,
154
+ environment,
155
+ })
156
+ generator.generate()
157
+ const newTemplateBody = generator.toJSON()
158
+ const newTemplate = JSON.parse(newTemplateBody)
159
+
160
+ // Get existing template from CloudFormation
161
+ cli.step('Fetching deployed template...')
162
+ const cfn = new CloudFormationClient(region)
163
+
164
+ let existingTemplate: any = null
165
+ try {
166
+ const result = await cfn.getTemplate(stackName)
167
+ if (result.TemplateBody) {
168
+ existingTemplate = JSON.parse(result.TemplateBody)
169
+ }
170
+ }
171
+ catch (error: any) {
172
+ if (error.message?.includes('does not exist')) {
173
+ cli.warn(`Stack "${stackName}" does not exist yet`)
174
+ cli.info('\nThis will be a new deployment with the following resources:')
175
+
176
+ const resourceCount = Object.keys(newTemplate.Resources || {}).length
177
+ cli.info(`\nResources to create: ${resourceCount}`)
178
+
179
+ // Count and display resource types
180
+ const typeCounts: Record<string, number> = {}
181
+ for (const resource of Object.values(newTemplate.Resources || {})) {
182
+ const type = (resource as any).Type
183
+ typeCounts[type] = (typeCounts[type] || 0) + 1
184
+ }
185
+
186
+ for (const [type, count] of Object.entries(typeCounts).sort((a, b) => b[1] - a[1])) {
187
+ cli.info(` + ${type}: ${count}`)
188
+ }
189
+
190
+ cli.info('\nRun `cloud deploy` to create this stack')
191
+ return
192
+ }
193
+ throw error
194
+ }
195
+
196
+ // Compare templates
197
+ cli.step('Comparing templates...')
198
+
199
+ const existingResources = existingTemplate.Resources || {}
200
+ const newResources = newTemplate.Resources || {}
201
+
202
+ const existingKeys = new Set(Object.keys(existingResources))
203
+ const newKeys = new Set(Object.keys(newResources))
204
+
205
+ // Find added resources
206
+ const added: string[] = []
207
+ for (const key of newKeys) {
208
+ if (!existingKeys.has(key)) {
209
+ added.push(key)
210
+ }
211
+ }
212
+
213
+ // Find removed resources
214
+ const removed: string[] = []
215
+ for (const key of existingKeys) {
216
+ if (!newKeys.has(key)) {
217
+ removed.push(key)
218
+ }
219
+ }
220
+
221
+ // Find modified resources
222
+ const modified: string[] = []
223
+ for (const key of newKeys) {
224
+ if (existingKeys.has(key)) {
225
+ const existingJson = JSON.stringify(existingResources[key])
226
+ const newJson = JSON.stringify(newResources[key])
227
+ if (existingJson !== newJson) {
228
+ modified.push(key)
229
+ }
230
+ }
231
+ }
232
+
233
+ // Display results
234
+ if (added.length === 0 && removed.length === 0 && modified.length === 0) {
235
+ cli.success('\nNo changes detected - infrastructure is up to date')
236
+ return
237
+ }
238
+
239
+ cli.info('\nChanges detected:\n')
240
+
241
+ if (added.length > 0) {
242
+ cli.success(`Resources to add (${added.length}):`)
243
+ for (const key of added) {
244
+ const type = newResources[key].Type
245
+ cli.info(` + ${key} (${type})`)
246
+ }
247
+ console.log()
248
+ }
249
+
250
+ if (removed.length > 0) {
251
+ cli.error(`Resources to remove (${removed.length}):`)
252
+ for (const key of removed) {
253
+ const type = existingResources[key].Type
254
+ cli.info(` - ${key} (${type})`)
255
+ }
256
+ console.log()
257
+ }
258
+
259
+ if (modified.length > 0) {
260
+ cli.warn(`Resources to modify (${modified.length}):`)
261
+ for (const key of modified) {
262
+ const type = newResources[key].Type
263
+ cli.info(` ~ ${key} (${type})`)
264
+ }
265
+ console.log()
266
+ }
267
+
268
+ // Summary
269
+ cli.info('Summary:')
270
+ cli.info(` - Add: ${added.length}`)
271
+ cli.info(` - Remove: ${removed.length}`)
272
+ cli.info(` - Modify: ${modified.length}`)
273
+
274
+ cli.info('\nRun `cloud deploy` to apply these changes')
275
+ }
276
+ catch (error: any) {
277
+ cli.error(`Diff failed: ${error.message}`)
278
+ }
279
+ })
280
+ }
@@ -0,0 +1,139 @@
1
+ import type { CLI } from '@stacksjs/clapp'
2
+ import * as cli from '../../src/utils/cli'
3
+
4
+ export function registerGitCommands(app: CLI): void {
5
+ app
6
+ .command('git:add <repo>', 'Connect git repository')
7
+ .option('--branch <branch>', 'Default branch to deploy', { default: 'main' })
8
+ .action(async (repo: string, options?: { branch?: string }) => {
9
+ const branch = options?.branch || 'main'
10
+
11
+ cli.header('Connecting Git Repository')
12
+
13
+ cli.info(`Repository: ${repo}`)
14
+ cli.info(`Default branch: ${branch}`)
15
+
16
+ const confirm = await cli.confirm('\nConnect this repository?', true)
17
+ if (!confirm) {
18
+ cli.info('Operation cancelled')
19
+ return
20
+ }
21
+
22
+ const spinner = new cli.Spinner('Setting up git integration...')
23
+ spinner.start()
24
+
25
+ // TODO: Store repo config, setup deploy keys
26
+ await new Promise(resolve => setTimeout(resolve, 2000))
27
+
28
+ spinner.succeed('Repository connected')
29
+
30
+ cli.success('\nGit repository connected!')
31
+ cli.info('\nNext steps:')
32
+ cli.info(' - Deploy: cloud git:deploy main')
33
+ cli.info(' - Add webhook: cloud git:webhook:add')
34
+ })
35
+
36
+ app
37
+ .command('git:deploy <branch>', 'Deploy from git branch')
38
+ .option('--env <environment>', 'Target environment')
39
+ .action(async (branch: string, options?: { env?: string }) => {
40
+ const environment = options?.env || 'production'
41
+
42
+ cli.header(`Deploying from Git: ${branch}`)
43
+
44
+ cli.info(`Branch: ${branch}`)
45
+ cli.info(`Environment: ${environment}`)
46
+
47
+ const confirm = await cli.confirm('\nDeploy this branch?', true)
48
+ if (!confirm) {
49
+ cli.info('Operation cancelled')
50
+ return
51
+ }
52
+
53
+ const spinner = new cli.Spinner('Pulling latest changes and deploying...')
54
+ spinner.start()
55
+
56
+ // TODO: Git pull and deploy
57
+ await new Promise(resolve => setTimeout(resolve, 4000))
58
+
59
+ spinner.succeed('Deployment complete')
60
+
61
+ cli.success('\nDeployed successfully!')
62
+ cli.info(`Branch ${branch} is now live on ${environment}`)
63
+ })
64
+
65
+ app
66
+ .command('git:webhook:add <repo>', 'Add webhook for auto-deploy')
67
+ .action(async (repo: string) => {
68
+ cli.header('Adding Deploy Webhook')
69
+
70
+ cli.info(`Repository: ${repo}`)
71
+
72
+ const spinner = new cli.Spinner('Creating webhook endpoint...')
73
+ spinner.start()
74
+
75
+ // TODO: Create API Gateway webhook endpoint
76
+ await new Promise(resolve => setTimeout(resolve, 2000))
77
+
78
+ spinner.succeed('Webhook created')
79
+
80
+ cli.success('\nWebhook endpoint created!')
81
+ cli.info('\nWebhook URL:')
82
+ cli.info(' https://api.example.com/webhooks/deploy/abc123')
83
+
84
+ cli.info('\nAdd this webhook to your repository:')
85
+ cli.info(' - GitHub: Settings > Webhooks > Add webhook')
86
+ cli.info(' - GitLab: Settings > Webhooks > Add webhook')
87
+ cli.info(' - Event: Push events')
88
+ })
89
+
90
+ app
91
+ .command('git:webhook:remove <repo>', 'Remove webhook')
92
+ .action(async (repo: string) => {
93
+ cli.header('Removing Deploy Webhook')
94
+
95
+ cli.info(`Repository: ${repo}`)
96
+
97
+ const confirm = await cli.confirm('\nRemove this webhook?', true)
98
+ if (!confirm) {
99
+ cli.info('Operation cancelled')
100
+ return
101
+ }
102
+
103
+ const spinner = new cli.Spinner('Removing webhook...')
104
+ spinner.start()
105
+
106
+ // TODO: Delete webhook endpoint
107
+ await new Promise(resolve => setTimeout(resolve, 1500))
108
+
109
+ spinner.succeed('Webhook removed')
110
+
111
+ cli.success('\nWebhook deleted!')
112
+ })
113
+
114
+ app
115
+ .command('git:branches', 'List deployable branches')
116
+ .action(async () => {
117
+ cli.header('Deployable Branches')
118
+
119
+ const spinner = new cli.Spinner('Fetching branches...')
120
+ spinner.start()
121
+
122
+ // TODO: Fetch from git repository
123
+ await new Promise(resolve => setTimeout(resolve, 1500))
124
+
125
+ spinner.stop()
126
+
127
+ cli.table(
128
+ ['Branch', 'Last Commit', 'Author', 'Deployed To'],
129
+ [
130
+ ['main', '2h ago', 'john@example.com', 'production'],
131
+ ['develop', '30m ago', 'jane@example.com', 'staging'],
132
+ ['feature/new-ui', '1d ago', 'bob@example.com', '-'],
133
+ ['hotfix/bug-123', '5h ago', 'alice@example.com', '-'],
134
+ ],
135
+ )
136
+
137
+ cli.info('\nTip: Deploy a branch with `cloud git:deploy <branch>`')
138
+ })
139
+ }