stackport 0.3.4__tar.gz → 0.3.6__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.
- {stackport-0.3.4/stackport.egg-info → stackport-0.3.6}/PKG-INFO +2 -1
- {stackport-0.3.4 → stackport-0.3.6}/backend/main.py +3 -2
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/ec2.py +43 -0
- stackport-0.3.6/backend/routes/rds.py +447 -0
- {stackport-0.3.4 → stackport-0.3.6}/pyproject.toml +2 -2
- {stackport-0.3.4 → stackport-0.3.6/stackport.egg-info}/PKG-INFO +2 -1
- {stackport-0.3.4 → stackport-0.3.6}/stackport.egg-info/SOURCES.txt +6 -4
- {stackport-0.3.4 → stackport-0.3.6}/stackport.egg-info/requires.txt +1 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_ec2_routes.py +66 -0
- stackport-0.3.6/tests/test_rds_routes.py +554 -0
- stackport-0.3.4/ui/dist/assets/StateMachineGraph-B8vy_SNe.js → stackport-0.3.6/ui/dist/assets/StateMachineGraph-DnspmX4c.js +1 -1
- stackport-0.3.6/ui/dist/assets/index-CVBAWnD3.js +652 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/index.html +1 -1
- stackport-0.3.4/ui/dist/assets/index-DsnQ23fm.js +0 -652
- {stackport-0.3.4 → stackport-0.3.6}/LICENSE +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/MANIFEST.in +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/README.md +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/__init__.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/aws_client.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/cache.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/cli.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/config.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/endpoint_store.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/__init__.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/common.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/dynamodb.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/endpoints.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/iam.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/lambda_svc.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/logs.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/resources.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/s3.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/secretsmanager.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/sqs.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/stats.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/stepfunctions.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/routes/tags.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/__init__.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/dynamodb.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/endpoints.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/lambda_svc.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/s3.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/secretsmanager.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/sqs.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/stepfunctions.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/schemas/tags.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/backend/websocket.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/setup.cfg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/stackport.egg-info/dependency_links.txt +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/stackport.egg-info/entry_points.txt +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/stackport.egg-info/top_level.txt +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_cache.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_cli.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_client.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_config.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_dynamodb_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_endpoint_store.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_endpoints.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_iam_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_lambda_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_logs_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_multi_endpoint.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_readonly_middleware.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_registries.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_s3_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_s3_upload_limit_env.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_secretsmanager_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_sqs_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_tags_routes.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/tests/test_websocket.py +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/assets/index-BoJK2una.css +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/assets/stepfunctions-graph-B6nvU4OB.js +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/acm.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/apigateway.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/appsync.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/athena.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/cloudformation.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/cloudfront.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/cognito-idp.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/dynamodb.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/ec2.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/ecr.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/ecs.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/elasticache.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/elasticfilesystem.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/elasticloadbalancing.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/elasticmapreduce.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/events.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/firehose.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/glue.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/iam.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/kinesis.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/kms.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/lambda.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/logs.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/monitoring.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/rds.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/route53.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/s3.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/secretsmanager.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/ses.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/sns.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/sqs.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/ssm.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/stepfunctions.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/aws-icons/wafv2.svg +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/favicon.png +0 -0
- {stackport-0.3.4 → stackport-0.3.6}/ui/dist/favicon.svg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stackport
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Universal AWS resource browser for local emulators
|
|
5
5
|
Author: Davi Reis Vieira
|
|
6
6
|
License: MIT
|
|
@@ -29,6 +29,7 @@ Requires-Dist: websockets>=12.0
|
|
|
29
29
|
Requires-Dist: python-multipart>=0.0.26
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
|
|
32
33
|
Requires-Dist: httpx>=0.27.0; extra == "dev"
|
|
33
34
|
Requires-Dist: moto[dynamodb,iam,lambda,s3,sqs]>=5.0; extra == "dev"
|
|
34
35
|
Dynamic: license-file
|
|
@@ -17,7 +17,7 @@ from botocore.exceptions import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from backend.config import LOG_LEVEL, STACKPORT_ALLOW_WRITES, STACKPORT_PORT
|
|
20
|
-
from backend.routes import dynamodb, ec2, endpoints, iam, lambda_svc, logs, resources, s3, secretsmanager, sqs, stats, stepfunctions, tags
|
|
20
|
+
from backend.routes import dynamodb, ec2, endpoints, iam, lambda_svc, logs, resources, rds, s3, secretsmanager, sqs, stats, stepfunctions, tags
|
|
21
21
|
from backend.websocket import probe_loop, websocket_endpoint
|
|
22
22
|
|
|
23
23
|
|
|
@@ -120,12 +120,13 @@ app.include_router(dynamodb.router, prefix="/api/dynamodb")
|
|
|
120
120
|
app.include_router(lambda_svc.router, prefix="/api/lambda", tags=["lambda"])
|
|
121
121
|
app.include_router(sqs.router, prefix="/api/sqs", tags=["sqs"])
|
|
122
122
|
app.include_router(iam.router, prefix="/api/iam", tags=["iam"])
|
|
123
|
-
app.include_router(ec2.router, prefix="/api/ec2", tags=["ec2"])
|
|
123
|
+
app.include_router(ec2.router, prefix="/api/ec2", tags=["ec2", "autoscaling"])
|
|
124
124
|
app.include_router(logs.router, prefix="/api/logs", tags=["logs"])
|
|
125
125
|
app.include_router(secretsmanager.router, prefix="/api/secretsmanager", tags=["secretsmanager"])
|
|
126
126
|
app.include_router(stepfunctions.router, prefix="/api/stepfunctions", tags=["stepfunctions"])
|
|
127
127
|
app.include_router(tags.router, prefix="/api", tags=["tags"])
|
|
128
128
|
app.include_router(resources.router, prefix="/api")
|
|
129
|
+
app.include_router(rds.router, prefix="/api/rds", tags=["rds"])
|
|
129
130
|
|
|
130
131
|
|
|
131
132
|
# WebSocket endpoint for real-time updates
|
|
@@ -38,6 +38,49 @@ def _decode_user_data(encoded: str | None) -> str | None:
|
|
|
38
38
|
except Exception:
|
|
39
39
|
return None
|
|
40
40
|
|
|
41
|
+
def _map_asg_instance(inst: dict) -> dict:
|
|
42
|
+
"""Map an Auto Scaling Group instance to a camelCase shape."""
|
|
43
|
+
return {
|
|
44
|
+
"instanceId": inst.get("InstanceId"),
|
|
45
|
+
"instanceType": inst.get("InstanceType"),
|
|
46
|
+
"lifecycleState": inst.get("LifecycleState"),
|
|
47
|
+
"healthStatus": inst.get("HealthStatus"),
|
|
48
|
+
"availabilityZone": inst.get("AvailabilityZone"),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@router.get("/asgs")
|
|
53
|
+
def list_autoscaling_groups(ep: EndpointInfo = Depends(get_endpoint_info)) -> dict[str, Any]:
|
|
54
|
+
"""List all Auto Scaling Groups with their instances."""
|
|
55
|
+
try:
|
|
56
|
+
client = get_client("autoscaling", **ep.client_kwargs())
|
|
57
|
+
paginator = client.get_paginator("describe_auto_scaling_groups")
|
|
58
|
+
|
|
59
|
+
all_groups = []
|
|
60
|
+
for page in paginator.paginate():
|
|
61
|
+
for group in page.get("AutoScalingGroups", []):
|
|
62
|
+
created_time = group.get("CreatedTime")
|
|
63
|
+
instances = group.get("Instances", [])
|
|
64
|
+
all_groups.append(
|
|
65
|
+
{
|
|
66
|
+
"autoScalingGroupARN": group["AutoScalingGroupARN"],
|
|
67
|
+
"autoScalingGroupName": group["AutoScalingGroupName"],
|
|
68
|
+
"createdTime": created_time.isoformat() if created_time else None,
|
|
69
|
+
"desiredCapacity": group["DesiredCapacity"],
|
|
70
|
+
"maxSize": group["MaxSize"],
|
|
71
|
+
"minSize": group["MinSize"],
|
|
72
|
+
"availabilityZones": group.get("AvailabilityZones", []),
|
|
73
|
+
"healthCheckGracePeriod": group.get("HealthCheckGracePeriod", 0),
|
|
74
|
+
"instanceCount": len(instances),
|
|
75
|
+
"instances": [_map_asg_instance(i) for i in instances],
|
|
76
|
+
"loadBalancerNames": group.get("LoadBalancerNames", []),
|
|
77
|
+
"tags": group.get("Tags", []),
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
return {"auto_scaling_groups": all_groups}
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
83
|
+
|
|
41
84
|
|
|
42
85
|
@router.get("/instances")
|
|
43
86
|
def list_instances(ep: EndpointInfo = Depends(get_endpoint_info)) -> dict[str, Any]:
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
4
|
+
|
|
5
|
+
from backend.aws_client import get_client
|
|
6
|
+
from backend.routes.common import EndpointInfo, get_endpoint_info
|
|
7
|
+
|
|
8
|
+
router = APIRouter()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _flatten_db_instances(response: dict) -> list[dict]:
|
|
12
|
+
"""Extract DBInstances from describe_db_instances response."""
|
|
13
|
+
return response.get("DBInstances", [])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _flatten_db_clusters(response: dict) -> list[dict]:
|
|
17
|
+
"""Extract DBClusters from describe_db_clusters response."""
|
|
18
|
+
return response.get("DBClusters", [])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_endpoint_address(instance: dict) -> str:
|
|
22
|
+
"""Get the endpoint address from an RDS instance."""
|
|
23
|
+
endpoint = instance.get("Endpoint", {})
|
|
24
|
+
if isinstance(endpoint, dict):
|
|
25
|
+
return endpoint.get("Address", "")
|
|
26
|
+
return ""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_endpoint_port(instance: dict) -> int:
|
|
30
|
+
"""Get the endpoint port from an RDS instance."""
|
|
31
|
+
endpoint = instance.get("Endpoint", {})
|
|
32
|
+
if isinstance(endpoint, dict):
|
|
33
|
+
return endpoint.get("Port", 0)
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_reader_endpoint_address(cluster: dict) -> str:
|
|
38
|
+
"""Get the reader endpoint address from an RDS cluster."""
|
|
39
|
+
endpoint = cluster.get("ReaderEndpoint", "")
|
|
40
|
+
return endpoint if isinstance(endpoint, str) else ""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@router.get("/instances")
|
|
44
|
+
def list_db_instances(ep: EndpointInfo = Depends(get_endpoint_info)) -> dict[str, Any]:
|
|
45
|
+
"""List all RDS DB instances with enriched metadata."""
|
|
46
|
+
try:
|
|
47
|
+
client = get_client("rds", **ep.client_kwargs())
|
|
48
|
+
paginator = client.get_paginator("describe_db_instances")
|
|
49
|
+
|
|
50
|
+
all_instances = []
|
|
51
|
+
for page in paginator.paginate():
|
|
52
|
+
instances = _flatten_db_instances(page)
|
|
53
|
+
for instance in instances:
|
|
54
|
+
endpoint_addr = _get_endpoint_address(instance)
|
|
55
|
+
endpoint_port = _get_endpoint_port(instance)
|
|
56
|
+
|
|
57
|
+
all_instances.append(
|
|
58
|
+
{
|
|
59
|
+
"dbInstanceIdentifier": instance["DBInstanceIdentifier"],
|
|
60
|
+
"dbInstanceClass": instance.get("DBInstanceClass", ""),
|
|
61
|
+
"engine": instance.get("Engine", ""),
|
|
62
|
+
"engineVersion": instance.get("EngineVersion", ""),
|
|
63
|
+
"status": instance.get("DBInstanceStatus", ""),
|
|
64
|
+
"masterUsername": instance.get("MasterUsername", ""),
|
|
65
|
+
"endpoint": endpoint_addr,
|
|
66
|
+
"port": endpoint_port,
|
|
67
|
+
"multiAz": instance.get("MultiAZ", False),
|
|
68
|
+
"availabilityZone": instance.get("AvailabilityZone", ""),
|
|
69
|
+
"storageType": instance.get("StorageType", ""),
|
|
70
|
+
"allocatedStorage": instance.get("AllocatedStorage", 0),
|
|
71
|
+
"storageEncrypted": instance.get("StorageEncrypted", False),
|
|
72
|
+
"publiclyAccessible": instance.get("PubliclyAccessible", False),
|
|
73
|
+
"vpcSecurityGroups": instance.get("VpcSecurityGroups", []),
|
|
74
|
+
"dbSubnetGroup": instance.get("DBSubnetGroup", {}),
|
|
75
|
+
"parameterGroup": instance.get("DBParameterGroups", [{}])[0] if instance.get("DBParameterGroups") else {},
|
|
76
|
+
"tags": instance.get("TagList", []),
|
|
77
|
+
"createdTime": instance.get("InstanceCreateTime"),
|
|
78
|
+
"readReplicaSourceIdentifier": instance.get("ReadReplicaSourceDBInstanceIdentifier"),
|
|
79
|
+
"readReplicaIdentifiers": instance.get("ReadReplicaDBInstanceIdentifiers", []),
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return {"instances": all_instances}
|
|
84
|
+
except Exception as e:
|
|
85
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@router.get("/instances/{instance_id}")
|
|
89
|
+
def get_db_instance_detail(instance_id: str, ep: EndpointInfo = Depends(get_endpoint_info)) -> dict[str, Any]:
|
|
90
|
+
"""Get detailed information for a specific DB instance."""
|
|
91
|
+
try:
|
|
92
|
+
client = get_client("rds", **ep.client_kwargs())
|
|
93
|
+
|
|
94
|
+
response = client.describe_db_instances(DBInstanceIdentifier=instance_id)
|
|
95
|
+
instances = _flatten_db_instances(response)
|
|
96
|
+
|
|
97
|
+
if not instances:
|
|
98
|
+
raise HTTPException(status_code=404, detail=f"DB Instance {instance_id} not found")
|
|
99
|
+
|
|
100
|
+
instance = instances[0]
|
|
101
|
+
endpoint_addr = _get_endpoint_address(instance)
|
|
102
|
+
endpoint_port = _get_endpoint_port(instance)
|
|
103
|
+
|
|
104
|
+
# Get tags separately
|
|
105
|
+
try:
|
|
106
|
+
tags_response = client.list_tags_for_resource(ResourceName=instance.get("DbiResourceId", ""))
|
|
107
|
+
tags = tags_response.get("TagList", [])
|
|
108
|
+
except Exception:
|
|
109
|
+
tags = instance.get("TagList", [])
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
"instance": {
|
|
113
|
+
"dbInstanceIdentifier": instance["DBInstanceIdentifier"],
|
|
114
|
+
"dbInstanceClass": instance.get("DBInstanceClass", ""),
|
|
115
|
+
"engine": instance.get("Engine", ""),
|
|
116
|
+
"engineVersion": instance.get("EngineVersion", ""),
|
|
117
|
+
"status": instance.get("DBInstanceStatus", ""),
|
|
118
|
+
"masterUsername": instance.get("MasterUsername", ""),
|
|
119
|
+
"endpoint": endpoint_addr,
|
|
120
|
+
"port": endpoint_port,
|
|
121
|
+
"multiAz": instance.get("MultiAZ", False),
|
|
122
|
+
"availabilityZone": instance.get("AvailabilityZone", ""),
|
|
123
|
+
"storageType": instance.get("StorageType", ""),
|
|
124
|
+
"allocatedStorage": instance.get("AllocatedStorage", 0),
|
|
125
|
+
"iops": instance.get("Iops"),
|
|
126
|
+
"storageEncrypted": instance.get("StorageEncrypted", False),
|
|
127
|
+
"kmsKeyId": instance.get("KmsKeyId"),
|
|
128
|
+
"publiclyAccessible": instance.get("PubliclyAccessible", False),
|
|
129
|
+
"vpcSecurityGroups": instance.get("VpcSecurityGroups", []),
|
|
130
|
+
"dbSubnetGroup": instance.get("DBSubnetGroup", {}),
|
|
131
|
+
"dbParameterGroups": instance.get("DBParameterGroups", []),
|
|
132
|
+
"optionGroupMemberships": instance.get("OptionGroupMemberships", []),
|
|
133
|
+
"tags": tags,
|
|
134
|
+
"createdTime": instance.get("InstanceCreateTime"),
|
|
135
|
+
"backupRetentionPeriod": instance.get("BackupRetentionPeriod", 0),
|
|
136
|
+
"preferredBackupWindow": instance.get("PreferredBackupWindow", ""),
|
|
137
|
+
"preferredMaintenanceWindow": instance.get("PreferredMaintenanceWindow", ""),
|
|
138
|
+
"readReplicaSourceIdentifier": instance.get("ReadReplicaSourceDBInstanceIdentifier"),
|
|
139
|
+
"readReplicaIdentifiers": instance.get("ReadReplicaDBInstanceIdentifiers", []),
|
|
140
|
+
"certificateDetails": instance.get("CertificateDetails", {}),
|
|
141
|
+
"pendingModifiedValues": instance.get("PendingModifiedValues", {}),
|
|
142
|
+
"latestRestorableTime": instance.get("LatestRestorableTime"),
|
|
143
|
+
"earliestRestorableTime": instance.get("EarliestRestorableTime"),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
except HTTPException:
|
|
147
|
+
raise
|
|
148
|
+
except Exception as e:
|
|
149
|
+
error_msg = str(e)
|
|
150
|
+
if "DBInstanceNotFound" in error_msg:
|
|
151
|
+
raise HTTPException(status_code=404, detail=f"DB Instance {instance_id} not found")
|
|
152
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@router.get("/clusters")
|
|
156
|
+
def list_db_clusters(ep: EndpointInfo = Depends(get_endpoint_info)) -> dict[str, Any]:
|
|
157
|
+
"""List all RDS DB clusters with enriched metadata."""
|
|
158
|
+
try:
|
|
159
|
+
client = get_client("rds", **ep.client_kwargs())
|
|
160
|
+
paginator = client.get_paginator("describe_db_clusters")
|
|
161
|
+
|
|
162
|
+
all_clusters = []
|
|
163
|
+
for page in paginator.paginate():
|
|
164
|
+
clusters = _flatten_db_clusters(page)
|
|
165
|
+
for cluster in clusters:
|
|
166
|
+
all_clusters.append(
|
|
167
|
+
{
|
|
168
|
+
"dbClusterIdentifier": cluster["DBClusterIdentifier"],
|
|
169
|
+
"engine": cluster.get("Engine", ""),
|
|
170
|
+
"engineVersion": cluster.get("EngineVersion", ""),
|
|
171
|
+
"status": cluster.get("Status", ""),
|
|
172
|
+
"masterUsername": cluster.get("MasterUsername", ""),
|
|
173
|
+
"endpoint": cluster.get("Endpoint", ""),
|
|
174
|
+
"readerEndpoint": cluster.get("ReaderEndpoint", ""),
|
|
175
|
+
"port": cluster.get("Port", 0),
|
|
176
|
+
"multiAz": cluster.get("MultiAZ", False),
|
|
177
|
+
"storageType": cluster.get("StorageType", ""),
|
|
178
|
+
"allocatedStorage": cluster.get("AllocatedStorage", 0),
|
|
179
|
+
"storageEncrypted": cluster.get("StorageEncrypted", False),
|
|
180
|
+
"vpcSecurityGroups": cluster.get("VpcSecurityGroups", []),
|
|
181
|
+
"dbSubnetGroup": cluster.get("DBSubnetGroup", ""),
|
|
182
|
+
"parameterGroup": cluster.get("DBClusterParameterGroup", ""),
|
|
183
|
+
"tags": cluster.get("TagList", []),
|
|
184
|
+
"createdTime": cluster.get("ClusterCreateTime"),
|
|
185
|
+
"earliestRestorableTime": cluster.get("EarliestRestorableTime"),
|
|
186
|
+
"latestRestorableTime": cluster.get("LatestRestorableTime"),
|
|
187
|
+
"backupRetentionPeriod": cluster.get("BackupRetentionPeriod", 0),
|
|
188
|
+
"preferredBackupWindow": cluster.get("PreferredBackupWindow", ""),
|
|
189
|
+
"preferredMaintenanceWindow": cluster.get("PreferredMaintenanceWindow", ""),
|
|
190
|
+
"readReplicaIdentifiers": cluster.get("ReadReplicaDBClusterIdentifiers", []),
|
|
191
|
+
"dbClusterMembers": cluster.get("DBClusterMembers", []),
|
|
192
|
+
"serverlessV2ScalingConfiguration": cluster.get("ServerlessV2ScalingConfiguration", {}),
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return {"clusters": all_clusters}
|
|
197
|
+
except Exception as e:
|
|
198
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@router.get("/clusters/{cluster_id}")
|
|
202
|
+
def get_db_cluster_detail(cluster_id: str, ep: EndpointInfo = Depends(get_endpoint_info)) -> dict[str, Any]:
|
|
203
|
+
"""Get detailed information for a specific DB cluster."""
|
|
204
|
+
try:
|
|
205
|
+
client = get_client("rds", **ep.client_kwargs())
|
|
206
|
+
|
|
207
|
+
response = client.describe_db_clusters(DBClusterIdentifier=cluster_id)
|
|
208
|
+
clusters = _flatten_db_clusters(response)
|
|
209
|
+
|
|
210
|
+
if not clusters:
|
|
211
|
+
raise HTTPException(status_code=404, detail=f"DB Cluster {cluster_id} not found")
|
|
212
|
+
|
|
213
|
+
cluster = clusters[0]
|
|
214
|
+
|
|
215
|
+
# Get tags separately
|
|
216
|
+
try:
|
|
217
|
+
tags_response = client.list_tags_for_resource(ResourceName=cluster.get("DbClusterResourceId", ""))
|
|
218
|
+
tags = tags_response.get("TagList", [])
|
|
219
|
+
except Exception:
|
|
220
|
+
tags = cluster.get("TagList", [])
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
"cluster": {
|
|
224
|
+
"dbClusterIdentifier": cluster["DBClusterIdentifier"],
|
|
225
|
+
"engine": cluster.get("Engine", ""),
|
|
226
|
+
"engineVersion": cluster.get("EngineVersion", ""),
|
|
227
|
+
"status": cluster.get("Status", ""),
|
|
228
|
+
"masterUsername": cluster.get("MasterUsername", ""),
|
|
229
|
+
"endpoint": cluster.get("Endpoint", ""),
|
|
230
|
+
"readerEndpoint": cluster.get("ReaderEndpoint", ""),
|
|
231
|
+
"port": cluster.get("Port", 0),
|
|
232
|
+
"multiAz": cluster.get("MultiAZ", False),
|
|
233
|
+
"storageType": cluster.get("StorageType", ""),
|
|
234
|
+
"allocatedStorage": cluster.get("AllocatedStorage", 0),
|
|
235
|
+
"storageEncrypted": cluster.get("StorageEncrypted", False),
|
|
236
|
+
"kmsKeyId": cluster.get("KmsKeyId"),
|
|
237
|
+
"vpcSecurityGroups": cluster.get("VpcSecurityGroups", []),
|
|
238
|
+
"dbSubnetGroup": cluster.get("DBSubnetGroup", ""),
|
|
239
|
+
"dbClusterParameterGroup": cluster.get("DBClusterParameterGroup", ""),
|
|
240
|
+
"optionGroupMemberships": cluster.get("OptionGroupMemberships", []),
|
|
241
|
+
"tags": tags,
|
|
242
|
+
"createdTime": cluster.get("ClusterCreateTime"),
|
|
243
|
+
"earliestRestorableTime": cluster.get("EarliestRestorableTime"),
|
|
244
|
+
"latestRestorableTime": cluster.get("LatestRestorableTime"),
|
|
245
|
+
"backupRetentionPeriod": cluster.get("BackupRetentionPeriod", 0),
|
|
246
|
+
"preferredBackupWindow": cluster.get("PreferredBackupWindow", ""),
|
|
247
|
+
"preferredMaintenanceWindow": cluster.get("PreferredMaintenanceWindow", ""),
|
|
248
|
+
"readReplicaIdentifiers": cluster.get("ReadReplicaDBClusterIdentifiers", []),
|
|
249
|
+
"dbClusterMembers": cluster.get("DBClusterMembers", []),
|
|
250
|
+
"serverlessV2ScalingConfiguration": cluster.get("ServerlessV2ScalingConfiguration", {}),
|
|
251
|
+
"scalingConfigurationInfo": cluster.get("ScalingConfigurationInfo", {}),
|
|
252
|
+
"pendingModifiedValues": cluster.get("PendingModifiedValues", {}),
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
except HTTPException:
|
|
256
|
+
raise
|
|
257
|
+
except Exception as e:
|
|
258
|
+
error_msg = str(e)
|
|
259
|
+
if "DBClusterNotFound" in error_msg:
|
|
260
|
+
raise HTTPException(status_code=404, detail=f"DB Cluster {cluster_id} not found")
|
|
261
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@router.get("/snapshots")
|
|
265
|
+
def list_db_snapshots(
|
|
266
|
+
instance_id: str | None = Query(None, description="Filter by DB instance identifier"),
|
|
267
|
+
cluster_id: str | None = Query(None, description="Filter by DB cluster identifier"),
|
|
268
|
+
snapshot_type: str | None = Query(None, description="Filter by snapshot type (automated/manual)"),
|
|
269
|
+
ep: EndpointInfo = Depends(get_endpoint_info),
|
|
270
|
+
) -> dict[str, Any]:
|
|
271
|
+
"""List RDS DB snapshots with optional filters."""
|
|
272
|
+
try:
|
|
273
|
+
client = get_client("rds", **ep.client_kwargs())
|
|
274
|
+
|
|
275
|
+
snapshots = []
|
|
276
|
+
|
|
277
|
+
if instance_id:
|
|
278
|
+
# Get snapshots for a specific instance
|
|
279
|
+
paginator = client.get_paginator("describe_db_snapshots")
|
|
280
|
+
for page in paginator.paginate(DBInstanceIdentifier=instance_id):
|
|
281
|
+
snapshots.extend(page.get("DBSnapshots", []))
|
|
282
|
+
elif cluster_id:
|
|
283
|
+
# Get cluster snapshots
|
|
284
|
+
paginator = client.get_paginator("describe_db_cluster_snapshots")
|
|
285
|
+
for page in paginator.paginate(DBClusterIdentifier=cluster_id):
|
|
286
|
+
snapshots.extend(page.get("DBClusterSnapshots", []))
|
|
287
|
+
elif snapshot_type:
|
|
288
|
+
# Get snapshots by type
|
|
289
|
+
paginator = client.get_paginator("describe_db_snapshots")
|
|
290
|
+
for page in paginator.paginate(SnapshotType=snapshot_type):
|
|
291
|
+
snapshots.extend(page.get("DBSnapshots", []))
|
|
292
|
+
else:
|
|
293
|
+
# Get all snapshots (instance snapshots)
|
|
294
|
+
paginator = client.get_paginator("describe_db_snapshots")
|
|
295
|
+
for page in paginator.paginate():
|
|
296
|
+
snapshots.extend(page.get("DBSnapshots", []))
|
|
297
|
+
|
|
298
|
+
result = []
|
|
299
|
+
for snapshot in snapshots:
|
|
300
|
+
# Handle both DBSnapshot and DBClusterSnapshot
|
|
301
|
+
is_cluster = "DBClusterSnapshotIdentifier" in snapshot
|
|
302
|
+
result.append(
|
|
303
|
+
{
|
|
304
|
+
"snapshotIdentifier": snapshot.get("DBSnapshotIdentifier" if not is_cluster else "DBClusterSnapshotIdentifier", ""),
|
|
305
|
+
"snapshotType": snapshot.get("SnapshotType", ""),
|
|
306
|
+
"status": snapshot.get("Status", ""),
|
|
307
|
+
"sourceType": "cluster" if is_cluster else "instance",
|
|
308
|
+
"sourceIdentifier": snapshot.get("DBInstanceIdentifier" if not is_cluster else "DBClusterIdentifier", ""),
|
|
309
|
+
"engine": snapshot.get("Engine", ""),
|
|
310
|
+
"engineVersion": snapshot.get("EngineVersion", ""),
|
|
311
|
+
"allocatedStorage": snapshot.get("AllocatedStorage", 0),
|
|
312
|
+
"snapshotCreateTime": snapshot.get("SnapshotCreateTime"),
|
|
313
|
+
"snapshotSize": snapshot.get("SnapshotSize", 0),
|
|
314
|
+
"encrypted": snapshot.get("Encrypted", False),
|
|
315
|
+
"kmsKeyId": snapshot.get("KmsKeyId"),
|
|
316
|
+
"tags": snapshot.get("TagList", []),
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
return {"snapshots": result}
|
|
321
|
+
except Exception as e:
|
|
322
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@router.get("/parameter-groups")
|
|
326
|
+
def list_db_parameter_groups(
|
|
327
|
+
source: str = Query("all", description="Source type: all, instance, or cluster"),
|
|
328
|
+
ep: EndpointInfo = Depends(get_endpoint_info),
|
|
329
|
+
) -> dict[str, Any]:
|
|
330
|
+
"""List RDS DB parameter groups."""
|
|
331
|
+
try:
|
|
332
|
+
client = get_client("rds", **ep.client_kwargs())
|
|
333
|
+
|
|
334
|
+
parameter_groups = []
|
|
335
|
+
|
|
336
|
+
if source in ("all", "instance"):
|
|
337
|
+
# Get instance parameter groups
|
|
338
|
+
paginator = client.get_paginator("describe_db_parameter_groups")
|
|
339
|
+
for page in paginator.paginate():
|
|
340
|
+
for group in page.get("DBParameterGroups", []):
|
|
341
|
+
parameter_groups.append(
|
|
342
|
+
{
|
|
343
|
+
"name": group.get("DBParameterGroupName", ""),
|
|
344
|
+
"family": group.get("DBParameterGroupFamily", ""),
|
|
345
|
+
"description": group.get("Description", ""),
|
|
346
|
+
"source": "instance",
|
|
347
|
+
"tags": group.get("TagList", []),
|
|
348
|
+
}
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if source in ("all", "cluster"):
|
|
352
|
+
# Get cluster parameter groups
|
|
353
|
+
paginator = client.get_paginator("describe_db_cluster_parameter_groups")
|
|
354
|
+
for page in paginator.paginate():
|
|
355
|
+
for group in page.get("DBClusterParameterGroups", []):
|
|
356
|
+
parameter_groups.append(
|
|
357
|
+
{
|
|
358
|
+
"name": group.get("DBClusterParameterGroupName", ""),
|
|
359
|
+
"family": group.get("DBClusterParameterGroupFamily", ""),
|
|
360
|
+
"description": group.get("Description", ""),
|
|
361
|
+
"source": "cluster",
|
|
362
|
+
"tags": group.get("TagList", []),
|
|
363
|
+
}
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
return {"parameterGroups": parameter_groups}
|
|
367
|
+
except Exception as e:
|
|
368
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@router.get("/parameter-groups/{group_name}")
|
|
372
|
+
def get_db_parameter_group_detail(
|
|
373
|
+
group_name: str,
|
|
374
|
+
source: str = Query("instance", description="Source type: instance or cluster"),
|
|
375
|
+
ep: EndpointInfo = Depends(get_endpoint_info),
|
|
376
|
+
) -> dict[str, Any]:
|
|
377
|
+
"""Get detailed information for a specific parameter group including parameters."""
|
|
378
|
+
try:
|
|
379
|
+
client = get_client("rds", **ep.client_kwargs())
|
|
380
|
+
|
|
381
|
+
if source == "cluster":
|
|
382
|
+
# Get cluster parameter group details
|
|
383
|
+
response = client.describe_db_cluster_parameters(DBClusterParameterGroupName=group_name)
|
|
384
|
+
parameters = response.get("Parameters", [])
|
|
385
|
+
|
|
386
|
+
# Get group info
|
|
387
|
+
groups_response = client.describe_db_cluster_parameter_groups(DBClusterParameterGroupName=group_name)
|
|
388
|
+
groups = groups_response.get("DBClusterParameterGroups", [])
|
|
389
|
+
group_info = groups[0] if groups else {}
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
"parameterGroup": {
|
|
393
|
+
"name": group_info.get("DBClusterParameterGroupName", group_name),
|
|
394
|
+
"family": group_info.get("DBClusterParameterGroupFamily", ""),
|
|
395
|
+
"description": group_info.get("Description", ""),
|
|
396
|
+
"source": "cluster",
|
|
397
|
+
"parameters": [
|
|
398
|
+
{
|
|
399
|
+
"name": p.get("ParameterName", ""),
|
|
400
|
+
"value": p.get("ParameterValue", ""),
|
|
401
|
+
"description": p.get("Description", ""),
|
|
402
|
+
"dataType": p.get("DataType", ""),
|
|
403
|
+
"allowedValues": p.get("AllowedValues", ""),
|
|
404
|
+
"isModifiable": p.get("IsModifiable", False),
|
|
405
|
+
"applyMethod": p.get("ApplyMethod", "immediate"),
|
|
406
|
+
}
|
|
407
|
+
for p in parameters
|
|
408
|
+
],
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else:
|
|
412
|
+
# Get instance parameter group details
|
|
413
|
+
response = client.describe_db_parameters(DBParameterGroupName=group_name)
|
|
414
|
+
parameters = response.get("Parameters", [])
|
|
415
|
+
|
|
416
|
+
# Get group info
|
|
417
|
+
groups_response = client.describe_db_parameter_groups(DBParameterGroupName=group_name)
|
|
418
|
+
groups = groups_response.get("DBParameterGroups", [])
|
|
419
|
+
group_info = groups[0] if groups else {}
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
"parameterGroup": {
|
|
423
|
+
"name": group_info.get("DBParameterGroupName", group_name),
|
|
424
|
+
"family": group_info.get("DBParameterGroupFamily", ""),
|
|
425
|
+
"description": group_info.get("Description", ""),
|
|
426
|
+
"source": "instance",
|
|
427
|
+
"parameters": [
|
|
428
|
+
{
|
|
429
|
+
"name": p.get("ParameterName", ""),
|
|
430
|
+
"value": p.get("ParameterValue", ""),
|
|
431
|
+
"description": p.get("Description", ""),
|
|
432
|
+
"dataType": p.get("DataType", ""),
|
|
433
|
+
"allowedValues": p.get("AllowedValues", ""),
|
|
434
|
+
"isModifiable": p.get("IsModifiable", False),
|
|
435
|
+
"applyMethod": p.get("ApplyMethod", "immediate"),
|
|
436
|
+
}
|
|
437
|
+
for p in parameters
|
|
438
|
+
],
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
except HTTPException:
|
|
442
|
+
raise
|
|
443
|
+
except Exception as e:
|
|
444
|
+
error_msg = str(e)
|
|
445
|
+
if "DBParameterGroupNotFound" in error_msg or "DBClusterParameterGroupNotFound" in error_msg:
|
|
446
|
+
raise HTTPException(status_code=404, detail=f"Parameter Group {group_name} not found")
|
|
447
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stackport"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.6"
|
|
4
4
|
description = "Universal AWS resource browser for local emulators"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -34,7 +34,7 @@ Repository = "https://github.com/DaviReisVieira/stackport"
|
|
|
34
34
|
Issues = "https://github.com/DaviReisVieira/stackport/issues"
|
|
35
35
|
|
|
36
36
|
[project.optional-dependencies]
|
|
37
|
-
dev = ["pytest>=8.0", "httpx>=0.27.0", "moto[s3,sqs,lambda,dynamodb,iam]>=5.0"]
|
|
37
|
+
dev = ["pytest>=8.0", "pytest-mock>=3.12.0", "httpx>=0.27.0", "moto[s3,sqs,lambda,dynamodb,iam]>=5.0"]
|
|
38
38
|
|
|
39
39
|
[tool.pytest.ini_options]
|
|
40
40
|
testpaths = ["tests"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stackport
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Universal AWS resource browser for local emulators
|
|
5
5
|
Author: Davi Reis Vieira
|
|
6
6
|
License: MIT
|
|
@@ -29,6 +29,7 @@ Requires-Dist: websockets>=12.0
|
|
|
29
29
|
Requires-Dist: python-multipart>=0.0.26
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
|
|
32
33
|
Requires-Dist: httpx>=0.27.0; extra == "dev"
|
|
33
34
|
Requires-Dist: moto[dynamodb,iam,lambda,s3,sqs]>=5.0; extra == "dev"
|
|
34
35
|
Dynamic: license-file
|
|
@@ -13,9 +13,9 @@ backend/websocket.py
|
|
|
13
13
|
backend/../ui/dist/favicon.png
|
|
14
14
|
backend/../ui/dist/favicon.svg
|
|
15
15
|
backend/../ui/dist/index.html
|
|
16
|
-
backend/../ui/dist/assets/StateMachineGraph-
|
|
16
|
+
backend/../ui/dist/assets/StateMachineGraph-DnspmX4c.js
|
|
17
17
|
backend/../ui/dist/assets/index-BoJK2una.css
|
|
18
|
-
backend/../ui/dist/assets/index-
|
|
18
|
+
backend/../ui/dist/assets/index-CVBAWnD3.js
|
|
19
19
|
backend/../ui/dist/assets/stepfunctions-graph-B6nvU4OB.js
|
|
20
20
|
backend/../ui/dist/aws-icons/acm.svg
|
|
21
21
|
backend/../ui/dist/aws-icons/apigateway.svg
|
|
@@ -59,6 +59,7 @@ backend/routes/endpoints.py
|
|
|
59
59
|
backend/routes/iam.py
|
|
60
60
|
backend/routes/lambda_svc.py
|
|
61
61
|
backend/routes/logs.py
|
|
62
|
+
backend/routes/rds.py
|
|
62
63
|
backend/routes/resources.py
|
|
63
64
|
backend/routes/s3.py
|
|
64
65
|
backend/routes/secretsmanager.py
|
|
@@ -93,6 +94,7 @@ tests/test_iam_routes.py
|
|
|
93
94
|
tests/test_lambda_routes.py
|
|
94
95
|
tests/test_logs_routes.py
|
|
95
96
|
tests/test_multi_endpoint.py
|
|
97
|
+
tests/test_rds_routes.py
|
|
96
98
|
tests/test_readonly_middleware.py
|
|
97
99
|
tests/test_registries.py
|
|
98
100
|
tests/test_routes.py
|
|
@@ -105,9 +107,9 @@ tests/test_websocket.py
|
|
|
105
107
|
ui/dist/favicon.png
|
|
106
108
|
ui/dist/favicon.svg
|
|
107
109
|
ui/dist/index.html
|
|
108
|
-
ui/dist/assets/StateMachineGraph-
|
|
110
|
+
ui/dist/assets/StateMachineGraph-DnspmX4c.js
|
|
109
111
|
ui/dist/assets/index-BoJK2una.css
|
|
110
|
-
ui/dist/assets/index-
|
|
112
|
+
ui/dist/assets/index-CVBAWnD3.js
|
|
111
113
|
ui/dist/assets/stepfunctions-graph-B6nvU4OB.js
|
|
112
114
|
ui/dist/aws-icons/acm.svg
|
|
113
115
|
ui/dist/aws-icons/apigateway.svg
|