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 - - - 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 :433HTTPS :433InternetInternetAWS CloudAWS CloudxPublic subnetPublic subnetPrivate subnetPrivate subnet10.0.1.0/24 - 10.0.2.0/2410.0.1.0/24 - 10.0.2.0/2410.0.3.0/24 - 10.0.4.0/2410.0.3.0/24 - 10.0.4.0/24Elastic IPElastic IPElastic Container ServiceElastic Container ServiceChatServer:8080ChatServer...PresenceServer:8000PresenceServer...ApiServer:8000ApiServer...RDS:5432RDS...PresenceServer Internal ALBPresenceServer Inter...HTTP /Websocket :8080JWT - Websocketsx-internal - HTTPHTTP /...ChatServer NLBChatServer NLBHTTP :8000ecs-tasks-sgJWTHTTP :8000...ApiServer ALBApiServer ALBRedis:6379Redis...Redis SerializationProtocol:6379elasticache-sgTLSRedis Serialization...PostgreSQL :5432rds-sgTLSPostgreSQL :5432...HTTP :80internal-alb-sgx-internalHTTP :80...HTTP :80x-internalHTTP :80...SQS callsDynamoDB callsECR callsHTTPS :433SQS calls...HTTP :8000ecs-tasks-sgHTTP :8000...KMSKMSKMSKMSNAT GatewayNAT GatewayECR callsHTTPS :433ECR calls...VPCVPCHTTP :80alb-sgHTTP :80...TCP :80TCP :80Internet GatewayInternet GatewaySecrets ManagerSecrets ManagerSQS API EndpointSQS API EndpointSQSSQSDynamoDB API EndpointDynamoDB API EndpointDynamoDBDynamoDBECRECRS3 (ALB logs, CloudTrail, Client)S3 (ALB logs, CloudT...CloudFrontCloudFrontAES-256AES-256AWS AccountAWS AccountCloudTrailCloudTrail10.0.0.0/1610.0.0.0/16VPC Flow LogsVPC Flow LogsCloudWatchCloudWatchHTTPS :433HTTPS :433HTTPS :433HTTPS :433HTTPS :433HTTPS :433Network/Traffic DiagramNetwork/Traffic DiagramIAM/Permissions DiagramIAM/Permissions DiagramECS TASK STARTUP (Execution Roles)ECS TASK STARTUP (Execution Roles)ApiServerApiServerChatServerChatServerPresenceServerPresenceServerecs-execution-apiecs-execution-apiecs-execution-chatserverecs-execution-chatse...ecs-execution-presenceserverecs-execution-presen...secretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuechatsystem/dbchatsystem/dbchatsystem/jwt_secretchatsystem/jwt_secretchatsystem/internal_api_keychatsystem/internal_api_keychatsystem/jwt_secretchatsystem/jwt_secretchatsystem/internal_api_keychatsystem/internal_api_keychatsystem/jwt_secretchatsystem/jwt_secretchatsystem/internal_api_keychatsystem/internal_api_keyRUNTIME (Task Roles)RUNTIME (Task Roles)ChatServerChatServerchatserver-taskchatserver-taskdynamodb: GetItem, PutItem, DeleteItem, UpdateItem, Query, DescribeTable, Scandynamodb: GetItem, PutItem, DeleteIt...ServerRegistryServerRegistryUserConnectionsUserConnectionsConversationMembersConversationMembersMessagesMessagesPendingMessagesPendingMessagessqs: SendMessage, ReceiveMessage, DeleteMessage, GetQueue, CreateQueue, DeleteQueuesqs: SendMessage, ReceiveMessage, De...chat-server-* (wildcard)chat-server-* (wildcard)RESOURCE ACCESS POLICIESRESOURCE ACCESS POLICIESVPC Flow LogsVPC Flow Logsvpc-flow-logsvpc-flow-logslogs: CreateLogGroup, CreateLogStream, PutLogEvents, DescribeLogGroups, DescribeLogStreamlogs: CreateLogGroup, CreateLogStrea...**clientclientcloudtrailcloudtrailalb-logsalb-logsS3 Bucket PoliciesS3 Bucket Policiescloudfront.amazonaws.comcloudfront.amazonaws.comcloudtrail.amazonaws.comcloudtrail.amazonaws.comELB Service AccountELB Service Accountdelivery.logs.amazonaws.comdelivery.logs.amazonaws.coms3: GetObjects3: GetObjectbucket/*bucket/*s3: GetBucketAcls3: GetBucketAcls3: PutObjects3: PutObjectbucketbucketbucket/AWSLogs/{account_id}/*bucket/AWSLogs/{account_id}/*s3: PutObjects3: PutObjects3: GetBucketAcls3: GetBucketAcls3: PutObjects3: PutObjectbucket/*bucket/*bucketbucketbucket/*bucket/*ServiceServiceRoleRoleActionActionResourceResourceServiceServicePrincipalPrincipalActionActionResourceResourceServiceServiceRoleRoleActionActionResourceResourceConversationMembers/index/*ConversationMembers/index/*Text is not SVG - cannot display \ No newline at end of file +HTTPS :433HTTPS :433InternetInternetAWS CloudAWS CloudxPublic subnetPublic subnetPrivate subnetPrivate subnet10.0.1.0/24 - 10.0.2.0/2410.0.1.0/24 - 10.0.2.0/2410.0.3.0/24 - 10.0.4.0/2410.0.3.0/24 - 10.0.4.0/24Elastic IPElastic IPElastic Container ServiceElastic Container ServiceChatServer:8080ChatServer...PresenceServer:8000PresenceServer...ApiServer:8000ApiServer...RDS:5432RDS...PresenceServer Internal ALBPresenceServer Inter...HTTP /Websocket :8080JWT - Websocketsx-internal - HTTPHTTP /...ChatServer NLBChatServer NLBHTTP :8000ecs-tasks-sgJWTHTTP :8000...ApiServer ALBApiServer ALBRedis:6379Redis...Redis SerializationProtocol:6379elasticache-sgTLSRedis Serialization...PostgreSQL :5432rds-sgTLSPostgreSQL :5432...HTTP :80internal-alb-sgx-internalHTTP :80...HTTP :80x-internalHTTP :80...SQS callsDynamoDB callsECR callsHTTPS :433SQS calls...HTTP :8000ecs-tasks-sgHTTP :8000...KMSKMSKMSKMSNAT GatewayNAT GatewayECR callsXRAY callsHTTPS :433ECR calls...VPCVPCHTTP :80alb-sgHTTP :80...TCP :80TCP :80Internet GatewayInternet GatewaySecrets ManagerSecrets ManagerSQS API EndpointSQS API EndpointSQSSQSDynamoDB API EndpointDynamoDB API EndpointDynamoDBDynamoDBECRECRS3 (ALB logs, CloudTrail, Client)S3 (ALB logs, CloudT...CloudFrontCloudFrontAES-256AES-256AWS AccountAWS AccountCloudTrailCloudTrail10.0.0.0/1610.0.0.0/16VPC Flow LogsVPC Flow LogsCloudWatchCloudWatchHTTPS :433HTTPS :433HTTPS :433HTTPS :433X-RayX-RayHTTPS :433HTTPS :433Network/Traffic DiagramNetwork/Traffic DiagramIAM/Permissions DiagramIAM/Permissions DiagramECS TASK STARTUP (Execution Roles)ECS TASK STARTUP (Execution Roles)ApiServerApiServerChatServerChatServerPresenceServerPresenceServerecs-execution-apiecs-execution-apiecs-execution-chatserverecs-execution-chatse...ecs-execution-presenceserverecs-execution-presen...secretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuesecretsmanager:GetSecretValuechatsystem/dbchatsystem/dbchatsystem/jwt_secretchatsystem/jwt_secretchatsystem/internal_api_keychatsystem/internal_api_keychatsystem/jwt_secretchatsystem/jwt_secretchatsystem/internal_api_keychatsystem/internal_api_keychatsystem/jwt_secretchatsystem/jwt_secretchatsystem/internal_api_keychatsystem/internal_api_keyRUNTIME (Task Roles)RUNTIME (Task Roles)RESOURCE ACCESS POLICIESRESOURCE ACCESS POLICIESVPC Flow LogsVPC Flow Logsvpc-flow-logsvpc-flow-logslogs: CreateLogGroup, CreateLogStream, PutLogEvents, DescribeLogGroups, DescribeLogStreamlogs: CreateLogGroup, CreateLogStrea...**clientclientcloudtrailcloudtrailalb-logsalb-logsS3 Bucket PoliciesS3 Bucket Policiescloudfront.amazonaws.comcloudfront.amazonaws.comcloudtrail.amazonaws.comcloudtrail.amazonaws.comELB Service AccountELB Service Accountdelivery.logs.amazonaws.comdelivery.logs.amazonaws.coms3: GetObjects3: GetObjectbucket/*bucket/*s3: GetBucketAcls3: GetBucketAcls3: PutObjects3: PutObjectbucketbucketbucket/AWSLogs/{account_id}/*bucket/AWSLogs/{account_id}/*s3: PutObjects3: PutObjects3: GetBucketAcls3: GetBucketAcls3: PutObjects3: PutObjectbucket/*bucket/*bucketbucketbucket/*bucket/*RoleRoleActionActionResourceResourceServiceServicePrincipalPrincipalActionActionResourceResourceServiceServiceRoleRoleActionActionResourceResourcexray: PutTraceSegments, PutTelemetryRecords, GetSamplingRules, GetSamplingTargets, GetSamplingStatisticSummariesxray: PutTraceSegments, PutTelemetry...ServiceServiceChatServerChatServerchatserver-taskchatserver-taskchatserver-xraychatserver-xraydynamodb: GetItem, PutItem, DeleteItem, UpdateItem, Query, DescribeTable, Scandynamodb: GetItem, PutItem, DeleteIt...sqs: SendMessage, ReceiveMessage, DeleteMessage, GetQueue, CreateQueue, DeleteQueuesqs: SendMessage, ReceiveMessage, De...ServerRegistryServerRegistryUserConnectionsUserConnectionsConversationMembersConversationMembersMessagesMessagesPendingMessagesPendingMessageschat-server-* (wildcard)chat-server-* (wildcard)ConversationMembers/index/*ConversationMembers/index/*PresenceServerPresenceServerxray: PutTraceSegments, PutTelemetryRecords, GetSamplingRules, GetSamplingTargets, GetSamplingStatisticSummariesxray: PutTraceSegments, PutTelemetry...xray: PutTraceSegments, PutTelemetryRecords, GetSamplingRules, GetSamplingTargets, GetSamplingStatisticSummariesxray: PutTraceSegments, PutTelemetry...presenceserver-xraypresenceserver-xrayapiserver-xrayapiserver-xrayApiServerApiServer******Text is not SVG - cannot display \ No newline at end of file