@stacksjs/ts-cloud 0.1.3 → 0.1.6
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/README.md +98 -13
- package/dist/aws/acm.d.ts +129 -0
- package/dist/aws/application-autoscaling.d.ts +282 -0
- package/dist/aws/bedrock.d.ts +2292 -0
- package/dist/aws/client.d.ts +79 -0
- package/dist/aws/cloudformation.d.ts +105 -0
- package/dist/aws/cloudfront.d.ts +265 -0
- package/dist/aws/cloudwatch-logs.d.ts +48 -0
- package/dist/aws/comprehend.d.ts +505 -0
- package/dist/aws/connect.d.ts +377 -0
- package/dist/aws/deploy-imap.d.ts +14 -0
- package/dist/aws/dynamodb.d.ts +176 -0
- package/dist/aws/ec2.d.ts +272 -0
- package/dist/aws/ecr.d.ts +149 -0
- package/dist/aws/ecs.d.ts +162 -0
- package/dist/aws/elasticache.d.ts +71 -0
- package/dist/aws/elbv2.d.ts +248 -0
- package/dist/aws/email.d.ts +175 -0
- package/dist/aws/eventbridge.d.ts +142 -0
- package/dist/aws/iam.d.ts +638 -0
- package/dist/aws/imap-server.d.ts +119 -0
- package/{src/aws/index.ts → dist/aws/index.d.ts} +62 -83
- package/{src/aws/kendra.ts → dist/aws/kendra.d.ts} +71 -386
- package/dist/aws/lambda.d.ts +232 -0
- package/dist/aws/opensearch.d.ts +87 -0
- package/dist/aws/personalize.d.ts +516 -0
- package/dist/aws/polly.d.ts +214 -0
- package/dist/aws/rds.d.ts +240 -0
- package/dist/aws/rekognition.d.ts +543 -0
- package/dist/aws/route53-domains.d.ts +113 -0
- package/dist/aws/route53.d.ts +215 -0
- package/dist/aws/s3.d.ts +212 -0
- package/dist/aws/scheduler.d.ts +140 -0
- package/dist/aws/secrets-manager.d.ts +170 -0
- package/dist/aws/ses.d.ts +288 -0
- package/dist/aws/setup-phone.d.ts +0 -0
- package/dist/aws/setup-sms.d.ts +115 -0
- package/dist/aws/sms.d.ts +304 -0
- package/dist/aws/smtp-server.d.ts +61 -0
- package/dist/aws/sns.d.ts +117 -0
- package/dist/aws/sqs.d.ts +65 -0
- package/dist/aws/ssm.d.ts +179 -0
- package/dist/aws/sts.d.ts +15 -0
- package/dist/aws/support.d.ts +104 -0
- package/dist/aws/test-imap.d.ts +0 -0
- package/dist/aws/textract.d.ts +403 -0
- package/dist/aws/transcribe.d.ts +60 -0
- package/dist/aws/translate.d.ts +358 -0
- package/dist/aws/voice.d.ts +219 -0
- package/dist/bin/cli.js +1724 -0
- package/dist/config.d.ts +7 -0
- package/dist/deploy/index.d.ts +2 -0
- package/dist/deploy/static-site-external-dns.d.ts +51 -0
- package/dist/deploy/static-site.d.ts +71 -0
- package/dist/dns/cloudflare.d.ts +52 -0
- package/dist/dns/godaddy.d.ts +38 -0
- package/dist/dns/index.d.ts +45 -0
- package/dist/dns/porkbun.d.ts +18 -0
- package/dist/dns/route53-adapter.d.ts +38 -0
- package/{src/dns/types.ts → dist/dns/types.d.ts} +26 -63
- package/dist/dns/validator.d.ts +78 -0
- package/dist/generators/index.d.ts +1 -0
- package/dist/generators/infrastructure.d.ts +30 -0
- package/{src/index.ts → dist/index.d.ts} +70 -93
- package/dist/index.js +7881 -0
- package/dist/push/apns.d.ts +60 -0
- package/dist/push/fcm.d.ts +117 -0
- package/dist/push/index.d.ts +14 -0
- package/dist/security/pre-deploy-scanner.d.ts +69 -0
- package/dist/ssl/acme-client.d.ts +67 -0
- package/dist/ssl/index.d.ts +2 -0
- package/dist/ssl/letsencrypt.d.ts +48 -0
- package/dist/types.d.ts +1 -0
- package/dist/utils/cli.d.ts +123 -0
- package/dist/validation/index.d.ts +1 -0
- package/dist/validation/template.d.ts +23 -0
- package/package.json +8 -8
- package/bin/cli.ts +0 -133
- package/bin/commands/analytics.ts +0 -328
- package/bin/commands/api.ts +0 -379
- package/bin/commands/assets.ts +0 -221
- package/bin/commands/audit.ts +0 -501
- package/bin/commands/backup.ts +0 -682
- package/bin/commands/cache.ts +0 -294
- package/bin/commands/cdn.ts +0 -281
- package/bin/commands/config.ts +0 -202
- package/bin/commands/container.ts +0 -105
- package/bin/commands/cost.ts +0 -208
- package/bin/commands/database.ts +0 -401
- package/bin/commands/deploy.ts +0 -674
- package/bin/commands/domain.ts +0 -397
- package/bin/commands/email.ts +0 -423
- package/bin/commands/environment.ts +0 -285
- package/bin/commands/events.ts +0 -424
- package/bin/commands/firewall.ts +0 -145
- package/bin/commands/function.ts +0 -116
- package/bin/commands/generate.ts +0 -280
- package/bin/commands/git.ts +0 -139
- package/bin/commands/iam.ts +0 -464
- package/bin/commands/index.ts +0 -48
- package/bin/commands/init.ts +0 -120
- package/bin/commands/logs.ts +0 -148
- package/bin/commands/network.ts +0 -579
- package/bin/commands/notify.ts +0 -489
- package/bin/commands/queue.ts +0 -407
- package/bin/commands/scheduler.ts +0 -370
- package/bin/commands/secrets.ts +0 -54
- package/bin/commands/server.ts +0 -629
- package/bin/commands/shared.ts +0 -97
- package/bin/commands/ssl.ts +0 -138
- package/bin/commands/stack.ts +0 -325
- package/bin/commands/status.ts +0 -385
- package/bin/commands/storage.ts +0 -450
- package/bin/commands/team.ts +0 -96
- package/bin/commands/tunnel.ts +0 -489
- package/bin/commands/utils.ts +0 -202
- package/build.ts +0 -15
- package/cloud +0 -2
- package/src/aws/acm.ts +0 -768
- package/src/aws/application-autoscaling.ts +0 -845
- package/src/aws/bedrock.ts +0 -4074
- package/src/aws/client.ts +0 -878
- package/src/aws/cloudformation.ts +0 -896
- package/src/aws/cloudfront.ts +0 -1531
- package/src/aws/cloudwatch-logs.ts +0 -154
- package/src/aws/comprehend.ts +0 -839
- package/src/aws/connect.ts +0 -1056
- package/src/aws/deploy-imap.ts +0 -384
- package/src/aws/dynamodb.ts +0 -340
- package/src/aws/ec2.ts +0 -1385
- package/src/aws/ecr.ts +0 -621
- package/src/aws/ecs.ts +0 -615
- package/src/aws/elasticache.ts +0 -301
- package/src/aws/elbv2.ts +0 -942
- package/src/aws/email.ts +0 -928
- package/src/aws/eventbridge.ts +0 -248
- package/src/aws/iam.ts +0 -1689
- package/src/aws/imap-server.ts +0 -2100
- package/src/aws/lambda.ts +0 -786
- package/src/aws/opensearch.ts +0 -158
- package/src/aws/personalize.ts +0 -977
- package/src/aws/polly.ts +0 -559
- package/src/aws/rds.ts +0 -888
- package/src/aws/rekognition.ts +0 -846
- package/src/aws/route53-domains.ts +0 -359
- package/src/aws/route53.ts +0 -1046
- package/src/aws/s3.ts +0 -2318
- package/src/aws/scheduler.ts +0 -571
- package/src/aws/secrets-manager.ts +0 -769
- package/src/aws/ses.ts +0 -1081
- package/src/aws/setup-phone.ts +0 -104
- package/src/aws/setup-sms.ts +0 -580
- package/src/aws/sms.ts +0 -1735
- package/src/aws/smtp-server.ts +0 -531
- package/src/aws/sns.ts +0 -758
- package/src/aws/sqs.ts +0 -382
- package/src/aws/ssm.ts +0 -807
- package/src/aws/sts.ts +0 -92
- package/src/aws/support.ts +0 -391
- package/src/aws/test-imap.ts +0 -86
- package/src/aws/textract.ts +0 -780
- package/src/aws/transcribe.ts +0 -108
- package/src/aws/translate.ts +0 -641
- package/src/aws/voice.ts +0 -1379
- package/src/config.ts +0 -35
- package/src/deploy/index.ts +0 -7
- package/src/deploy/static-site-external-dns.ts +0 -906
- package/src/deploy/static-site.ts +0 -1125
- package/src/dns/godaddy.ts +0 -412
- package/src/dns/index.ts +0 -183
- package/src/dns/porkbun.ts +0 -362
- package/src/dns/route53-adapter.ts +0 -414
- package/src/dns/validator.ts +0 -369
- package/src/generators/index.ts +0 -5
- package/src/generators/infrastructure.ts +0 -1660
- package/src/push/apns.ts +0 -452
- package/src/push/fcm.ts +0 -506
- package/src/push/index.ts +0 -58
- package/src/ssl/acme-client.ts +0 -478
- package/src/ssl/index.ts +0 -7
- package/src/ssl/letsencrypt.ts +0 -747
- package/src/types.ts +0 -2
- package/src/utils/cli.ts +0 -398
- package/src/validation/index.ts +0 -5
- package/src/validation/template.ts +0 -405
- package/test/index.test.ts +0 -128
- package/tsconfig.json +0 -18
package/bin/commands/tunnel.ts
DELETED
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
import type { CLI } from '@stacksjs/clapp'
|
|
2
|
-
import * as cli from '../../src/utils/cli'
|
|
3
|
-
|
|
4
|
-
export function registerTunnelCommands(app: CLI): void {
|
|
5
|
-
app
|
|
6
|
-
.command('tunnel', 'Start a local tunnel to expose your local server')
|
|
7
|
-
.option('--port <number>', 'Local port to expose', { default: '3000' })
|
|
8
|
-
.option('--subdomain <name>', 'Custom subdomain (if available)')
|
|
9
|
-
.option('--host <hostname>', 'Local hostname', { default: 'localhost' })
|
|
10
|
-
.option('--server <url>', 'Tunnel server URL', { default: 'localtunnel.dev' })
|
|
11
|
-
.option('--verbose', 'Enable verbose logging')
|
|
12
|
-
.action(async (options: { port: string; subdomain?: string; host: string; server: string; verbose?: boolean }) => {
|
|
13
|
-
cli.header('Local Tunnel')
|
|
14
|
-
|
|
15
|
-
const port = Number.parseInt(options.port)
|
|
16
|
-
|
|
17
|
-
cli.info(`Local server: ${options.host}:${port}`)
|
|
18
|
-
cli.info(`Tunnel server: ${options.server}`)
|
|
19
|
-
|
|
20
|
-
if (options.subdomain) {
|
|
21
|
-
cli.info(`Requested subdomain: ${options.subdomain}`)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const spinner = new cli.Spinner('Connecting to tunnel server...')
|
|
25
|
-
spinner.start()
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
// Try to dynamically import localtunnels
|
|
29
|
-
let TunnelClient: any
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
// @ts-expect-error -- localtunnels is an optional dependency
|
|
33
|
-
const localtunnels = await import('localtunnels')
|
|
34
|
-
TunnelClient = localtunnels.TunnelClient
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
spinner.fail('localtunnels package not found')
|
|
38
|
-
cli.info('\nTo use tunnels, install the localtunnels package:')
|
|
39
|
-
cli.info(' bun add localtunnels')
|
|
40
|
-
cli.info('\nOr use the standalone CLI:')
|
|
41
|
-
cli.info(' bunx localtunnels --port 3000')
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const serverHost = options.server.replace(/^(wss?|https?):\/\//, '')
|
|
46
|
-
const secure = options.server.startsWith('wss://') || options.server.startsWith('https://') || serverHost === 'localtunnel.dev'
|
|
47
|
-
|
|
48
|
-
const client = new TunnelClient({
|
|
49
|
-
localPort: port,
|
|
50
|
-
localHost: options.host,
|
|
51
|
-
subdomain: options.subdomain,
|
|
52
|
-
host: serverHost,
|
|
53
|
-
port: secure ? 443 : 80,
|
|
54
|
-
secure,
|
|
55
|
-
verbose: options.verbose,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
client.on('connected', (info: { url: string; subdomain: string }) => {
|
|
59
|
-
spinner.succeed('Connected!')
|
|
60
|
-
cli.success(`\nYour tunnel URL: ${info.url}`)
|
|
61
|
-
cli.info(`Subdomain: ${info.subdomain}`)
|
|
62
|
-
cli.info(`\nForwarding: ${info.url} -> http://${options.host}:${port}`)
|
|
63
|
-
cli.info('\nPress Ctrl+C to stop the tunnel')
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
client.on('request', (req: { method: string; url: string }) => {
|
|
67
|
-
if (options.verbose) {
|
|
68
|
-
cli.info(`→ ${req.method} ${req.url}`)
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
client.on('response', (res: { status: number; size: number; duration?: number }) => {
|
|
73
|
-
if (options.verbose) {
|
|
74
|
-
cli.info(`← ${res.status} (${res.size} bytes${res.duration ? `, ${res.duration}ms` : ''})`)
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
client.on('reconnecting', (info: { attempt: number; maxAttempts: number }) => {
|
|
79
|
-
cli.info(`Reconnecting... (attempt ${info.attempt}/${info.maxAttempts})`)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
client.on('error', (error: Error) => {
|
|
83
|
-
cli.error(`Tunnel error: ${error.message}`)
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
client.on('close', () => {
|
|
87
|
-
cli.info('\nTunnel closed')
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
// Handle process signals for graceful shutdown
|
|
91
|
-
const cleanup = () => {
|
|
92
|
-
cli.info('\nShutting down tunnel...')
|
|
93
|
-
client.disconnect()
|
|
94
|
-
process.exit(0)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
process.on('SIGINT', cleanup)
|
|
98
|
-
process.on('SIGTERM', cleanup)
|
|
99
|
-
|
|
100
|
-
await client.connect()
|
|
101
|
-
|
|
102
|
-
// Keep the process running
|
|
103
|
-
await new Promise(() => {})
|
|
104
|
-
}
|
|
105
|
-
catch (error: any) {
|
|
106
|
-
spinner.fail('Failed to connect')
|
|
107
|
-
cli.error(`Error: ${error.message}`)
|
|
108
|
-
process.exit(1)
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
app
|
|
113
|
-
.command('tunnel:status', 'Check tunnel server status')
|
|
114
|
-
.option('--server <url>', 'Tunnel server URL', { default: 'https://localtunnel.dev' })
|
|
115
|
-
.action(async (options: { server: string }) => {
|
|
116
|
-
cli.header('Tunnel Server Status')
|
|
117
|
-
|
|
118
|
-
const spinner = new cli.Spinner('Checking server status...')
|
|
119
|
-
spinner.start()
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const serverUrl = options.server.startsWith('http')
|
|
123
|
-
? options.server
|
|
124
|
-
: `https://${options.server}`
|
|
125
|
-
|
|
126
|
-
const response = await fetch(`${serverUrl}/status`, {
|
|
127
|
-
method: 'GET',
|
|
128
|
-
headers: { Accept: 'application/json' },
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
if (response.ok) {
|
|
132
|
-
const status = await response.json() as Record<string, any>
|
|
133
|
-
spinner.succeed('Server is online')
|
|
134
|
-
|
|
135
|
-
cli.info(`\nServer: ${options.server}`)
|
|
136
|
-
cli.info(`Status: ${status.status || 'operational'}`)
|
|
137
|
-
|
|
138
|
-
if (status.version) {
|
|
139
|
-
cli.info(`Version: ${status.version}`)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (status.connections !== undefined) {
|
|
143
|
-
cli.info(`Active connections: ${status.connections}`)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (status.uptime) {
|
|
147
|
-
cli.info(`Uptime: ${status.uptime}`)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (status.activeSubdomains?.length) {
|
|
151
|
-
cli.info(`Active subdomains: ${status.activeSubdomains.join(', ')}`)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
spinner.fail(`Server returned status ${response.status}`)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
catch (error: any) {
|
|
159
|
-
spinner.fail('Failed to check server status')
|
|
160
|
-
cli.error(`Error: ${error.message}`)
|
|
161
|
-
cli.info('\nThe tunnel server may be offline or unreachable.')
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
app
|
|
166
|
-
.command('tunnel:info', 'Show tunnel configuration and setup info')
|
|
167
|
-
.action(async () => {
|
|
168
|
-
cli.header('Local Tunnel Information')
|
|
169
|
-
|
|
170
|
-
cli.info('ts-cloud uses localtunnels for secure tunnel connections.')
|
|
171
|
-
cli.info('')
|
|
172
|
-
cli.info('Default server: localtunnel.dev')
|
|
173
|
-
cli.info('')
|
|
174
|
-
cli.info('Usage:')
|
|
175
|
-
cli.info(' cloud tunnel --port 3000 # Expose port 3000')
|
|
176
|
-
cli.info(' cloud tunnel --port 8080 --subdomain myapp')
|
|
177
|
-
cli.info('')
|
|
178
|
-
cli.info('Features:')
|
|
179
|
-
cli.info(' - Secure WebSocket-based tunnels')
|
|
180
|
-
cli.info(' - Custom subdomains (when available)')
|
|
181
|
-
cli.info(' - Automatic reconnection')
|
|
182
|
-
cli.info(' - Request logging')
|
|
183
|
-
cli.info(' - Binary data support')
|
|
184
|
-
cli.info('')
|
|
185
|
-
cli.info('Self-hosted tunnel server:')
|
|
186
|
-
cli.info(' You can run your own tunnel server using localtunnels.')
|
|
187
|
-
cli.info(' See: https://github.com/stacksjs/localtunnels')
|
|
188
|
-
cli.info('')
|
|
189
|
-
cli.info('Environment variables:')
|
|
190
|
-
cli.info(' TUNNEL_SERVER - Custom tunnel server URL')
|
|
191
|
-
cli.info(' TUNNEL_SUBDOMAIN - Default subdomain to request')
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
app
|
|
195
|
-
.command('tunnel:deploy', 'Deploy tunnel infrastructure to AWS')
|
|
196
|
-
.option('--region <region>', 'AWS region', { default: 'us-east-1' })
|
|
197
|
-
.option('--prefix <prefix>', 'Resource name prefix', { default: 'localtunnel' })
|
|
198
|
-
.option('--verbose', 'Enable verbose logging')
|
|
199
|
-
.action(async (options: { region: string; prefix: string; verbose?: boolean }) => {
|
|
200
|
-
cli.header('Deploy Tunnel Infrastructure')
|
|
201
|
-
|
|
202
|
-
cli.info(`Region: ${options.region}`)
|
|
203
|
-
cli.info(`Prefix: ${options.prefix}`)
|
|
204
|
-
cli.info('')
|
|
205
|
-
|
|
206
|
-
cli.info('This will deploy:')
|
|
207
|
-
cli.info(' - DynamoDB tables for connection tracking')
|
|
208
|
-
cli.info(' - Lambda functions for handling requests')
|
|
209
|
-
cli.info(' - Lambda Function URLs for public access')
|
|
210
|
-
cli.info('')
|
|
211
|
-
|
|
212
|
-
const confirmed = await cli.confirm('Deploy tunnel infrastructure?', false)
|
|
213
|
-
if (!confirmed) {
|
|
214
|
-
cli.info('Operation cancelled')
|
|
215
|
-
return
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const spinner = new cli.Spinner('Deploying infrastructure...')
|
|
219
|
-
spinner.start()
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
// Try to import the deploy function from localtunnels
|
|
223
|
-
let deployTunnelInfrastructure: any
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
// @ts-expect-error -- localtunnels is an optional dependency
|
|
227
|
-
const cloudModule = await import('localtunnels/cloud')
|
|
228
|
-
deployTunnelInfrastructure = cloudModule.deployTunnelInfrastructure
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
spinner.fail('localtunnels package not found')
|
|
232
|
-
cli.info('\nTo deploy tunnel infrastructure, install localtunnels:')
|
|
233
|
-
cli.info(' bun add localtunnels')
|
|
234
|
-
cli.info('\nOr deploy using the localtunnels CLI directly:')
|
|
235
|
-
cli.info(' bunx localtunnels deploy --region us-east-1')
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const result = await deployTunnelInfrastructure({
|
|
240
|
-
region: options.region,
|
|
241
|
-
prefix: options.prefix,
|
|
242
|
-
verbose: options.verbose,
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
spinner.succeed('Deployment complete!')
|
|
246
|
-
|
|
247
|
-
cli.info('')
|
|
248
|
-
cli.info('Resources created:')
|
|
249
|
-
cli.info(` DynamoDB Tables:`)
|
|
250
|
-
cli.info(` - ${result.connectionsTable}`)
|
|
251
|
-
cli.info(` - ${result.responsesTable}`)
|
|
252
|
-
cli.info('')
|
|
253
|
-
cli.info(` Lambda Functions:`)
|
|
254
|
-
cli.info(` - ${result.functions.http}`)
|
|
255
|
-
cli.info(` - ${result.functions.message}`)
|
|
256
|
-
cli.info('')
|
|
257
|
-
|
|
258
|
-
if (result.httpUrl || result.wsUrl) {
|
|
259
|
-
cli.info('Endpoints:')
|
|
260
|
-
if (result.httpUrl) {
|
|
261
|
-
cli.info(` HTTP URL: ${result.httpUrl}`)
|
|
262
|
-
}
|
|
263
|
-
if (result.wsUrl) {
|
|
264
|
-
cli.info(` WebSocket URL: ${result.wsUrl}`)
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
catch (error: any) {
|
|
269
|
-
spinner.fail('Deployment failed')
|
|
270
|
-
cli.error(`Error: ${error.message}`)
|
|
271
|
-
if (options.verbose) {
|
|
272
|
-
console.error(error.stack)
|
|
273
|
-
}
|
|
274
|
-
process.exit(1)
|
|
275
|
-
}
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
app
|
|
279
|
-
.command('tunnel:destroy', 'Destroy tunnel infrastructure from AWS')
|
|
280
|
-
.option('--region <region>', 'AWS region', { default: 'us-east-1' })
|
|
281
|
-
.option('--prefix <prefix>', 'Resource name prefix', { default: 'localtunnel' })
|
|
282
|
-
.option('--verbose', 'Enable verbose logging')
|
|
283
|
-
.action(async (options: { region: string; prefix: string; verbose?: boolean }) => {
|
|
284
|
-
cli.header('Destroy Tunnel Infrastructure')
|
|
285
|
-
|
|
286
|
-
cli.info(`Region: ${options.region}`)
|
|
287
|
-
cli.info(`Prefix: ${options.prefix}`)
|
|
288
|
-
cli.info('')
|
|
289
|
-
|
|
290
|
-
cli.warn('This will permanently delete:')
|
|
291
|
-
cli.warn(' - All DynamoDB tables and data')
|
|
292
|
-
cli.warn(' - All Lambda functions')
|
|
293
|
-
cli.warn(' - All IAM roles and policies')
|
|
294
|
-
cli.info('')
|
|
295
|
-
|
|
296
|
-
const confirmed = await cli.confirm('Are you sure you want to destroy this infrastructure?', false)
|
|
297
|
-
if (!confirmed) {
|
|
298
|
-
cli.info('Operation cancelled')
|
|
299
|
-
return
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const spinner = new cli.Spinner('Destroying infrastructure...')
|
|
303
|
-
spinner.start()
|
|
304
|
-
|
|
305
|
-
try {
|
|
306
|
-
let destroyTunnelInfrastructure: any
|
|
307
|
-
|
|
308
|
-
try {
|
|
309
|
-
// @ts-expect-error -- localtunnels is an optional dependency
|
|
310
|
-
const cloudModule = await import('localtunnels/cloud')
|
|
311
|
-
destroyTunnelInfrastructure = cloudModule.destroyTunnelInfrastructure
|
|
312
|
-
}
|
|
313
|
-
catch {
|
|
314
|
-
spinner.fail('localtunnels package not found')
|
|
315
|
-
cli.info('\nTo destroy tunnel infrastructure, install localtunnels:')
|
|
316
|
-
cli.info(' bun add localtunnels')
|
|
317
|
-
cli.info('\nOr destroy using the localtunnels CLI directly:')
|
|
318
|
-
cli.info(' bunx localtunnels destroy --region us-east-1')
|
|
319
|
-
return
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
await destroyTunnelInfrastructure({
|
|
323
|
-
region: options.region,
|
|
324
|
-
prefix: options.prefix,
|
|
325
|
-
verbose: options.verbose,
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
spinner.succeed('Infrastructure destroyed!')
|
|
329
|
-
}
|
|
330
|
-
catch (error: any) {
|
|
331
|
-
spinner.fail('Destruction failed')
|
|
332
|
-
cli.error(`Error: ${error.message}`)
|
|
333
|
-
if (options.verbose) {
|
|
334
|
-
console.error(error.stack)
|
|
335
|
-
}
|
|
336
|
-
process.exit(1)
|
|
337
|
-
}
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
app
|
|
341
|
-
.command('tunnel:logs', 'View tunnel server logs')
|
|
342
|
-
.option('--region <region>', 'AWS region', { default: 'us-east-1' })
|
|
343
|
-
.option('--prefix <prefix>', 'Resource name prefix', { default: 'localtunnel' })
|
|
344
|
-
.option('--tail', 'Tail the logs')
|
|
345
|
-
.action(async (options: { region: string; prefix: string; tail?: boolean }) => {
|
|
346
|
-
cli.header('Tunnel Server Logs')
|
|
347
|
-
|
|
348
|
-
cli.info('Tunnel server log groups:')
|
|
349
|
-
cli.info(` - /aws/lambda/${options.prefix}-connect`)
|
|
350
|
-
cli.info(` - /aws/lambda/${options.prefix}-disconnect`)
|
|
351
|
-
cli.info(` - /aws/lambda/${options.prefix}-message`)
|
|
352
|
-
cli.info(` - /aws/lambda/${options.prefix}-http`)
|
|
353
|
-
cli.info('')
|
|
354
|
-
|
|
355
|
-
cli.info('To view logs:')
|
|
356
|
-
cli.info(` aws logs tail /aws/lambda/${options.prefix}-http --region ${options.region}${options.tail ? ' --follow' : ''}`)
|
|
357
|
-
cli.info('')
|
|
358
|
-
|
|
359
|
-
cli.info('Or use CloudWatch Insights:')
|
|
360
|
-
cli.info(` 1. Go to CloudWatch > Logs Insights`)
|
|
361
|
-
cli.info(` 2. Select the tunnel log groups`)
|
|
362
|
-
cli.info(` 3. Run queries like:`)
|
|
363
|
-
cli.info(` fields @timestamp, @message | filter @message like /error/i`)
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
app
|
|
367
|
-
.command('tunnel:test <url>', 'Test a tunnel connection')
|
|
368
|
-
.action(async (url: string) => {
|
|
369
|
-
cli.header('Test Tunnel Connection')
|
|
370
|
-
|
|
371
|
-
const spinner = new cli.Spinner(`Testing ${url}...`)
|
|
372
|
-
spinner.start()
|
|
373
|
-
|
|
374
|
-
try {
|
|
375
|
-
const startTime = Date.now()
|
|
376
|
-
const response = await fetch(url, {
|
|
377
|
-
method: 'GET',
|
|
378
|
-
headers: {
|
|
379
|
-
'User-Agent': 'ts-cloud-tunnel-test',
|
|
380
|
-
},
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
const elapsed = Date.now() - startTime
|
|
384
|
-
|
|
385
|
-
spinner.succeed(`Connected in ${elapsed}ms`)
|
|
386
|
-
|
|
387
|
-
cli.info(`\nStatus: ${response.status} ${response.statusText}`)
|
|
388
|
-
cli.info('\nResponse headers:')
|
|
389
|
-
response.headers.forEach((value, key) => {
|
|
390
|
-
cli.info(` ${key}: ${value}`)
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
const body = await response.text()
|
|
394
|
-
if (body.length > 0) {
|
|
395
|
-
cli.info('\nResponse body (first 500 chars):')
|
|
396
|
-
console.log(body.substring(0, 500))
|
|
397
|
-
if (body.length > 500) {
|
|
398
|
-
cli.info(`... (${body.length - 500} more characters)`)
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
catch (error: any) {
|
|
403
|
-
spinner.fail('Connection failed')
|
|
404
|
-
cli.error(`Error: ${error.message}`)
|
|
405
|
-
}
|
|
406
|
-
})
|
|
407
|
-
|
|
408
|
-
app
|
|
409
|
-
.command('tunnel:server', 'Start a self-hosted tunnel server')
|
|
410
|
-
.option('--port <number>', 'Port to listen on', { default: '3000' })
|
|
411
|
-
.option('--host <hostname>', 'Host to bind to', { default: '0.0.0.0' })
|
|
412
|
-
.option('--domain <domain>', 'Domain for tunnel URLs', { default: 'localhost' })
|
|
413
|
-
.option('--verbose', 'Enable verbose logging')
|
|
414
|
-
.action(async (options: { port: string; host: string; domain: string; verbose?: boolean }) => {
|
|
415
|
-
cli.header('Local Tunnel Server')
|
|
416
|
-
|
|
417
|
-
const port = Number.parseInt(options.port)
|
|
418
|
-
|
|
419
|
-
cli.info(`Listening on: ${options.host}:${port}`)
|
|
420
|
-
cli.info(`Domain: ${options.domain}`)
|
|
421
|
-
cli.info('')
|
|
422
|
-
|
|
423
|
-
const spinner = new cli.Spinner('Starting tunnel server...')
|
|
424
|
-
spinner.start()
|
|
425
|
-
|
|
426
|
-
try {
|
|
427
|
-
let TunnelServer: any
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
// @ts-expect-error -- localtunnels is an optional dependency
|
|
431
|
-
const localtunnels = await import('localtunnels')
|
|
432
|
-
TunnelServer = localtunnels.TunnelServer
|
|
433
|
-
}
|
|
434
|
-
catch {
|
|
435
|
-
spinner.fail('localtunnels package not found')
|
|
436
|
-
cli.info('\nTo run a tunnel server, install localtunnels:')
|
|
437
|
-
cli.info(' bun add localtunnels')
|
|
438
|
-
cli.info('\nOr use the standalone CLI:')
|
|
439
|
-
cli.info(' bunx localtunnels server --port 3000')
|
|
440
|
-
return
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const server = new TunnelServer({
|
|
444
|
-
port,
|
|
445
|
-
host: options.host,
|
|
446
|
-
verbose: options.verbose,
|
|
447
|
-
})
|
|
448
|
-
|
|
449
|
-
server.on('connection', (info: { subdomain: string; totalConnections: number }) => {
|
|
450
|
-
cli.info(`+ Client connected: ${info.subdomain} (total: ${info.totalConnections})`)
|
|
451
|
-
})
|
|
452
|
-
|
|
453
|
-
server.on('disconnection', (info: { subdomain: string }) => {
|
|
454
|
-
cli.info(`- Client disconnected: ${info.subdomain}`)
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
// Handle process signals for graceful shutdown
|
|
458
|
-
const cleanup = () => {
|
|
459
|
-
cli.info('\nShutting down server...')
|
|
460
|
-
server.stop()
|
|
461
|
-
process.exit(0)
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
process.on('SIGINT', cleanup)
|
|
465
|
-
process.on('SIGTERM', cleanup)
|
|
466
|
-
|
|
467
|
-
await server.start()
|
|
468
|
-
|
|
469
|
-
spinner.succeed('Server running!')
|
|
470
|
-
|
|
471
|
-
cli.info('')
|
|
472
|
-
cli.info(`WebSocket URL: ws://${options.host === '0.0.0.0' ? 'localhost' : options.host}:${port}`)
|
|
473
|
-
cli.info(`HTTP URL: http://${options.host === '0.0.0.0' ? 'localhost' : options.host}:${port}`)
|
|
474
|
-
cli.info('')
|
|
475
|
-
cli.info('Clients can connect with:')
|
|
476
|
-
cli.info(` cloud tunnel --port 3000 --server ${options.host === '0.0.0.0' ? 'localhost' : options.host}:${port}`)
|
|
477
|
-
cli.info('')
|
|
478
|
-
cli.info('Press Ctrl+C to stop the server')
|
|
479
|
-
|
|
480
|
-
// Keep the process running
|
|
481
|
-
await new Promise(() => {})
|
|
482
|
-
}
|
|
483
|
-
catch (error: any) {
|
|
484
|
-
spinner.fail('Failed to start server')
|
|
485
|
-
cli.error(`Error: ${error.message}`)
|
|
486
|
-
process.exit(1)
|
|
487
|
-
}
|
|
488
|
-
})
|
|
489
|
-
}
|
package/bin/commands/utils.ts
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import type { CLI } from '@stacksjs/clapp'
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
3
|
-
import * as cli from '../../src/utils/cli'
|
|
4
|
-
import { CloudFormationClient } from '../../src/aws/cloudformation'
|
|
5
|
-
import { ACMClient } from '../../src/aws/acm'
|
|
6
|
-
|
|
7
|
-
export function registerUtilsCommands(app: CLI, version: string): void {
|
|
8
|
-
app
|
|
9
|
-
.command('upgrade', 'Upgrade CLI to latest version')
|
|
10
|
-
.action(async () => {
|
|
11
|
-
cli.header('Upgrading ts-cloud CLI')
|
|
12
|
-
|
|
13
|
-
const spinner = new cli.Spinner('Checking for updates...')
|
|
14
|
-
spinner.start()
|
|
15
|
-
|
|
16
|
-
// TODO: Check npm/GitHub for latest version
|
|
17
|
-
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
18
|
-
|
|
19
|
-
spinner.stop()
|
|
20
|
-
|
|
21
|
-
cli.info('\nCurrent version: 0.1.0')
|
|
22
|
-
cli.info('Latest version: 0.2.0')
|
|
23
|
-
|
|
24
|
-
const confirm = await cli.confirm('\nUpgrade to latest version?', true)
|
|
25
|
-
if (!confirm) {
|
|
26
|
-
cli.info('Operation cancelled')
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const upgradeSpinner = new cli.Spinner('Upgrading...')
|
|
31
|
-
upgradeSpinner.start()
|
|
32
|
-
|
|
33
|
-
// TODO: Run npm/bun upgrade command
|
|
34
|
-
await new Promise(resolve => setTimeout(resolve, 3000))
|
|
35
|
-
|
|
36
|
-
upgradeSpinner.succeed('Upgrade completed successfully')
|
|
37
|
-
|
|
38
|
-
cli.success('\nts-cloud CLI upgraded to v0.2.0!')
|
|
39
|
-
|
|
40
|
-
cli.info('\nWhat\'s new in v0.2.0:')
|
|
41
|
-
cli.info(' - New cost optimization commands')
|
|
42
|
-
cli.info(' - Improved team collaboration features')
|
|
43
|
-
cli.info(' - Better error messages')
|
|
44
|
-
cli.info(' - Performance improvements')
|
|
45
|
-
|
|
46
|
-
cli.info('\nTip: Run `cloud --help` to see all available commands')
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
app
|
|
50
|
-
.command('doctor', 'Check system requirements and AWS credentials')
|
|
51
|
-
.action(async () => {
|
|
52
|
-
cli.header('System Diagnostics')
|
|
53
|
-
|
|
54
|
-
// Check Bun
|
|
55
|
-
cli.step('Checking Bun...')
|
|
56
|
-
cli.success(`Bun ${process.versions.bun}`)
|
|
57
|
-
|
|
58
|
-
// Check AWS CLI
|
|
59
|
-
cli.step('Checking AWS CLI...')
|
|
60
|
-
const hasAwsCli = await cli.checkAwsCli()
|
|
61
|
-
if (hasAwsCli) {
|
|
62
|
-
cli.success('AWS CLI is installed')
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
cli.error('AWS CLI is not installed')
|
|
66
|
-
cli.info('Install: https://aws.amazon.com/cli/')
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Check AWS credentials
|
|
70
|
-
cli.step('Checking AWS credentials...')
|
|
71
|
-
const hasCredentials = await cli.checkAwsCredentials()
|
|
72
|
-
if (hasCredentials) {
|
|
73
|
-
cli.success('AWS credentials are configured')
|
|
74
|
-
const accountId = await cli.getAwsAccountId()
|
|
75
|
-
if (accountId) {
|
|
76
|
-
cli.info(`Account ID: ${accountId}`)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
cli.error('AWS credentials are not configured')
|
|
81
|
-
cli.info('Run: aws configure')
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Check CloudFront access (list)
|
|
85
|
-
cli.step('Checking CloudFront list access...')
|
|
86
|
-
let cloudfrontListOk = false
|
|
87
|
-
try {
|
|
88
|
-
const { CloudFrontClient } = await import('../../src/aws/cloudfront')
|
|
89
|
-
const cloudfront = new CloudFrontClient()
|
|
90
|
-
await cloudfront.listDistributions()
|
|
91
|
-
cli.success('CloudFront list access is enabled')
|
|
92
|
-
cloudfrontListOk = true
|
|
93
|
-
}
|
|
94
|
-
catch (error: any) {
|
|
95
|
-
cli.warn(`CloudFront list check failed: ${error.message}`)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check CloudFront create access (via CloudFormation - the common issue point)
|
|
99
|
-
if (cloudfrontListOk) {
|
|
100
|
-
cli.step('Checking CloudFront create access...')
|
|
101
|
-
try {
|
|
102
|
-
// Try to validate a CloudFormation template with CloudFront resource
|
|
103
|
-
const cfn = new CloudFormationClient('us-east-1')
|
|
104
|
-
const testTemplate = JSON.stringify({
|
|
105
|
-
AWSTemplateFormatVersion: '2010-09-09',
|
|
106
|
-
Description: 'Test CloudFront access',
|
|
107
|
-
Resources: {
|
|
108
|
-
TestOAC: {
|
|
109
|
-
Type: 'AWS::CloudFront::OriginAccessControl',
|
|
110
|
-
Properties: {
|
|
111
|
-
OriginAccessControlConfig: {
|
|
112
|
-
Name: 'test-oac-validation',
|
|
113
|
-
OriginAccessControlOriginType: 's3',
|
|
114
|
-
SigningBehavior: 'always',
|
|
115
|
-
SigningProtocol: 'sigv4',
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
})
|
|
121
|
-
await cfn.validateTemplate(testTemplate)
|
|
122
|
-
cli.success('CloudFront create access appears enabled')
|
|
123
|
-
}
|
|
124
|
-
catch (error: any) {
|
|
125
|
-
if (error.message?.includes('403') || error.message?.includes('AccessDenied') || error.message?.includes('must be verified')) {
|
|
126
|
-
cli.error('CloudFront create access denied - account verification required')
|
|
127
|
-
cli.info('')
|
|
128
|
-
cli.info('Your AWS account needs to be verified for CloudFront.')
|
|
129
|
-
cli.info('This is required before you can create CloudFront distributions.')
|
|
130
|
-
cli.info('')
|
|
131
|
-
cli.info('To verify your account:')
|
|
132
|
-
cli.info(' 1. Go to: https://console.aws.amazon.com/support/home#/')
|
|
133
|
-
cli.info(' 2. Create a support case')
|
|
134
|
-
cli.info(' 3. Select: Service limit increase > CloudFront')
|
|
135
|
-
cli.info(' 4. Request: "Please verify my account for CloudFront access"')
|
|
136
|
-
cli.info('')
|
|
137
|
-
cli.info('Verification usually takes 1-2 business days.')
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
// Template validation might fail for other reasons, that's ok
|
|
141
|
-
cli.success('CloudFront create access appears enabled (validation passed)')
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Check S3 access
|
|
147
|
-
cli.step('Checking S3 access...')
|
|
148
|
-
try {
|
|
149
|
-
const { S3Client } = await import('../../src/aws/s3')
|
|
150
|
-
const s3 = new S3Client('us-east-1')
|
|
151
|
-
await s3.listBuckets()
|
|
152
|
-
cli.success('S3 access is enabled')
|
|
153
|
-
}
|
|
154
|
-
catch (error: any) {
|
|
155
|
-
cli.warn(`S3 check failed: ${error.message}`)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Check ACM access
|
|
159
|
-
cli.step('Checking ACM (SSL certificates) access...')
|
|
160
|
-
try {
|
|
161
|
-
const acm = new ACMClient('us-east-1')
|
|
162
|
-
await acm.listCertificates()
|
|
163
|
-
cli.success('ACM access is enabled')
|
|
164
|
-
}
|
|
165
|
-
catch (error: any) {
|
|
166
|
-
cli.warn(`ACM check failed: ${error.message}`)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Check for cloud.config.ts
|
|
170
|
-
cli.step('Checking configuration...')
|
|
171
|
-
if (existsSync('cloud.config.ts')) {
|
|
172
|
-
cli.success('cloud.config.ts found')
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
cli.warn('cloud.config.ts not found')
|
|
176
|
-
cli.info('Run: cloud init')
|
|
177
|
-
}
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
app
|
|
181
|
-
.command('regions', 'List available AWS regions')
|
|
182
|
-
.action(async () => {
|
|
183
|
-
cli.header('AWS Regions')
|
|
184
|
-
|
|
185
|
-
const spinner = new cli.Spinner('Fetching regions...')
|
|
186
|
-
spinner.start()
|
|
187
|
-
|
|
188
|
-
const regions = await cli.getAwsRegions()
|
|
189
|
-
spinner.stop()
|
|
190
|
-
|
|
191
|
-
regions.forEach((region) => {
|
|
192
|
-
console.log(` ${region}`)
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
app
|
|
197
|
-
.command('version', 'Show the version of the CLI')
|
|
198
|
-
.alias('v')
|
|
199
|
-
.action(() => {
|
|
200
|
-
console.log(`ts-cloud v${version}`)
|
|
201
|
-
})
|
|
202
|
-
}
|