diff --git a/.github/workflows/_reusable-ci.yml b/.github/workflows/_reusable-ci.yml new file mode 100644 index 00000000..5594d304 --- /dev/null +++ b/.github/workflows/_reusable-ci.yml @@ -0,0 +1,93 @@ +name: reusable-ci + +on: + workflow_call: + inputs: + working-directory: + required: true + type: string + node-version: + type: string + default: '20' + run-tests: + type: boolean + default: true + build-docker: + type: boolean + default: false + image-name: + type: string + default: '' + docker-tag: + type: string + default: '' + +jobs: + ci: + name: CI - ${{ inputs.working-directory }} + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + steps: + - uses: actions/checkout@v4 + + - name: Detect package.json + id: detect + working-directory: ${{ inputs.working-directory }} + run: | + if [ -f package.json ]; then echo "has_pkg=true" >> $GITHUB_OUTPUT; else echo "has_pkg=false" >> $GITHUB_OUTPUT; fi + + - if: steps.detect.outputs.has_pkg == 'true' + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: npm + cache-dependency-path: ${{ inputs.working-directory }}/package-lock.json + + - if: steps.detect.outputs.has_pkg == 'true' + working-directory: ${{ inputs.working-directory }} + run: | + if [ -f package-lock.json ]; then npm ci; else npm install; fi + + - if: steps.detect.outputs.has_pkg == 'true' + working-directory: ${{ inputs.working-directory }} + run: npm run lint --if-present + + - if: steps.detect.outputs.has_pkg == 'true' && inputs.run-tests + working-directory: ${{ inputs.working-directory }} + run: npm test --if-present -- --ci + + docker: + name: Docker build/publish - ${{ inputs.image-name }} + if: inputs.build-docker + needs: ci + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - name: Verify Dockerfile is present + run: | + echo "Expecting: ${{ inputs.working-directory }}/Dockerfile" + ls -la "${{ inputs.working-directory }}" + test -f "${{ inputs.working-directory }}/Dockerfile" + + - name: Docker Login + uses: docker/login-action@v3 + with: + registry: ${{ secrets.ACR_LOGIN_SERVER }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} + + - name: Build & push + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.working-directory }} + file: ${{ inputs.working-directory }}/Dockerfile + push: true + tags: | + ${{ secrets.ACR_LOGIN_SERVER }}/week08/${{ inputs.image-name }}:${{ inputs.docker-tag || github.ref_name }} + ${{ secrets.ACR_LOGIN_SERVER }}/week08/${{ inputs.image-name }}:sha-${{ github.sha }} diff --git a/.github/workflows/backend-cd.yml b/.github/workflows/backend-cd.yml deleted file mode 100644 index 6035ed15..00000000 --- a/.github/workflows/backend-cd.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: CD - Deploy Backend Services to AKS - -on: - workflow_dispatch: - inputs: - aks_cluster_name: - description: 'Name of the AKS Cluster to deploy to' - required: true - default: '' - aks_resource_group: - description: 'Resource Group of the AKS Cluster' - required: true - default: '' - aks_acr_name: - description: 'Name of ACR' - required: true - default: '' - -jobs: - deploy_backend: - runs-on: ubuntu-latest - environment: Production - - outputs: - PRODUCT_API_IP: ${{ steps.get_product_ip.outputs.external_ip }} - ORDER_API_IP: ${{ steps.get_order_ip.outputs.external_ip }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Log in to Azure - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - enable-AzPSSession: true - - - name: Set Kubernetes context (get AKS credentials) - run: | - az aks get-credentials --resource-group ${{ github.event.inputs.aks_resource_group }} --name ${{ github.event.inputs.aks_cluster_name }} --overwrite-existing - - - name: Attach ACR - run: | - az aks update --name ${{ github.event.inputs.aks_cluster_name }} --resource-group ${{ github.event.inputs.aks_resource_group }} --attach-acr ${{ github.event.inputs.aks_acr_name }} - - - name: Deploy Backend Infrastructure (Namespace, ConfigMaps, Secrets, Databases) - run: | - echo "Deploying backend infrastructure..." - cd k8s/ - kubectl apply -f configmaps.yaml - kubectl apply -f secrets.yaml - kubectl apply -f product-db.yaml - kubectl apply -f order-db.yaml - - - name: Deploy Backend Microservices (Product, Order) - run: | - echo "Deploying backend microservices..." - cd k8s/ - kubectl apply -f product-service.yaml - kubectl apply -f order-service.yaml - - - name: Wait for Backend LoadBalancer IPs - run: | - echo "Waiting for Product, Order LoadBalancer IPs to be assigned (up to 5 minutes)..." - PRODUCT_IP="" - ORDER_IP="" - - for i in $(seq 1 60); do - echo "Attempt $i/60 to get IPs..." - PRODUCT_IP=$(kubectl get service product-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - ORDER_IP=$(kubectl get service order-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - - if [[ -n "$PRODUCT_IP" && -n "$ORDER_IP" ]]; then - echo "All backend LoadBalancer IPs assigned!" - echo "Product Service IP: $PRODUCT_IP" - echo "Order Service IP: $ORDER_IP" - break - fi - sleep 5 # Wait 5 seconds before next attempt - done - - if [[ -z "$PRODUCT_IP" || -z "$ORDER_IP" ]]; then - echo "Error: One or more LoadBalancer IPs not assigned after timeout." - exit 1 # Fail the job if IPs are not obtained - fi - - # These are environment variables for subsequent steps in the *same job* - # And used to set the job outputs - echo "PRODUCT_IP=$PRODUCT_IP" >> $GITHUB_ENV - echo "ORDER_IP=$ORDER_IP" >> $GITHUB_ENV - - - name: Capture Product Service IP for Workflow Output - id: get_product_ip - run: echo "external_ip=${{ env.PRODUCT_IP }}" >> $GITHUB_OUTPUT - - - name: Capture Order Service IP for Workflow Output - id: get_order_ip - run: echo "external_ip=${{ env.ORDER_IP }}" >> $GITHUB_OUTPUT - - - name: Logout from Azure - run: az logout diff --git a/.github/workflows/backend_ci.yml b/.github/workflows/backend_ci.yml deleted file mode 100644 index d69725aa..00000000 --- a/.github/workflows/backend_ci.yml +++ /dev/null @@ -1,146 +0,0 @@ -# week08/.github/workflows/backend_ci.yml - -name: Backend CI - Test, Build and Push Images to ACR - -# Trigger the workflow on pushes to the 'main' branch -# You can also add 'pull_request:' to run on PRs -on: - # Manual trigger - workflow_dispatch: - - # Automatically on pushes to main branch - push: - branches: - - main - paths: # Only trigger if changes are in backend directories - - 'backend/**' - - '.github/workflows/backend_ci.yml' # Trigger if this workflow file changes - -# Define global environment variables that can be used across jobs -env: - # ACR Login Server (e.g., myregistry.azurecr.io) - # This needs to be set as a GitHub Repository Secret - ACR_LOGIN_SERVER: ${{ secrets.AZURE_CONTAINER_REGISTRY }} - # Dynamically generate image tags based on Git SHA and GitHub Run ID - # This provides unique, traceable tags for each image build - IMAGE_TAG: ${{ github.sha }}-${{ github.run_id }} - -jobs: - # Job 1: Run tests and linting for all backend services - test_and_lint_backends: - runs-on: ubuntu-latest # Use a GitHub-hosted runner - - services: - # Product DB container - product_db: - image: postgres:15 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: products - # Make pg_isready available so the service is healthy before tests run - options: >- - --health-cmd "pg_isready -U postgres" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - - # Order DB - order_db: - image: postgres:15 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: orders - ports: - - 5433:5432 - options: >- - --health-cmd "pg_isready -U postgres" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - steps: - # 1. Checkout the repository code to the runner - - name: Checkout repository - uses: actions/checkout@v4 # Action to check out your repository code - - # 2. Set up Python environment - - name: Set up Python 3.10 - uses: actions/setup-python@v5 # Action to set up Python environment - with: - python-version: '3.10' - - # 3. Install dependencies and run code quality checks - - name: Install dependencies - run: | # Use a multi-line script to install pip dependencies - pip install --upgrade pip - # Loop through each backend service folder - for req in backend/*/requirements.txt; do - echo "Installing $req" - pip install -r "$req" - done - # Install CI tools - pip install pytest httpx - - # 5. Run tests for product service - - name: Run product_service tests - working-directory: backend/product_service - env: - POSTGRES_HOST: localhost - POSTGRES_PORT: 5432 - POSTGRES_DB: products - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - run: | - pytest tests --maxfail=1 --disable-warnings -q - - # 6. Run tests for order service - - name: Run order_service tests - working-directory: backend/order_service - env: - POSTGRES_HOST: localhost - POSTGRES_PORT: 5433 - POSTGRES_DB: orders - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - run: | - pytest tests --maxfail=1 --disable-warnings -q - - # Job 2: Build and Push Docker Images (runs only if tests pass) - build_and_push_images: - runs-on: ubuntu-latest - needs: test_and_lint_backends - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Azure login using a Service Principal secret - - name: Azure Login - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} # Needs to be set as a GitHub Secret (Service Principal JSON) - - # Login to Azure Container Registry (ACR) - - name: Login to Azure Container Registry - run: az acr login --name ${{ env.ACR_LOGIN_SERVER }} - - # Build and Push Docker image for Product Service - - name: Build and Push Product Service Image - run: | - docker build -t ${{ env.ACR_LOGIN_SERVER }}/product_service:latest ./backend/product_service/ - docker push ${{ env.ACR_LOGIN_SERVER }}/product_service:latest - - # Build and Push Docker image for Order Service - - name: Build and Push Order Service Image - run: | - docker build -t ${{ env.ACR_LOGIN_SERVER }}/order_service:latest ./backend/order_service/ - docker push ${{ env.ACR_LOGIN_SERVER }}/order_service:latest - - # Logout from Azure for security (runs even if image push fails) - - name: Logout from Azure - run: az logout - if: always() diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml new file mode 100644 index 00000000..e7a4118c --- /dev/null +++ b/.github/workflows/ci-pr.yml @@ -0,0 +1,28 @@ +name: PR CI +on: + pull_request: + branches: [ develop, main ] + paths: + - 'frontend/**' + - 'backend/**' + - '.github/workflows/**' + push: + branches: [ 'feature/**' ] + paths: + - 'frontend/**' + - 'backend/**' + +jobs: + frontend: + uses: ./.github/workflows/_reusable-ci.yml + with: + working-directory: frontend + run-tests: true + build-docker: false + + backend: + uses: ./.github/workflows/_reusable-ci.yml + with: + working-directory: backend + run-tests: true + build-docker: false diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml new file mode 100644 index 00000000..e5d03120 --- /dev/null +++ b/.github/workflows/deploy-production.yml @@ -0,0 +1,49 @@ +name: Deploy to Production (AKS) + +on: + push: + branches: [ main ] + +permissions: + id-token: write + contents: read + +concurrency: + group: deploy-production-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy: + runs-on: ubuntu-latest + environment: production + + steps: + - uses: actions/checkout@v4 + + - name: Azure login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Set AKS context + uses: azure/aks-set-context@v4 + with: + resource-group: ${{ secrets.AKS_RESOURCE_GROUP }} + cluster-name: ${{ secrets.AKS_CLUSTER_NAME }} + + - name: Deploy manifests to AKS (prod) + uses: azure/k8s-deploy@v4 + with: + namespace: default + manifests: | + k8s/frontend.yaml + k8s/backend.yaml + images: | + frontend-container=${{ secrets.ACR_LOGIN_SERVER }}/week08/frontend:latest + backend-container=${{ secrets.ACR_LOGIN_SERVER }}/week08/backend:latest + + - name: Show rollout status & services + run: | + kubectl rollout status deploy/frontend --timeout=180s || true + kubectl rollout status deploy/backend --timeout=180s || true + kubectl get deploy,po,svc -o wide diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml new file mode 100644 index 00000000..2005f942 --- /dev/null +++ b/.github/workflows/deploy-staging.yml @@ -0,0 +1,49 @@ +name: Deploy to Staging (AKS) + +on: + push: + branches: [ develop ] + +permissions: + id-token: write + contents: read + +concurrency: + group: deploy-staging-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy: + runs-on: ubuntu-latest + environment: staging + + steps: + - uses: actions/checkout@v4 + + - name: Azure login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Set AKS context + uses: azure/aks-set-context@v4 + with: + resource-group: ${{ secrets.AKS_RESOURCE_GROUP }} + cluster-name: ${{ secrets.AKS_CLUSTER_NAME }} + + - name: Deploy manifests to AKS (staging) + uses: azure/k8s-deploy@v4 + with: + namespace: default + manifests: | + k8s/frontend.yaml + k8s/backend.yaml + images: | + frontend-container=${{ secrets.ACR_LOGIN_SERVER }}/week08/frontend:develop + backend-container=${{ secrets.ACR_LOGIN_SERVER }}/week08/backend:develop + + - name: Show rollout status & services + run: | + kubectl rollout status deploy/frontend --timeout=180s || true + kubectl rollout status deploy/backend --timeout=180s || true + kubectl get deploy,po,svc -o wide diff --git a/.github/workflows/frontend-cd.yml b/.github/workflows/frontend-cd.yml deleted file mode 100644 index 0a0879c8..00000000 --- a/.github/workflows/frontend-cd.yml +++ /dev/null @@ -1,93 +0,0 @@ -# week08/.github/workflows/frontend-cd.yml - -name: CD - Deploy Frontend to AKS - -# This workflow can be called by other workflows and takes inputs. -# Or it can be run manually if you provide the IPs. -on: - workflow_dispatch: - inputs: - product_api_ip: - description: 'External IP of Product Service' - required: true - default: 'http://:8000' - order_api_ip: - description: 'External IP of Order Service (e.g., http://Y.Y.Y.Y:8001)' - required: true - default: 'http://:8001' - aks_cluster_name: - description: 'Name of the AKS Cluster to deploy to' - required: true - default: '' - aks_resource_group: - description: 'Resource Group of the AKS Cluster' - required: true - default: '<' - - workflow_call: - inputs: - product_api_ip: - required: true - type: string - order_api_ip: - required: true - type: string - aks_cluster_name: - required: true - type: string - aks_resource_group: - required: true - type: string - -jobs: - deploy_frontend: - runs-on: ubuntu-latest - environment: Production - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Azure login using a Service Principal secret - - name: Azure Login - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - # Login to Azure Container Registry (ACR) - - name: Login to Azure Container Registry - run: az acr login --name ${{ secrets.AZURE_CONTAINER_REGISTRY }} - - - name: Inject Backend IPs into Frontend main.js - run: | - echo "Injecting IPs into frontend/static/js/main.js" - # Ensure frontend/main.js is directly in the path for sed - sed -i "s|_PRODUCT_API_URL_|${{ inputs.product_api_ip }}|g" frontend/main.js - sed -i "s|_ORDER_API_URL_|${{ inputs.order_api_ip }}|g" frontend/main.js - - # Display the modified file content for debugging - echo "--- Modified main.js content ---" - cat frontend/main.js - echo "---------------------------------" - - # Build and Push Docker image for Frontend - - name: Build and Push Frontend Image - run: | - docker build -t ${{ secrets.AZURE_CONTAINER_REGISTRY }}/frontend:latest ./frontend/ - docker push ${{ secrets.AZURE_CONTAINER_REGISTRY }}/frontend:latest - - - name: Set Kubernetes context (get AKS credentials) - uses: azure/aks-set-context@v3 - with: - resource-group: ${{ inputs.aks_resource_group }} - cluster-name: ${{ inputs.aks_cluster_name }} - - - name: Deploy Frontend to AKS - run: | - echo "Deploying frontend with latest tag to AKS cluster: ${{ inputs.aks_cluster_name }}" - cd k8s/ - # Ensure frontend-service.yaml is configured with your ACR - kubectl apply -f frontend.yaml - - - name: Logout from Azure (AKS deployment) - run: az logout diff --git a/.github/workflows/frontend_ci.yml b/.github/workflows/frontend_ci.yml deleted file mode 100644 index 9f9e76d9..00000000 --- a/.github/workflows/frontend_ci.yml +++ /dev/null @@ -1,53 +0,0 @@ -# week08/.github/workflows/frontend_ci.yml - -name: Frontend CI - Build & Push Image - -on: - # Manual trigger - workflow_dispatch: - - # Automatically on pushes to main branch - push: - branches: - - main - paths: # Only trigger if changes are in the frontend directory - - 'frontend/**' - - '.github/workflows/frontend_ci.yml' # Trigger if this workflow file changes - -# Define global environment variables that can be used across jobs -env: - # ACR Login Server (e.g., myregistry.azurecr.io) - # This needs to be set as a GitHub Repository Secret - ACR_LOGIN_SERVER: ${{ secrets.AZURE_CONTAINER_REGISTRY }} - # Dynamically generate image tags based on Git SHA and GitHub Run ID - # This provides unique, traceable tags for each image build - IMAGE_TAG: ${{ github.sha }}-${{ github.run_id }} - -jobs: - build_and_push_frontend: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Azure login using a Service Principal secret - - name: Azure Login - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - # Login to Azure Container Registry (ACR) - - name: Login to Azure Container Registry - run: az acr login --name ${{ env.ACR_LOGIN_SERVER }} - - # Build and Push Docker image for Frontend - - name: Build and Push Frontend Image - run: | - docker build -t ${{ env.ACR_LOGIN_SERVER }}/frontend:latest ./frontend/ - docker push ${{ env.ACR_LOGIN_SERVER }}/frontend:latest - - # Logout from Azure for security (runs even if image push fails) - - name: Logout from Azure - run: az logout - if: always() diff --git a/.github/workflows/policy.yml b/.github/workflows/policy.yml new file mode 100644 index 00000000..fb7ada4a --- /dev/null +++ b/.github/workflows/policy.yml @@ -0,0 +1,25 @@ +name: Policy Checks +on: { pull_request: { branches: [develop, main] } } +permissions: { contents: read, security-events: write } +jobs: + codeql: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: { languages: javascript } + - uses: github/codeql-action/analyze@v3 + no-big-binaries: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Fail on files > 20MB + run: | + base="origin/${{ github.base_ref }}" + git fetch origin ${{ github.base_ref }} --depth=1 + for f in $(git diff --name-only $base); do + if [ -f "$f" ] && [ $(stat -c%s "$f") -gt 20000000 ]; then + echo "$f exceeds 20MB"; exit 1 + fi + done + echo "OK" diff --git a/.github/workflows/release-images.yml b/.github/workflows/release-images.yml new file mode 100644 index 00000000..c4842492 --- /dev/null +++ b/.github/workflows/release-images.yml @@ -0,0 +1,49 @@ +name: Release images (ACR) + +on: + push: + branches: [ develop, main ] + +permissions: + contents: read + id-token: write # needed by some actions; fine to keep + +env: + REGISTRY: ${{ secrets.ACR_LOGIN_SERVER }} # e.g. kiranacr.azurecr.io + USERNAME: ${{ secrets.ACR_USERNAME }} + PASSWORD: ${{ secrets.ACR_PASSWORD }} + NS: week08 + TAG: ${{ github.ref_name == 'main' && 'latest' || 'develop' }} + +jobs: + build-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Login to ACR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.USERNAME }} + password: ${{ env.PASSWORD }} + + # ---- Frontend image ---- + - name: Build & push frontend + uses: docker/build-push-action@v5 + with: + context: ./frontend + # if your frontend has a Dockerfile at ./frontend/Dockerfile this works. + # remove "file:" line if your Dockerfile name is exactly "Dockerfile". + file: ./frontend/Dockerfile + push: true + tags: ${{ env.REGISTRY }}/${{ env.NS }}/frontend:${{ env.TAG }} + + # ---- Backend image ---- + - name: Build & push backend + uses: docker/build-push-action@v5 + with: + context: ./backend + file: ./backend/Dockerfile + push: true + tags: ${{ env.REGISTRY }}/${{ env.NS }}/backend:${{ env.TAG }} diff --git a/.gitignore b/.gitignore index cfa76a7f..fbdcec39 100644 --- a/.gitignore +++ b/.gitignore @@ -194,4 +194,5 @@ cython_debug/ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data # refer to https://docs.cursor.com/context/ignore-files .cursorignore -.cursorindexingignore \ No newline at end of file +.cursorindexingignore +azure_credentials.json diff --git a/backend/.ci-trigger b/backend/.ci-trigger new file mode 100644 index 00000000..eac0ecc7 --- /dev/null +++ b/backend/.ci-trigger @@ -0,0 +1,3 @@ +trigger 09/22/2025 15:53:49 +trigger 09/22/2025 15:57:23 +trigger 09/22/2025 16:01:46 diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 00000000..ddcbe9e2 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,17 @@ +# backend/Dockerfile +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app + +# deps +COPY backend/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# app +COPY backend/ ./ + +EXPOSE 8000 +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 00000000..e5649744 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,2 @@ + +Trigger CI at 09/11/2025 12:38:46 diff --git a/backend/app.py b/backend/app.py new file mode 100644 index 00000000..c5b92976 --- /dev/null +++ b/backend/app.py @@ -0,0 +1,12 @@ +from fastapi import FastAPI +from fastapi.responses import JSONResponse + +app = FastAPI() + +@app.get("/") +def root(): + return {"status": "ok", "service": "backend"} + +@app.get("/health") +def health(): + return JSONResponse({"healthy": True}) diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 00000000..3253bba0 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,2 @@ +fastapi==0.112.0 +uvicorn==0.30.6 diff --git a/frontend/index.html b/frontend/index.html index 99da8409..680f8d22 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,6 +4,7 @@ E-commerce Catalog & Orders (Week 08) + Deployed via CI · STAGING