seamless-cli 0.4.0 → 0.5.0

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 (68) hide show
  1. package/dist/commands/init.js +39 -4
  2. package/dist/commands/init.js.map +1 -1
  3. package/dist/core/images.js +1 -1
  4. package/dist/core/oauthProviders.js +89 -0
  5. package/dist/core/oauthProviders.js.map +1 -0
  6. package/dist/core/templates.js.map +1 -1
  7. package/dist/generators/auth/auth.js +4 -4
  8. package/dist/generators/auth/auth.js.map +1 -1
  9. package/dist/generators/docker/docker.js +28 -10
  10. package/dist/generators/docker/docker.js.map +1 -1
  11. package/dist/index.js +0 -0
  12. package/dist/prompts/oauthSetup.js +36 -0
  13. package/dist/prompts/oauthSetup.js.map +1 -0
  14. package/package.json +1 -1
  15. package/dist/commands/contribute.js +0 -121
  16. package/dist/commands/contribute.js.map +0 -1
  17. package/dist/commands/deploy.js +0 -190
  18. package/dist/commands/deploy.js.map +0 -1
  19. package/dist/commands/destroy.js +0 -122
  20. package/dist/commands/destroy.js.map +0 -1
  21. package/dist/contributor/catalog.js +0 -129
  22. package/dist/contributor/catalog.js.map +0 -1
  23. package/dist/contributor/workspace.js +0 -551
  24. package/dist/contributor/workspace.js.map +0 -1
  25. package/dist/core/acm.js +0 -24
  26. package/dist/core/acm.js.map +0 -1
  27. package/dist/core/aws.js +0 -374
  28. package/dist/core/aws.js.map +0 -1
  29. package/dist/core/buildPush.js +0 -91
  30. package/dist/core/buildPush.js.map +0 -1
  31. package/dist/core/configure.js +0 -25
  32. package/dist/core/configure.js.map +0 -1
  33. package/dist/core/deployConfig.js +0 -66
  34. package/dist/core/deployConfig.js.map +0 -1
  35. package/dist/core/deployManifest.js +0 -163
  36. package/dist/core/deployManifest.js.map +0 -1
  37. package/dist/core/deploySecrets.js +0 -31
  38. package/dist/core/deploySecrets.js.map +0 -1
  39. package/dist/core/destroy.js +0 -38
  40. package/dist/core/destroy.js.map +0 -1
  41. package/dist/core/envDiscovery.js +0 -153
  42. package/dist/core/envDiscovery.js.map +0 -1
  43. package/dist/core/envRegistry.js +0 -76
  44. package/dist/core/envRegistry.js.map +0 -1
  45. package/dist/core/health.js +0 -11
  46. package/dist/core/health.js.map +0 -1
  47. package/dist/core/help.js +0 -75
  48. package/dist/core/help.js.map +0 -1
  49. package/dist/core/preReqs.js +0 -14
  50. package/dist/core/preReqs.js.map +0 -1
  51. package/dist/core/terraform.js +0 -34
  52. package/dist/core/terraform.js.map +0 -1
  53. package/dist/core/tfVars.js +0 -69
  54. package/dist/core/tfVars.js.map +0 -1
  55. package/dist/generators/backend/express.js +0 -13
  56. package/dist/generators/backend/express.js.map +0 -1
  57. package/dist/generators/deploy/aws/dev/files.js +0 -36
  58. package/dist/generators/deploy/aws/dev/files.js.map +0 -1
  59. package/dist/generators/deploy/aws/dev/template.js +0 -1240
  60. package/dist/generators/deploy/aws/dev/template.js.map +0 -1
  61. package/dist/generators/frontend/react.js +0 -13
  62. package/dist/generators/frontend/react.js.map +0 -1
  63. package/dist/prompts/contributeSetup.js +0 -39
  64. package/dist/prompts/contributeSetup.js.map +0 -1
  65. package/dist/prompts/deploySetup.js +0 -244
  66. package/dist/prompts/deploySetup.js.map +0 -1
  67. package/dist/utils/repoUtils.js +0 -23
  68. package/dist/utils/repoUtils.js.map +0 -1
