stackport 0.4.2__tar.gz → 0.4.4__tar.gz

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 (149) hide show
  1. {stackport-0.4.2/stackport.egg-info → stackport-0.4.4}/PKG-INFO +31 -8
  2. {stackport-0.4.2 → stackport-0.4.4}/README.md +29 -6
  3. {stackport-0.4.2 → stackport-0.4.4}/backend/cli.py +37 -35
  4. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/dynamodb.py +66 -11
  5. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/resources.py +6 -0
  6. {stackport-0.4.2 → stackport-0.4.4}/pyproject.toml +2 -2
  7. {stackport-0.4.2 → stackport-0.4.4/stackport.egg-info}/PKG-INFO +31 -8
  8. {stackport-0.4.2 → stackport-0.4.4}/stackport.egg-info/SOURCES.txt +26 -26
  9. {stackport-0.4.2 → stackport-0.4.4}/tests/test_cli.py +65 -0
  10. {stackport-0.4.2 → stackport-0.4.4}/tests/test_dynamodb_routes.py +167 -0
  11. stackport-0.4.4/tests/test_registries.py +142 -0
  12. stackport-0.4.2/ui/dist/assets/CloudscapeAbout-DccVyQFu.js → stackport-0.4.4/ui/dist/assets/CloudscapeAbout-BRbsDskR.js +1 -1
  13. stackport-0.4.2/ui/dist/assets/CloudscapeDashboard-DtV4-eQq.js → stackport-0.4.4/ui/dist/assets/CloudscapeDashboard-j2f4Elz2.js +1 -1
  14. stackport-0.4.2/ui/dist/assets/CloudscapeLearn-DW6RZAj4.js → stackport-0.4.4/ui/dist/assets/CloudscapeLearn-CVW3k3X0.js +1 -1
  15. stackport-0.4.2/ui/dist/assets/CloudscapeResourceBrowser-FpsXtjf_.js → stackport-0.4.4/ui/dist/assets/CloudscapeResourceBrowser-CgWdgn20.js +10 -10
  16. stackport-0.4.2/ui/dist/assets/CloudscapeSettings-UdgiI6dm.js → stackport-0.4.4/ui/dist/assets/CloudscapeSettings-CIXFvPKv.js +1 -1
  17. stackport-0.4.2/ui/dist/assets/CloudscapeShell-Be6uFzsz.js → stackport-0.4.4/ui/dist/assets/CloudscapeShell-DNfhSg0O.js +1 -1
  18. stackport-0.4.2/ui/dist/assets/StateMachineGraph-G76o42iO.js → stackport-0.4.4/ui/dist/assets/StateMachineGraph-DvF_kIOO.js +1 -1
  19. stackport-0.4.2/ui/dist/assets/index-B3zGVRXf.js → stackport-0.4.4/ui/dist/assets/index-BC7E-Fm4.js +1 -1
  20. stackport-0.4.2/ui/dist/assets/index-hhBdqMqs.js → stackport-0.4.4/ui/dist/assets/index-BLdRlH9d.js +2 -2
  21. stackport-0.4.2/ui/dist/assets/index-B3bka_BF.js → stackport-0.4.4/ui/dist/assets/index-Ba-oOcd3.js +1 -1
  22. stackport-0.4.2/ui/dist/assets/index-DhD1oh4N.js → stackport-0.4.4/ui/dist/assets/index-BkSgVQ0X.js +1 -1
  23. stackport-0.4.2/ui/dist/assets/index-CNWD72L_.js → stackport-0.4.4/ui/dist/assets/index-C5z3cir-.js +1 -1
  24. stackport-0.4.2/ui/dist/assets/index-B33aGwWE.js → stackport-0.4.4/ui/dist/assets/index-Dw6TGweb.js +1 -1
  25. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/index.html +1 -1
  26. stackport-0.4.2/tests/test_registries.py +0 -69
  27. {stackport-0.4.2 → stackport-0.4.4}/LICENSE +0 -0
  28. {stackport-0.4.2 → stackport-0.4.4}/MANIFEST.in +0 -0
  29. {stackport-0.4.2 → stackport-0.4.4}/backend/__init__.py +0 -0
  30. {stackport-0.4.2 → stackport-0.4.4}/backend/aws_client.py +0 -0
  31. {stackport-0.4.2 → stackport-0.4.4}/backend/cache.py +0 -0
  32. {stackport-0.4.2 → stackport-0.4.4}/backend/config.py +0 -0
  33. {stackport-0.4.2 → stackport-0.4.4}/backend/endpoint_store.py +0 -0
  34. {stackport-0.4.2 → stackport-0.4.4}/backend/learn/__init__.py +0 -0
  35. {stackport-0.4.2 → stackport-0.4.4}/backend/learn/content.py +0 -0
  36. {stackport-0.4.2 → stackport-0.4.4}/backend/learn/hotspots.py +0 -0
  37. {stackport-0.4.2 → stackport-0.4.4}/backend/learn/trails/aws-first-steps.json +0 -0
  38. {stackport-0.4.2 → stackport-0.4.4}/backend/learn/verify.py +0 -0
  39. {stackport-0.4.2 → stackport-0.4.4}/backend/learn_store.py +0 -0
  40. {stackport-0.4.2 → stackport-0.4.4}/backend/main.py +0 -0
  41. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/__init__.py +0 -0
  42. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/common.py +0 -0
  43. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/ec2.py +0 -0
  44. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/endpoints.py +0 -0
  45. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/iam.py +0 -0
  46. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/lambda_svc.py +0 -0
  47. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/learn.py +0 -0
  48. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/logs.py +0 -0
  49. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/monitoring.py +0 -0
  50. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/rds.py +0 -0
  51. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/s3.py +0 -0
  52. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/secretsmanager.py +0 -0
  53. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/sns.py +0 -0
  54. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/sqs.py +0 -0
  55. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/stats.py +0 -0
  56. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/stepfunctions.py +0 -0
  57. {stackport-0.4.2 → stackport-0.4.4}/backend/routes/tags.py +0 -0
  58. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/__init__.py +0 -0
  59. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/dynamodb.py +0 -0
  60. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/endpoints.py +0 -0
  61. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/lambda_svc.py +0 -0
  62. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/learn.py +0 -0
  63. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/logs.py +0 -0
  64. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/monitoring.py +0 -0
  65. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/s3.py +0 -0
  66. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/secretsmanager.py +0 -0
  67. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/sns.py +0 -0
  68. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/sqs.py +0 -0
  69. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/stepfunctions.py +0 -0
  70. {stackport-0.4.2 → stackport-0.4.4}/backend/schemas/tags.py +0 -0
  71. {stackport-0.4.2 → stackport-0.4.4}/backend/websocket.py +0 -0
  72. {stackport-0.4.2 → stackport-0.4.4}/setup.cfg +0 -0
  73. {stackport-0.4.2 → stackport-0.4.4}/stackport.egg-info/dependency_links.txt +0 -0
  74. {stackport-0.4.2 → stackport-0.4.4}/stackport.egg-info/entry_points.txt +0 -0
  75. {stackport-0.4.2 → stackport-0.4.4}/stackport.egg-info/requires.txt +0 -0
  76. {stackport-0.4.2 → stackport-0.4.4}/stackport.egg-info/top_level.txt +0 -0
  77. {stackport-0.4.2 → stackport-0.4.4}/tests/test_cache.py +0 -0
  78. {stackport-0.4.2 → stackport-0.4.4}/tests/test_client.py +0 -0
  79. {stackport-0.4.2 → stackport-0.4.4}/tests/test_config.py +0 -0
  80. {stackport-0.4.2 → stackport-0.4.4}/tests/test_create_resources.py +0 -0
  81. {stackport-0.4.2 → stackport-0.4.4}/tests/test_ec2_routes.py +0 -0
  82. {stackport-0.4.2 → stackport-0.4.4}/tests/test_endpoint_store.py +0 -0
  83. {stackport-0.4.2 → stackport-0.4.4}/tests/test_endpoints.py +0 -0
  84. {stackport-0.4.2 → stackport-0.4.4}/tests/test_iam_routes.py +0 -0
  85. {stackport-0.4.2 → stackport-0.4.4}/tests/test_lambda_routes.py +0 -0
  86. {stackport-0.4.2 → stackport-0.4.4}/tests/test_learn_content.py +0 -0
  87. {stackport-0.4.2 → stackport-0.4.4}/tests/test_learn_routes.py +0 -0
  88. {stackport-0.4.2 → stackport-0.4.4}/tests/test_learn_store.py +0 -0
  89. {stackport-0.4.2 → stackport-0.4.4}/tests/test_logs_routes.py +0 -0
  90. {stackport-0.4.2 → stackport-0.4.4}/tests/test_logs_tail.py +0 -0
  91. {stackport-0.4.2 → stackport-0.4.4}/tests/test_monitoring_routes.py +0 -0
  92. {stackport-0.4.2 → stackport-0.4.4}/tests/test_multi_endpoint.py +0 -0
  93. {stackport-0.4.2 → stackport-0.4.4}/tests/test_rds_routes.py +0 -0
  94. {stackport-0.4.2 → stackport-0.4.4}/tests/test_readonly_middleware.py +0 -0
  95. {stackport-0.4.2 → stackport-0.4.4}/tests/test_routes.py +0 -0
  96. {stackport-0.4.2 → stackport-0.4.4}/tests/test_s3_routes.py +0 -0
  97. {stackport-0.4.2 → stackport-0.4.4}/tests/test_s3_upload_limit_env.py +0 -0
  98. {stackport-0.4.2 → stackport-0.4.4}/tests/test_secretsmanager_routes.py +0 -0
  99. {stackport-0.4.2 → stackport-0.4.4}/tests/test_sns_routes.py +0 -0
  100. {stackport-0.4.2 → stackport-0.4.4}/tests/test_sqs_routes.py +0 -0
  101. {stackport-0.4.2 → stackport-0.4.4}/tests/test_tags_routes.py +0 -0
  102. {stackport-0.4.2 → stackport-0.4.4}/tests/test_websocket.py +0 -0
  103. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/CloudscapeResourceBrowser-CzwePG-V.css +0 -0
  104. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/CloudscapeShell-BXHVWjDE.css +0 -0
  105. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/index-DE--A2EE.css +0 -0
  106. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/index-DKIdXFX0.css +0 -0
  107. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/index-DNBdeKId.css +0 -0
  108. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/index-DoY0HscW.css +0 -0
  109. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/index-DrkkXAoG.css +0 -0
  110. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/index-Vf0UFiCT.css +0 -0
  111. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/stepfunctions-graph-B6nvU4OB.js +0 -0
  112. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/assets/utils-Cfmwk2UT.js +0 -0
  113. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/acm.svg +0 -0
  114. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/apigateway.svg +0 -0
  115. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/appsync.svg +0 -0
  116. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/athena.svg +0 -0
  117. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/cloudformation.svg +0 -0
  118. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/cloudfront.svg +0 -0
  119. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/cognito-idp.svg +0 -0
  120. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/dynamodb.svg +0 -0
  121. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/ec2.svg +0 -0
  122. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/ecr.svg +0 -0
  123. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/ecs.svg +0 -0
  124. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/elasticache.svg +0 -0
  125. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/elasticfilesystem.svg +0 -0
  126. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/elasticloadbalancing.svg +0 -0
  127. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/elasticmapreduce.svg +0 -0
  128. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/events.svg +0 -0
  129. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/firehose.svg +0 -0
  130. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/glue.svg +0 -0
  131. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/iam.svg +0 -0
  132. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/kinesis.svg +0 -0
  133. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/kms.svg +0 -0
  134. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/lambda.svg +0 -0
  135. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/logs.svg +0 -0
  136. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/monitoring.svg +0 -0
  137. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/rds.svg +0 -0
  138. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/route53.svg +0 -0
  139. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/s3.svg +0 -0
  140. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/secretsmanager.svg +0 -0
  141. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/ses.svg +0 -0
  142. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/sns.svg +0 -0
  143. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/sqs.svg +0 -0
  144. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/ssm.svg +0 -0
  145. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/stepfunctions.svg +0 -0
  146. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/aws-icons/wafv2.svg +0 -0
  147. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/favicon.png +0 -0
  148. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/favicon.svg +0 -0
  149. {stackport-0.4.2 → stackport-0.4.4}/ui/dist/stackport-logo.svg +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackport
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Universal AWS resource browser for local emulators
5
5
  Author: Davi Reis Vieira
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/DaviReisVieira/stackport
8
8
  Project-URL: Repository, https://github.com/DaviReisVieira/stackport
