@@ -2,6 +2,21 @@ import type { CLI } from '@stacksjs/types'
22import { readFileSync , existsSync } from 'node:fs'
33import { email as emailConfig } from '@stacksjs/config'
44
5+ const TIMEOUT_MS = 30000 // 30 second timeout for AWS operations
6+
7+ // Helper to run async operations with timeout
8+ async function withTimeout < T > ( promise : Promise < T > , ms : number = TIMEOUT_MS ) : Promise < T > {
9+ let timeoutId : ReturnType < typeof setTimeout >
10+ const timeoutPromise = new Promise < never > ( ( _ , reject ) => {
11+ timeoutId = setTimeout ( ( ) => reject ( new Error ( `Operation timed out after ${ ms } ms` ) ) , ms )
12+ } )
13+ try {
14+ return await Promise . race ( [ promise , timeoutPromise ] )
15+ } finally {
16+ clearTimeout ( timeoutId ! )
17+ }
18+ }
19+
520// Load AWS credentials from .env.production
621function loadAwsCredentials ( ) : void {
722 const envPath = '.env.production'
@@ -52,7 +67,7 @@ export function email(buddy: CLI): void {
5267 const emailDomain = emailConfig ?. from ?. address ?. split ( '@' ) [ 1 ] || 'stacksjs.com'
5368 console . log ( `Domain: ${ emailDomain } ` )
5469
55- const identity = await ses . getEmailIdentity ( emailDomain )
70+ const identity = await withTimeout ( ses . getEmailIdentity ( emailDomain ) )
5671
5772 if ( identity ) {
5873 console . log ( `\n✅ Domain Status: ${ identity . VerificationStatus || 'Unknown' } ` )
@@ -75,6 +90,7 @@ export function email(buddy: CLI): void {
7590 catch ( error : any ) {
7691 console . error ( '\n❌ Error checking verification:' , error . message )
7792 }
93+ process . exit ( 0 )
7894 } )
7995
8096 buddy
@@ -91,7 +107,7 @@ export function email(buddy: CLI): void {
91107 const emailDomain = emailConfig ?. from ?. address ?. split ( '@' ) [ 1 ] || 'stacksjs.com'
92108 const from = `noreply@${ emailDomain } `
93109
94- const result = await ses . sendEmail ( {
110+ const result = await withTimeout ( ses . sendEmail ( {
95111 FromEmailAddress : from ,
96112 Destination : {
97113 ToAddresses : [ to ] ,
@@ -126,7 +142,7 @@ export function email(buddy: CLI): void {
126142 } ,
127143 } ,
128144 } ,
129- } )
145+ } ) )
130146
131147 console . log ( '✅ Test email sent successfully!' )
132148 console . log ( ` Message ID: ${ result . MessageId } ` )
@@ -138,6 +154,7 @@ export function email(buddy: CLI): void {
138154 console . log ( ' Run `buddy email:verify` to check status.' )
139155 }
140156 }
157+ process . exit ( 0 )
141158 } )
142159
143160 buddy
@@ -164,6 +181,7 @@ export function email(buddy: CLI): void {
164181 }
165182 }
166183 console . log ( '' )
184+ process . exit ( 0 )
167185 } )
168186
169187 buddy
@@ -184,12 +202,12 @@ export function email(buddy: CLI): void {
184202 console . log ( `Showing last ${ options . lines } events...\n` )
185203
186204 // First get the latest log stream
187- const streams = await logs . describeLogStreams ( {
205+ const streams = await withTimeout ( logs . describeLogStreams ( {
188206 logGroupName,
189207 orderBy : 'LastEventTime' ,
190208 descending : true ,
191209 limit : 1 ,
192- } )
210+ } ) )
193211
194212 if ( ! streams . logStreams || streams . logStreams . length === 0 ) {
195213 console . log ( 'No log streams found.' )
@@ -203,11 +221,11 @@ export function email(buddy: CLI): void {
203221 return
204222 }
205223
206- const events = await logs . getLogEvents ( {
224+ const events = await withTimeout ( logs . getLogEvents ( {
207225 logGroupName,
208226 logStreamName,
209227 limit : parseInt ( options . lines || '20' , 10 ) ,
210- } )
228+ } ) )
211229
212230 if ( events . events && events . events . length > 0 ) {
213231 for ( const event of events . events ) {
@@ -221,13 +239,14 @@ export function email(buddy: CLI): void {
221239 }
222240 }
223241 catch ( error : any ) {
224- if ( error . message . includes ( 'ResourceNotFoundException' ) ) {
225- console . log ( 'Log group not found. No emails have been processed yet.' )
242+ if ( error . message . includes ( 'ResourceNotFoundException' ) || error . message . includes ( 'timed out' ) ) {
243+ console . log ( 'Log group not found or not accessible . No emails have been processed yet.' )
226244 }
227245 else {
228246 console . error ( 'Error fetching logs:' , error . message )
229247 }
230248 }
249+ process . exit ( 0 )
231250 } )
232251
233252 buddy
@@ -243,7 +262,7 @@ export function email(buddy: CLI): void {
243262 const appName = ( process . env . APP_NAME || 'stacks' ) . toLowerCase ( ) . replace ( / [ ^ a - z 0 - 9 - ] / g, '-' )
244263 const stackName = `${ appName } -cloud`
245264
246- const result = await cf . listStackResources ( stackName )
265+ const result = await withTimeout ( cf . listStackResources ( stackName ) )
247266 const emailResources = result . StackResourceSummaries ?. filter (
248267 ( r : any ) => r . LogicalResourceId . includes ( 'Email' ) || r . LogicalResourceId . includes ( 'Inbound' ) || r . LogicalResourceId . includes ( 'Outbound' )
249268 ) || [ ]
@@ -262,7 +281,7 @@ export function email(buddy: CLI): void {
262281 }
263282
264283 // Get outputs
265- const stacks = await cf . describeStacks ( { stackName } )
284+ const stacks = await withTimeout ( cf . describeStacks ( { stackName } ) )
266285 const outputs = stacks . Stacks ?. [ 0 ] ?. Outputs || [ ]
267286 const emailOutputs = outputs . filter ( ( o : any ) => o . OutputKey ?. includes ( 'Email' ) )
268287
@@ -276,5 +295,6 @@ export function email(buddy: CLI): void {
276295 catch ( error : any ) {
277296 console . error ( 'Error checking status:' , error . message )
278297 }
298+ process . exit ( 0 )
279299 } )
280300}
0 commit comments