@@ -1,1240 +0,0 @@
1
- function tf(value) {
2
- return value.replace(/"/g, '\\"');
3
- }
4
- function escapeTfString(value) {
5
- return value
6
- .replace(/\\/g, "\\\\")
7
- .replace(/"/g, '\\"')
8
- .replace(/\n/g, "\\n");
9
- }
10
- function renderEnvEntries(vars) {
11
- return vars
12
- .map((envVar) => `{ name = "${escapeTfString(envVar.name)}", value = "${escapeTfString(envVar.value)}" }`)
13
- .join(",\n ");
14
- }
15
- function renderUserEnvVars(vars) {
16
- return vars
17
- .filter((envVar) => envVar.target === "task_definition")
18
- .map((envVar) => ({
19
- name: envVar.key,
20
- value: envVar.value,
21
- }));
22
- }
23
- export function renderMainTf() {
24
- return `
25
- terraform {
26
- required_version = ">= 1.5.0"
27
-
28
- required_providers {
29
- aws = {
30
- source = "hashicorp/aws"
31
- version = "~> 5.0"
32
- }
33
-
34
- random = {
35
- source = "hashicorp/random"
36
- version = "~> 3.6"
37
- }
38
- }
39
- }
40
-
41
- provider "aws" {
42
- region = var.aws_region
43
- profile = var.aws_profile
44
- }
45
- `.trimStart();
46
- }
47
- export function renderVariablesTf(manifest) {
48
- return `
49
- variable "project_name" {
50
- type = string
51
- default = "${tf(manifest.projectName)}"
52
- }
53
-
54
- variable "aws_region" {
55
- type = string
56
- default = "${tf(manifest.region)}"
57
- }
58
-
59
- variable "aws_profile" {
60
- type = string
61
- default = "${tf(manifest.awsProfile)}"
62
- }
63
-
64
- variable "root_domain" {
65
- type = string
66
- default = "${tf(manifest.domain.root)}"
67
- }
68
-
69
- variable "hosted_zone_domain" {
70
- type = string
71
- default = "${tf(manifest.domain.hostedZoneDomain ?? manifest.domain.root)}"
72
- }
73
-
74
- variable "web_host" {
75
- type = string
76
- default = "${tf(manifest.domain.webHost)}"
77
- }
78
-
79
- variable "api_host" {
80
- type = string
81
- default = "${tf(manifest.domain.apiHost)}"
82
- }
83
-
84
- variable "auth_host" {
85
- type = string
86
- default = "${tf(manifest.domain.authHost)}"
87
- }
88
-
89
- variable "admin_host" {
90
- type = string
91
- default = "${tf(manifest.domain.adminHost)}"
92
- }
93
-
94
- variable "backup_enabled" {
95
- type = bool
96
- default = ${manifest.backup.enabled ? "true" : "false"}
97
- }
98
-
99
- variable "backup_retention_days" {
100
- type = number
101
- default = ${manifest.backup.retentionDays}
102
- }
103
-
104
- variable "web_image" {
105
- type = string
106
- default = "REPLACE_WEB_IMAGE_URI"
107
- }
108
-
109
- variable "api_image" {
110
- type = string
111
- default = "REPLACE_API_IMAGE_URI"
112
- }
113
-
114
- variable "auth_image" {
115
- type = string
116
- default = "REPLACE_AUTH_IMAGE_URI"
117
- }
118
-
119
- variable "admin_image" {
120
- type = string
121
- default = "REPLACE_ADMIN_IMAGE_URI"
122
- }
123
-
124
- variable "postgres_image" {
125
- type = string
126
- default = "REPLACE_POSTGRES_IMAGE_URI"
127
- }
128
-
129
- variable "ecs_instance_type" {
130
- type = string
131
- default = "t3.large"
132
- }
133
-
134
- variable "desired_capacity" {
135
- type = number
136
- default = 1
137
- }
138
-
139
- variable "api_origin" {
140
- type = string
141
- }
142
-
143
- variable "auth_issuer" {
144
- type = string
145
- }
146
-
147
- variable "web_origin" {
148
- type = string
149
- }
150
-
151
- variable "admin_origin" {
152
- type = string
153
- }
154
-
155
- variable "rpid" {
156
- type = string
157
- }
158
-
159
- variable "api_service_token" {
160
- type = string
161
- sensitive = true
162
- }
163
-
164
- variable "cookie_signing_key" {
165
- type = string
166
- sensitive = true
167
- }
168
-
169
- variable "bootstrap_secret" {
170
- type = string
171
- sensitive = true
172
- }
173
-
174
- variable "auth_db_user" {
175
- type = string
176
- }
177
-
178
- variable "auth_db_password" {
179
- type = string
180
- sensitive = true
181
- }
182
-
183
- variable "auth_db_name" {
184
- type = string
185
- }
186
-
187
- variable "api_db_user" {
188
- type = string
189
- }
190
-
191
- variable "api_db_password" {
192
- type = string
193
- sensitive = true
194
- }
195
-
196
- variable "api_db_name" {
197
- type = string
198
- }
199
-
200
- variable "jwks_active_kid" {
201
- type = string
202
- }
203
-
204
- variable "seamless_jwks_active_kid" {
205
- type = string
206
- }
207
-
208
- variable "jwks_private_key" {
209
- type = string
210
- sensitive = true
211
- }
212
-
213
- variable "jwks_public_keys" {
214
- type = string
215
- sensitive = true
216
- }
217
-
218
- locals {
219
- app_slug = replace(lower(var.project_name), " ", "-")
220
- common_tags = {
221
- Project = var.project_name
222
- Tier = "dev"
223
- Managed = "seamless-cli"
224
- }
225
- }
226
- `.trimStart();
227
- }
228
- export function renderNetworkingTf() {
229
- return `
230
- resource "aws_vpc" "main" {
231
- cidr_block = "10.40.0.0/16"
232
- enable_dns_hostnames = true
233
- enable_dns_support = true
234
-
235
- tags = merge(local.common_tags, {
236
- Name = "\${local.app_slug}-vpc"
237
- })
238
- }
239
-
240
- resource "aws_internet_gateway" "main" {
241
- vpc_id = aws_vpc.main.id
242
-
243
- tags = merge(local.common_tags, {
244
- Name = "\${local.app_slug}-igw"
245
- })
246
- }
247
-
248
- resource "aws_subnet" "public_a" {
249
- vpc_id = aws_vpc.main.id
250
- cidr_block = "10.40.1.0/24"
251
- map_public_ip_on_launch = true
252
- availability_zone = "\${var.aws_region}a"
253
-
254
- tags = merge(local.common_tags, {
255
- Name = "\${local.app_slug}-public-a"
256
- })
257
- }
258
-
259
- resource "aws_subnet" "public_b" {
260
- vpc_id = aws_vpc.main.id
261
- cidr_block = "10.40.2.0/24"
262
- map_public_ip_on_launch = true
263
- availability_zone = "\${var.aws_region}b"
264
-
265
- tags = merge(local.common_tags, {
266
- Name = "\${local.app_slug}-public-b"
267
- })
268
- }
269
-
270
- resource "aws_route_table" "public" {
271
- vpc_id = aws_vpc.main.id
272
-
273
- tags = merge(local.common_tags, {
274
- Name = "\${local.app_slug}-public-rt"
275
- })
276
- }
277
-
278
- resource "aws_route" "internet_access" {
279
- route_table_id = aws_route_table.public.id
280
- destination_cidr_block = "0.0.0.0/0"
281
- gateway_id = aws_internet_gateway.main.id
282
- }
283
-
284
- resource "aws_route_table_association" "public_a" {
285
- subnet_id = aws_subnet.public_a.id
286
- route_table_id = aws_route_table.public.id
287
- }
288
-
289
- resource "aws_route_table_association" "public_b" {
290
- subnet_id = aws_subnet.public_b.id
291
- route_table_id = aws_route_table.public.id
292
- }
293
- `.trimStart();
294
- }
295
- export function renderRoute53Tf() {
296
- return `
297
- data "aws_route53_zone" "main" {
298
- name = var.hosted_zone_domain
299
- private_zone = false
300
- }
301
-
302
- resource "aws_route53_record" "web" {
303
- zone_id = data.aws_route53_zone.main.zone_id
304
- name = var.web_host
305
- type = "A"
306
-
307
- alias {
308
- name = aws_lb.main.dns_name
309
- zone_id = aws_lb.main.zone_id
310
- evaluate_target_health = true
311
- }
312
- }
313
-
314
- resource "aws_route53_record" "api" {
315
- zone_id = data.aws_route53_zone.main.zone_id
316
- name = var.api_host
317
- type = "A"
318
-
319
- alias {
320
- name = aws_lb.main.dns_name
321
- zone_id = aws_lb.main.zone_id
322
- evaluate_target_health = true
323
- }
324
- }
325
-
326
- resource "aws_route53_record" "auth" {
327
- zone_id = data.aws_route53_zone.main.zone_id
328
- name = var.auth_host
329
- type = "A"
330
-
331
- alias {
332
- name = aws_lb.main.dns_name
333
- zone_id = aws_lb.main.zone_id
334
- evaluate_target_health = true
335
- }
336
- }
337
-
338
- resource "aws_route53_record" "admin" {
339
- zone_id = data.aws_route53_zone.main.zone_id
340
- name = var.admin_host
341
- type = "A"
342
-
343
- alias {
344
- name = aws_lb.main.dns_name
345
- zone_id = aws_lb.main.zone_id
346
- evaluate_target_health = true
347
- }
348
- }
349
- `.trimStart();
350
- }
351
- export function renderAcmTf() {
352
- return `
353
- variable "acm_certificate_arn" {
354
- type = string
355
- }
356
- `.trimStart();
357
- }
358
- export function renderAlbTf() {
359
- return `
360
- resource "aws_security_group" "alb" {
361
- name = "\${local.app_slug}-alb-sg"
362
- description = "ALB security group"
363
- vpc_id = aws_vpc.main.id
364
-
365
- ingress {
366
- from_port = 80
367
- to_port = 80
368
- protocol = "tcp"
369
- cidr_blocks = ["0.0.0.0/0"]
370
- }
371
-
372
- ingress {
373
- from_port = 443
374
- to_port = 443
375
- protocol = "tcp"
376
- cidr_blocks = ["0.0.0.0/0"]
377
- }
378
-
379
- egress {
380
- from_port = 0
381
- to_port = 0
382
- protocol = "-1"
383
- cidr_blocks = ["0.0.0.0/0"]
384
- }
385
-
386
- tags = local.common_tags
387
- }
388
-
389
- resource "aws_lb" "main" {
390
- name = substr("\${local.app_slug}-alb", 0, 32)
391
- internal = false
392
- load_balancer_type = "application"
393
- security_groups = [aws_security_group.alb.id]
394
- subnets = [aws_subnet.public_a.id, aws_subnet.public_b.id]
395
-
396
- tags = local.common_tags
397
- }
398
-
399
- resource "aws_lb_target_group" "web" {
400
- name = substr("\${local.app_slug}-web", 0, 32)
401
- port = 80
402
- protocol = "HTTP"
403
- target_type = "instance"
404
- vpc_id = aws_vpc.main.id
405
-
406
- health_check {
407
- path = "/"
408
- }
409
-
410
- tags = local.common_tags
411
- }
412
-
413
- resource "aws_lb_target_group" "api" {
414
- name = substr("\${local.app_slug}-api", 0, 32)
415
- port = 3000
416
- protocol = "HTTP"
417
- target_type = "instance"
418
- vpc_id = aws_vpc.main.id
419
-
420
- health_check {
421
- path = "/health"
422
- }
423
-
424
- tags = local.common_tags
425
- }
426
-
427
- resource "aws_lb_target_group" "admin" {
428
- name = substr("\${local.app_slug}-admin", 0, 32)
429
- port = 80
430
- protocol = "HTTP"
431
- target_type = "instance"
432
- vpc_id = aws_vpc.main.id
433
-
434
- health_check {
435
- path = "/"
436
- }
437
-
438
- tags = local.common_tags
439
- }
440
-
441
- resource "aws_lb_target_group" "auth" {
442
- name = substr("\${local.app_slug}-auth", 0, 32)
443
- port = 5312
444
- protocol = "HTTP"
445
- target_type = "instance"
446
- vpc_id = aws_vpc.main.id
447
-
448
- health_check {
449
- path = "/health/status"
450
- }
451
-
452
- tags = local.common_tags
453
- }
454
-
455
- resource "aws_lb_listener" "http" {
456
- load_balancer_arn = aws_lb.main.arn
457
- port = 80
458
- protocol = "HTTP"
459
-
460
- default_action {
461
- type = "redirect"
462
-
463
- redirect {
464
- port = "443"
465
- protocol = "HTTPS"
466
- status_code = "HTTP_301"
467
- }
468
- }
469
- }
470
-
471
- resource "aws_lb_listener" "https" {
472
- load_balancer_arn = aws_lb.main.arn
473
- port = 443
474
- protocol = "HTTPS"
475
- certificate_arn = var.acm_certificate_arn
476
- ssl_policy = "ELBSecurityPolicy-2016-08"
477
-
478
- default_action {
479
- type = "forward"
480
- target_group_arn = aws_lb_target_group.web.arn
481
- }
482
- }
483
-
484
- resource "aws_lb_listener_rule" "api" {
485
- listener_arn = aws_lb_listener.https.arn
486
- priority = 10
487
-
488
- condition {
489
- host_header {
490
- values = [var.api_host]
491
- }
492
- }
493
-
494
- action {
495
- type = "forward"
496
- target_group_arn = aws_lb_target_group.api.arn
497
- }
498
- }
499
-
500
- resource "aws_lb_listener_rule" "auth" {
501
- listener_arn = aws_lb_listener.https.arn
502
- priority = 20
503
-
504
- condition {
505
- host_header {
506
- values = [var.auth_host]
507
- }
508
- }
509
-
510
- action {
511
- type = "forward"
512
- target_group_arn = aws_lb_target_group.auth.arn
513
- }
514
- }
515
-
516
- resource "aws_lb_listener_rule" "admin" {
517
- listener_arn = aws_lb_listener.https.arn
518
- priority = 30
519
-
520
- condition {
521
- host_header {
522
- values = [var.admin_host]
523
- }
524
- }
525
-
526
- action {
527
- type = "forward"
528
- target_group_arn = aws_lb_target_group.admin.arn
529
- }
530
- }
531
- `.trimStart();
532
- }
533
- export function renderLogsTf(manifest) {
534
- return `
535
- resource "aws_cloudwatch_log_group" "web" {
536
- name = "/${tf(manifest.projectName)}/dev/web"
537
- retention_in_days = 14
538
- tags = local.common_tags
539
- }
540
-
541
- resource "aws_cloudwatch_log_group" "api" {
542
- name = "/${tf(manifest.projectName)}/dev/api"
543
- retention_in_days = 14
544
- tags = local.common_tags
545
- }
546
-
547
- resource "aws_cloudwatch_log_group" "auth" {
548
- name = "/${tf(manifest.projectName)}/dev/auth"
549
- retention_in_days = 14
550
- tags = local.common_tags
551
- }
552
-
553
- resource "aws_cloudwatch_log_group" "admin" {
554
- name = "/${tf(manifest.projectName)}/dev/admin"
555
- retention_in_days = 14
556
- tags = local.common_tags
557
- }
558
-
559
- resource "aws_cloudwatch_log_group" "postgres" {
560
- name = "/${tf(manifest.projectName)}/dev/postgres"
561
- retention_in_days = 14
562
- tags = local.common_tags
563
- }
564
-
565
- resource "aws_cloudwatch_log_group" "backup" {
566
- name = "/${tf(manifest.projectName)}/dev/backup"
567
- retention_in_days = 14
568
- tags = local.common_tags
569
- }
570
- `.trimStart();
571
- }
572
- export function renderS3Tf() {
573
- return `
574
- resource "random_id" "bucket_suffix" {
575
- byte_length = 4
576
- }
577
-
578
- resource "aws_s3_bucket" "backups" {
579
- bucket = "\${local.app_slug}-db-backups-\${random_id.bucket_suffix.hex}"
580
-
581
- tags = local.common_tags
582
- }
583
-
584
- resource "aws_s3_bucket_versioning" "backups" {
585
- bucket = aws_s3_bucket.backups.id
586
-
587
- versioning_configuration {
588
- status = "Enabled"
589
- }
590
- }
591
-
592
- resource "aws_s3_bucket_lifecycle_configuration" "backups" {
593
- bucket = aws_s3_bucket.backups.id
594
-
595
- rule {
596
- id = "expire-old-backups"
597
- status = "Enabled"
598
-
599
- filter {}
600
-
601
- expiration {
602
- days = var.backup_retention_days
603
- }
604
- }
605
- }
606
- `.trimStart();
607
- }
608
- export function renderIamTf() {
609
- return `
610
- resource "aws_iam_role" "ecs_task_execution" {
611
- name = "\${local.app_slug}-ecs-task-execution-role"
612
-
613
- assume_role_policy = jsonencode({
614
- Version = "2012-10-17",
615
- Statement = [{
616
- Action = "sts:AssumeRole",
617
- Effect = "Allow",
618
- Principal = {
619
- Service = "ecs-tasks.amazonaws.com"
620
- }
621
- }]
622
- })
623
-
624
- tags = local.common_tags
625
- }
626
-
627
- resource "aws_iam_role_policy_attachment" "ecs_task_execution_default" {
628
- role = aws_iam_role.ecs_task_execution.name
629
- policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
630
- }
631
-
632
- resource "aws_iam_role" "ecs_task_role" {
633
- name = "\${local.app_slug}-ecs-task-role"
634
-
635
- assume_role_policy = jsonencode({
636
- Version = "2012-10-17",
637
- Statement = [{
638
- Action = "sts:AssumeRole",
639
- Effect = "Allow",
640
- Principal = {
641
- Service = "ecs-tasks.amazonaws.com"
642
- }
643
- }]
644
- })
645
-
646
- tags = local.common_tags
647
- }
648
-
649
- resource "aws_iam_policy" "backup_s3" {
650
- name = "\${local.app_slug}-backup-s3-policy"
651
-
652
- policy = jsonencode({
653
- Version = "2012-10-17",
654
- Statement = [
655
- {
656
- Effect = "Allow",
657
- Action = [
658
- "s3:PutObject",
659
- "s3:ListBucket"
660
- ],
661
- Resource = [
662
- aws_s3_bucket.backups.arn,
663
- "\${aws_s3_bucket.backups.arn}/*"
664
- ]
665
- }
666
- ]
667
- })
668
- }
669
-
670
- resource "aws_iam_role_policy_attachment" "ecs_task_backup_s3" {
671
- role = aws_iam_role.ecs_task_role.name
672
- policy_arn = aws_iam_policy.backup_s3.arn
673
- }
674
-
675
- resource "aws_iam_role" "ecs_instance_role" {
676
- name = "\${local.app_slug}-ecs-instance-role"
677
-
678
- assume_role_policy = jsonencode({
679
- Version = "2012-10-17",
680
- Statement = [{
681
- Action = "sts:AssumeRole",
682
- Effect = "Allow",
683
- Principal = {
684
- Service = "ec2.amazonaws.com"
685
- }
686
- }]
687
- })
688
-
689
- tags = local.common_tags
690
- }
691
-
692
- resource "aws_iam_role_policy_attachment" "ecs_instance_ec2_container" {
693
- role = aws_iam_role.ecs_instance_role.name
694
- policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
695
- }
696
-
697
- resource "aws_iam_role_policy_attachment" "ecs_instance_ssm" {
698
- role = aws_iam_role.ecs_instance_role.name
699
- policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
700
- }
701
-
702
- resource "aws_iam_instance_profile" "ecs" {
703
- name = "\${local.app_slug}-ecs-instance-profile"
704
- role = aws_iam_role.ecs_instance_role.name
705
- }
706
- `.trimStart();
707
- }
708
- export function renderEc2Tf() {
709
- return `
710
- data "aws_ssm_parameter" "ecs_optimized_ami" {
711
- name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id"
712
- }
713
-
714
- resource "aws_security_group" "ecs_host" {
715
- name = "\${local.app_slug}-ecs-host-sg"
716
- description = "ECS host security group"
717
- vpc_id = aws_vpc.main.id
718
-
719
- ingress {
720
- from_port = 32768
721
- to_port = 65535
722
- protocol = "tcp"
723
- security_groups = [aws_security_group.alb.id]
724
- }
725
-
726
- ingress {
727
- from_port = 80
728
- to_port = 80
729
- protocol = "tcp"
730
- security_groups = [aws_security_group.alb.id]
731
- }
732
-
733
- ingress {
734
- from_port = 3001
735
- to_port = 3001
736
- protocol = "tcp"
737
- security_groups = [aws_security_group.alb.id]
738
- }
739
-
740
- ingress {
741
- from_port = 4173
742
- to_port = 4173
743
- protocol = "tcp"
744
- security_groups = [aws_security_group.alb.id]
745
- }
746
-
747
- ingress {
748
- from_port = 5312
749
- to_port = 5312
750
- protocol = "tcp"
751
- security_groups = [aws_security_group.alb.id]
752
- }
753
-
754
- egress {
755
- from_port = 0
756
- to_port = 0
757
- protocol = "-1"
758
- cidr_blocks = ["0.0.0.0/0"]
759
- }
760
-
761
- tags = local.common_tags
762
- }
763
-
764
- resource "aws_launch_template" "ecs" {
765
- name_prefix = "\${local.app_slug}-ecs-"
766
- image_id = data.aws_ssm_parameter.ecs_optimized_ami.value
767
- instance_type = var.ecs_instance_type
768
-
769
- iam_instance_profile {
770
- arn = aws_iam_instance_profile.ecs.arn
771
- }
772
-
773
- network_interfaces {
774
- associate_public_ip_address = true
775
- security_groups = [aws_security_group.ecs_host.id]
776
- }
777
-
778
- user_data = base64encode(<<-EOF
779
- #!/bin/bash
780
- echo ECS_CLUSTER=\${aws_ecs_cluster.main.name} >> /etc/ecs/ecs.config
781
- EOF
782
- )
783
-
784
- tag_specifications {
785
- resource_type = "instance"
786
-
787
- tags = merge(local.common_tags, {
788
- Name = "\${local.app_slug}-ecs-instance"
789
- })
790
- }
791
-
792
- tags = local.common_tags
793
- }
794
-
795
- resource "aws_autoscaling_group" "ecs" {
796
- name = "\${local.app_slug}-ecs-asg"
797
- vpc_zone_identifier = [aws_subnet.public_a.id]
798
- desired_capacity = var.desired_capacity
799
- min_size = 1
800
- max_size = 1
801
- health_check_type = "EC2"
802
-
803
- launch_template {
804
- id = aws_launch_template.ecs.id
805
- version = "$Latest"
806
- }
807
-
808
- tag {
809
- key = "Name"
810
- value = "\${local.app_slug}-ecs-instance"
811
- propagate_at_launch = true
812
- }
813
-
814
- tag {
815
- key = "Project"
816
- value = var.project_name
817
- propagate_at_launch = true
818
- }
819
- }
820
- `.trimStart();
821
- }
822
- export function renderEcsTf(manifest) {
823
- const webEnv = [
824
- { name: "API_URL", value: "${var.api_origin}" },
825
- ...renderUserEnvVars(manifest.env.web),
826
- ];
827
- const apiEnv = [
828
- { name: "AUTH_SERVER_URL", value: "http://127.0.0.1:5312" },
829
- { name: "APP_ORIGINS", value: "${var.api_origin}" },
830
- { name: "UI_ORIGINS", value: "${var.web_origin},${var.admin_origin}" },
831
- { name: "COOKIE_SIGNING_KEY", value: "${var.cookie_signing_key}" },
832
- { name: "API_SERVICE_TOKEN", value: "${var.api_service_token}" },
833
- { name: "JWKS_KID", value: "${var.jwks_active_kid}" },
834
- { name: "DB_HOST", value: "127.0.0.1" },
835
- { name: "DB_PORT", value: "5432" },
836
- { name: "DB_USER", value: "${var.api_db_user}" },
837
- { name: "DB_PASSWORD", value: "${var.api_db_password}" },
838
- { name: "DB_NAME", value: "${var.api_db_name}" },
839
- { name: "SQL_LOGGING", value: "false" },
840
- ...renderUserEnvVars(manifest.env.api),
841
- ];
842
- const authEnv = [
843
- { name: "NODE_ENV", value: "production" },
844
- { name: "APP_NAME", value: "${var.project_name}" },
845
- { name: "APP_ID", value: "${local.app_slug}-dev" },
846
- { name: "APP_ORIGINS", value: "${var.api_origin}" },
847
- { name: "ISSUER", value: "${var.auth_issuer}" },
848
- { name: "AUTH_MODE", value: "server" },
849
- { name: "DEMO", value: "false" },
850
- { name: "DEFAULT_ROLES", value: "user,betaUser" },
851
- { name: "AVAILABLE_ROLES", value: "user,admin,betaUser,team" },
852
- { name: "DB_LOGGING", value: "false" },
853
- { name: "DB_HOST", value: "127.0.0.1" },
854
- { name: "DB_PORT", value: "5432" },
855
- { name: "DB_NAME", value: "${var.auth_db_name}" },
856
- { name: "DB_USER", value: "${var.auth_db_user}" },
857
- { name: "DB_PASSWORD", value: "${var.auth_db_password}" },
858
- { name: "ACCESS_TOKEN_TTL", value: "30m" },
859
- { name: "REFRESH_TOKEN_TTL", value: "1h" },
860
- { name: "RATE_LIMIT", value: "100" },
861
- { name: "DELAY_AFTER", value: "50" },
862
- { name: "API_SERVICE_TOKEN", value: "${var.api_service_token}" },
863
- { name: "JWKS_ACTIVE_KID", value: "${var.jwks_active_kid}" },
864
- {
865
- name: "SEAMLESS_JWKS_ACTIVE_KID",
866
- value: "${var.seamless_jwks_active_kid}",
867
- },
868
- {
869
- name: "SEAMLESS_JWKS_KEY_main_PRIVATE",
870
- value: "${var.jwks_private_key}",
871
- },
872
- { name: "JWKS_PUBLIC_KEYS", value: "${var.jwks_public_keys}" },
873
- { name: "RPID", value: "${var.rpid}" },
874
- { name: "ORIGINS", value: "${var.web_origin},${var.admin_origin}" },
875
- { name: "SEAMLESS_BOOTSTRAP_ENABLED", value: "true" },
876
- { name: "SEAMLESS_BOOTSTRAP_SECRET", value: "${var.bootstrap_secret}" },
877
- ...renderUserEnvVars(manifest.env.auth),
878
- ];
879
- const adminEnv = [
880
- { name: "API_URL", value: "${var.api_origin}" },
881
- { name: "AUTH_MODE", value: "server" },
882
- ...renderUserEnvVars(manifest.env.admin),
883
- ];
884
- return `
885
- resource "aws_ecs_cluster" "main" {
886
- name = "\${local.app_slug}-cluster"
887
- tags = local.common_tags
888
- }
889
-
890
- resource "aws_ecs_task_definition" "app" {
891
- family = "\${local.app_slug}-app"
892
- network_mode = "bridge"
893
- requires_compatibilities = ["EC2"]
894
- cpu = "1024"
895
- memory = "3584"
896
- execution_role_arn = aws_iam_role.ecs_task_execution.arn
897
- task_role_arn = aws_iam_role.ecs_task_role.arn
898
-
899
- container_definitions = jsonencode([
900
- {
901
- name = "web"
902
- image = var.web_image
903
- essential = true
904
- memoryReservation = 128
905
- environment = [
906
- ${renderEnvEntries(webEnv)}
907
- ]
908
- portMappings = [
909
- {
910
- containerPort = 80
911
- hostPort = 0
912
- protocol = "tcp"
913
- }
914
- ]
915
- logConfiguration = {
916
- logDriver = "awslogs"
917
- options = {
918
- awslogs-group = aws_cloudwatch_log_group.web.name
919
- awslogs-region = var.aws_region
920
- awslogs-stream-prefix = "web"
921
- }
922
- }
923
- },
924
- {
925
- name = "api"
926
- image = var.api_image
927
- essential = true
928
- memoryReservation = 512
929
- environment = [
930
- ${renderEnvEntries(apiEnv)}
931
- ]
932
- portMappings = [
933
- {
934
- containerPort = 3000
935
- hostPort = 0
936
- protocol = "tcp"
937
- }
938
- ]
939
- logConfiguration = {
940
- logDriver = "awslogs"
941
- options = {
942
- awslogs-group = aws_cloudwatch_log_group.api.name
943
- awslogs-region = var.aws_region
944
- awslogs-stream-prefix = "api"
945
- }
946
- }
947
- },
948
- {
949
- name = "auth"
950
- image = var.auth_image
951
- essential = true
952
- memoryReservation = 512
953
- environment = [
954
- ${renderEnvEntries(authEnv)}
955
- ]
956
- portMappings = [
957
- {
958
- containerPort = 5312
959
- hostPort = 0
960
- protocol = "tcp"
961
- }
962
- ]
963
- logConfiguration = {
964
- logDriver = "awslogs"
965
- options = {
966
- awslogs-group = aws_cloudwatch_log_group.auth.name
967
- awslogs-region = var.aws_region
968
- awslogs-stream-prefix = "auth"
969
- }
970
- }
971
- },
972
- {
973
- name = "admin"
974
- image = var.admin_image
975
- essential = true
976
- memoryReservation = 128
977
- environment = [
978
- ${renderEnvEntries(adminEnv)}
979
- ]
980
- portMappings = [
981
- {
982
- containerPort = 80
983
- hostPort = 0
984
- protocol = "tcp"
985
- }
986
- ]
987
- logConfiguration = {
988
- logDriver = "awslogs"
989
- options = {
990
- awslogs-group = aws_cloudwatch_log_group.admin.name
991
- awslogs-region = var.aws_region
992
- awslogs-stream-prefix = "admin"
993
- }
994
- }
995
- },
996
- {
997
- name = "postgres"
998
- image = var.postgres_image
999
- essential = true
1000
- memoryReservation = 1024
1001
- environment = [
1002
- { name = "POSTGRES_USER", value = "postgres" },
1003
- { name = "POSTGRES_PASSWORD", value = "postgres-dev-root-password" }
1004
- ]
1005
- portMappings = [
1006
- {
1007
- containerPort = 5432
1008
- hostPort = 5432
1009
- protocol = "tcp"
1010
- }
1011
- ]
1012
- logConfiguration = {
1013
- logDriver = "awslogs"
1014
- options = {
1015
- awslogs-group = aws_cloudwatch_log_group.postgres.name
1016
- awslogs-region = var.aws_region
1017
- awslogs-stream-prefix = "postgres"
1018
- }
1019
- }
1020
- mountPoints = [
1021
- {
1022
- sourceVolume = "postgres_data"
1023
- containerPath = "/var/lib/postgresql/data"
1024
- readOnly = false
1025
- }
1026
- ]
1027
- }
1028
- ])
1029
-
1030
- volume {
1031
- name = "postgres_data"
1032
- }
1033
-
1034
- tags = local.common_tags
1035
- }
1036
-
1037
- resource "aws_ecs_service" "app" {
1038
- name = "\${local.app_slug}-service"
1039
- cluster = aws_ecs_cluster.main.id
1040
- task_definition = aws_ecs_task_definition.app.arn
1041
- desired_count = 1
1042
- launch_type = "EC2"
1043
-
1044
- load_balancer {
1045
- target_group_arn = aws_lb_target_group.web.arn
1046
- container_name = "web"
1047
- container_port = 80
1048
- }
1049
-
1050
- load_balancer {
1051
- target_group_arn = aws_lb_target_group.api.arn
1052
- container_name = "api"
1053
- container_port = 3000
1054
- }
1055
-
1056
- load_balancer {
1057
- target_group_arn = aws_lb_target_group.auth.arn
1058
- container_name = "auth"
1059
- container_port = 5312
1060
- }
1061
-
1062
- load_balancer {
1063
- target_group_arn = aws_lb_target_group.admin.arn
1064
- container_name = "admin"
1065
- container_port = 80
1066
- }
1067
-
1068
- depends_on = [
1069
- aws_lb_listener.https,
1070
- aws_autoscaling_group.ecs
1071
- ]
1072
- }
1073
- `.trimStart();
1074
- }
1075
- export function renderBackupTf() {
1076
- return `
1077
- locals {
1078
- backup_schedule_expression = (
1079
- var.backup_enabled == false ? null :
1080
- "cron(0 5 * * ? *)"
1081
- )
1082
-
1083
- backup_command = "export PGPASSWORD=postgres-dev-root-password && mkdir -p /tmp/backups && pg_dump -h 127.0.0.1 -U postgres -d postgres | gzip > /tmp/backups/backup.sql.gz && apt-get update >/dev/null 2>&1 && apt-get install -y awscli >/dev/null 2>&1 && aws s3 cp /tmp/backups/backup.sql.gz s3://\${aws_s3_bucket.backups.bucket}/postgres/dev/backup-$(date +%Y%m%d%H%M%S).sql.gz"
1084
- }
1085
-
1086
- resource "aws_ecs_task_definition" "backup" {
1087
- count = var.backup_enabled ? 1 : 0
1088
- family = "\${local.app_slug}-backup"
1089
- network_mode = "bridge"
1090
- requires_compatibilities = ["EC2"]
1091
- cpu = "128"
1092
- memory = "256"
1093
- execution_role_arn = aws_iam_role.ecs_task_execution.arn
1094
- task_role_arn = aws_iam_role.ecs_task_role.arn
1095
-
1096
- container_definitions = jsonencode([
1097
- {
1098
- name = "backup"
1099
- image = "postgres:16"
1100
- essential = true
1101
- entryPoint = ["/bin/sh", "-lc"]
1102
- command = [local.backup_command]
1103
- logConfiguration = {
1104
- logDriver = "awslogs"
1105
- options = {
1106
- awslogs-group = aws_cloudwatch_log_group.backup.name
1107
- awslogs-region = var.aws_region
1108
- awslogs-stream-prefix = "backup"
1109
- }
1110
- }
1111
- }
1112
- ])
1113
-
1114
- tags = local.common_tags
1115
- }
1116
-
1117
- resource "aws_cloudwatch_event_rule" "backup" {
1118
- count = var.backup_enabled ? 1 : 0
1119
- name = "\${local.app_slug}-backup-schedule"
1120
- schedule_expression = local.backup_schedule_expression
1121
- }
1122
-
1123
- resource "aws_iam_role" "events_invoke_ecs" {
1124
- count = var.backup_enabled ? 1 : 0
1125
- name = "\${local.app_slug}-events-invoke-ecs-role"
1126
-
1127
- assume_role_policy = jsonencode({
1128
- Version = "2012-10-17",
1129
- Statement = [{
1130
- Effect = "Allow",
1131
- Principal = { Service = "events.amazonaws.com" },
1132
- Action = "sts:AssumeRole"
1133
- }]
1134
- })
1135
-
1136
- tags = local.common_tags
1137
- }
1138
-
1139
- resource "aws_iam_role_policy" "events_invoke_ecs" {
1140
- count = var.backup_enabled ? 1 : 0
1141
- name = "\${local.app_slug}-events-invoke-ecs-policy"
1142
- role = aws_iam_role.events_invoke_ecs[0].id
1143
-
1144
- policy = jsonencode({
1145
- Version = "2012-10-17",
1146
- Statement = [
1147
- {
1148
- Effect = "Allow",
1149
- Action = ["ecs:RunTask"],
1150
- Resource = [aws_ecs_task_definition.backup[0].arn]
1151
- },
1152
- {
1153
- Effect = "Allow",
1154
- Action = ["iam:PassRole"],
1155
- Resource = [
1156
- aws_iam_role.ecs_task_execution.arn,
1157
- aws_iam_role.ecs_task_role.arn
1158
- ]
1159
- }
1160
- ]
1161
- })
1162
- }
1163
-
1164
- resource "aws_cloudwatch_event_target" "backup" {
1165
- count = var.backup_enabled ? 1 : 0
1166
- rule = aws_cloudwatch_event_rule.backup[0].name
1167
- arn = aws_ecs_cluster.main.arn
1168
- role_arn = aws_iam_role.events_invoke_ecs[0].arn
1169
-
1170
- ecs_target {
1171
- launch_type = "EC2"
1172
- task_definition_arn = aws_ecs_task_definition.backup[0].arn
1173
- task_count = 1
1174
- }
1175
- }
1176
- `.trimStart();
1177
- }
1178
- export function renderOutputsTf() {
1179
- return `
1180
- output "web_url" {
1181
- value = "https://\${var.web_host}"
1182
- }
1183
-
1184
- output "api_url" {
1185
- value = "https://\${var.api_host}"
1186
- }
1187
-
1188
- output "auth_url" {
1189
- value = "https://\${var.auth_host}"
1190
- }
1191
-
1192
- output "admin_url" {
1193
- value = "https://\${var.admin_host}"
1194
- }
1195
-
1196
- output "backup_bucket_name" {
1197
- value = aws_s3_bucket.backups.bucket
1198
- }
1199
- `.trimStart();
1200
- }
1201
- export function renderPostgresInitSql(manifest) {
1202
- const authUser = manifest.runtime.secrets.authDbUser.replace(/'/g, "''");
1203
- const authPassword = manifest.runtime.secrets.authDbPassword.replace(/'/g, "''");
1204
- const authDbName = manifest.runtime.secrets.authDbName.replace(/'/g, "''");
1205
- const apiUser = manifest.runtime.secrets.apiDbUser.replace(/'/g, "''");
1206
- const apiPassword = manifest.runtime.secrets.apiDbPassword.replace(/'/g, "''");
1207
- const apiDbName = manifest.runtime.secrets.apiDbName.replace(/'/g, "''");
1208
- return `
1209
- DO
1210
- $$
1211
- BEGIN
1212
- IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '${authUser}') THEN
1213
- CREATE ROLE ${authUser} LOGIN PASSWORD '${authPassword}';
1214
- END IF;
1215
- END
1216
- $$;
1217
-
1218
- DO
1219
- $$
1220
- BEGIN
1221
- IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '${apiUser}') THEN
1222
- CREATE ROLE ${apiUser} LOGIN PASSWORD '${apiPassword}';
1223
- END IF;
1224
- END
1225
- $$;
1226
-
1227
- CREATE DATABASE ${authDbName} OWNER ${authUser};
1228
- CREATE DATABASE ${apiDbName} OWNER ${apiUser};
1229
-
1230
- GRANT ALL PRIVILEGES ON DATABASE ${authDbName} TO ${authUser};
1231
- GRANT ALL PRIVILEGES ON DATABASE ${apiDbName} TO ${apiUser};
1232
- `.trimStart();
1233
- }
1234
- export function renderPostgresDockerfile() {
1235
- return `
1236
- FROM postgres:16
1237
- COPY init.sql /docker-entrypoint-initdb.d/001-init.sql
1238
- `.trimStart();
1239
- }
1240
- //# sourceMappingURL=template.js.map