9
9
  Project-URL: Issues, https://github.com/DaviReisVieira/stackport/issues
10
- Keywords: aws,ministack,localstack,browser,devtools,emulator
10
+ Keywords: aws,ministack,floci,localstack,browser,devtools,emulator
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Framework :: FastAPI
13
13
  Classifier: Intended Audience :: Developers
@@ -75,15 +75,18 @@ Dynamic: license-file
75
75
  - **CLI** — `stackport status`, `list`, `describe`, `export` with JSON/CSV/table output
76
76
  - **Real-time dashboard** with WebSocket-powered live updates
77
77
  - **Keyboard shortcuts** — 16 shortcuts for fast navigation (press `?` to view)
78
- - Single Docker image, works with MiniStack, LocalStack, Moto, or any AWS-compatible endpoint
78
+ - Single Docker image, works with [MiniStack](https://github.com/ministackorg/ministack), [Floci](https://github.com/floci-io/floci), LocalStack, Moto, or any AWS-compatible endpoint
79
79
 
80
80
  ## Quick Start
81
81
 
82
- ### With a local emulator (recommended)
82
+ ### With a local emulator
83
+
84
+ Start whichever emulator you use, then point StackPort at it:
83
85
 
84
86
  ```bash
85
- # Start MiniStack (or LocalStack, Moto, etc.)
86
- pip install ministack && ministack
87
+ # Start an emulator on :4566 (pick one)
88
+ docker run -d -p 4566:4566 ministackorg/ministack # MiniStack
89
+ docker run -d -p 4566:4566 floci/floci # Floci
87
90
 
88
91
  # Start StackPort
89
92
  pip install stackport
@@ -108,16 +111,32 @@ You can also configure per-endpoint authentication from the Settings UI — sele
108
111
 
109
112
  When connected to real AWS, StackPort shows a warning banner and operates in read-only mode unless writes are explicitly enabled.
110
113
 
111
- ### Docker Compose (MiniStack + StackPort)
114
+ ### Docker Compose
115
+
116
+ Each example runs the emulator, StackPort, and a one-shot `seed` container that creates a few resources (S3, SQS, SNS, DynamoDB, Secrets Manager, IAM, Logs, Step Functions) so the dashboard has something to show on first load. Both use the same seed. Swap in LocalStack, Moto, or any AWS-compatible endpoint by replacing the emulator service and updating `AWS_ENDPOINT_URL`.
112
117
 
113
- This example uses [MiniStack](https://github.com/Nahuel990/ministack) as the emulator, but you can swap it for LocalStack, Moto, or any AWS-compatible endpoint — just update `AWS_ENDPOINT_URL`.
118
+ **MiniStack + StackPort** — [`examples/docker-compose.yml`](examples/docker-compose.yml)
114
119
 
115
120
  ```bash
116
121
  curl -O https://raw.githubusercontent.com/DaviReisVieira/stackport/main/examples/docker-compose.yml
117
122
  docker compose up -d
118
123
  # Open http://localhost:8080
124
+ # If the dashboard is empty: docker compose logs seed
119
125
  ```
120
126
 
127
+ [MiniStack](https://github.com/ministackorg/ministack) derives the URLs it returns (SQS queue URLs, pre-signed URLs) from the request `Host`, so they resolve from other containers and from the host without extra configuration.
128
+
129
+ **Floci + StackPort** — [`examples/docker-compose.floci.yml`](examples/docker-compose.floci.yml)
130
+
131
+ ```bash
132
+ curl -O https://raw.githubusercontent.com/DaviReisVieira/stackport/main/examples/docker-compose.floci.yml
133
+ docker compose -f docker-compose.floci.yml up -d
134
+ # Open http://localhost:8080
135
+ # If the dashboard is empty: docker compose -f docker-compose.floci.yml logs seed
136
+ ```
137
+
138
+ [Floci](https://github.com/floci-io/floci) takes the hostname for the URLs it returns from `FLOCI_HOSTNAME`, which the example sets to `floci` so they resolve from other containers. From the host, reach the same resources through `http://localhost:4566`.
139
+
121
140
  ### Docker (standalone)
122
141
 
123
142
  ```bash
@@ -351,6 +370,10 @@ ui/src/
351
370
  lib/ API client, types, service icons, utils
352
371
  ```
353
372
 
373
+ ## Origin
374
+
375
+ StackPort started as a web UI pull request to [MiniStack](https://github.com/ministackorg/ministack) ([ministack#116](https://github.com/ministackorg/ministack/pull/116)). The MiniStack maintainers suggested it would be more useful as a standalone tool that talks to any emulator over the standard AWS API, and that is what it became. Thanks to [@Nahuel990](https://github.com/Nahuel990) and [@McDoit](https://github.com/McDoit) for pushing it in that direction.
376
+
354
377
  ## Contributors
355
378
 
356
379
  <a href="https://github.com/DaviReisVieira/stackport/graphs/contributors">
@@ -39,15 +39,18 @@
39
39
  - **CLI** — `stackport status`, `list`, `describe`, `export` with JSON/CSV/table output
40
40
  - **Real-time dashboard** with WebSocket-powered live updates
41
41
  - **Keyboard shortcuts** — 16 shortcuts for fast navigation (press `?` to view)
42
- - Single Docker image, works with MiniStack, LocalStack, Moto, or any AWS-compatible endpoint
42
+ - Single Docker image, works with [MiniStack](https://github.com/ministackorg/ministack), [Floci](https://github.com/floci-io/floci), LocalStack, Moto, or any AWS-compatible endpoint
43
43
 
44
44
  ## Quick Start
45
45
 
46
- ### With a local emulator (recommended)
46
+ ### With a local emulator
47
+
48
+ Start whichever emulator you use, then point StackPort at it:
47
49
 
48
50
  ```bash
49
- # Start MiniStack (or LocalStack, Moto, etc.)
50
- pip install ministack && ministack
51
+ # Start an emulator on :4566 (pick one)
52
+ docker run -d -p 4566:4566 ministackorg/ministack # MiniStack
53
+ docker run -d -p 4566:4566 floci/floci # Floci
51
54
 
52
55
  # Start StackPort
53
56
  pip install stackport
@@ -72,16 +75,32 @@ You can also configure per-endpoint authentication from the Settings UI — sele
72
75
 
73
76
  When connected to real AWS, StackPort shows a warning banner and operates in read-only mode unless writes are explicitly enabled.
74
77
 
75
- ### Docker Compose (MiniStack + StackPort)
78
+ ### Docker Compose
79
+
80
+ Each example runs the emulator, StackPort, and a one-shot `seed` container that creates a few resources (S3, SQS, SNS, DynamoDB, Secrets Manager, IAM, Logs, Step Functions) so the dashboard has something to show on first load. Both use the same seed. Swap in LocalStack, Moto, or any AWS-compatible endpoint by replacing the emulator service and updating `AWS_ENDPOINT_URL`.
76
81
 
77
- This example uses [MiniStack](https://github.com/Nahuel990/ministack) as the emulator, but you can swap it for LocalStack, Moto, or any AWS-compatible endpoint — just update `AWS_ENDPOINT_URL`.
82
+ **MiniStack + StackPort** — [`examples/docker-compose.yml`](examples/docker-compose.yml)
78
83
 
79
84
  ```bash
80
85
  curl -O https://raw.githubusercontent.com/DaviReisVieira/stackport/main/examples/docker-compose.yml
81
86
  docker compose up -d
82
87
  # Open http://localhost:8080
88
+ # If the dashboard is empty: docker compose logs seed
83
89
  ```
84
90
 
91
+ [MiniStack](https://github.com/ministackorg/ministack) derives the URLs it returns (SQS queue URLs, pre-signed URLs) from the request `Host`, so they resolve from other containers and from the host without extra configuration.
92
+
93
+ **Floci + StackPort** — [`examples/docker-compose.floci.yml`](examples/docker-compose.floci.yml)
94
+
95
+ ```bash
96
+ curl -O https://raw.githubusercontent.com/DaviReisVieira/stackport/main/examples/docker-compose.floci.yml
97
+ docker compose -f docker-compose.floci.yml up -d
98
+ # Open http://localhost:8080
99
+ # If the dashboard is empty: docker compose -f docker-compose.floci.yml logs seed
100
+ ```
101
+
102
+ [Floci](https://github.com/floci-io/floci) takes the hostname for the URLs it returns from `FLOCI_HOSTNAME`, which the example sets to `floci` so they resolve from other containers. From the host, reach the same resources through `http://localhost:4566`.
103
+
85
104
  ### Docker (standalone)
86
105
 
87
106
  ```bash
@@ -315,6 +334,10 @@ ui/src/
315
334
  lib/ API client, types, service icons, utils
316
335
  ```
317
336
 
337
+ ## Origin
338
+
339
+ StackPort started as a web UI pull request to [MiniStack](https://github.com/ministackorg/ministack) ([ministack#116](https://github.com/ministackorg/ministack/pull/116)). The MiniStack maintainers suggested it would be more useful as a standalone tool that talks to any emulator over the standard AWS API, and that is what it became. Thanks to [@Nahuel990](https://github.com/Nahuel990) and [@McDoit](https://github.com/McDoit) for pushing it in that direction.
340
+
318
341
  ## Contributors
319
342
 
320
343
  <a href="https://github.com/DaviReisVieira/stackport/graphs/contributors">
@@ -10,13 +10,37 @@ import click
10
10
  import uvicorn
11
11
 
12
12
  from backend.aws_client import get_client
13
- from backend.config import AWS_ENDPOINT_URL, AWS_REGION, LOG_LEVEL, STACKPORT_PORT, STACKPORT_SERVICES
14
- from backend.routes.resources import DESCRIBE_REGISTRY, _extract_id, _serialize
13
+ from backend.config import (
14
+ AWS_ENDPOINT_URL,
15
+ AWS_REGION,
16
+ LOG_LEVEL,
17
+ STACKPORT_PORT,
18
+ STACKPORT_SERVICES,
19
+ endpoint_store,
20
+ )
21
+ from backend.routes.resources import (
22
+ DESCRIBE_REGISTRY,
23
+ _PREFERRED_ID_FIELD,
24
+ _extract_id,
25
+ _serialize,
26
+ )
15
27
  from backend.routes.stats import SERVICE_REGISTRY, _METHOD_KWARGS, _count_items, _probe_service
16
28
 
17
29
  logger = logging.getLogger(__name__)
18
30
 
19
31
 
32
+ def _client_kwargs(endpoint: str | None, region: str | None) -> dict:
33
+ """Resolve where a CLI command talks to.
34
+
35
+ An explicit --endpoint (or AWS_ENDPOINT_URL at startup) wins. Without one,
36
+ fall back to the saved default endpoint, the same one the web UI uses.
37
+ Passing the result to get_client explicitly is what makes the flag work
38
+ once ~/.stackport/endpoints.json exists.
39
+ """
40
+ url = endpoint if endpoint is not None else endpoint_store.get_default_url()
41
+ return {"endpoint_url": url, "region": region}
42
+
43
+
20
44
  @click.group(invoke_without_command=True)
21
45
  @click.version_option(package_name="stackport")
22
46
  @click.pass_context
@@ -39,22 +63,14 @@ def serve(port):
39
63
  @click.option("--output", type=click.Choice(["json", "table"]), default="table", help="Output format")
40
64
  def status(endpoint, region, output):
41
65
  """Show all services with availability and resource counts."""
42
- # Override endpoint/region if provided
43
- if endpoint != AWS_ENDPOINT_URL:
44
- import os
45
-
46
- os.environ["AWS_ENDPOINT_URL"] = endpoint
47
- if region != AWS_REGION:
48
- import os
49
-
50
- os.environ["AWS_REGION"] = region
66
+ client_kwargs = _client_kwargs(endpoint, region)
51
67
 
52
68
  enabled_services = [s.strip() for s in STACKPORT_SERVICES.split(",") if s.strip()]
53
69
  services = {}
54
70
 
55
71
  try:
56
72
  with ThreadPoolExecutor(max_workers=min(len(enabled_services), 10)) as executor:
57
- futures = {executor.submit(_probe_service, svc): svc for svc in enabled_services}
73
+ futures = {executor.submit(_probe_service, svc, **client_kwargs): svc for svc in enabled_services}
58
74
  for future in as_completed(futures):
59
75
  svc_name, result = future.result()
60
76
  services[svc_name] = result
@@ -89,15 +105,7 @@ def status(endpoint, region, output):
89
105
  @click.option("--output", type=click.Choice(["json", "table", "csv"]), default="table", help="Output format")
90
106
  def list(service, endpoint, region, output):
91
107
  """List resources for a service."""
92
- # Override endpoint/region if provided
93
- if endpoint != AWS_ENDPOINT_URL:
94
- import os
95
-
96
- os.environ["AWS_ENDPOINT_URL"] = endpoint
97
- if region != AWS_REGION:
98
- import os
99
-
100
- os.environ["AWS_REGION"] = region
108
+ client_kwargs = _client_kwargs(endpoint, region)
101
109
 
102
110
  registry_entries = SERVICE_REGISTRY.get(service)
103
111
  if not registry_entries:
@@ -109,7 +117,7 @@ def list(service, endpoint, region, output):
109
117
  resources = {}
110
118
  for resource_type, boto3_service, method_name, response_key in registry_entries:
111
119
  try:
112
- client = get_client(boto3_service)
120
+ client = get_client(boto3_service, **client_kwargs)
113
121
  method = getattr(client, method_name)
114
122
  kwargs = _METHOD_KWARGS.get((boto3_service, method_name), {})
115
123
  resp = method(**kwargs)
@@ -129,8 +137,9 @@ def list(service, endpoint, region, output):
129
137
  writer = csv.writer(sys.stdout)
130
138
  writer.writerow(["service", "resource_type", "resource_id", "name"])
131
139
  for resource_type, items in resources.items():
140
+ preferred = _PREFERRED_ID_FIELD.get((service, resource_type))
132
141
  for item in items:
133
- resource_id = _extract_id(item)
142
+ resource_id = _extract_id(item, preferred)
134
143
  name = item.get("Name", item.get("FunctionName", item.get("TableName", resource_id))) if isinstance(item, dict) else resource_id
135
144
  writer.writerow([service, resource_type, resource_id, name])
136
145
  else:
@@ -139,8 +148,9 @@ def list(service, endpoint, region, output):
139
148
  if items:
140
149
  click.echo(f"\n{resource_type.upper()} ({len(items)}):")
141
150
  click.echo("-" * 60)
151
+ preferred = _PREFERRED_ID_FIELD.get((service, resource_type))
142
152
  for item in items[:20]: # Limit to first 20 for readability
143
- resource_id = _extract_id(item)
153
+ resource_id = _extract_id(item, preferred)
144
154
  if isinstance(item, dict):
145
155
  name = item.get("Name", item.get("FunctionName", item.get("TableName", "")))
146
156
  if name and name != resource_id:
@@ -164,20 +174,12 @@ def list(service, endpoint, region, output):
164
174
  @click.option("--output", type=click.Choice(["json", "table"]), default="json", help="Output format")
165
175
  def describe(service, resource_type, resource_id, endpoint, region, output):
166
176
  """Describe a specific resource."""
167
- # Override endpoint/region if provided
168
- if endpoint != AWS_ENDPOINT_URL:
169
- import os
170
-
171
- os.environ["AWS_ENDPOINT_URL"] = endpoint
172
- if region != AWS_REGION:
173
- import os
174
-
175
- os.environ["AWS_REGION"] = region
177
+ client_kwargs = _client_kwargs(endpoint, region)
176
178
 
177
179
  # Special case for WAFv2
178
180
  if (service, resource_type) == ("wafv2", "web_acls"):
179
181
  try:
180
- client = get_client("wafv2")
182
+ client = get_client("wafv2", **client_kwargs)
181
183
  acls = client.list_web_acls(Scope="REGIONAL").get("WebACLs", [])
182
184
  match = next((a for a in acls if a.get("Name") == resource_id), None)
183
185
  if not match:
@@ -211,7 +213,7 @@ def describe(service, resource_type, resource_id, endpoint, region, output):
211
213
  }
212
214
 
213
215
  try:
214
- client = get_client(boto3_service)
216
+ client = get_client(boto3_service, **client_kwargs)
215
217
  method = getattr(client, method_name)
216
218
  if id_param in _LIST_PARAMS:
217
219
  resp = method(**{id_param: [resource_id]})
@@ -1,4 +1,5 @@
1
1
  import base64
2
+ import binascii
2
3
  import json
3
4
  import logging
4
5
  from typing import Any
@@ -27,6 +28,58 @@ def _plain_to_dynamodb_item(plain: dict[str, Any]) -> dict[str, Any]:
27
28
  return {k: _serializer.serialize(v) for k, v in plain.items()}
28
29
 
29
30
 
31
+ # Binary (B / BS) travels as base64 over StackPort's HTTP API, the same way AWS
32
+ # writes it in DynamoDB JSON. boto3 hands us raw bytes, which JSON cannot carry.
33
+
34
+
35
+ def _to_json_safe(obj: Any) -> Any:
36
+ """Replace every bytes value with its base64 text. Safe to apply to whole
37
+ low-level responses: DynamoDB only returns bytes inside B and BS."""
38
+ if isinstance(obj, (bytes, bytearray)):
39
+ return base64.b64encode(obj).decode("ascii")
40
+ if isinstance(obj, dict):
41
+ return {k: _to_json_safe(v) for k, v in obj.items()}
42
+ if isinstance(obj, list):
43
+ return [_to_json_safe(v) for v in obj]
44
+ return obj
45
+
46
+
47
+ def _b64_to_bytes(value: Any, name: str) -> bytes:
48
+ if isinstance(value, (bytes, bytearray)):
49
+ return bytes(value)
50
+ try:
51
+ return base64.b64decode(value, validate=True)
52
+ except (binascii.Error, TypeError, ValueError) as e:
53
+ raise HTTPException(status_code=400, detail=f"Attribute {name!r} is not valid base64 for type B") from e
54
+
55
+
56
+ def _attr_from_json(av: Any, name: str) -> Any:
57
+ """Inverse of _to_json_safe for one DynamoDB attribute value. Type-aware, so
58
+ strings under S stay strings and only B / BS are decoded."""
59
+ if not isinstance(av, dict):
60
+ return av
61
+ if "B" in av:
62
+ return {**av, "B": _b64_to_bytes(av["B"], name)}
63
+ if "BS" in av and isinstance(av["BS"], list):
64
+ return {**av, "BS": [_b64_to_bytes(v, name) for v in av["BS"]]}
65
+ if "L" in av and isinstance(av["L"], list):
66
+ return {**av, "L": [_attr_from_json(v, name) for v in av["L"]]}
67
+ if "M" in av and isinstance(av["M"], dict):
68
+ return {**av, "M": {k: _attr_from_json(v, f"{name}.{k}") for k, v in av["M"].items()}}
69
+ return av
70
+
71
+
72
+ def _item_from_json(item: dict[str, Any]) -> dict[str, Any]:
73
+ return {k: _attr_from_json(v, k) for k, v in item.items()}
74
+
75
+
76
+ def _key_value(attr_type: str, value: str, name: str) -> dict[str, Any]:
77
+ """Typed key value for a query condition. Binary keys arrive as base64."""
78
+ if attr_type == "B":
79
+ return {"B": _b64_to_bytes(value, name)}
80
+ return {attr_type: value}
81
+
82
+
30
83
  def _get_partition_sort_keys(dynamodb: Any, table_name: str) -> tuple[str | None, str | None]:
31
84
  table_resp = dynamodb.describe_table(TableName=table_name)
32
85
  key_schema = table_resp["Table"].get("KeySchema", [])
@@ -56,7 +109,7 @@ def _coerce_item_dict(raw: dict[str, Any], item_format: str) -> dict[str, Any]:
56
109
  return _plain_to_dynamodb_item(raw)
57
110
  except TypeError as e:
58
111
  raise HTTPException(status_code=400, detail=f"Could not convert plain item to DynamoDB types: {e}") from e
59
- return raw
112
+ return _item_from_json(raw)
60
113
 
61
114
 
62
115
  def _coerce_key_dict(raw: dict[str, Any], item_format: str) -> dict[str, Any]:
@@ -65,7 +118,7 @@ def _coerce_key_dict(raw: dict[str, Any], item_format: str) -> dict[str, Any]:
65
118
  return _plain_to_dynamodb_item(raw)
66
119
  except TypeError as e:
67
120
  raise HTTPException(status_code=400, detail=f"Could not convert plain key to DynamoDB types: {e}") from e
68
- return raw
121
+ return _item_from_json(raw)
69
122
 
70
123
 
71
124
  def _invalidate_table_item_count(table_name: str, endpoint_url: str | None) -> None:
@@ -237,7 +290,7 @@ def scan_table(
237
290
  if exclusive_start_key:
238
291
  try:
239
292
  decoded = base64.b64decode(exclusive_start_key).decode("utf-8")
240
- scan_params["ExclusiveStartKey"] = json.loads(decoded)
293
+ scan_params["ExclusiveStartKey"] = _item_from_json(json.loads(decoded))
241
294
  except Exception:
242
295
  logger.debug("Invalid exclusive_start_key", exc_info=True)
243
296
 
@@ -248,11 +301,11 @@ def scan_table(
248
301
  last_evaluated_key = resp.get("LastEvaluatedKey")
249
302
  next_token = None
250
303
  if last_evaluated_key:
251
- next_token = base64.b64encode(json.dumps(last_evaluated_key).encode("utf-8")).decode("utf-8")
304
+ next_token = base64.b64encode(json.dumps(_to_json_safe(last_evaluated_key)).encode("utf-8")).decode("utf-8")
252
305
 
253
306
  return {
254
307
  "table": name,
255
- "items": items,
308
+ "items": _to_json_safe(items),
256
309
  "count": len(items),
257
310
  "scanned_count": resp.get("ScannedCount", len(items)),
258
311
  "next_token": next_token,
@@ -285,19 +338,19 @@ def query_table(name: str, request: QueryRequest, ep: EndpointInfo = Depends(get
285
338
  # Build key condition expression
286
339
  key_condition = f"#{partition_key} = :pk"
287
340
  expression_attr_names = {f"#{partition_key}": partition_key}
288
- expression_attr_values = {":pk": {partition_key_type: request.partition_key_value}}
341
+ expression_attr_values = {":pk": _key_value(partition_key_type, request.partition_key_value, partition_key)}
289
342
 
290
343
  if sort_key and request.sort_key_value:
291
344
  sort_key_type = attribute_defs.get(sort_key, "S")
292
345
  if request.sort_key_operator == "=":
293
346
  key_condition += f" AND #{sort_key} = :sk"
294
- expression_attr_values[":sk"] = {sort_key_type: request.sort_key_value}
347
+ expression_attr_values[":sk"] = _key_value(sort_key_type, request.sort_key_value, sort_key)
295
348
  elif request.sort_key_operator in ("<", "<=", ">", ">="):
296
349
  key_condition += f" AND #{sort_key} {request.sort_key_operator} :sk"
297
- expression_attr_values[":sk"] = {sort_key_type: request.sort_key_value}
350
+ expression_attr_values[":sk"] = _key_value(sort_key_type, request.sort_key_value, sort_key)
298
351
  elif request.sort_key_operator == "BEGINS_WITH":
299
352
  key_condition += f" AND begins_with(#{sort_key}, :sk)"
300
- expression_attr_values[":sk"] = {sort_key_type: request.sort_key_value}
353
+ expression_attr_values[":sk"] = _key_value(sort_key_type, request.sort_key_value, sort_key)
301
354
  expression_attr_names[f"#{sort_key}"] = sort_key
302
355
 
303
356
  query_params = {
@@ -313,10 +366,12 @@ def query_table(name: str, request: QueryRequest, ep: EndpointInfo = Depends(get
313
366
 
314
367
  return {
315
368
  "table": name,
316
- "items": items,
369
+ "items": _to_json_safe(items),
317
370
  "count": len(items),
318
371
  "scanned_count": resp.get("ScannedCount", len(items)),
319
372
  }
373
+ except HTTPException:
374
+ raise
320
375
  except Exception as e:
321
376
  logger.error("Failed to query table %s: %s", name, e, exc_info=True)
322
377
  return {"error": str(e), "items": [], "count": 0}
@@ -411,7 +466,7 @@ def batch_write_items(
411
466
  return {
412
467
  "ok": True,
413
468
  "table": name,
414
- "unprocessed": uproc,
469
+ "unprocessed": _to_json_safe(uproc),
415
470
  "message": "Some items were not processed; retry with returned keys.",
416
471
  }
417
472
 
@@ -84,6 +84,12 @@ _PREFERRED_ID_FIELD: dict[tuple[str, str], str] = {
84
84
  ("elasticmapreduce", "clusters"): "Id",
85
85
  ("cognito-idp", "user_pools"): "Id",
86
86
  ("apigateway", "rest_apis"): "id",
87
+ ("apigateway", "apis"): "ApiId",
88
+ ("iam", "policies"): "Arn",
89
+ ("ec2", "subnets"): "SubnetId",
90
+ ("ec2", "security_groups"): "GroupId",
91
+ ("elasticfilesystem", "file_systems"): "FileSystemId",
92
+ ("rds", "db_clusters"): "DBClusterIdentifier",
87
93
  }
88
94
 
89
95
  # Known ID field names for extracting a resource identifier from list results
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "stackport"
3
- version = "0.4.2"
3
+ version = "0.4.4"
4
4
  description = "Universal AWS resource browser for local emulators"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
7
7
  requires-python = ">=3.10"
8
8
  authors = [{name = "Davi Reis Vieira"}]
9
- keywords = ["aws", "ministack", "localstack", "browser", "devtools", "emulator"]
9
+ keywords = ["aws", "ministack", "floci", "localstack", "browser", "devtools", "emulator"]
10
10
  classifiers = [
11
11
  "Development Status :: 4 - Beta",
12
12
  "Framework :: FastAPI",
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackport
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Universal AWS resource browser for local emulators
5
5
  Author: Davi Reis Vieira
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/DaviReisVieira/stackport
8
8
  Project-URL: Repository, https://github.com/DaviReisVieira/stackport
9
9
  Project-URL: Issues, https://github.com/DaviReisVieira/stackport/issues
10
- Keywords: aws,ministack,localstack,browser,devtools,emulator
10
+ Keywords: aws,ministack,floci,localstack,browser,devtools,emulator
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Framework :: FastAPI
13
13
  Classifier: Intended Audience :: Developers
@@ -75,15 +75,18 @@ Dynamic: license-file
75
75
  - **CLI** — `stackport status`, `list`, `describe`, `export` with JSON/CSV/table output
76
76
  - **Real-time dashboard** with WebSocket-powered live updates
77
77
  - **Keyboard shortcuts** — 16 shortcuts for fast navigation (press `?` to view)
78
- - Single Docker image, works with MiniStack, LocalStack, Moto, or any AWS-compatible endpoint
78
+ - Single Docker image, works with [MiniStack](https://github.com/ministackorg/ministack), [Floci](https://github.com/floci-io/floci), LocalStack, Moto, or any AWS-compatible endpoint
79
79
 
80
80
  ## Quick Start
81
81
 
82
- ### With a local emulator (recommended)
82
+ ### With a local emulator
83
+
84
+ Start whichever emulator you use, then point StackPort at it:
83
85
 
84
86
  ```bash
85
- # Start MiniStack (or LocalStack, Moto, etc.)
86
- pip install ministack && ministack
87
+ # Start an emulator on :4566 (pick one)
88
+ docker run -d -p 4566:4566 ministackorg/ministack # MiniStack
89
+ docker run -d -p 4566:4566 floci/floci # Floci
87
90
 
88
91
  # Start StackPort
89
92
  pip install stackport
@@ -108,16 +111,32 @@ You can also configure per-endpoint authentication from the Settings UI — sele
108
111
 
109
112
  When connected to real AWS, StackPort shows a warning banner and operates in read-only mode unless writes are explicitly enabled.
110
113
 
111
- ### Docker Compose (MiniStack + StackPort)
114
+ ### Docker Compose
115
+
116
+ Each example runs the emulator, StackPort, and a one-shot `seed` container that creates a few resources (S3, SQS, SNS, DynamoDB, Secrets Manager, IAM, Logs, Step Functions) so the dashboard has something to show on first load. Both use the same seed. Swap in LocalStack, Moto, or any AWS-compatible endpoint by replacing the emulator service and updating `AWS_ENDPOINT_URL`.
112
117
 
113
- This example uses [MiniStack](https://github.com/Nahuel990/ministack) as the emulator, but you can swap it for LocalStack, Moto, or any AWS-compatible endpoint — just update `AWS_ENDPOINT_URL`.
118
+ **MiniStack + StackPort** — [`examples/docker-compose.yml`](examples/docker-compose.yml)
114
119
 
115
120
  ```bash
116
121
  curl -O https://raw.githubusercontent.com/DaviReisVieira/stackport/main/examples/docker-compose.yml
117
122
  docker compose up -d
118
123
  # Open http://localhost:8080
124
+ # If the dashboard is empty: docker compose logs seed
119
125
  ```
120
126
 
127
+ [MiniStack](https://github.com/ministackorg/ministack) derives the URLs it returns (SQS queue URLs, pre-signed URLs) from the request `Host`, so they resolve from other containers and from the host without extra configuration.
128
+
129
+ **Floci + StackPort** — [`examples/docker-compose.floci.yml`](examples/docker-compose.floci.yml)
130
+
131
+ ```bash
132
+ curl -O https://raw.githubusercontent.com/DaviReisVieira/stackport/main/examples/docker-compose.floci.yml
133
+ docker compose -f docker-compose.floci.yml up -d
134
+ # Open http://localhost:8080
135
+ # If the dashboard is empty: docker compose -f docker-compose.floci.yml logs seed
136
+ ```
137
+
138
+ [Floci](https://github.com/floci-io/floci) takes the hostname for the URLs it returns from `FLOCI_HOSTNAME`, which the example sets to `floci` so they resolve from other containers. From the host, reach the same resources through `http://localhost:4566`.
139
+
121
140
  ### Docker (standalone)
122
141
 
123
142
  ```bash
@@ -351,6 +370,10 @@ ui/src/
351
370
  lib/ API client, types, service icons, utils
352
371
  ```
353
372
 
373
+ ## Origin
374
+
375
+ StackPort started as a web UI pull request to [MiniStack](https://github.com/ministackorg/ministack) ([ministack#116](https://github.com/ministackorg/ministack/pull/116)). The MiniStack maintainers suggested it would be more useful as a standalone tool that talks to any emulator over the standard AWS API, and that is what it became. Thanks to [@Nahuel990](https://github.com/Nahuel990) and [@McDoit](https://github.com/McDoit) for pushing it in that direction.
376
+
354
377
  ## Contributors
355
378
 
356
379
  <a href="https://github.com/DaviReisVieira/stackport/graphs/contributors">
@@ -15,27 +15,27 @@ backend/../ui/dist/favicon.png
15
15
  backend/../ui/dist/favicon.svg
16
16
  backend/../ui/dist/index.html
17
17
  backend/../ui/dist/stackport-logo.svg
18
- backend/../ui/dist/assets/CloudscapeAbout-DccVyQFu.js
19
- backend/../ui/dist/assets/CloudscapeDashboard-DtV4-eQq.js
20
- backend/../ui/dist/assets/CloudscapeLearn-DW6RZAj4.js
18
+ backend/../ui/dist/assets/CloudscapeAbout-BRbsDskR.js
19
+ backend/../ui/dist/assets/CloudscapeDashboard-j2f4Elz2.js
20
+ backend/../ui/dist/assets/CloudscapeLearn-CVW3k3X0.js
21
+ backend/../ui/dist/assets/CloudscapeResourceBrowser-CgWdgn20.js
21
22
  backend/../ui/dist/assets/CloudscapeResourceBrowser-CzwePG-V.css
22
- backend/../ui/dist/assets/CloudscapeResourceBrowser-FpsXtjf_.js
23
- backend/../ui/dist/assets/CloudscapeSettings-UdgiI6dm.js
23
+ backend/../ui/dist/assets/CloudscapeSettings-CIXFvPKv.js
24
24
  backend/../ui/dist/assets/CloudscapeShell-BXHVWjDE.css
25
- backend/../ui/dist/assets/CloudscapeShell-Be6uFzsz.js
26
- backend/../ui/dist/assets/StateMachineGraph-G76o42iO.js
27
- backend/../ui/dist/assets/index-B33aGwWE.js
28
- backend/../ui/dist/assets/index-B3bka_BF.js
29
- backend/../ui/dist/assets/index-B3zGVRXf.js
30
- backend/../ui/dist/assets/index-CNWD72L_.js
25
+ backend/../ui/dist/assets/CloudscapeShell-DNfhSg0O.js
26
+ backend/../ui/dist/assets/StateMachineGraph-DvF_kIOO.js
27
+ backend/../ui/dist/assets/index-BC7E-Fm4.js
28
+ backend/../ui/dist/assets/index-BLdRlH9d.js
29
+ backend/../ui/dist/assets/index-Ba-oOcd3.js
30
+ backend/../ui/dist/assets/index-BkSgVQ0X.js
31
+ backend/../ui/dist/assets/index-C5z3cir-.js
31
32
  backend/../ui/dist/assets/index-DE--A2EE.css
32
33
  backend/../ui/dist/assets/index-DKIdXFX0.css
33
34
  backend/../ui/dist/assets/index-DNBdeKId.css
34
- backend/../ui/dist/assets/index-DhD1oh4N.js
35
35
  backend/../ui/dist/assets/index-DoY0HscW.css
36
36
  backend/../ui/dist/assets/index-DrkkXAoG.css
37
+ backend/../ui/dist/assets/index-Dw6TGweb.js
37
38
  backend/../ui/dist/assets/index-Vf0UFiCT.css
38
- backend/../ui/dist/assets/index-hhBdqMqs.js
39
39
  backend/../ui/dist/assets/stepfunctions-graph-B6nvU4OB.js
40
40
  backend/../ui/dist/assets/utils-Cfmwk2UT.js
41
41
  backend/../ui/dist/aws-icons/acm.svg
@@ -148,27 +148,27 @@ ui/dist/favicon.png
148
148
  ui/dist/favicon.svg
149
149
  ui/dist/index.html
150
150
  ui/dist/stackport-logo.svg
151
- ui/dist/assets/CloudscapeAbout-DccVyQFu.js
152
- ui/dist/assets/CloudscapeDashboard-DtV4-eQq.js
153
- ui/dist/assets/CloudscapeLearn-DW6RZAj4.js
151
+ ui/dist/assets/CloudscapeAbout-BRbsDskR.js
152
+ ui/dist/assets/CloudscapeDashboard-j2f4Elz2.js
153
+ ui/dist/assets/CloudscapeLearn-CVW3k3X0.js
154
+ ui/dist/assets/CloudscapeResourceBrowser-CgWdgn20.js
154
155
  ui/dist/assets/CloudscapeResourceBrowser-CzwePG-V.css
155
- ui/dist/assets/CloudscapeResourceBrowser-FpsXtjf_.js
156
- ui/dist/assets/CloudscapeSettings-UdgiI6dm.js
156
+ ui/dist/assets/CloudscapeSettings-CIXFvPKv.js
157
157
  ui/dist/assets/CloudscapeShell-BXHVWjDE.css
158
- ui/dist/assets/CloudscapeShell-Be6uFzsz.js
159
- ui/dist/assets/StateMachineGraph-G76o42iO.js
160
- ui/dist/assets/index-B33aGwWE.js
161
- ui/dist/assets/index-B3bka_BF.js
162
- ui/dist/assets/index-B3zGVRXf.js
163
- ui/dist/assets/index-CNWD72L_.js
158
+ ui/dist/assets/CloudscapeShell-DNfhSg0O.js
159
+ ui/dist/assets/StateMachineGraph-DvF_kIOO.js
160
+ ui/dist/assets/index-BC7E-Fm4.js
161
+ ui/dist/assets/index-BLdRlH9d.js
162
+ ui/dist/assets/index-Ba-oOcd3.js
163
+ ui/dist/assets/index-BkSgVQ0X.js
164
+ ui/dist/assets/index-C5z3cir-.js
164
165
  ui/dist/assets/index-DE--A2EE.css
165
166
  ui/dist/assets/index-DKIdXFX0.css
166
167
  ui/dist/assets/index-DNBdeKId.css
167
- ui/dist/assets/index-DhD1oh4N.js
168
168
  ui/dist/assets/index-DoY0HscW.css
169
169
  ui/dist/assets/index-DrkkXAoG.css
170
+ ui/dist/assets/index-Dw6TGweb.js
170
171
  ui/dist/assets/index-Vf0UFiCT.css
171
- ui/dist/assets/index-hhBdqMqs.js
172
172
  ui/dist/assets/stepfunctions-graph-B6nvU4OB.js
173
173
  ui/dist/assets/utils-Cfmwk2UT.js
174
174
  ui/dist/aws-icons/acm.svg