UNPKG

11.9 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3let npm = require('./package.json');
4let aws = require('aws-sdk');
5let term = require('terminal-kit').terminal;
6let update = require('./modules/update');
7let create = require('./modules/create');
8let program = require('commander');
9let request = require('request');
10
11// _____ ______ _______ _______ _____ _ _ _____ _____
12// / ____| | ____| |__ __| |__ __| |_ _| | \ | | / ____| / ____|
13// | (___ | |__ | | | | | | | \| | | | __ | (___
14// \___ \ | __| | | | | | | | . ` | | | |_ | \___ \
15// ____) | | |____ | | | | _| |_ | |\ | | |__| | ____) |
16// |_____/ |______| |_| |_| |_____| |_| \_| \_____| |_____/
17//
18
19//
20// The CLI options for this app. At this moment we just support Version
21//
22program
23 .version(npm.version)
24 .option('-s, --source', 'path to the folder to upload')
25 .option('-u, --update', 'perform an update')
26 .option('-c, --create', 'create a new site')
27 .option('-b, --bucket', 'S3 bucket name')
28 .option('-d, --domain', 'domain of the site')
29 .parse(process.argv);
30
31//
32// Just add an empty line at the end of the help to make the text more clear
33// to the user
34//
35program.on('--help', function() {
36 console.log("");
37});
38
39//
40// Pass the user input to the module
41//
42program.parse(process.argv);
43
44//
45// Listen for key preses
46//
47term.on('key', function(name, matches, data ) {
48
49 //
50 // 1. If we detect CTR+C we kill the app
51 //
52 if(name === 'CTRL_C' )
53 {
54 //
55 // 1. Lets make a nice user experience and clean the terminal window
56 // before closing the app
57 //
58 term.clear();
59
60 //
61 // -> Kill the app
62 //
63 process.exit();
64 }
65
66});
67
68//
69// Check if the user provided the dir source where to copy the file from
70//
71if(!program.source)
72{
73 console.log('Missing source');
74 process.exit(0);
75}
76
77// __ __ _____ _ _
78// | \/ | /\ |_ _| | \ | |
79// | \ / | / \ | | | \| |
80// | |\/| | / /\ \ | | | . ` |
81// | | | | / ____ \ _| |_ | |\ |
82// |_| |_| /_/ \_\ |_____| |_| \_|
83//
84
85//
86// Before we start working, we clean the terminal window
87//
88term.clear();
89
90//
91// The main container that will be passed around in each chain to collect
92// all the data and keep it in one place
93//
94let container = {
95 dir: process.cwd() + "/" + process.argv[3],
96 region: 'us-east-1',
97 ask_for_credentials: false
98};
99
100//
101// Start the chain
102//
103save_cli_data(container)
104 .then(function(container) {
105
106 return display_the_welcome_message(container);
107
108 }).then(function(container) {
109
110 return check_for_the_environment(container);
111
112 }).then(function(container) {
113
114 return ask_for_aws_key(container);
115
116 }).then(function(container) {
117
118 return ask_for_aws_secret(container);
119
120 }).then(function(container) {
121
122 return create_aws_objects(container);
123
124 }).then(function(container) {
125
126 return ask_what_to_do(container);
127
128 }).then(function(container) {
129
130 return crossroad(container);
131
132 }).then(function(container) {
133
134 term("\n");
135 term("\n");
136
137 //
138 // -> Exit the app
139 //
140 process.exit();
141
142 }).catch(function(error) {
143
144 //
145 // 1. Clear the screen of necessary text
146 //
147 term.clear();
148
149 term("\n\n");
150
151 //
152 // 2. Show the error message
153 //
154 term.red("\t" + error);
155
156 term("\n\n");
157
158 //
159 // -> Exit the app
160 //
161 process.exit();
162
163 });
164
165// _____ _____ ____ __ __ _____ _____ ______ _____
166// | __ \ | __ \ / __ \ | \/ | |_ _| / ____| | ____| / ____|
167// | |__) | | |__) | | | | | | \ / | | | | (___ | |__ | (___
168// | ___/ | _ / | | | | | |\/| | | | \___ \ | __| \___ \
169// | | | | \ \ | |__| | | | | | _| |_ ____) | | |____ ____) |
170// |_| |_| \_\ \____/ |_| |_| |_____| |_____/ |______| |_____/
171//
172
173//
174// Check what type of information was passed and save it so we can skip
175// some menu actions and run the command programmatically
176//
177function save_cli_data(container)
178{
179 return new Promise(function(resolve, reject) {
180
181 //
182 // 1. Check if one of the options is enabled
183 //
184 if(program.create || program.update)
185 {
186 //
187 // 1. By default assume always that we are dealing with
188 // an update.
189 //
190 container.selection = 'Update';
191
192 //
193 // 2. Check if we need to change opinion.
194 //
195 if(program.create)
196 {
197 container.selection = 'Create';
198 }
199 }
200
201 //
202 // 2. Check if the bucket name was passed.
203 //
204 if(program.bucket)
205 {
206 container.bucket = program.args[1];
207 }
208
209 //
210 // -> Move to the next promise
211 //
212 return resolve(container);
213
214 });
215
216
217}
218
219//
220// Draw on the screen a nice welcome message to show our user how
221// cool we are :)
222//
223function display_the_welcome_message(container)
224{
225 return new Promise(function(resolve, reject) {
226
227 //
228 // 1. Don't display the welcome message if we are dealing with the
229 // CLI
230 //
231 if(program.create || program.update)
232 {
233 //
234 // -> Move to the next promise
235 //
236 return resolve(container);
237 }
238
239 term("\n");
240
241 //
242 // 2. Set the options that will draw the banner
243 //
244 let options = {
245 flashStyle: term.brightWhite,
246 style: term.brightYellow,
247 delay: 20
248 }
249
250 //
251 // 3. The text to be displayed on the screen
252 //
253 let text = "\tStarting Potato";
254
255 //
256 // 4. Draw the text
257 //
258 term.slowTyping(text, options, function() {
259
260 //
261 // -> Move to the next step once the animation finishes drawing
262 //
263 return resolve(container);
264
265 });
266
267 });
268}
269
270//
271// Query the EC2 Instance Metadata to find out if a IAM Role is attached
272// to the instance, this way we can either ask the user for credentials
273// or let the SDK use the Role attached to the EC2 Instance
274//
275function check_for_the_environment(container)
276{
277 return new Promise(function(resolve, reject) {
278
279 //
280 // 1. Prepare the request information
281 //
282 let options = {
283 url: 'http://169.254.169.254/latest/meta-data/iam/',
284 timeout: 1000
285 }
286
287 //
288 // 2. Make the request
289 //
290 request.get(options, function(error, data) {
291
292 //
293 // 1. We don't check for an error since when you use the
294 // timeout flag, request will throw an error when the time
295 // out happens.
296 //
297 // In this case we just don't want for this check to hang
298 // forever
299 //
300
301 //
302 // 2. Check to see if we got something back. If Potato
303 // is running on a EC2 instance we should get back the
304 // ROLE_NAME. And if that is the case we know that the
305 // AWS SDK will pick the credentials from the Role
306 // attached to the EC2 Instance.
307 //
308 if(data)
309 {
310 if(data.body.length > 0)
311 {
312 container.ask_for_credentials = true
313 }
314 }
315
316 //
317 // -> Move to the next chain
318 //
319 return resolve(container);
320
321 });
322
323 });
324}
325
326//
327// Make sure the Configuration file is actually available in the system
328//
329function ask_for_aws_key(container)
330{
331 return new Promise(function(resolve, reject) {
332
333 //
334 // 1. Check if we have to ask the for credentials or we can
335 // use the EC2 Instance role
336 //
337 if(container.ask_for_credentials)
338 {
339 //
340 // -> Move to the next chain
341 //
342 return resolve(container);
343 }
344
345 term.clear();
346
347 term("\n");
348
349 //
350 // 2. Ask input from the user
351 //
352 term.yellow("\tPlease paste your AWS Access Key ID: ");
353
354 //
355 // 3. Listen for the user input
356 //
357 term.inputField({}, function(error, aws_access_key_id) {
358
359 term("\n");
360
361 term.yellow("\tLoading...");
362
363 //
364 // 1. Save the URL
365 //
366 container.aws_access_key_id = aws_access_key_id;
367
368 //
369 // -> Move to the next chain
370 //
371 return resolve(container);
372
373 });
374
375 });
376}
377
378//
379// Make sure the Credentials file is actually available in the system
380//
381function ask_for_aws_secret(container)
382{
383 return new Promise(function(resolve, reject) {
384
385 //
386 // 1. Check if we have to ask the for credentials or we can
387 // use the EC2 Instance role
388 //
389 if(container.ask_for_credentials)
390 {
391 //
392 // -> Move to the next chain
393 //
394 return resolve(container);
395 }
396
397 term.clear();
398
399 term("\n");
400
401 //
402 // 1. Ask input from the user
403 //
404 term.yellow("\tPlease paste your AWS Secret Access Key: ");
405
406 //
407 // 2. Listen for the user input
408 //
409 term.inputField({}, function(error, aws_secret_access_key) {
410
411 term("\n");
412
413 term.yellow("\tLoading...");
414
415 //
416 // 1. Save the URL
417 //
418 container.aws_secret_access_key = aws_secret_access_key;
419
420 //
421 // -> Move to the next chain
422 //
423 return resolve(container);
424
425 });
426
427 });
428}
429
430//
431// After we get all the necessary credentials we use them to create
432// all the AWS object used to programmatically make all the work
433//
434function create_aws_objects(container)
435{
436 return new Promise(function(resolve, reject) {
437
438 //
439 // 1. Create basic settings for the constructor
440 //
441 let s3 = {
442 region: container.region
443 }
444
445 let cloudfront = {
446 region: container.region
447 }
448
449 let route53 = {
450 region: container.region
451 }
452
453 let acm = {
454 region: container.region
455 }
456
457 //
458 // 2. Update constructor settings if the user had to past the
459 // credentials
460 //
461 if(!container.ask_for_credentials)
462 {
463 s3.accessKeyId = container.aws_access_key_id
464 s3.secretAccessKey = container.aws_secret_access_key
465
466 cloudfront.accessKeyId = container.aws_access_key_id
467 cloudfront.secretAccessKey = container.aws_secret_access_key
468
469 route53.accessKeyId = container.aws_access_key_id
470 route53.secretAccessKey = container.aws_secret_access_key
471
472 acm.accessKeyId = container.aws_access_key_id
473 acm.secretAccessKey = container.aws_secret_access_key
474 }
475
476 //
477 // 3. Create the AWS S3 object
478 //
479 container.s3 = new aws.S3(s3);
480
481 //
482 // 4. Create the AWS CloudFront object
483 //
484 container.cloudfront = new aws.CloudFront(cloudfront);
485
486 //
487 // 5. Create the AWS Route 53 object
488 //
489 container.route53 = new aws.Route53(route53);
490
491 //
492 // 6. Create the AWS Certificate Manager object
493 //
494 container.acm = new aws.ACM(acm);
495
496 //
497 // -> Move to the next chain
498 //
499 return resolve(container);
500
501 });
502}
503
504//
505// Ask the user what to do, since this app can create or update a project
506//
507function ask_what_to_do(container)
508{
509 return new Promise(function(resolve, reject) {
510
511 //
512 // 1. Skip this view if the information was already passed in the
513 // CLI
514 //
515 if(container.selection)
516 {
517 //
518 // -> Move to the next chain
519 //
520 return resolve(container);
521 }
522
523 term.clear();
524
525 term("\n");
526
527 term.yellow("\tUpdate or create a new website?");
528
529 term('\n');
530
531 //
532 // 2. Default settings how to draw the ASCII menu
533 //
534 let options = {
535 leftPadding: "\t"
536 };
537
538 //
539 // 3. The two options to show the user
540 //
541 let question = [
542 'Update',
543 'Create'
544 ];
545
546 //
547 // 4. Draw the drop down menu
548 //
549 term.singleColumnMenu(question, options, function(error, res) {
550
551 term("\n");
552 term("\n");
553
554 term.yellow("\tLoading...");
555
556 term("\n");
557 term("\n");
558
559 //
560 // 1. Get the Property name based on the user selection
561 //
562 let selection = question[res.selectedIndex];
563
564 //
565 // 2. Save the user selection
566 //
567 container.selection = selection;
568
569 //
570 // -> Move to the next chain
571 //
572 return resolve(container);
573
574 });
575
576 });
577}
578
579// ______ _ _ _ _ _____ _______ _____ ____ _ _ _____
580// | ____|| | | || \ | | / ____||__ __||_ _|/ __ \ | \ | | / ____|
581// | |__ | | | || \| || | | | | | | | | || \| || (___
582// | __| | | | || . ` || | | | | | | | | || . ` | \___ \
583// | | | |__| || |\ || |____ | | _| |_| |__| || |\ | ____) |
584// |_| \____/ |_| \_| \_____| |_| |_____|\____/ |_| \_||_____/
585//
586
587//
588// This is a function that depending on the option selected by the user
589// returns just one specific promises that will then perform the
590// action selected by the user.
591//
592function crossroad(container)
593{
594 if(container.selection == 'Update')
595 {
596 return update(container);
597 }
598
599 if(container.selection == 'Create')
600 {
601 return create(container);
602 }
603}