diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75609f9..03e4e7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,6 @@ name: CI on: - push: pull_request: concurrency: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffe7c5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +__pycache__/ +*.pyc diff --git a/.idea/ChatSystem.iml b/.idea/ChatSystem.iml deleted file mode 100644 index 50e439f..0000000 --- a/.idea/ChatSystem.iml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 655edb9..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 1aa3f63..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - { - "associatedIndex": 6 -} - - - - { - "keyToString": { - "Gradle./home/cole/Projects/ChatSystem/ChatServer [ktlintFormat].executor": "Run", - "Gradle./home/cole/Projects/ChatSystem/ChatServer [test clean].executor": "Run", - "ModuleVcsDetector.initialDetectionPerformed": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", - "RunOnceActivity.git.unshallow": "true", - "git-widget-placeholder": "feature/cleanup-codebase-for-stability", - "kotlin-language-version-configured": "true", - "last_opened_file_path": "/home/cole/Projects/ChatSystem" - } -} - - - - - - - true - true - false - false - false - false - false - - - - - - - - true - true - false - false - false - false - false - - - - - - - - - - - - - 1775100397928 - - - - \ No newline at end of file diff --git a/ApiServer/.idea/.gitignore b/ApiServer/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/ApiServer/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/ApiServer/.idea/ApiServer.iml b/ApiServer/.idea/ApiServer.iml deleted file mode 100644 index 0f7c2b7..0000000 --- a/ApiServer/.idea/ApiServer.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/ApiServer/.idea/inspectionProfiles/profiles_settings.xml b/ApiServer/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/ApiServer/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/ApiServer/.idea/misc.xml b/ApiServer/.idea/misc.xml deleted file mode 100644 index 790b6cf..0000000 --- a/ApiServer/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/ApiServer/.idea/modules.xml b/ApiServer/.idea/modules.xml deleted file mode 100644 index 39b73b1..0000000 --- a/ApiServer/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/ApiServer/.idea/vcs.xml b/ApiServer/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/ApiServer/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/ApiServer/__pycache__/main.cpython-314.pyc b/ApiServer/__pycache__/main.cpython-314.pyc deleted file mode 100644 index 2203fe2..0000000 Binary files a/ApiServer/__pycache__/main.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/__pycache__/__init__.cpython-314.pyc b/ApiServer/app/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 88c1512..0000000 Binary files a/ApiServer/app/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/__pycache__/chat_client.cpython-314.pyc b/ApiServer/app/__pycache__/chat_client.cpython-314.pyc deleted file mode 100644 index a365aaf..0000000 Binary files a/ApiServer/app/__pycache__/chat_client.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/__pycache__/config.cpython-314.pyc b/ApiServer/app/__pycache__/config.cpython-314.pyc deleted file mode 100644 index e66d2d9..0000000 Binary files a/ApiServer/app/__pycache__/config.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/__pycache__/database.cpython-314.pyc b/ApiServer/app/__pycache__/database.cpython-314.pyc deleted file mode 100644 index f7fe46d..0000000 Binary files a/ApiServer/app/__pycache__/database.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/__pycache__/dependencies.cpython-314.pyc b/ApiServer/app/__pycache__/dependencies.cpython-314.pyc deleted file mode 100644 index 593214e..0000000 Binary files a/ApiServer/app/__pycache__/dependencies.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/models/__pycache__/__init__.cpython-314.pyc b/ApiServer/app/models/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 9293028..0000000 Binary files a/ApiServer/app/models/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/models/__pycache__/group.cpython-314.pyc b/ApiServer/app/models/__pycache__/group.cpython-314.pyc deleted file mode 100644 index 676c925..0000000 Binary files a/ApiServer/app/models/__pycache__/group.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/models/__pycache__/user.cpython-314.pyc b/ApiServer/app/models/__pycache__/user.cpython-314.pyc deleted file mode 100644 index 0650e2a..0000000 Binary files a/ApiServer/app/models/__pycache__/user.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/routers/__pycache__/__init__.cpython-314.pyc b/ApiServer/app/routers/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index d4bfcf9..0000000 Binary files a/ApiServer/app/routers/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/routers/__pycache__/auth.cpython-314.pyc b/ApiServer/app/routers/__pycache__/auth.cpython-314.pyc deleted file mode 100644 index 396b108..0000000 Binary files a/ApiServer/app/routers/__pycache__/auth.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/routers/__pycache__/conversations.cpython-314.pyc b/ApiServer/app/routers/__pycache__/conversations.cpython-314.pyc deleted file mode 100644 index d26e675..0000000 Binary files a/ApiServer/app/routers/__pycache__/conversations.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/routers/__pycache__/groups.cpython-314.pyc b/ApiServer/app/routers/__pycache__/groups.cpython-314.pyc deleted file mode 100644 index 0ab1538..0000000 Binary files a/ApiServer/app/routers/__pycache__/groups.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/routers/__pycache__/presence.cpython-314.pyc b/ApiServer/app/routers/__pycache__/presence.cpython-314.pyc deleted file mode 100644 index d867a00..0000000 Binary files a/ApiServer/app/routers/__pycache__/presence.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/routers/__pycache__/users.cpython-314.pyc b/ApiServer/app/routers/__pycache__/users.cpython-314.pyc deleted file mode 100644 index d8e61de..0000000 Binary files a/ApiServer/app/routers/__pycache__/users.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/schemas/__pycache__/__init__.cpython-314.pyc b/ApiServer/app/schemas/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 7d4dc5b..0000000 Binary files a/ApiServer/app/schemas/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/schemas/__pycache__/group.cpython-314.pyc b/ApiServer/app/schemas/__pycache__/group.cpython-314.pyc deleted file mode 100644 index 8c9c3fe..0000000 Binary files a/ApiServer/app/schemas/__pycache__/group.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/schemas/__pycache__/presence.cpython-314.pyc b/ApiServer/app/schemas/__pycache__/presence.cpython-314.pyc deleted file mode 100644 index 5640316..0000000 Binary files a/ApiServer/app/schemas/__pycache__/presence.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/schemas/__pycache__/token.cpython-314.pyc b/ApiServer/app/schemas/__pycache__/token.cpython-314.pyc deleted file mode 100644 index 6b1c5dd..0000000 Binary files a/ApiServer/app/schemas/__pycache__/token.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/app/schemas/__pycache__/user.cpython-314.pyc b/ApiServer/app/schemas/__pycache__/user.cpython-314.pyc deleted file mode 100644 index 1f8333e..0000000 Binary files a/ApiServer/app/schemas/__pycache__/user.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/main.py b/ApiServer/main.py index e7d5287..655219e 100644 --- a/ApiServer/main.py +++ b/ApiServer/main.py @@ -3,7 +3,8 @@ import sys from contextlib import asynccontextmanager -from fastapi import FastAPI +from aws_xray_sdk.core import xray_recorder +from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from sqlalchemy import text @@ -48,6 +49,16 @@ async def lifespan(app: FastAPI): app = FastAPI(title="ChatSystem API", lifespan=lifespan) + +@app.middleware("http") +async def xray_middleware(request: Request, call_next): + xray_recorder.begin_segment(f"{request.method} {request.url.path}") + try: + return await call_next(request) + finally: + xray_recorder.end_segment() + + app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins_list, diff --git a/ApiServer/requirements.txt b/ApiServer/requirements.txt index 0be7091..815794a 100644 --- a/ApiServer/requirements.txt +++ b/ApiServer/requirements.txt @@ -1,4 +1,5 @@ annotated-doc==0.0.4 +aws-xray-sdk annotated-types==0.7.0 anyio==4.13.0 asyncpg==0.31.0 diff --git a/ApiServer/tests/__pycache__/__init__.cpython-314.pyc b/ApiServer/tests/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index f433eb8..0000000 Binary files a/ApiServer/tests/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/ApiServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc b/ApiServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index 6f828a5..0000000 Binary files a/ApiServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/ApiServer/tests/__pycache__/test_auth.cpython-314-pytest-9.0.2.pyc b/ApiServer/tests/__pycache__/test_auth.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index 4deed8f..0000000 Binary files a/ApiServer/tests/__pycache__/test_auth.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/ApiServer/tests/__pycache__/test_conversations.cpython-314-pytest-9.0.2.pyc b/ApiServer/tests/__pycache__/test_conversations.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index c4e2638..0000000 Binary files a/ApiServer/tests/__pycache__/test_conversations.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/ApiServer/tests/__pycache__/test_groups.cpython-314-pytest-9.0.2.pyc b/ApiServer/tests/__pycache__/test_groups.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index b230a30..0000000 Binary files a/ApiServer/tests/__pycache__/test_groups.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/ApiServer/tests/__pycache__/test_presence_proxy.cpython-314-pytest-9.0.2.pyc b/ApiServer/tests/__pycache__/test_presence_proxy.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index a730c96..0000000 Binary files a/ApiServer/tests/__pycache__/test_presence_proxy.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/ApiServer/tests/__pycache__/test_users.cpython-314-pytest-9.0.2.pyc b/ApiServer/tests/__pycache__/test_users.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index b730b65..0000000 Binary files a/ApiServer/tests/__pycache__/test_users.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/ChatInfra/.terraform.lock.hcl b/ChatInfra/environments/dev/.terraform.lock.hcl similarity index 100% rename from ChatInfra/.terraform.lock.hcl rename to ChatInfra/environments/dev/.terraform.lock.hcl diff --git a/ChatInfra/environments/dev/main.tf b/ChatInfra/environments/dev/main.tf index 6670c28..83e70d5 100644 --- a/ChatInfra/environments/dev/main.tf +++ b/ChatInfra/environments/dev/main.tf @@ -78,6 +78,8 @@ module "ecs_services" { execution_role_chatserver_arn = module.ecs.execution_role_chatserver_arn execution_role_presenceserver_arn = module.ecs.execution_role_presenceserver_arn chatserver_task_role_arn = module.ecs.chatserver_task_role_arn + apiserver_task_role_arn = module.ecs.apiserver_task_role_arn + presenceserver_task_role_arn = module.ecs.presenceserver_task_role_arn private_subnet_ids = module.vpc.private_subnets ecs_tasks_sg_id = module.security_groups.ecs_tasks_sg_id chatserver_nlb_dns_name = module.alb.chatserver_nlb_dns_name @@ -92,5 +94,8 @@ module "ecs_services" { apiserver_log_group = module.ecs.apiserver_log_group chatserver_log_group = module.ecs.chatserver_log_group presenceserver_log_group = module.ecs.presenceserver_log_group + apiserver_xray_log_group = module.ecs.apiserver_xray_log_group + chatserver_xray_log_group = module.ecs.chatserver_xray_log_group + presenceserver_xray_log_group = module.ecs.presenceserver_xray_log_group cloudfront_domain = module.static_site.cloudfront_domain } diff --git a/ChatInfra/modules/ecs/main.tf b/ChatInfra/modules/ecs/main.tf index e69ad7a..b6c4500 100644 --- a/ChatInfra/modules/ecs/main.tf +++ b/ChatInfra/modules/ecs/main.tf @@ -219,3 +219,126 @@ resource "aws_iam_role_policy" "chatserver_sqs" { }] }) } + +resource "aws_iam_role_policy" "chatserver_xray" { + name = "${var.project_name}-chatserver-xray" + role = aws_iam_role.chatserver_task.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + "xray:GetSamplingRules", + "xray:GetSamplingTargets", + "xray:GetSamplingStatisticSummaries", + ] + Resource = "*" + }] + }) +} + +# --- ApiServer task role --- + +resource "aws_iam_role" "apiserver_task" { + name = "${var.project_name}-apiserver-task" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = { Service = "ecs-tasks.amazonaws.com" } + Action = "sts:AssumeRole" + Condition = { + ArnLike = { + "aws:SourceArn" = "arn:aws:ecs:${var.region}:${data.aws_caller_identity.current.account_id}:*" + } + StringEquals = { + "aws:SourceAccount" = data.aws_caller_identity.current.account_id + } + } + }] + }) +} + +resource "aws_iam_role_policy" "apiserver_xray" { + name = "${var.project_name}-apiserver-xray" + role = aws_iam_role.apiserver_task.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + "xray:GetSamplingRules", + "xray:GetSamplingTargets", + "xray:GetSamplingStatisticSummaries", + ] + Resource = "*" + }] + }) +} + +# --- PresenceServer task role --- + +resource "aws_iam_role" "presenceserver_task" { + name = "${var.project_name}-presenceserver-task" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = { Service = "ecs-tasks.amazonaws.com" } + Action = "sts:AssumeRole" + Condition = { + ArnLike = { + "aws:SourceArn" = "arn:aws:ecs:${var.region}:${data.aws_caller_identity.current.account_id}:*" + } + StringEquals = { + "aws:SourceAccount" = data.aws_caller_identity.current.account_id + } + } + }] + }) +} + +resource "aws_iam_role_policy" "presenceserver_xray" { + name = "${var.project_name}-presenceserver-xray" + role = aws_iam_role.presenceserver_task.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + "xray:GetSamplingRules", + "xray:GetSamplingTargets", + "xray:GetSamplingStatisticSummaries", + ] + Resource = "*" + }] + }) +} + +# --- X-Ray daemon log groups --- + +resource "aws_cloudwatch_log_group" "apiserver_xray" { + name = "/ecs/${var.project_name}/apiserver-xray" + retention_in_days = 365 +} + +resource "aws_cloudwatch_log_group" "chatserver_xray" { + name = "/ecs/${var.project_name}/chatserver-xray" + retention_in_days = 365 +} + +resource "aws_cloudwatch_log_group" "presenceserver_xray" { + name = "/ecs/${var.project_name}/presenceserver-xray" + retention_in_days = 365 +} diff --git a/ChatInfra/modules/ecs/outputs.tf b/ChatInfra/modules/ecs/outputs.tf index 237b0c2..2c01bb5 100644 --- a/ChatInfra/modules/ecs/outputs.tf +++ b/ChatInfra/modules/ecs/outputs.tf @@ -18,6 +18,14 @@ output "chatserver_task_role_arn" { value = aws_iam_role.chatserver_task.arn } +output "apiserver_task_role_arn" { + value = aws_iam_role.apiserver_task.arn +} + +output "presenceserver_task_role_arn" { + value = aws_iam_role.presenceserver_task.arn +} + output "apiserver_log_group" { value = aws_cloudwatch_log_group.apiserver.name } @@ -29,3 +37,15 @@ output "chatserver_log_group" { output "presenceserver_log_group" { value = aws_cloudwatch_log_group.presenceserver.name } + +output "apiserver_xray_log_group" { + value = aws_cloudwatch_log_group.apiserver_xray.name +} + +output "chatserver_xray_log_group" { + value = aws_cloudwatch_log_group.chatserver_xray.name +} + +output "presenceserver_xray_log_group" { + value = aws_cloudwatch_log_group.presenceserver_xray.name +} diff --git a/ChatInfra/modules/ecs_services/main.tf b/ChatInfra/modules/ecs_services/main.tf index 7c45ec2..2770f87 100644 --- a/ChatInfra/modules/ecs_services/main.tf +++ b/ChatInfra/modules/ecs_services/main.tf @@ -9,37 +9,63 @@ resource "aws_ecs_task_definition" "apiserver" { cpu = 256 memory = 512 execution_role_arn = var.execution_role_api_arn + task_role_arn = var.apiserver_task_role_arn - container_definitions = jsonencode([{ - name = "apiserver" - image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/chatsystem/apiserver:latest" - essential = true - - portMappings = [{ - containerPort = 8000 - protocol = "tcp" - }] - - environment = [ - { name = "CHAT_SERVER_URL", value = "http://${var.chatserver_nlb_dns_name}" }, - { name = "CORS_ORIGINS", value = "https://${var.cloudfront_domain}" }, - ] - - secrets = [ - { name = "DATABASE_URL", valueFrom = "${var.db_secret_arn}:database_url::" }, - { name = "SECRET_KEY", valueFrom = var.jwt_secret_arn }, - { name = "INTERNAL_API_KEY", valueFrom = var.internal_api_key_arn }, - ] - - logConfiguration = { - logDriver = "awslogs" - options = { - awslogs-group = var.apiserver_log_group - awslogs-region = var.region - awslogs-stream-prefix = "apiserver" + container_definitions = jsonencode([ + { + name = "apiserver" + image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/chatsystem/apiserver:latest" + essential = true + + portMappings = [{ + containerPort = 8000 + protocol = "tcp" + }] + + environment = [ + { name = "CHAT_SERVER_URL", value = "http://${var.chatserver_nlb_dns_name}" }, + { name = "CORS_ORIGINS", value = "https://${var.cloudfront_domain}" }, + ] + + secrets = [ + { name = "DATABASE_URL", valueFrom = "${var.db_secret_arn}:database_url::" }, + { name = "SECRET_KEY", valueFrom = var.jwt_secret_arn }, + { name = "INTERNAL_API_KEY", valueFrom = var.internal_api_key_arn }, + ] + + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = var.apiserver_log_group + awslogs-region = var.region + awslogs-stream-prefix = "apiserver" + } + } + }, + { + name = "xray-daemon" + image = "public.ecr.aws/xray/aws-xray-daemon:latest" + essential = false + + portMappings = [{ + containerPort = 2000 + protocol = "udp" + }] + + environment = [ + { name = "AWS_REGION", value = var.region }, + ] + + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = var.apiserver_xray_log_group + awslogs-region = var.region + awslogs-stream-prefix = "xray" + } } } - }]) + ]) } resource "aws_ecs_service" "apiserver" { @@ -71,35 +97,61 @@ resource "aws_ecs_task_definition" "presenceserver" { cpu = 256 memory = 512 execution_role_arn = var.execution_role_presenceserver_arn + task_role_arn = var.presenceserver_task_role_arn + + container_definitions = jsonencode([ + { + name = "presenceserver" + image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/chatsystem/presenceserver:latest" + essential = true + + portMappings = [{ + containerPort = 8000 + protocol = "tcp" + }] + + environment = [ + { name = "REDIS_URL", value = "rediss://${var.redis_address}:6379" } + ] + + secrets = [ + { name = "SECRET_KEY", valueFrom = var.jwt_secret_arn }, + { name = "INTERNAL_API_KEY", valueFrom = var.internal_api_key_arn }, + ] - container_definitions = jsonencode([{ - name = "presenceserver" - image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/chatsystem/presenceserver:latest" - essential = true - - portMappings = [{ - containerPort = 8000 - protocol = "tcp" - }] - - environment = [ - { name = "REDIS_URL", value = "rediss://${var.redis_address}:6379" } - ] - - secrets = [ - { name = "SECRET_KEY", valueFrom = var.jwt_secret_arn }, - { name = "INTERNAL_API_KEY", valueFrom = var.internal_api_key_arn }, - ] - - logConfiguration = { - logDriver = "awslogs" - options = { - awslogs-group = var.presenceserver_log_group - awslogs-region = var.region - awslogs-stream-prefix = "presenceserver" + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = var.presenceserver_log_group + awslogs-region = var.region + awslogs-stream-prefix = "presenceserver" + } + } + }, + { + name = "xray-daemon" + image = "public.ecr.aws/xray/aws-xray-daemon:latest" + essential = false + + portMappings = [{ + containerPort = 2000 + protocol = "udp" + }] + + environment = [ + { name = "AWS_REGION", value = var.region }, + ] + + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = var.presenceserver_xray_log_group + awslogs-region = var.region + awslogs-stream-prefix = "xray" + } } } - }]) + ]) } resource "aws_ecs_service" "presenceserver" { @@ -133,35 +185,60 @@ resource "aws_ecs_task_definition" "chatserver" { execution_role_arn = var.execution_role_chatserver_arn task_role_arn = var.chatserver_task_role_arn - container_definitions = jsonencode([{ - name = "chatserver" - image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/chatsystem/chatserver:latest" - essential = true - - portMappings = [{ - containerPort = 8080 - protocol = "tcp" - }] - - environment = [ - { name = "PRESENCE_SERVER_URL", value = "http://${var.internal_alb_dns_name}" }, - { name = "CORS_ORIGINS", value = "https://${var.cloudfront_domain}" }, - ] - - secrets = [ - { name = "JWT_SECRET", valueFrom = var.jwt_secret_arn }, - { name = "INTERNAL_API_KEY", valueFrom = var.internal_api_key_arn }, - ] - - logConfiguration = { - logDriver = "awslogs" - options = { - awslogs-group = var.chatserver_log_group - awslogs-region = var.region - awslogs-stream-prefix = "chatserver" + container_definitions = jsonencode([ + { + name = "chatserver" + image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/chatsystem/chatserver:latest" + essential = true + + portMappings = [{ + containerPort = 8080 + protocol = "tcp" + }] + + environment = [ + { name = "PRESENCE_SERVER_URL", value = "http://${var.internal_alb_dns_name}" }, + { name = "CORS_ORIGINS", value = "https://${var.cloudfront_domain}" }, + ] + + secrets = [ + { name = "JWT_SECRET", valueFrom = var.jwt_secret_arn }, + { name = "INTERNAL_API_KEY", valueFrom = var.internal_api_key_arn }, + ] + + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = var.chatserver_log_group + awslogs-region = var.region + awslogs-stream-prefix = "chatserver" + } + } + }, + { + name = "xray-daemon" + image = "public.ecr.aws/xray/aws-xray-daemon:latest" + essential = false + + portMappings = [{ + containerPort = 2000 + protocol = "udp" + }] + + environment = [ + { name = "AWS_REGION", value = var.region }, + ] + + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = var.chatserver_xray_log_group + awslogs-region = var.region + awslogs-stream-prefix = "xray" + } } } - }]) + ]) } resource "aws_ecs_service" "chatserver" { diff --git a/ChatInfra/modules/ecs_services/variables.tf b/ChatInfra/modules/ecs_services/variables.tf index b9995c5..e8648d1 100644 --- a/ChatInfra/modules/ecs_services/variables.tf +++ b/ChatInfra/modules/ecs_services/variables.tf @@ -26,6 +26,14 @@ variable "chatserver_task_role_arn" { type = string } +variable "apiserver_task_role_arn" { + type = string +} + +variable "presenceserver_task_role_arn" { + type = string +} + variable "private_subnet_ids" { type = list(string) } @@ -86,3 +94,15 @@ variable "cloudfront_domain" { type = string description = "CloudFront domain used as CORS allowed origin for ApiServer and ChatServer" } + +variable "apiserver_xray_log_group" { + type = string +} + +variable "chatserver_xray_log_group" { + type = string +} + +variable "presenceserver_xray_log_group" { + type = string +} diff --git a/ChatInfra/deploy-client.sh b/ChatInfra/scripts/deploy-client-dev.sh similarity index 57% rename from ChatInfra/deploy-client.sh rename to ChatInfra/scripts/deploy-client-dev.sh index 840750c..263603a 100755 --- a/ChatInfra/deploy-client.sh +++ b/ChatInfra/scripts/deploy-client-dev.sh @@ -3,14 +3,16 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + echo "Reading Terraform outputs..." -API_URL=$(terraform -chdir="$SCRIPT_DIR" output -raw apiserver_url) -WS_URL=$(terraform -chdir="$SCRIPT_DIR" output -raw chatserver_ws_url) -BUCKET=$(terraform -chdir="$SCRIPT_DIR" output -raw chatclient_bucket) -DIST_ID=$(terraform -chdir="$SCRIPT_DIR" output -raw chatclient_distribution_id) +TF_DIR="$SCRIPT_DIR/../environments/dev" +API_URL=$(terraform -chdir="$TF_DIR" output -raw apiserver_url) +WS_URL=$(terraform -chdir="$TF_DIR" output -raw chatserver_ws_url) +BUCKET=$(terraform -chdir="$TF_DIR" output -raw chatclient_bucket) +DIST_ID=$(terraform -chdir="$TF_DIR" output -raw chatclient_distribution_id) echo "Building ChatClient..." -cd "$SCRIPT_DIR/../ChatClient" +cd "$SCRIPT_DIR/../../ChatClient" VITE_API_BASE=$API_URL VITE_WS_URL=$WS_URL npm run build echo "Syncing to S3..." diff --git a/ChatServer/.idea/.gitignore b/ChatServer/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/ChatServer/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/ChatServer/.idea/codeStyles/codeStyleConfig.xml b/ChatServer/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/ChatServer/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/ChatServer/.idea/gradle.xml b/ChatServer/.idea/gradle.xml deleted file mode 100644 index 2a65317..0000000 --- a/ChatServer/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/ChatServer/.idea/kotlinc.xml b/ChatServer/.idea/kotlinc.xml deleted file mode 100644 index 739bc36..0000000 --- a/ChatServer/.idea/kotlinc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/ChatServer/.idea/misc.xml b/ChatServer/.idea/misc.xml deleted file mode 100644 index 5cd9a10..0000000 --- a/ChatServer/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/ChatServer/.idea/vcs.xml b/ChatServer/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/ChatServer/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/ChatServer/build.gradle.kts b/ChatServer/build.gradle.kts index fcc545c..d92929a 100644 --- a/ChatServer/build.gradle.kts +++ b/ChatServer/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { val koinVersion = "3.5.6" val awsSdkVersion = "2.28.0" + val xrayVersion = "2.15.3" val mockkVersion = "1.13.10" testImplementation(kotlin("test")) @@ -51,6 +52,10 @@ dependencies { implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") + // X-Ray + implementation("com.amazonaws:aws-xray-recorder-sdk-core:$xrayVersion") + implementation("com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2:$xrayVersion") + // Logging implementation("ch.qos.logback:logback-classic:1.4.14") diff --git a/ChatServer/src/main/kotlin/Main.kt b/ChatServer/src/main/kotlin/Main.kt index 4a05032..f365037 100644 --- a/ChatServer/src/main/kotlin/Main.kt +++ b/ChatServer/src/main/kotlin/Main.kt @@ -11,10 +11,12 @@ import org.chatserver.plugins.configureRouting import org.chatserver.plugins.configureSerialization import org.chatserver.plugins.configureSockets import org.chatserver.plugins.configureSqs +import org.chatserver.plugins.configureXRay fun main() { embeddedServer(CIO, port = 8080) { configureDI() + configureXRay() configureCors() configureSerialization() configureSqs() diff --git a/ChatServer/src/main/kotlin/data/registry/UserRegistry.kt b/ChatServer/src/main/kotlin/data/registry/UserRegistry.kt index 71e7d2f..6f76fd7 100644 --- a/ChatServer/src/main/kotlin/data/registry/UserRegistry.kt +++ b/ChatServer/src/main/kotlin/data/registry/UserRegistry.kt @@ -73,13 +73,21 @@ class UserRegistry( return connected } - fun deregister(userId: String) { - dynamoClient.deleteItem( - DeleteItemRequest.builder() - .tableName(tableName) - .key(mapOf("userId" to AttributeValue.fromS(userId))) - .build(), - ) - logger.info("User $userId deregistered from server $serverId") + fun deregister(userId: String): Boolean { + return try { + dynamoClient.deleteItem( + DeleteItemRequest.builder() + .tableName(tableName) + .key(mapOf("userId" to AttributeValue.fromS(userId))) + .conditionExpression("serverId = :sid") + .expressionAttributeValues(mapOf(":sid" to AttributeValue.fromS(serverId))) + .build(), + ) + logger.info("User $userId deregistered from server $serverId") + true + } catch (e: software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException) { + logger.info("User $userId already claimed by another server, skipping deregister") + false + } } } diff --git a/ChatServer/src/main/kotlin/di/AppModule.kt b/ChatServer/src/main/kotlin/di/AppModule.kt index 427293d..0899811 100644 --- a/ChatServer/src/main/kotlin/di/AppModule.kt +++ b/ChatServer/src/main/kotlin/di/AppModule.kt @@ -1,5 +1,6 @@ package org.chatserver.di +import com.amazonaws.xray.interceptors.TracingInterceptor import org.chatserver.data.registry.ConversationRegistry import org.chatserver.data.registry.ServerRegistry import org.chatserver.data.registry.UserRegistry @@ -13,6 +14,7 @@ import org.chatserver.session.SessionStore import org.koin.core.qualifier.named import org.koin.dsl.module import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration import software.amazon.awssdk.regions.Region import software.amazon.awssdk.services.dynamodb.DynamoDbClient import software.amazon.awssdk.services.sqs.SqsClient @@ -29,6 +31,7 @@ val appModule = DynamoDbClient.builder() .region(Region.US_WEST_1) .credentialsProvider(DefaultCredentialsProvider.create()) + .overrideConfiguration(ClientOverrideConfiguration.builder().addExecutionInterceptor(TracingInterceptor()).build()) .apply { if (endpoint != null) endpointOverride(URI.create(endpoint)) } .build() } @@ -38,6 +41,7 @@ val appModule = SqsClient.builder() .region(Region.US_WEST_1) .credentialsProvider(DefaultCredentialsProvider.create()) + .overrideConfiguration(ClientOverrideConfiguration.builder().addExecutionInterceptor(TracingInterceptor()).build()) .apply { if (endpoint != null) endpointOverride(URI.create(endpoint)) } .build() } diff --git a/ChatServer/src/main/kotlin/plugins/Sockets.kt b/ChatServer/src/main/kotlin/plugins/Sockets.kt index b46efad..f7839ea 100644 --- a/ChatServer/src/main/kotlin/plugins/Sockets.kt +++ b/ChatServer/src/main/kotlin/plugins/Sockets.kt @@ -1,5 +1,6 @@ package org.chatserver.plugins +import com.amazonaws.xray.AWSXRay import io.ktor.server.application.Application import io.ktor.server.application.install import io.ktor.server.auth.authenticate @@ -22,7 +23,7 @@ import org.chatserver.models.PresenceEvent import org.chatserver.models.SqsEnvelope import org.chatserver.routing.MessageRouter import org.chatserver.session.SessionStore -import org.koin.ktor.ext.inject +import org.koin.ktor.ext.getKoin import org.slf4j.LoggerFactory import software.amazon.awssdk.services.sqs.SqsClient @@ -31,13 +32,14 @@ private val logger = LoggerFactory.getLogger("Sockets") fun Application.configureSockets() { install(WebSockets) - val userRegistry by inject() - val conversationRegistry by inject() - val sessionStore by inject() - val messageRouter by inject() - val pendingMessageRepository by inject() - val serverRegistry by inject() - val sqsClient by inject() + val koin = getKoin() + val userRegistry = koin.get() + val conversationRegistry = koin.get() + val sessionStore = koin.get() + val messageRouter = koin.get() + val pendingMessageRepository = koin.get() + val serverRegistry = koin.get() + val sqsClient = koin.get() routing { authenticate("auth-jwt") { @@ -49,8 +51,10 @@ fun Application.configureSockets() { } logger.info("User $userId connected") + AWSXRay.beginSegment("ws-connect $userId") userRegistry.register(userId) sessionStore.add(userId, this) + AWSXRay.endSegment() try { val groupmates = conversationRegistry.getGroupmates(userId) @@ -80,9 +84,13 @@ fun Application.configureSockets() { } } } finally { + AWSXRay.beginSegment("ws-disconnect $userId") sessionStore.remove(userId) - userRegistry.deregister(userId) - broadcastPresence(userId, online = false, serverRegistry, sqsClient) + val deregistered = userRegistry.deregister(userId) + if (deregistered) { + broadcastPresence(userId, online = false, serverRegistry, sqsClient) + } + AWSXRay.endSegment() logger.info("User $userId disconnected") } } diff --git a/ChatServer/src/main/kotlin/plugins/XRay.kt b/ChatServer/src/main/kotlin/plugins/XRay.kt new file mode 100644 index 0000000..e195a82 --- /dev/null +++ b/ChatServer/src/main/kotlin/plugins/XRay.kt @@ -0,0 +1,23 @@ +package org.chatserver.plugins + +import com.amazonaws.xray.AWSXRay +import io.ktor.server.application.Application +import io.ktor.server.application.createApplicationPlugin +import io.ktor.server.application.install +import io.ktor.server.request.httpMethod +import io.ktor.server.request.path + +val XRayPlugin = + createApplicationPlugin("XRay") { + onCall { call -> + val segmentName = "${call.request.httpMethod.value} ${call.request.path()}" + AWSXRay.beginSegment(segmentName) + } + onCallRespond { _ -> + AWSXRay.endSegment() + } + } + +fun Application.configureXRay() { + install(XRayPlugin) +} diff --git a/ChatServer/src/test/kotlin/plugins/SocketsTest.kt b/ChatServer/src/test/kotlin/plugins/SocketsTest.kt index d5392e3..e49480c 100644 --- a/ChatServer/src/test/kotlin/plugins/SocketsTest.kt +++ b/ChatServer/src/test/kotlin/plugins/SocketsTest.kt @@ -76,10 +76,10 @@ class SocketsTest { client.webSocket("/ws?token=${token("alice")}") { close() } } - verify { userRegistry.register("alice") } - verify { sessionStore.add(eq("alice"), any()) } - verify { userRegistry.deregister("alice") } - verify { sessionStore.remove("alice") } + verify(timeout = 2000) { userRegistry.register("alice") } + verify(timeout = 2000) { sessionStore.add(eq("alice"), any()) } + verify(timeout = 2000) { userRegistry.deregister("alice") } + verify(timeout = 2000) { sessionStore.remove("alice") } } @Test diff --git a/PresenceServer/.idea/.gitignore b/PresenceServer/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/PresenceServer/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/PresenceServer/.idea/PresenceServer.iml b/PresenceServer/.idea/PresenceServer.iml deleted file mode 100644 index 4fe5e42..0000000 --- a/PresenceServer/.idea/PresenceServer.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/PresenceServer/.idea/inspectionProfiles/profiles_settings.xml b/PresenceServer/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/PresenceServer/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/PresenceServer/.idea/misc.xml b/PresenceServer/.idea/misc.xml deleted file mode 100644 index 41fe1e0..0000000 --- a/PresenceServer/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/PresenceServer/.idea/modules.xml b/PresenceServer/.idea/modules.xml deleted file mode 100644 index be67521..0000000 --- a/PresenceServer/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/PresenceServer/.idea/vcs.xml b/PresenceServer/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/PresenceServer/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/PresenceServer/__pycache__/main.cpython-314.pyc b/PresenceServer/__pycache__/main.cpython-314.pyc deleted file mode 100644 index 3eed7b0..0000000 Binary files a/PresenceServer/__pycache__/main.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/__pycache__/__init__.cpython-314.pyc b/PresenceServer/app/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 521928d..0000000 Binary files a/PresenceServer/app/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/__pycache__/config.cpython-314.pyc b/PresenceServer/app/__pycache__/config.cpython-314.pyc deleted file mode 100644 index b2f671a..0000000 Binary files a/PresenceServer/app/__pycache__/config.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/__pycache__/dependencies.cpython-314.pyc b/PresenceServer/app/__pycache__/dependencies.cpython-314.pyc deleted file mode 100644 index 5005d63..0000000 Binary files a/PresenceServer/app/__pycache__/dependencies.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/__pycache__/redis.cpython-314.pyc b/PresenceServer/app/__pycache__/redis.cpython-314.pyc deleted file mode 100644 index 6fc09fe..0000000 Binary files a/PresenceServer/app/__pycache__/redis.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/repos/__pycache__/__init__.cpython-314.pyc b/PresenceServer/app/repos/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 0fbea16..0000000 Binary files a/PresenceServer/app/repos/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/repos/__pycache__/presence.cpython-314.pyc b/PresenceServer/app/repos/__pycache__/presence.cpython-314.pyc deleted file mode 100644 index a7468d3..0000000 Binary files a/PresenceServer/app/repos/__pycache__/presence.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/repos/presence.py b/PresenceServer/app/repos/presence.py index 1bc0742..c933879 100644 --- a/PresenceServer/app/repos/presence.py +++ b/PresenceServer/app/repos/presence.py @@ -1,9 +1,14 @@ +import logging + from redis.asyncio import Redis from app.config import settings +logger = logging.getLogger(__name__) + async def set_online(user_id: str, redis: Redis) -> None: + logger.info("set_online user_id=%s ttl=%ds", user_id, settings.heartbeat_ttl_seconds) await redis.set(f"presence:{user_id}", "1", ex=settings.heartbeat_ttl_seconds) diff --git a/PresenceServer/app/routers/__pycache__/__init__.cpython-314.pyc b/PresenceServer/app/routers/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index d18eb7f..0000000 Binary files a/PresenceServer/app/routers/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/routers/__pycache__/presence.cpython-314.pyc b/PresenceServer/app/routers/__pycache__/presence.cpython-314.pyc deleted file mode 100644 index d2f221a..0000000 Binary files a/PresenceServer/app/routers/__pycache__/presence.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/routers/presence.py b/PresenceServer/app/routers/presence.py index 1dc7a15..26cf514 100644 --- a/PresenceServer/app/routers/presence.py +++ b/PresenceServer/app/routers/presence.py @@ -1,3 +1,5 @@ +import logging + from fastapi import APIRouter, Depends from redis.asyncio import Redis @@ -6,6 +8,8 @@ from app.repos.presence import set_online, is_online, are_online from app.schemas.presence import HeartbeatRequest, PresenceResponse, BatchPresenceRequest, BatchPresenceResponse +logger = logging.getLogger(__name__) + router = APIRouter(prefix="/presence", tags=["presence"]) @@ -15,6 +19,7 @@ async def heartbeat( redis: Redis = Depends(get_redis), _: None = Depends(verify_internal_key), ): + logger.info("heartbeat user_ids=%s", payload.user_ids) for user_id in payload.user_ids: await set_online(user_id, redis) @@ -26,6 +31,7 @@ async def get_presence_batch( _: None = Depends(verify_internal_key), ): presence = await are_online(payload.user_ids, redis) + logger.info("batch presence check user_ids=%s result=%s", payload.user_ids, presence) return BatchPresenceResponse(presence=presence) @@ -36,4 +42,5 @@ async def get_presence( _: str = Depends(get_current_user_id), ): online = await is_online(user_id, redis) + logger.info("presence check user_id=%s online=%s", user_id, online) return PresenceResponse(user_id=user_id, online=online) diff --git a/PresenceServer/app/schemas/__pycache__/__init__.cpython-314.pyc b/PresenceServer/app/schemas/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 6b744fe..0000000 Binary files a/PresenceServer/app/schemas/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/app/schemas/__pycache__/presence.cpython-314.pyc b/PresenceServer/app/schemas/__pycache__/presence.cpython-314.pyc deleted file mode 100644 index 3c0b82b..0000000 Binary files a/PresenceServer/app/schemas/__pycache__/presence.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/main.py b/PresenceServer/main.py index d55021d..60b9f1e 100644 --- a/PresenceServer/main.py +++ b/PresenceServer/main.py @@ -1,11 +1,28 @@ -from fastapi import FastAPI +import logging + +from aws_xray_sdk.core import xray_recorder +from fastapi import FastAPI, Request from app.routers import presence +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s %(name)s: %(message)s", +) + app = FastAPI() app.include_router(presence.router) +@app.middleware("http") +async def xray_middleware(request: Request, call_next): + xray_recorder.begin_segment(f"{request.method} {request.url.path}") + try: + return await call_next(request) + finally: + xray_recorder.end_segment() + + @app.get("/health") async def health(): return {"status": "ok"} diff --git a/PresenceServer/requirements.txt b/PresenceServer/requirements.txt index 023c142..19387dc 100644 --- a/PresenceServer/requirements.txt +++ b/PresenceServer/requirements.txt @@ -3,3 +3,4 @@ uvicorn[standard] pydantic-settings redis[asyncio] python-jose[cryptography] +aws-xray-sdk diff --git a/PresenceServer/tests/__pycache__/__init__.cpython-314.pyc b/PresenceServer/tests/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 22f8a68..0000000 Binary files a/PresenceServer/tests/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/PresenceServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc b/PresenceServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index d0ac97c..0000000 Binary files a/PresenceServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/PresenceServer/tests/__pycache__/test_presence.cpython-314-pytest-9.0.2.pyc b/PresenceServer/tests/__pycache__/test_presence.cpython-314-pytest-9.0.2.pyc deleted file mode 100644 index a480267..0000000 Binary files a/PresenceServer/tests/__pycache__/test_presence.cpython-314-pytest-9.0.2.pyc and /dev/null differ diff --git a/diagram.svg b/diagram.svg index 3ccdeb4..559faa1 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1,4 +1,4 @@ -
HTTPS :433
HTTPS :433
Internet
Internet
AWS Cloud
AWS Cloud
x
Public subnet
Public subnet
Private subnet
Private subnet
10.0.1.0/24 - 10.0.2.0/24
10.0.1.0/24 - 10.0.2.0/24
10.0.3.0/24 - 10.0.4.0/24
10.0.3.0/24 - 10.0.4.0/24
Elastic IP
Elastic IP
Elastic Container Service
Elastic Container Service
ChatServer

:8080
ChatServer...
PresenceServer

:8000
PresenceServer...
ApiServer

:8000
ApiServer...
RDS

:5432
RDS...
PresenceServer Internal ALB
PresenceServer Inter...
HTTP /
Websocket :8080
JWT - Websockets
x-internal - HTTP
HTTP /...
ChatServer NLB
ChatServer NLB
HTTP :8000
ecs-tasks-sg
JWT
HTTP :8000...
ApiServer ALB
ApiServer ALB
Redis

:6379
Redis...
Redis Serialization
Protocol
:6379
elasticache-sg
TLS
Redis Serialization...
PostgreSQL :5432
rds-sg
TLS
PostgreSQL :5432...
HTTP :80
internal-alb-sg
x-internal
HTTP :80...
HTTP :80
x-internal
HTTP :80...
SQS calls
DynamoDB calls
ECR calls

HTTPS :433
SQS calls...
HTTP :8000
ecs-tasks-sg
HTTP :8000...
KMS
KMS
KMS
KMS
NAT Gateway
NAT Gateway
ECR calls

HTTPS :433
ECR calls...
VPC
VPC
HTTP :80
alb-sg
HTTP :80...
TCP :80
TCP :80
Internet Gateway
Internet Gateway
Secrets Manager
Secrets Manager
SQS API Endpoint
SQS API Endpoint
SQS
SQS
DynamoDB API Endpoint
DynamoDB API Endpoint
DynamoDB
DynamoDB
ECR
ECR
S3 (ALB logs, CloudTrail, Client)

S3 (ALB logs, CloudT...
CloudFront
CloudFront
AES-256
AES-256
AWS Account
AWS Account
CloudTrail
CloudTrail
10.0.0.0/16
10.0.0.0/16
VPC Flow Logs
VPC Flow Logs
CloudWatch
CloudWatch
HTTPS :433
HTTPS :433
HTTPS :433
HTTPS :433
HTTPS :433
HTTPS :433

Network/Traffic Diagram

Network/Traffic Diagram

IAM/Permissions Diagram

IAM/Permissions Diagram
ECS TASK STARTUP (Execution Roles)
ECS TASK STARTUP (Execution Roles)
ApiServer
ApiServer
ChatServer
ChatServer
PresenceServer
PresenceServer
ecs-execution-api
ecs-execution-api
ecs-execution-chatserver
ecs-execution-chatse...
ecs-execution-presenceserver
ecs-execution-presen...
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
chatsystem/db
chatsystem/db
chatsystem/jwt_secret
chatsystem/jwt_secret
chatsystem/internal_api_key
chatsystem/internal_api_key
chatsystem/jwt_secret
chatsystem/jwt_secret
chatsystem/internal_api_key
chatsystem/internal_api_key
chatsystem/jwt_secret
chatsystem/jwt_secret
chatsystem/internal_api_key
chatsystem/internal_api_key
RUNTIME (Task Roles)
RUNTIME (Task Roles)
ChatServer
ChatServer
chatserver-task
chatserver-task
dynamodb: GetItem, PutItem, DeleteItem, UpdateItem, Query, DescribeTable, Scan
dynamodb: GetItem, PutItem, DeleteIt...
ServerRegistry
ServerRegistry
UserConnections
UserConnections
ConversationMembers
ConversationMembers
Messages
Messages
PendingMessages
PendingMessages
sqs: SendMessage, ReceiveMessage, DeleteMessage, GetQueue, CreateQueue, DeleteQueue
sqs: SendMessage, ReceiveMessage, De...
chat-server-* (wildcard)
chat-server-* (wildcard)
RESOURCE ACCESS POLICIES
RESOURCE ACCESS POLICIES
VPC Flow Logs
VPC Flow Logs
vpc-flow-logs
vpc-flow-logs
logs: CreateLogGroup, CreateLogStream, PutLogEvents, DescribeLogGroups, DescribeLogStream
logs: CreateLogGroup, CreateLogStrea...
*
*
client
client
cloudtrail
cloudtrail
alb-logs
alb-logs
S3 Bucket Policies
S3 Bucket Policies
cloudfront.amazonaws.com
cloudfront.amazonaws.com
cloudtrail.amazonaws.com
cloudtrail.amazonaws.com
ELB Service Account
ELB Service Account
delivery.logs.amazonaws.com
delivery.logs.amazonaws.com
s3: GetObject
s3: GetObject
bucket/*
bucket/*
s3: GetBucketAcl
s3: GetBucketAcl
s3: PutObject
s3: PutObject
bucket
bucket
bucket/AWSLogs/{account_id}/*
bucket/AWSLogs/{account_id}/*
s3: PutObject
s3: PutObject
s3: GetBucketAcl
s3: GetBucketAcl
s3: PutObject
s3: PutObject
bucket/*
bucket/*
bucket
bucket
bucket/*
bucket/*
Service
Service
Role
Role
Action
Action
Resource
Resource
Service
Service
Principal
Principal
Action
Action
Resource
Resource
Service
Service
Role
Role
Action
Action
Resource
Resource
ConversationMembers/index/*
ConversationMembers/index/*
Text is not SVG - cannot display
\ No newline at end of file +
HTTPS :433
HTTPS :433
Internet
Internet
AWS Cloud
AWS Cloud
x
Public subnet
Public subnet
Private subnet
Private subnet
10.0.1.0/24 - 10.0.2.0/24
10.0.1.0/24 - 10.0.2.0/24
10.0.3.0/24 - 10.0.4.0/24
10.0.3.0/24 - 10.0.4.0/24
Elastic IP
Elastic IP
Elastic Container Service
Elastic Container Service
ChatServer

:8080
ChatServer...
PresenceServer

:8000
PresenceServer...
ApiServer

:8000
ApiServer...
RDS

:5432
RDS...
PresenceServer Internal ALB
PresenceServer Inter...
HTTP /
Websocket :8080
JWT - Websockets
x-internal - HTTP
HTTP /...
ChatServer NLB
ChatServer NLB
HTTP :8000
ecs-tasks-sg
JWT
HTTP :8000...
ApiServer ALB
ApiServer ALB
Redis

:6379
Redis...
Redis Serialization
Protocol
:6379
elasticache-sg
TLS
Redis Serialization...
PostgreSQL :5432
rds-sg
TLS
PostgreSQL :5432...
HTTP :80
internal-alb-sg
x-internal
HTTP :80...
HTTP :80
x-internal
HTTP :80...
SQS calls
DynamoDB calls
ECR calls

HTTPS :433
SQS calls...
HTTP :8000
ecs-tasks-sg
HTTP :8000...
KMS
KMS
KMS
KMS
NAT Gateway
NAT Gateway
ECR calls
XRAY calls

HTTPS :433
ECR calls...
VPC
VPC
HTTP :80
alb-sg
HTTP :80...
TCP :80
TCP :80
Internet Gateway
Internet Gateway
Secrets Manager
Secrets Manager
SQS API Endpoint
SQS API Endpoint
SQS
SQS
DynamoDB API Endpoint
DynamoDB API Endpoint
DynamoDB
DynamoDB
ECR
ECR
S3 (ALB logs, CloudTrail, Client)

S3 (ALB logs, CloudT...
CloudFront
CloudFront
AES-256
AES-256
AWS Account
AWS Account
CloudTrail
CloudTrail
10.0.0.0/16
10.0.0.0/16
VPC Flow Logs
VPC Flow Logs
CloudWatch
CloudWatch
HTTPS :433
HTTPS :433
HTTPS :433
HTTPS :433
X-Ray
X-Ray
HTTPS :433
HTTPS :433

Network/Traffic Diagram

Network/Traffic Diagram

IAM/Permissions Diagram

IAM/Permissions Diagram
ECS TASK STARTUP (Execution Roles)
ECS TASK STARTUP (Execution Roles)
ApiServer
ApiServer
ChatServer
ChatServer
PresenceServer
PresenceServer
ecs-execution-api
ecs-execution-api
ecs-execution-chatserver
ecs-execution-chatse...
ecs-execution-presenceserver
ecs-execution-presen...
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
secretsmanager:GetSecretValue
chatsystem/db
chatsystem/db
chatsystem/jwt_secret
chatsystem/jwt_secret
chatsystem/internal_api_key
chatsystem/internal_api_key
chatsystem/jwt_secret
chatsystem/jwt_secret
chatsystem/internal_api_key
chatsystem/internal_api_key
chatsystem/jwt_secret
chatsystem/jwt_secret
chatsystem/internal_api_key
chatsystem/internal_api_key
RUNTIME (Task Roles)
RUNTIME (Task Roles)
RESOURCE ACCESS POLICIES
RESOURCE ACCESS POLICIES
VPC Flow Logs
VPC Flow Logs
vpc-flow-logs
vpc-flow-logs
logs: CreateLogGroup, CreateLogStream, PutLogEvents, DescribeLogGroups, DescribeLogStream
logs: CreateLogGroup, CreateLogStrea...
*
*
client
client
cloudtrail
cloudtrail
alb-logs
alb-logs
S3 Bucket Policies
S3 Bucket Policies
cloudfront.amazonaws.com
cloudfront.amazonaws.com
cloudtrail.amazonaws.com
cloudtrail.amazonaws.com
ELB Service Account
ELB Service Account
delivery.logs.amazonaws.com
delivery.logs.amazonaws.com
s3: GetObject
s3: GetObject
bucket/*
bucket/*
s3: GetBucketAcl
s3: GetBucketAcl
s3: PutObject
s3: PutObject
bucket
bucket
bucket/AWSLogs/{account_id}/*
bucket/AWSLogs/{account_id}/*
s3: PutObject
s3: PutObject
s3: GetBucketAcl
s3: GetBucketAcl
s3: PutObject
s3: PutObject
bucket/*
bucket/*
bucket
bucket
bucket/*
bucket/*
Role
Role
Action
Action
Resource
Resource
Service
Service
Principal
Principal
Action
Action
Resource
Resource
Service
Service
Role
Role
Action
Action
Resource
Resource
xray: PutTraceSegments, PutTelemetryRecords, GetSamplingRules, GetSamplingTargets, GetSamplingStatisticSummaries
xray: PutTraceSegments, PutTelemetry...
Service
Service
ChatServer
ChatServer
chatserver-task
chatserver-task
chatserver-xray
chatserver-xray
dynamodb: GetItem, PutItem, DeleteItem, UpdateItem, Query, DescribeTable, Scan
dynamodb: GetItem, PutItem, DeleteIt...
sqs: SendMessage, ReceiveMessage, DeleteMessage, GetQueue, CreateQueue, DeleteQueue
sqs: SendMessage, ReceiveMessage, De...
ServerRegistry
ServerRegistry
UserConnections
UserConnections
ConversationMembers
ConversationMembers
Messages
Messages
PendingMessages
PendingMessages
chat-server-* (wildcard)
chat-server-* (wildcard)
ConversationMembers/index/*
ConversationMembers/index/*
PresenceServer
PresenceServer
xray: PutTraceSegments, PutTelemetryRecords, GetSamplingRules, GetSamplingTargets, GetSamplingStatisticSummaries
xray: PutTraceSegments, PutTelemetry...
xray: PutTraceSegments, PutTelemetryRecords, GetSamplingRules, GetSamplingTargets, GetSamplingStatisticSummaries
xray: PutTraceSegments, PutTelemetry...
presenceserver-xray
presenceserver-xray
apiserver-xray
apiserver-xray
ApiServer
ApiServer
*
*
*
*
*
*
Text is not SVG - cannot display
\ No newline at end of file