diff --git a/.env.example b/.env.example index a0ab8b0..ba0c8cf 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,11 @@ # copy these lines into `.env.development` or `.env` (see Vite docs at https://vite.dev/guide/env-and-mode) and adjust as needed for local development VITE_BACKEND_URL=http://localhost:8080 # backend API URL used by the frontend (dev server proxy target) VITE_SERVER_PORT=5173 # dev server port +VITE_ENABLE_HTTPS=false # set true to serve the GUI over HTTPS +VITE_HTTPS_CERT_FILE= # required when VITE_ENABLE_HTTPS=true +VITE_HTTPS_KEY_FILE= # required when VITE_ENABLE_HTTPS=true +VITE_VERIFY_SSL=false # set true to verify the backend TLS certificate +VITE_USE_POLLING=false # set true when the filesystem does not emit file events reliably ########## Docker Compose (dev) ########## # Values used by `docker-compose.dev.yml`. Compose will substitute ${VAR} from the @@ -18,10 +23,19 @@ VITE_SERVER_PORT=5173 # dev server port VITE_APP_VERSION=development VITE_BACKEND_URL=http://host.docker.internal:8080 # backend API URL used by the frontend inside the container (resolved via Compose host mapping) VITE_SERVER_PORT=5173 # dev server port inside the container +VITE_ENABLE_HTTPS=false # set true to serve the GUI over HTTPS +VITE_HTTPS_CERT_FILE=/app/certs/server.crt # certificate mounted from ./certs +VITE_HTTPS_KEY_FILE=/app/certs/server.key # certificate key mounted from ./certs +VITE_VERIFY_SSL=false # set true to verify the backend TLS certificate DOCKER_HOST_PORT=5173 # port on the host machine mapped to the container's VITE_SERVER_PORT (for browser access) ########## Production (nginx) ########## -# Values used at container runtime by nginx to proxy /api/ -NGINX_BACKEND_URL=http://host.docker.internal:8080/ # backend API URL used by nginx inside the container (resolved via Compose host mapping) +# Values used at container runtime by nginx to serve HTTPS and proxy /api/. +SOARCA_GUI_VERSION=latest # Docker Hub image tag +SOARCA_CERTS_VOLUME=soarca_certs_data_container # external volume created by SOARCA +NGINX_BACKEND_URL=https://host.docker.internal:8080/ # backend API URL used by nginx inside the container NGINX_SERVER_PORT=8081 # port nginx listens on inside the container +NGINX_CERT_FILE=/app/certs/server.crt # TLS certificate mounted from SOARCA_CERTS_VOLUME +NGINX_CERT_KEY_FILE=/app/certs/server.key # TLS certificate key mounted from SOARCA_CERTS_VOLUME +NGINX_PROXY_SSL_VERIFY=off # set to on when the backend certificate is trusted DOCKER_HOST_PORT=8081 # port on the host machine mapped to the container's NGINX_SERVER_PORT (for browser access) \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d94a1d1..bf380b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,5 +92,5 @@ jobs: target: production tags: cossas/soarca-gui:pr-${{ env.describe_version }} build-args: | - VERSION=${{ env.describe_version }} + VITE_APP_VERSION=${{ env.describe_version }} builder: default diff --git a/.github/workflows/development-release.yml b/.github/workflows/development-release.yml index 32b2fe2..c63ace4 100644 --- a/.github/workflows/development-release.yml +++ b/.github/workflows/development-release.yml @@ -62,7 +62,6 @@ jobs: - name: Get version run: | - export VERSION=$(git describe --tags --dirty) echo "describe_version=$(git describe --tags --dirty)" >> "$GITHUB_ENV" - name: Build and push @@ -70,7 +69,7 @@ jobs: with: context: . build-args: | - VERSION=${{ env.describe_version }} + VITE_APP_VERSION=${{ env.describe_version }} push: true target: production tags: cossas/soarca-gui:${{ env.describe_version }},cossas/soarca-gui:development diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e51142f..15385c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,9 @@ on: tags: - '[0-9]+.[0-9]+.[0-9]+\-?*' +permissions: + contents: read + jobs: build: name: Build with npm @@ -14,8 +17,7 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 - - name: Make repo safe - run: git config --global --add safe.directory /__w/SOARCA/SOARCA-GUI + - name: Use Node.js 24 uses: actions/setup-node@v6 with: @@ -37,41 +39,35 @@ jobs: - name: Build with npm run: npm run build - - name: "Upload Artifact" + - name: Prepare release SBOM + run: | + cp dist/cyclonedx/bom.json "soarca-gui-${GITHUB_REF_NAME}.cdx.json" + zip sbom.zip "soarca-gui-${GITHUB_REF_NAME}.cdx.json" + + - name: Upload SBOM artifact uses: actions/upload-artifact@v6 with: - name: ${{ github.sha }} - path: dist/* + name: release-sbom + path: sbom.zip + if-no-files-found: error retention-days: 1 docker-build: needs: build - name: Build docker image and push it to docker hub + name: Build Docker image and push it to Docker Hub runs-on: ubuntu-latest steps: - - name: Checkout Code + - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - - name: Make repo safe - run: git config --global --add safe.directory /__w/SOARCA-GUI/SOARCA-GUI - name: Set up QEMU uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Download dist - uses: actions/download-artifact@v7 - with: - pattern: ${{ github.sha }} - - - name: Move files to bin folder and make executable - run: | - mkdir -p bin - mv ${{ github.sha }}/* ./bin/ - chmod +x bin/soarca-gui-* - - name: Login to Docker Hub uses: docker/login-action@v3 with: @@ -80,7 +76,6 @@ jobs: - name: Get version run: | - export VERSION=$(git describe --tags --dirty) echo "describe_version=$(git describe --tags --dirty)" >> "$GITHUB_ENV" - name: Build and push @@ -88,72 +83,44 @@ jobs: with: context: . build-args: | - VERSION=${{ env.describe_version }} - push: false - tags: cossas/soarca-gui:${{ env.describe_version }},cossas/soarca-gui:latest - - release-artifacts: + VITE_APP_VERSION=${{ env.describe_version }} + push: true + target: production + tags: | + cossas/soarca-gui:${{ env.describe_version }} + cossas/soarca-gui:latest + + publish-sbom: needs: build - name: Create release artifacts + name: Publish SBOM to the GitHub release runs-on: ubuntu-latest + permissions: + contents: write steps: - - name: Setup Go - uses: actions/setup-go@v4 - with: - go-version: "1.23.x" - - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v6 - with: - gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.GPG_PASSPHRASE }} - - name: Checkout Code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - name: Make repo safe - run: git config --global --add safe.directory /__w/SOARCA-GUI/SOARCA-GUI - - - name: Setup node - uses: actions/setup-node@v6 + - name: Download SBOM artifact + uses: actions/download-artifact@v7 with: - node-version: 24 - - run: npm ci + name: release-sbom - - name: Build and sbom - run: | - go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest - go install github.com/a-h/templ/cmd/templ@v0.3.819 - make sbom - zip -r bin/sbom.zip bin - - - name: Release soarca gui binary - uses: goreleaser/goreleaser-action@v5 - with: - distribution: goreleaser - version: latest - args: release --clean - workdir: server + - name: Create GitHub release and publish SBOM env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} - - - name: Upload release sbom - uses: actions/github-script@v4 - with: - script: | - const fs = require('fs'); - const tag = context.ref.replace("refs/tags/", ""); - // Get release for this tag - const release = await github.repos.getReleaseByTag({ - owner: context.repo.owner, - repo: context.repo.repo, - tag - }); - // Upload the release asset - await github.repos.uploadReleaseAsset({ - owner: context.repo.owner, - repo: context.repo.repo, - release_id: release.data.id, - name: "sbom.zip", - data: await fs.readFileSync("bin/sbom.zip") - }); + GH_TOKEN: ${{ github.token }} + run: | + if ! gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then + if [[ "$GITHUB_REF_NAME" == *-* ]]; then + gh release create "$GITHUB_REF_NAME" \ + --verify-tag \ + --generate-notes \ + --prerelease \ + --title "$GITHUB_REF_NAME" + else + gh release create "$GITHUB_REF_NAME" \ + --verify-tag \ + --generate-notes \ + --title "$GITHUB_REF_NAME" + fi + fi + + gh release upload "$GITHUB_REF_NAME" \ + sbom.zip \ + --clobber diff --git a/.gitignore b/.gitignore index b58fd44..6c53f85 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ lerna-debug.log* node_modules dist +certs dist-ssr *.local *.tsbuildinfo diff --git a/Dockerfile b/Dockerfile index 6afb121..050a7c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,12 @@ FROM node:24-alpine AS builder RUN apk add --no-cache git WORKDIR /app +ARG VITE_APP_VERSION=development +ENV VITE_APP_VERSION=${VITE_APP_VERSION} COPY package*.json ./ -RUN npm install +RUN npm ci COPY . . -RUN npm run build +RUN npm run build && rm -rf dist/cyclonedx # Serve with nginx FROM nginx:alpine AS production diff --git a/README.md b/README.md index 3229e2d..f43e637 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,10 @@ flowchart TB - `VITE_BACKEND_URL` - Backend API URL (proxied by Vite dev server) - `VITE_SERVER_PORT` - Port where Vite dev server listens +- `VITE_ENABLE_HTTPS` - Set to `true` to serve the development or preview build over HTTPS +- `VITE_HTTPS_CERT_FILE` - Path to the TLS certificate used by Vite +- `VITE_HTTPS_KEY_FILE` - Path to the TLS certificate key used by Vite +- `VITE_VERIFY_SSL` - Set to `true` to verify the backend certificate when Vite proxies API requests - `DOCKER_HOST_PORT` - The port of the host machine that is mapped to the `VITE_SERVER_PORT` of the Vite dev server running in the container. This is only relevant when running the Development Container. - `HOST_PORT` - is the same of the `VITE_SERVER_PORT` if run locally or `DOCKER_HOST_PORT` if running the Development Container. @@ -125,7 +129,12 @@ flowchart TB If no enviroment variables are provided, the defaults are: - `VITE_BACKEND_URL`: `http://localhost:8080` - (SOARCA default) - - `VITE_SERVER_PORT`: `5713` - (Vite default) + - `VITE_SERVER_PORT`: `5173` - (Vite default) + - `VITE_ENABLE_HTTPS`: `false` + + To enable HTTPS, set `VITE_ENABLE_HTTPS=true` and provide + `VITE_HTTPS_CERT_FILE` and `VITE_HTTPS_KEY_FILE`. Set + `VITE_BACKEND_URL=https://localhost:8080` when SOARCA also uses HTTPS. 3. **Start the dev server:** @@ -145,6 +154,10 @@ flowchart TB - `VITE_SERVER_PORT`: `5173` - `DOCKER_HOST_PORT` — port on the host mapped to the `VITE_SERVER_PORT` (default: `5173`) + For HTTPS, place `server.crt` and `server.key` in `./certs`, then set + `VITE_ENABLE_HTTPS=true`. The development Compose file mounts that directory + read-only at `/app/certs`. + 2. **Start the container:** ```bash docker compose -f docker-compose.dev.yml up @@ -257,10 +270,14 @@ flowchart TB **Environment variables involved:** -- `VITE_BACKEND_URL` - Backend API URL embedded into the client bundle at build time (must be set before running `npm run build`) - `VITE_APP_VERSION` - Application version embedded into the client bundle at build time (optional, falls back to git describe or "development") - `NGINX_BACKEND_URL` - Backend API URL used by Nginx at runtime to proxy `/api/*` requests. This is only relevant when running in Docker Container Mode. - `NGINX_SERVER_PORT` - Port where Nginx listens inside the container. This is only relevant when running in Docker Container Mode. +- `NGINX_CERT_FILE` - TLS certificate path inside the container +- `NGINX_CERT_KEY_FILE` - TLS certificate key path inside the container +- `NGINX_PROXY_SSL_VERIFY` - Controls verification of the backend TLS certificate +- `SOARCA_CERTS_VOLUME` - External Docker volume containing the SOARCA TLS certificate and key +- `SOARCA_GUI_VERSION` - Docker Hub image tag to run, defaulting to `latest` - `DOCKER_HOST_PORT` - The port of the host machine that is mapped to the `NGINX_SERVER_PORT` of the Nginx server running in the container. This is only relevant when running in Docker Container Mode. - `HOST_PORT` - Port 4173 (Vite default) for local preview mode (`npm run preview`) or `DOCKER_HOST_PORT` when running in Docker Container Mode. @@ -276,7 +293,7 @@ flowchart TB Optionally create a `.env` file (explanation and defaults can be found in `.env.example`). Note that `VITE_` variables must be set at _build time_ to be embedded into the static bundle. If no environment variables are provided, the defaults are: - - `VITE_BACKEND_URL`: `http://localhost:8080` - (SOARCA default) + - `VITE_ENABLE_HTTPS`: `false` 3. **Build and preview:** @@ -293,21 +310,24 @@ flowchart TB #### Running in Docker Container (recommended for production) 1. **Configure environment variables:** - Optionally provide values via the shell, a project `.env`, or `--env-file` when starting Compose (explanation and defaults can be found in `.env.example`). Note that `VITE_` variables are build-time only (embedded at image build), while `NGINX_` variables are runtime (used by the running container). + Optionally provide values via the shell, a project `.env`, or `--env-file` when starting Compose (explanation and defaults can be found in `.env.example`). The production container serves HTTPS and expects the certificate volume created by the SOARCA deployment. If no environment variables are provided, the defaults are: - - `VITE_BACKEND_URL`: `http://localhost:8080` - embedded at build time - - `NGINX_BACKEND_URL`: `http://host.docker.internal:8080/` - used at runtime + - `SOARCA_GUI_VERSION`: `latest` + - `SOARCA_CERTS_VOLUME`: `soarca_certs_data_container` + - `NGINX_BACKEND_URL`: `https://host.docker.internal:8080/` - `NGINX_SERVER_PORT`: `8081` - - `DOCKER_HOST_PORT`: `8081` — port on the host mapped to the `NGINX_SERVER_PORT` + - `NGINX_PROXY_SSL_VERIFY`: `off`, to support SOARCA's generated self-signed certificate + - `DOCKER_HOST_PORT`: `8081` - port on the host mapped to the `NGINX_SERVER_PORT` -2. **Build and start the container:** +2. **Pull and start the container:** ```bash - docker compose up --build + docker compose pull + docker compose up -d ``` - The app will be available at `http://localhost:DOCKER_HOST_PORT`. + The app will be available at `https://localhost:DOCKER_HOST_PORT`. ### NPM Scripts for local run diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6254a2c..a4ad789 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -16,8 +16,14 @@ services: - NODE_ENV=development - VITE_BACKEND_URL=${VITE_BACKEND_URL:-http://host.docker.internal:8080} - VITE_SERVER_PORT=${VITE_SERVER_PORT:-5173} + - VITE_ENABLE_HTTPS=${VITE_ENABLE_HTTPS:-false} + - VITE_HTTPS_CERT_FILE=${VITE_HTTPS_CERT_FILE:-/app/certs/server.crt} + - VITE_HTTPS_KEY_FILE=${VITE_HTTPS_KEY_FILE:-/app/certs/server.key} + - VITE_VERIFY_SSL=${VITE_VERIFY_SSL:-false} + - VITE_IS_DOCKER=true volumes: - .:/app + - ./certs:/app/certs:ro - node_modules_volume:/app/node_modules command: sh -c "npm install && npm run dev" diff --git a/docker-compose.yml b/docker-compose.yml index ee604c9..5352e6f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: soarca-gui: container_name: soarca-gui - image: soarca-gui:latest + image: cossas/soarca-gui:${SOARCA_GUI_VERSION:-latest} extra_hosts: - "host.docker.internal:host-gateway" build: @@ -9,12 +9,20 @@ services: dockerfile: Dockerfile args: VITE_APP_VERSION: ${VITE_APP_VERSION:-development} - NGINX_BACKEND_URL: ${NGINX_BACKEND_URL:-http://host.docker.internal:8080/} - NGINX_SERVER_PORT: ${NGINX_SERVER_PORT:-8081} ports: - "${DOCKER_HOST_PORT:-8081}:${NGINX_SERVER_PORT:-8081}" + volumes: + - soarca-certs:/app/certs:ro environment: - NODE_ENV=production - - NGINX_BACKEND_URL=${NGINX_BACKEND_URL:-http://host.docker.internal:8080/} + - NGINX_BACKEND_URL=${NGINX_BACKEND_URL:-https://host.docker.internal:8080/} - NGINX_SERVER_PORT=${NGINX_SERVER_PORT:-8081} + - NGINX_CERT_FILE=${NGINX_CERT_FILE:-/app/certs/server.crt} + - NGINX_CERT_KEY_FILE=${NGINX_CERT_KEY_FILE:-/app/certs/server.key} + - NGINX_PROXY_SSL_VERIFY=${NGINX_PROXY_SSL_VERIFY:-off} restart: unless-stopped + +volumes: + soarca-certs: + external: true + name: ${SOARCA_CERTS_VOLUME:-soarca_certs_data_container} diff --git a/nginx.conf b/nginx.conf index 372c1f2..e6dbeda 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,11 +1,20 @@ # Nginx template: variables are substituted at container start (envsubst) # Variables expected: # - ${NGINX_SERVER_PORT} (default: 8081): port nginx will listen on inside the container -# - ${NGINX_BACKEND_URL} (default: http://host.docker.internal:8080/): backend to proxy /api/ to (include trailing slash) +# - ${NGINX_BACKEND_URL} (default: https://host.docker.internal:8080/): backend to proxy /api/ to (include trailing slash) +# - ${NGINX_CERT_FILE} (default: /app/certs/server.crt): TLS certificate file +# - ${NGINX_CERT_KEY_FILE} (default: /app/certs/server.key): TLS certificate key file +# - ${NGINX_PROXY_SSL_VERIFY} (default: off): verify the backend TLS certificate server { - listen ${NGINX_SERVER_PORT}; + listen ${NGINX_SERVER_PORT} ssl; server_name localhost; + ssl_certificate ${NGINX_CERT_FILE}; + ssl_certificate_key ${NGINX_CERT_KEY_FILE}; + ssl_protocols TLSv1.3; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + location / { root /usr/share/nginx/html; index index.html; @@ -14,6 +23,8 @@ server { location /api/ { proxy_pass ${NGINX_BACKEND_URL}; + proxy_ssl_verify ${NGINX_PROXY_SSL_VERIFY}; + proxy_ssl_server_name on; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx index bec1a15..806c878 100644 --- a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx +++ b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx @@ -9,7 +9,7 @@ import toast from "react-hot-toast"; import { getStepManualData, postStepActionResult } from "@/api/manual"; import { formatErrorForToast } from "@/api/utils"; -import { Button, Modal, RadioGroup, Spinner, ThemeVariant } from "@/components"; +import { Button, Modal, Spinner, ThemeVariant } from "@/components"; import { Execution, ManualOutArgsUpdatePayload, @@ -69,9 +69,6 @@ export const ManualActionModal: React.FC = ({ const [variableValues, setVariableValues] = useState>( {}, ); - const [variableChoices, setVariableChoices] = useState< - Record - >({}); const { data: interactionData, isLoading: isLoadingOutArgs } = useQuery({ queryKey: ["manual", executionId, activeStep?.step_id], @@ -96,10 +93,6 @@ export const ManualActionModal: React.FC = ({ }, }); - const handleChoiceChange = (key: string, value: ActionType) => { - setVariableChoices((prev) => ({ ...prev, [key]: value })); - }; - const handleValueChange = (key: string, value: string) => { setVariableValues((prev) => ({ ...prev, [key]: value })); }; @@ -111,23 +104,12 @@ export const ManualActionModal: React.FC = ({ const outArgs = interactionData?.out_args ?? {}; Object.entries(outArgs).forEach(([key, variable]) => { - const hasExistingValue = variable?.value && variable.value.trim() !== ""; - - if (hasExistingValue) { - response_out_args[key] = { - ...variable, - name: variable?.name || key, - type: variable?.type || "string", - value: variable.value, - }; - } else { - response_out_args[key] = { - ...variable, - name: variable?.name || key, - type: variable?.type || "string", - value: variableValues[key] || "", - }; - } + response_out_args[key] = { + ...variable, + name: variable?.name || key, + type: variable?.type || "string", + value: variableValues[key] ?? variable?.value ?? "", + }; }); const responseStatus: ManualResponseStatus = @@ -161,8 +143,6 @@ export const ManualActionModal: React.FC = ({ isLoadingOutArgs={isLoadingOutArgs} mutation={mutation} variableValues={variableValues} - variableChoices={variableChoices} - onChoiceChange={handleChoiceChange} onValueChange={handleValueChange} /> @@ -208,8 +188,6 @@ interface ModalContentProps { unknown >; variableValues: Record; - variableChoices: Record; - onChoiceChange: (key: string, value: ActionType) => void; onValueChange: (key: string, value: string) => void; } @@ -219,8 +197,6 @@ const ModalContent: React.FC = ({ isLoadingOutArgs, mutation, variableValues, - variableChoices, - onChoiceChange, onValueChange, }) => { const outArgEntries = Object.entries(outArgs); @@ -254,8 +230,6 @@ const ModalContent: React.FC = ({ Variables {outArgEntries.map(([key, variable]) => { - const hasExistingValue = - variable?.value && variable.value.trim() !== ""; const varType = variable?.type || "string"; return ( @@ -269,30 +243,15 @@ const ModalContent: React.FC = ({ )} - {hasExistingValue ? ( - - onChoiceChange(key, value as ActionType) - } - $disabled={mutation.isPending} + + - ) : ( - - - - )} + ); })} diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx index 52f441c..3e28e36 100644 --- a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx +++ b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx @@ -102,12 +102,12 @@ export const TimelineTabView: React.FC = ({ const isStepTerminated = status !== StepExecutionStatus.Running; const isStepManualAndOngoing = - !isStepTerminated && !automated_execution; + !isStepTerminated && automated_execution === false; // Determine if we should display the user action cards showing user responses. // We only display it if the step is not automated, it has been completed successfully. const shouldDisplayUserActionResponseCard = - isStepTerminated && !automated_execution; + isStepTerminated && automated_execution === false; const userResponses = shouldDisplayUserActionResponseCard ? getUserResponses(step) diff --git a/vite.config.ts b/vite.config.ts index 73a10ee..7397178 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,10 @@ import react from "@vitejs/plugin-react-swc"; import { execSync } from "child_process"; +import fs from "node:fs"; +import type { ServerOptions as HttpsServerOptions } from "node:https"; +import path from "node:path"; import sbom from "rollup-plugin-sbom"; -import { defineConfig } from "vite"; +import { defineConfig, loadEnv } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; /** @@ -27,27 +30,61 @@ const getAppVersion = (): string => { return "development"; }; -export default defineConfig({ - plugins: [react(), tsconfigPaths(), sbom()], - define: { - __APP_VERSION__: JSON.stringify(getAppVersion()), - }, - server: { - proxy: { - "/api": { - target: process.env.VITE_BACKEND_URL || "http://localhost:8080", - rewrite: (path) => path.replace(/^\/api/, ""), +const getHttpsOptions = ( + enabled: boolean, + certFile?: string, + keyFile?: string, +): HttpsServerOptions | undefined => { + if (!enabled) { + return undefined; + } + + if (!certFile || !keyFile) { + throw new Error( + "VITE_HTTPS_CERT_FILE and VITE_HTTPS_KEY_FILE are required when VITE_ENABLE_HTTPS=true.", + ); + } + + return { + cert: fs.readFileSync(path.resolve(certFile)), + key: fs.readFileSync(path.resolve(keyFile)), + }; +}; + +export default defineConfig(({ mode }) => { + const env = { ...loadEnv(mode, process.cwd(), ""), ...process.env }; + const https = getHttpsOptions( + env.VITE_ENABLE_HTTPS === "true", + env.VITE_HTTPS_CERT_FILE, + env.VITE_HTTPS_KEY_FILE, + ); + + return { + plugins: [react(), tsconfigPaths(), sbom()], + define: { + __APP_VERSION__: JSON.stringify(getAppVersion()), + }, + server: { + proxy: { + "/api": { + target: env.VITE_BACKEND_URL || "http://localhost:8080", + secure: env.VITE_VERIFY_SSL === "true", + rewrite: (requestPath) => requestPath.replace(/^\/api/, ""), + }, + }, + host: true, + port: Number(env.VITE_SERVER_PORT) || 5173, + strictPort: true, + https, + watch: { + usePolling: + env.VITE_USE_POLLING === "true" || env.VITE_IS_DOCKER === "true", }, }, - host: true, - port: Number(process.env.VITE_SERVER_PORT) || 5173, - strictPort: true, // Fail if port is already in use, as the docker container won't be able to use a different one - watch: { - usePolling: true, + preview: { + host: true, + port: 4173, + https, }, - }, - preview: { - host: true, - port: 4173, - }, + }; });