diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..453fbb4 --- /dev/null +++ b/.env.example @@ -0,0 +1,31 @@ +# Database Configuration +DATABASE_USER=SA +DATABASE_PASSWORD=YourStrong@Passw0rd +DATABASE_NAME=PathwayCoordinator +DATABASE_HOST=db +AZURE_WEB_JOBS_STORAGE=UseDevelopmentStorage=true +DatabaseConnectionString=Server=${DATABASE_HOST};Database=${DATABASE_NAME};User Id=${DATABASE_USER};Password=${DATABASE_PASSWORD};TrustServerCertificate=True +AzureWebJobsStorage=UseDevelopmentStorage=true +FUNCTIONS_WORKER_RUNTIME=dotnet-isolated +MailboxId=X26ABC1 +MeshSharedKey=TestKey +MeshPassword=password +BSSMailBox=X26ABC1 +MeshApiBaseUrl=http://localhost:8700/messageexchange +ASPNETCORE_ENVIRONMENT=Development +DiscoveryTimerExpression=*/5 * * * * +QueueUrl=http://127.0.0.1:10001 + +# API Configuration +API_PORT=7071 + +# Event Grid Configuration +EVENT_GRID_TOPIC_URL=https://localhost:60101/api/events +EVENT_GRID_TOPIC_KEY=TheLocal+DevelopmentKey= + +# Azurite Configuration +AZURITE_ACCOUNT_KEY=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw== +AZURITE_CONNECTION_STRING=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1 +AZURITE_BLOB_PORT=10000 +AZURITE_QUEUE_PORT=10001 +AZURITE_TABLE_PORT=10002 diff --git a/compose.yaml b/compose.yaml index 48fedf2..7b0059e 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,9 +1,8 @@ services: - ### ✅ API (Azure Functions in .NET 9) ### api: container_name: "api" build: - context: ./src + context: ./Src dockerfile: ServiceLayer.API/Dockerfile platform: linux/amd64 restart: always @@ -11,11 +10,88 @@ services: FUNCTIONS_WORKER_RUNTIME: "dotnet-isolated" AzureWebJobsStorage: "${AZURE_WEB_JOBS_STORAGE}" AzureWebJobsSecretStorageType: "files" + DatabaseConnectionString: "${DatabaseConnectionString}" + EVENT_GRID_TOPIC_URL: "${EVENT_GRID_TOPIC_URL}" + EVENT_GRID_TOPIC_KEY: "${EVENT_GRID_TOPIC_KEY}" ports: - "${API_PORT}:80" + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:80/api/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + depends_on: + azurite: + condition: service_healthy + db: + condition: service_healthy + networks: + - backend + + azurite: + container_name: "azurite" + image: mcr.microsoft.com/azure-storage/azurite:latest + restart: always + command: "azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --location /data --debug /data/debug.log" + ports: + - "${AZURITE_BLOB_PORT}:10000" + - "${AZURITE_QUEUE_PORT}:10001" + - "${AZURITE_TABLE_PORT}:10002" + healthcheck: + test: ["CMD-SHELL", "/bin/sh -c 'nc -z 127.0.0.1 10000 || exit 1'"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 15s + volumes: + - azurite-data:/data + networks: + - backend + + db: + container_name: "db" + image: mcr.microsoft.com/mssql/server:2022-latest + restart: always + environment: + ACCEPT_EULA: "Y" + MSSQL_SA_PASSWORD: "${DATABASE_PASSWORD}" + MSSQL_PID: "Developer" + ports: + - "1433:1433" + user: "root" + volumes: + - db-data:/var/opt/mssql + healthcheck: + test: ["CMD-SHELL", "pgrep -f sqlservr || exit 1"] + interval: 20s + timeout: 10s + retries: 6 + start_period: 60s + networks: + - backend + + db-setup: + container_name: "db-setup" + build: + context: ./database + dockerfile: Dockerfile + restart: "no" + environment: + DATABASE_PASSWORD: "${DATABASE_PASSWORD}" + depends_on: + db: + condition: service_healthy networks: - backend networks: backend: + name: backend-network driver: bridge +volumes: + azurite-data: + name: azurite-data + db-data: + name: db-data + driver: local diff --git a/database/Dockerfile b/database/Dockerfile new file mode 100644 index 0000000..bbe5048 --- /dev/null +++ b/database/Dockerfile @@ -0,0 +1,16 @@ +FROM mcr.microsoft.com/mssql-tools:v1 + +# Use root initially to set permissions +USER root +WORKDIR /database +COPY db-setup-entrypoint.sh /database/ +COPY create_database_statement.sql /database/ + +RUN chmod +x /database/db-setup-entrypoint.sh && \ + useradd -m -s /bin/bash mssqltools && \ + chown -R mssqltools:mssqltools /database + +# Switch to non-root user for security +USER mssqltools + +ENTRYPOINT ["/database/db-setup-entrypoint.sh"] diff --git a/database/create_database_statement.sql b/database/create_database_statement.sql new file mode 100644 index 0000000..a6db7d9 --- /dev/null +++ b/database/create_database_statement.sql @@ -0,0 +1,23 @@ +USE master; +GO + +-- Check if database exists and create it if it doesn't +IF NOT EXISTS ( + SELECT name + FROM sys.databases + WHERE name = N'ServiceLayer' + ) +BEGIN + CREATE DATABASE [ServiceLayer]; +END +GO + +USE [ServiceLayer]; +GO + +-- Only try to set QUERY_STORE if SQL Server version is higher than 12 (SQL 2014) +IF CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(128)) > '12' +BEGIN + ALTER DATABASE [ServiceLayer] SET QUERY_STORE = ON; +END +GO diff --git a/database/db-setup-entrypoint.sh b/database/db-setup-entrypoint.sh new file mode 100644 index 0000000..062f666 --- /dev/null +++ b/database/db-setup-entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash +echo "🔧 Starting DB setup..." + +# Check if the SQL script exists +if [ ! -f /database/create_database_statement.sql ]; then + echo "❌ SQL script not found!" + exit 1 +fi + +echo "📡 Connecting to SQL Server at db..." +until /opt/mssql-tools/bin/sqlcmd -S db -U SA -P "${DATABASE_PASSWORD}" -Q "SELECT 1;" > /dev/null 2>&1 +do + echo "⏳ Waiting for SQL Server..." + sleep 5 +done +echo "✅ SQL Server is ready." + +# Run the SQL setup script to create the database +/opt/mssql-tools/bin/sqlcmd -S db -U SA -P "${DATABASE_PASSWORD}" -d master -i /database/create_database_statement.sql + +echo "✅ Database setup completed."