diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 8a1f4d1e..18b2d313 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -40,7 +40,7 @@ jobs: uses: aws-actions/amazon-ecr-login@2fc7aceee09e9e4a7105c0d060c656fad0b4f63d # v1.7.0 - name: Build container - working-directory: ./app + working-directory: ./ run: | docker build \ --build-arg git_sha=$GITHUB_SHA \ @@ -70,6 +70,6 @@ jobs: uses: cds-snc/security-tools/.github/actions/generate-sbom@cfec0943e40dbb78cee115bbbe89dc17f07b7a0f # v2.1.3 with: docker_image: "${{ env.REGISTRY }}/sre-bot:latest" - dockerfile_path: "app/Dockerfile" + dockerfile_path: "./Dockerfile" sbom_name: "sre-bot" token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/ci_container.yml b/.github/workflows/ci_container.yml index 0a463e94..95d1fb86 100644 --- a/.github/workflows/ci_container.yml +++ b/.github/workflows/ci_container.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Build container - working-directory: ./app + working-directory: ./ run: | docker build \ --build-arg git_sha=$GITHUB_SHA \ diff --git a/.github/workflows/docker_vulnerability_scan.yml b/.github/workflows/docker_vulnerability_scan.yml index 48b424ed..d199dcc6 100644 --- a/.github/workflows/docker_vulnerability_scan.yml +++ b/.github/workflows/docker_vulnerability_scan.yml @@ -39,7 +39,7 @@ jobs: uses: cds-snc/security-tools/.github/actions/docker-scan@cfec0943e40dbb78cee115bbbe89dc17f07b7a0f # v2.1.3 with: docker_image: "${{ env.REGISTRY }}/sre-bot:latest" - dockerfile_path: "app/Dockerfile" + dockerfile_path: "Dockerfile" token: "${{ secrets.GITHUB_TOKEN }}" - name: Logout of Amazon ECR diff --git a/app/Dockerfile b/Dockerfile similarity index 74% rename from app/Dockerfile rename to Dockerfile index 30427845..4243275a 100644 --- a/app/Dockerfile +++ b/Dockerfile @@ -2,18 +2,26 @@ FROM python:3.11.5-slim RUN apt-get update \ && apt-get install -y wget \ + && apt-get install -y nodejs \ + npm \ && rm -rf /var/lib/apt/lists/* +WORKDIR frontend/ +COPY frontend/ . + +RUN npm install +RUN npm run build + WORKDIR /app # Set build variables ARG git_sha ENV GIT_SHA=$git_sha -COPY requirements.txt ./ +COPY app/requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt -COPY . . +COPY app/ . ARG LICENSE_KEY @@ -24,6 +32,6 @@ RUN cp /app/geodb/GeoLite2-City_*/GeoLite2-City.mmdb /app/geodb/GeoLite2-City.mm RUN rm -rf /app/geodb/GeoLite2-City_* RUN rm /app/geodb/GeoLite2-City.tar.gz -COPY bin/entry.sh /app/entry.sh +COPY app/bin/entry.sh /app/entry.sh -ENTRYPOINT [ "/app/entry.sh" ] \ No newline at end of file +ENTRYPOINT [ "/app/entry.sh" ] diff --git a/app/requirements.txt b/app/requirements.txt index e1733ea1..9a956476 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -7,6 +7,7 @@ geoip2==4.7.0 google-api-python-client==2.97.0 google-auth-httplib2==0.1.0 google-auth-oauthlib==0.8.0 +Jinja2==3.1.2 PyYaml==5.3.1 # need to pin down the PyYaml version since the newest version is currently broken - https://github.com/yaml/pyyaml/issues/724 python-dotenv==0.21.1 python-i18n==0.3.9 diff --git a/app/server/server.py b/app/server/server.py index 3b8bf39b..66ef5f41 100644 --- a/app/server/server.py +++ b/app/server/server.py @@ -6,6 +6,8 @@ from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel, Extra +from fastapi.templating import Jinja2Templates +from fastapi.staticfiles import StaticFiles from models import webhooks from commands.utils import log_ops_message, log_to_sentinel from integrations import maxmind @@ -63,6 +65,19 @@ class Config: handler = FastAPI() +# Set up the templates directory and static folder for the frontend with the build folder for production +if os.path.exists("../frontend/build"): + # Sets the templates directory to the React build folder + templates = Jinja2Templates(directory="../frontend/build") + # Mounts the static folder within the build forlder to the /static route. + handler.mount( + "/static", StaticFiles(directory="../frontend/build/static"), "static" + ) +else: + # Sets the templates directory to the React public folder for local dev + templates = Jinja2Templates(directory="../frontend/public") + handler.mount("/static", StaticFiles(directory="../frontend/public"), "static") + @handler.get("/geolocate/{ip}") def geolocate(ip): @@ -215,3 +230,10 @@ def append_incident_buttons(payload, webhook_id): } ] return payload + + +# Defines a route handler for `/*` essentially. +# NOTE: this needs to be the last route defined b/c it's a catch all route +@handler.get("/{rest_of_path:path}") +async def react_app(req: Request, rest_of_path: str): + return templates.TemplateResponse("index.html", {"request": req}) diff --git a/app/tests/server/test_server.py b/app/tests/server/test_server.py index 18c419d4..0278a668 100644 --- a/app/tests/server/test_server.py +++ b/app/tests/server/test_server.py @@ -290,3 +290,11 @@ def test_append_incident_buttons(): ] ), ] + + +# Unit test the react app +def test_react_app(): + # test the react app + response = client.get("/some/path") + assert response.status_code == 200 + assert "text/html" in response.headers["content-type"] diff --git a/frontend/src/App.js b/frontend/src/App.js index 49ca5e8a..73f92c9b 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,10 +1,11 @@ +import sre_bot_logo from './sre_bot_logo.png'; import './App.css'; function App() { return (