diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..f67f79287 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +**/node_modules +**/dist +**/.env +**/*.env +.git +docker/dev/mongo/data diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml new file mode 100644 index 000000000..f7e204159 --- /dev/null +++ b/.github/workflows/build-image.yml @@ -0,0 +1,103 @@ +name: Build image + +on: + push: + branches: ["develop", "master"] + tags: ["v*"] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # checkmate-backend-mono is a transitional alias so existing deployments + # keep receiving updates; drop it after one release cycle. + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/${{ github.repository_owner }}/checkmate + ghcr.io/${{ github.repository_owner }}/checkmate-backend-mono + flavor: | + latest=false + tags: | + type=ref,event=branch + type=ref,event=tag + type=semver,pattern={{version}} + type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} + + - name: Build and push multi-arch image + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # The hosted staging/prod instances share one host and one compose file; each + # deploy pulls and recreates only its own service. + deploy-staging: + needs: build-and-push + if: github.ref == 'refs/heads/develop' + runs-on: ubuntu-latest + steps: + - name: Deploy staging.checkmate.so + uses: appleboy/ssh-action@v1.2.2 + with: + host: ${{ secrets.DEMO_SERVER_HOST }} + username: ${{ secrets.DEMO_SERVER_USER }} + key: ${{ secrets.DEMO_SERVER_SSH_KEY }} + script: | + cd checkmate + git pull + cd docs/hosting/checkmate-so + docker compose pull checkmate-staging + docker compose up -d checkmate-staging + docker system prune -af + + deploy-prod: + needs: build-and-push + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - name: Deploy demo.checkmate.so + uses: appleboy/ssh-action@v1.2.2 + with: + host: ${{ secrets.DEMO_SERVER_HOST }} + username: ${{ secrets.DEMO_SERVER_USER }} + key: ${{ secrets.DEMO_SERVER_SSH_KEY }} + script: | + cd checkmate + git pull + cd docs/hosting/checkmate-so + docker compose pull checkmate-demo + docker compose up -d checkmate-demo + docker system prune -af diff --git a/.github/workflows/deploy-images-on-release.yml b/.github/workflows/deploy-images-on-release.yml deleted file mode 100644 index bf8f5069f..000000000 --- a/.github/workflows/deploy-images-on-release.yml +++ /dev/null @@ -1,148 +0,0 @@ -name: Deploy images on release - -on: - push: - tags: - - "v*" - workflow_dispatch: -jobs: - docker-build-and-push-client: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Extract version from tag - id: extract_tag - run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Client Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-client:${{ steps.extract_tag.outputs.version }} \ - -f ./docker/dist/client.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Client Docker image - run: | - docker push ghcr.io/bluewave-labs/checkmate-client:${{ steps.extract_tag.outputs.version }} - - docker-build-and-push-server: - needs: docker-build-and-push-client - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Extract version - id: extract_tag - run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-backend:${{ steps.extract_tag.outputs.version }} \ - -f ./docker/dist/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Server Docker image - run: | - docker push ghcr.io/bluewave-labs/checkmate-backend:${{ steps.extract_tag.outputs.version }} - - - name: Build Mongo Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-mongo:${{ steps.extract_tag.outputs.version }} \ - -f ./docker/dist/mongoDB.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push MongoDB Docker image - run: | - docker push ghcr.io/bluewave-labs/checkmate-mongo:${{ steps.extract_tag.outputs.version }} - - docker-build-and-push-server-mono-multiarch: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Extract version - id: extract_tag - run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push multi-arch Docker image - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/dist-arm/server.Dockerfile - push: true - no-cache: true - tags: | - ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:${{ steps.extract_tag.outputs.version }} - platforms: linux/amd64,linux/arm64 - labels: | - org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate - build-args: | - VITE_APP_VERSION=${{ steps.extract_tag.outputs.version }} - - docker-build-and-push-server-mono: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Extract version - id: extract_tag - run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-backend-mono:${{ steps.extract_tag.outputs.version }} \ - -f ./docker/dist-mono/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - --build-arg VITE_APP_VERSION=${{ steps.extract_tag.outputs.version }} \ - . - - - name: Push Server Docker image - run: docker push ghcr.io/bluewave-labs/checkmate-backend-mono:${{ steps.extract_tag.outputs.version }} diff --git a/.github/workflows/deploy-images.yml b/.github/workflows/deploy-images.yml deleted file mode 100644 index 5bd4ba1d5..000000000 --- a/.github/workflows/deploy-images.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Deploy images - -on: - push: - branches: ["master"] - workflow_dispatch: -jobs: - docker-build-and-push-client: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get version - id: vars - run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - - name: Build Client Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-client:latest \ - -f ./docker/dist/client.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ - . - - - name: Push Client Docker image - run: | - docker push ghcr.io/bluewave-labs/checkmate-client:latest - - docker-build-and-push-server: - needs: docker-build-and-push-client - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-backend:latest \ - -f ./docker/dist/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Server Docker image - run: | - docker push ghcr.io/bluewave-labs/checkmate-backend:latest - - - name: Build Mongo Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-mongo:latest \ - -f ./docker/dist/mongoDB.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push MongoDB Docker image - run: | - docker push ghcr.io/bluewave-labs/checkmate-mongo:latest - - docker-build-and-push-server-mono-multiarch: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get version - id: vars - run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - - - name: Build and push multi-arch Docker image - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/dist-arm/server.Dockerfile - push: true - no-cache: true - tags: | - ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:latest - platforms: linux/amd64,linux/arm64 - labels: | - org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate - build-args: | - VITE_APP_VERSION=${{ env.VERSION }} - - docker-build-and-push-server-mono: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get version - id: vars - run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-backend-mono:latest \ - -f ./docker/dist-mono/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ - . - - - name: Push Server Docker image - run: docker push ghcr.io/bluewave-labs/checkmate-backend-mono:latest diff --git a/.github/workflows/production-deploy.yml b/.github/workflows/production-deploy.yml deleted file mode 100644 index a42efc4a4..000000000 --- a/.github/workflows/production-deploy.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Demo deploy - -on: - push: - branches: [ "demo" ] - workflow_dispatch: -jobs: - docker-build-and-push-client: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get version - id: vars - run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - - name: Build Client Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:frontend-demo \ - -f ./docker/prod/client.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ - . - - - name: Push Client Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:frontend-demo - - docker-build-and-push-server: - needs: docker-build-and-push-client - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:backend-demo \ - -f ./docker/prod/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Server Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:backend-demo - - - name: Build Mongo Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:mongo-demo \ - -f ./docker/prod/mongoDB.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push MongoDB Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:mongo-demo - - deploy-to-demo: - needs: docker-build-and-push-server - runs-on: ubuntu-latest - steps: - - name: SSH into server and restart container using Docker Compose - uses: appleboy/ssh-action@v1.2.2 - with: - host: ${{ secrets.DEMO_SERVER_HOST }} - username: ${{ secrets.DEMO_SERVER_USER }} - key: ${{ secrets.DEMO_SERVER_SSH_KEY }} - script: | - cd checkmate - git pull - cd docker/unified - docker compose down - docker compose pull - docker compose up -d - docker system prune -af diff --git a/.github/workflows/staging-deploy.yml b/.github/workflows/staging-deploy.yml deleted file mode 100644 index e7983f249..000000000 --- a/.github/workflows/staging-deploy.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Staging deploy - -on: - push: - branches: [ "develop" ] - -jobs: - docker-build-and-push-client: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get version - id: vars - run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - - name: Build Client Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:frontend-staging \ - -f ./docker/staging/client.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ - . - - - name: Push Client Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:frontend-staging - - docker-build-and-push-server: - needs: docker-build-and-push-client - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:backend-staging \ - -f ./docker/staging/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Server Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:backend-staging - - - name: Build Mongo Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:mongo-staging \ - -f ./docker/staging/mongoDB.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push MongoDB Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:mongo-staging - - deploy-to-staging: - needs: docker-build-and-push-server - runs-on: ubuntu-latest - steps: - - name: SSH into server and restart container using Docker Compose - uses: appleboy/ssh-action@v1.2.2 - with: - host: ${{ secrets.DEMO_SERVER_HOST }} - username: ${{ secrets.DEMO_SERVER_USER }} - key: ${{ secrets.DEMO_SERVER_SSH_KEY }} - script: | - cd checkmate - git pull - cd docker/unified - docker compose down - docker compose pull - docker compose up -d - docker system prune -af diff --git a/CLAUDE.md b/CLAUDE.md index 1adfd703f..3e601b453 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,9 +35,9 @@ npm run format # Prettier formatting ### Docker Development ```bash -cd docker/dev -./build_images.sh -docker run -d -p 27017:27017 -v uptime_mongo_data:/data/db --name uptime_database_mongo mongo:6.0 +docker build -f docker/Dockerfile -t checkmate . # build the mono image (server + built client) +docker compose -f docker/dev/docker-compose.yaml up # local full stack (builds image, runs mongo + worker) +docker run -d -p 27017:27017 -v uptime_mongo_data:/data/db --name uptime_database_mongo mongo:8.0 # just a dev database ``` ## Environment Setup diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d19bd83be..ea93f8100 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,34 +88,10 @@ cd Checkmate #### Step 2: Set Up Docker Containers (MongoDB) -Navigate to the Docker dev directory: - -```bash -cd docker/dev -``` - -Build the Docker images: - -Linux: -```bash -./build_images.sh -``` - -Windows: -```bash -./build_images.bat -``` - Run MongoDB container: ```bash -docker run -d -p 27017:27017 -v uptime_mongo_data:/data/db --name uptime_database_mongo mongo:6.0 -``` - -Navigate back to the root directory: - -```bash -cd ../.. +docker run -d -p 27017:27017 -v uptime_mongo_data:/data/db --name uptime_database_mongo mongo:8.0 ``` #### Step 3: Set Up the Backend (Server) diff --git a/README.md b/README.md index 1e6e543e8..bcbd557d4 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,37 @@ Usage instructions can be found [here](https://checkmate.so/docs). ## Installation -See installation instructions in [Checkmate documentation portal](https://checkmate.so/docs). +The quickest way to run Checkmate is the reference Docker Compose file, which runs the all-in-one image (`ghcr.io/bluewave-labs/checkmate`) plus MongoDB: + +```bash +curl -O https://raw.githubusercontent.com/bluewave-labs/checkmate/master/docker/docker-compose.yaml +JWT_SECRET="$(openssl rand -hex 32)" docker compose up -d +``` + +Then open http://localhost:52345. If the app is reached at another origin (domain or LAN IP), set `CLIENT_HOST` accordingly. To build the image yourself, run `docker build -f docker/Dockerfile -t checkmate .` from a checkout. For TLS, put any reverse proxy (Caddy, Traefik, nginx) in front of port 52345. + +### Configuration + +The image is configured entirely through environment variables on the server container: + +| Variable | Required | Description | +|---|---|---| +| `DB_CONNECTION_STRING` | Yes | MongoDB connection string, e.g. `mongodb://mongodb:27017/uptime_db` | +| `JWT_SECRET` | Yes | Secret used to sign auth tokens; generate one with `openssl rand -hex 32` | +| `CLIENT_HOST` | Yes | The URL users reach the app at, e.g. `https://checkmate.example.com`; used for CORS and for links in notifications and emails | +| `LOG_LEVEL` | No | Server log level: `error`, `warn`, `info`, or `debug` (default `debug`) | + +The web client needs no configuration by default: it calls the API on the same origin it was served from (`/api/v1`). For setups where the defaults don't apply — for example, the API is reached through a different origin than the page — the server renders overrides into the client at runtime via these optional variables: + +| Variable | Description | +|---|---| +| `CLIENT_CONFIG_API_BASE_URL` | Full base URL the client calls the API at, e.g. `https://api.example.com/api/v1`; defaults to same-origin `/api/v1` | +| `CLIENT_CONFIG_CLIENT_HOST` | Origin used when the client builds absolute links (invites, status pages); defaults to the browser's current origin | +| `CLIENT_CONFIG_LOG_LEVEL` | Browser console log level: `error`, `warn`, `info`, or `debug` (default `error`) | + +> **Upgrading from an older image?** The `UPTIME_APP_*` variables (`UPTIME_APP_API_BASE_URL`, `UPTIME_APP_CLIENT_HOST`, `UPTIME_APP_LOG_LEVEL`) are no longer read. In most setups no replacement is needed — the same-origin defaults cover them; if you pointed the client at a different origin, use the `CLIENT_CONFIG_*` equivalents above. The `checkmate-client`, `checkmate-backend`, `checkmate-mongo`, and `checkmate-backend-mono-multiarch` images are no longer updated — switch to `ghcr.io/bluewave-labs/checkmate`, keeping your existing MongoDB service and data volume. + +See full installation instructions in the [Checkmate documentation portal](https://checkmate.so/docs). Alternatively, you can also use [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (South Africa), [Cloudzy](https://cloudzy.com/marketplace/checkmate) or [Pikapods](https://www.pikapods.com/) to quickly spin off a Checkmate instance. If you would like to monitor your server infrastructure, you'll need [Capture agent](https://github.com/bluewave-labs/capture). Capture repository also contains the installation instructions. diff --git a/charts/helm/checkmate/Chart.yaml b/charts/helm/checkmate/Chart.yaml index c9433f690..286f1c908 100644 --- a/charts/helm/checkmate/Chart.yaml +++ b/charts/helm/checkmate/Chart.yaml @@ -3,4 +3,8 @@ name: checkmate-chart description: A Helm chart for Checkmate App type: application version: 0.2.0 +# DO NOT bump appVersion past v3.8.1 until this chart is migrated to the all-in-one +# image (ghcr.io/bluewave-labs/checkmate). The checkmate-client / checkmate-backend / +# checkmate-mongo images this chart references are frozen at v3.8.1 — later versions +# are never published, so a bumped chart will fail to pull. appVersion: "v3.8.1" # "v" prefix: published image tags are v-prefixed (ghcr.io/.../checkmate-*:v3.8.1) diff --git a/client/.env.production b/client/.env.production index 25849c7b9..d5b77ff28 100644 --- a/client/.env.production +++ b/client/.env.production @@ -1,4 +1,6 @@ -VITE_APP_API_BASE_URL=UPTIME_APP_API_BASE_URL -VITE_APP_API_V2_BASE_URL=UPTIME_APP_API_V2_BASE_URL -VITE_APP_CLIENT_HOST=UPTIME_APP_CLIENT_HOST -VITE_APP_LOG_LEVEL=UPTIME_APP_LOG_LEVEL \ No newline at end of file +# Production builds use same-origin defaults: API at /api/v1, client host from +# window.location.origin. The empty values below override any local .env so a +# production build never bakes in a developer's localhost URLs. +VITE_APP_API_BASE_URL= +VITE_APP_CLIENT_HOST= +VITE_APP_LOG_LEVEL= diff --git a/client/.gitignore b/client/.gitignore index 23b6e49af..8882da3ee 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -28,5 +28,4 @@ dist-ssr # Scripts *.sh -!env.sh .env.development \ No newline at end of file diff --git a/client/env.sh b/client/env.sh deleted file mode 100755 index d95f8ee8f..000000000 --- a/client/env.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -for i in $(env | grep UPTIME_APP_) -do - key=$(echo $i | cut -d '=' -f 1) - value=$(echo $i | cut -d '=' -f 2-) - echo $key=$value - find /usr/share/nginx/html -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|${key}|${value}|g" '{}' + -done \ No newline at end of file diff --git a/client/index.html b/client/index.html index 4f949edbb..5f48a1bd3 100644 --- a/client/index.html +++ b/client/index.html @@ -12,6 +12,7 @@ /> Checkmate + diff --git a/client/package-lock.json b/client/package-lock.json index 8af4fb08a..ba1812e60 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -92,7 +92,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -408,7 +407,6 @@ "node_modules/@emotion/react": { "version": "11.14.0", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -446,7 +444,6 @@ "node_modules/@emotion/styled": { "version": "11.14.1", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1226,7 +1223,6 @@ "node_modules/@mui/material": { "version": "7.3.7", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "@mui/core-downloads-tracker": "^7.3.7", @@ -1331,7 +1327,6 @@ "node_modules/@mui/system": { "version": "7.3.7", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "@mui/private-theming": "^7.3.7", @@ -2376,7 +2371,6 @@ "version": "24.5.2", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.12.0" } @@ -2392,7 +2386,6 @@ "node_modules/@types/react": { "version": "18.3.27", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -2483,7 +2476,6 @@ "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -2764,7 +2756,6 @@ "version": "8.15.0", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3121,7 +3112,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001800", @@ -3565,8 +3555,7 @@ }, "node_modules/dayjs": { "version": "1.11.13", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", @@ -3965,7 +3954,6 @@ "version": "8.57.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4778,7 +4766,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.27.6" }, @@ -5590,7 +5577,6 @@ "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.19.0.tgz", "integrity": "sha512-REhYUN8gNP3HlcIZS6QU2uy8iovl31cXsrNDkCcqWSQbCkcpdYLczqDz5PVIwNH42UQNyvukjes/RoHPDrOUmQ==", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -6372,7 +6358,6 @@ "node_modules/react-dom": { "version": "18.3.1", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -6384,7 +6369,6 @@ "node_modules/react-hook-form": { "version": "7.71.1", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -6588,8 +6572,7 @@ }, "node_modules/redux": { "version": "5.0.1", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-persist": { "version": "6.0.0", @@ -6739,7 +6722,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.9" }, @@ -7581,7 +7563,6 @@ "version": "5.9.3", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7719,7 +7700,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/client/public/config.js b/client/public/config.js new file mode 100644 index 000000000..30686d2e0 --- /dev/null +++ b/client/public/config.js @@ -0,0 +1,4 @@ +// Fallback so /config.js always resolves (e.g. under the Vite dev server). In +// the Docker image the server shadows this path and renders the real config +// from its CLIENT_CONFIG_* environment variables. +window.__CHECKMATE_CONFIG__ = {}; diff --git a/client/src/Pages/Account/components/InviteTeamMemberDialog.tsx b/client/src/Pages/Account/components/InviteTeamMemberDialog.tsx index e23f24e53..d41bf3ca3 100644 --- a/client/src/Pages/Account/components/InviteTeamMemberDialog.tsx +++ b/client/src/Pages/Account/components/InviteTeamMemberDialog.tsx @@ -10,8 +10,9 @@ import { useInviteForm } from "@/Hooks/useInviteForm"; import type { InviteFormData } from "@/Validation/invite"; import { usePost } from "@/Hooks/UseApi"; import { LAYOUT } from "@/Utils/Theme/constants"; +import { runtimeConfig } from "@/Utils/runtimeConfig"; -const CLIENT_HOST = import.meta.env.VITE_APP_CLIENT_HOST; +const CLIENT_HOST = runtimeConfig.clientHost || import.meta.env.VITE_APP_CLIENT_HOST; interface InviteResponse { token: string; @@ -56,7 +57,7 @@ export const InviteTeamMemberDialog = ({ const result = await generateToken("/invite", data); if (result?.data?.token) { const token = result.data.token; - const link = CLIENT_HOST ? `${CLIENT_HOST}/register/${token}` : token; + const link = `${CLIENT_HOST || window.location.origin}/register/${token}`; setInviteLink(link); } }; diff --git a/client/src/Types/env.d.ts b/client/src/Types/env.d.ts index af8f983fb..0e753a580 100644 --- a/client/src/Types/env.d.ts +++ b/client/src/Types/env.d.ts @@ -3,7 +3,8 @@ declare const __APP_VERSION__: string; interface ImportMetaEnv { - readonly VITE_APP_API_V2_BASE_URL?: string; + readonly VITE_APP_API_BASE_URL?: string; + readonly VITE_APP_CLIENT_HOST?: string; readonly VITE_APP_LOG_LEVEL?: "debug" | "info" | "warn" | "error"; } diff --git a/client/src/Utils/ApiClient.ts b/client/src/Utils/ApiClient.ts index b18f2dfbc..c0468ed0e 100644 --- a/client/src/Utils/ApiClient.ts +++ b/client/src/Utils/ApiClient.ts @@ -1,8 +1,10 @@ import axios from "axios"; import type { AxiosError } from "axios"; import type { AxiosRequestConfig, AxiosResponse } from "axios"; +import { runtimeConfig } from "@/Utils/runtimeConfig"; -const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL; +const BASE_URL = + runtimeConfig.apiBaseUrl || import.meta.env.VITE_APP_API_BASE_URL || "/api/v1"; const api = axios.create({ baseURL: BASE_URL, diff --git a/client/src/Utils/logger.ts b/client/src/Utils/logger.ts index 4a97dbecb..74be87918 100644 --- a/client/src/Utils/logger.ts +++ b/client/src/Utils/logger.ts @@ -1,4 +1,5 @@ import type { LogLevel } from "@/Types/Log"; +import { runtimeConfig } from "@/Utils/runtimeConfig"; interface LogContext { [key: string]: any; @@ -25,7 +26,8 @@ interface ErrorLogData { userAgent: string; } -const configuredLevel: LogLevel = import.meta.env.VITE_APP_LOG_LEVEL || "error"; +const configuredLevel: LogLevel = + runtimeConfig.logLevel || import.meta.env.VITE_APP_LOG_LEVEL || "error"; const configuredPriority = LOG_LEVEL_PRIORITY[configuredLevel]; const shouldLog = (level: LogLevel): boolean => { diff --git a/client/src/Utils/runtimeConfig.ts b/client/src/Utils/runtimeConfig.ts new file mode 100644 index 000000000..2d5e4be94 --- /dev/null +++ b/client/src/Utils/runtimeConfig.ts @@ -0,0 +1,18 @@ +import type { LogLevel } from "@/Types/Log"; + +// Served by GET /config.js (rendered from server env) or a static config.js +// next to index.html; loaded before the bundle in index.html. +export interface RuntimeConfig { + apiBaseUrl?: string; + clientHost?: string; + logLevel?: LogLevel; +} + +declare global { + interface Window { + __CHECKMATE_CONFIG__?: RuntimeConfig; + } +} + +export const runtimeConfig: RuntimeConfig = + (typeof window !== "undefined" && window.__CHECKMATE_CONFIG__) || {}; diff --git a/client/src/Utils/statusPageUrl.ts b/client/src/Utils/statusPageUrl.ts index d5628d6bc..65109c198 100644 --- a/client/src/Utils/statusPageUrl.ts +++ b/client/src/Utils/statusPageUrl.ts @@ -1,6 +1,9 @@ import { PUBLIC_STATUS_PAGE_PREFIX, type StatusPage } from "@/Types/StatusPage"; +import { runtimeConfig } from "@/Utils/runtimeConfig"; -const CLIENT_HOST = import.meta.env.VITE_APP_CLIENT_HOST as string | undefined; +const CLIENT_HOST = + runtimeConfig.clientHost || + (import.meta.env.VITE_APP_CLIENT_HOST as string | undefined); const getClientOriginProtocol = (): string => { const source = diff --git a/docker/dist-arm/server.Dockerfile b/docker/Dockerfile similarity index 64% rename from docker/dist-arm/server.Dockerfile rename to docker/Dockerfile index 5bdefd5bf..380210f53 100644 --- a/docker/dist-arm/server.Dockerfile +++ b/docker/Dockerfile @@ -4,7 +4,7 @@ # Build on the host arch (BUILDPLATFORM) rather than under QEMU — the output is # pure JS so it's arch-independent, and this avoids the esbuild host/native # binary version mismatch under emulation. -FROM --platform=$BUILDPLATFORM node:20-slim AS frontend-build +FROM --platform=$BUILDPLATFORM node:22-slim AS frontend-build WORKDIR /app/client @@ -19,7 +19,7 @@ RUN npm run build # --------------------- # Backend stage # --------------------- -FROM node:22-slim AS backend +FROM node:22-slim AS app # Install ping RUN apt-get update \ @@ -28,23 +28,18 @@ RUN apt-get update \ WORKDIR /app/server -COPY server/package.json ./ +COPY server/package*.json ./ -RUN npm install +RUN npm ci COPY server ./ -RUN chmod +x ./scripts/inject-vars.sh - RUN npm run build COPY --from=frontend-build /app/client/dist ./public -RUN chown -R node:node ./public \ - && chown -R node:node ./dist \ - && chown -R node:node ./scripts +USER node +EXPOSE 52345 52346 -EXPOSE 52345 - -CMD ["sh", "-c", "./scripts/inject-vars.sh && node ./dist/index.js"] +CMD ["node", "dist/index.js"] diff --git a/docker/coolify/build_images.bat b/docker/coolify/build_images.bat deleted file mode 100644 index b940039b6..000000000 --- a/docker/coolify/build_images.bat +++ /dev/null @@ -1,33 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Change directory to root Server directory for correct Docker Context -cd /d "%~dp0" -cd ../.. - -REM Define service names and their corresponding Dockerfiles -set services[0]=bluewaveuptime/uptime_client -set dockerfiles[0]=.\docker\coolify\client.Dockerfile - -set services[1]=bluewaveuptime/uptime_database_mongo -set dockerfiles[1]=.\docker\coolify\mongoDB.Dockerfile - -set services[2]=bluewaveuptime/uptime_server -set dockerfiles[2]=.\docker\coolify\server.Dockerfile - -REM Loop through each service and build the corresponding image -for /L %%i in (0,1,2) do ( - set service=!services[%%i]! - set dockerfile=!dockerfiles[%%i]! - - docker build -f "!dockerfile!" -t "!service!" . - - REM Check if the build succeeded - if errorlevel 1 ( - echo Error building !service! image. Exiting... - exit /b 1 - ) -) - -echo All images built successfully -exit /b 0 diff --git a/docker/coolify/build_images.sh b/docker/coolify/build_images.sh deleted file mode 100755 index 1d29ae5b1..000000000 --- a/docker/coolify/build_images.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# Change directory to root Server directory for correct Docker Context -cd "$(dirname "$0")" -cd ../../ - -# Define an array of services and their Dockerfiles -declare -A services=( - ["bluewaveuptime/uptime_client"]="./docker/coolify/client.Dockerfile" - ["bluewaveuptime/uptime_database_mongo"]="./docker/coolify/mongoDB.Dockerfile" - ["bluewaveuptime/uptime_server"]="./docker/coolify/server.Dockerfile" -) - -# Loop through each service and build the corresponding image -for service in "${!services[@]}"; do - docker build -f "${services[$service]}" -t "$service" . - - # Check if the build succeeded - if [ $? -ne 0 ]; then - echo "Error building $service image. Exiting..." - exit 1 - fi -done - -echo "All images built successfully" diff --git a/docker/coolify/client.Dockerfile b/docker/coolify/client.Dockerfile deleted file mode 100755 index 8c3853b99..000000000 --- a/docker/coolify/client.Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM node:20-slim AS build - -ENV NODE_OPTIONS="--max-old-space-size=4096" - -WORKDIR /app - -COPY ./client/package*.json ./ - -RUN npm install - -COPY ./client . - -RUN npm run build - -FROM nginx:1.27.1-alpine - -COPY ./server/docker/dist/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf -COPY --from=build /app/dist /usr/share/nginx/html -COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh -RUN chmod +x /docker-entrypoint.d/env.sh - -CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/coolify/mongoDB.Dockerfile b/docker/coolify/mongoDB.Dockerfile deleted file mode 100755 index 969a320c2..000000000 --- a/docker/coolify/mongoDB.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mongo -EXPOSE 27017 -CMD ["mongod"] diff --git a/docker/coolify/nginx/conf.d/default.conf b/docker/coolify/nginx/conf.d/default.conf deleted file mode 100755 index 0592dcd06..000000000 --- a/docker/coolify/nginx/conf.d/default.conf +++ /dev/null @@ -1,35 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name checkmate-demo.bluewavelabs.ca; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_pass http://server:5000/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api-docs/ { - proxy_pass http://server:5000/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} \ No newline at end of file diff --git a/docker/coolify/server.Dockerfile b/docker/coolify/server.Dockerfile deleted file mode 100755 index e786a0cbf..000000000 --- a/docker/coolify/server.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:22-slim - -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -COPY ../../package*.json ./ - -RUN npm install - -COPY ../../ ./ - -RUN npm run build - -EXPOSE 5000 - -CMD ["node", "dist/index.js"] diff --git a/docker/dev/build_images.bat b/docker/dev/build_images.bat deleted file mode 100644 index ec3889418..000000000 --- a/docker/dev/build_images.bat +++ /dev/null @@ -1,33 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Change directory to root Server directory for correct Docker Context -cd /d "%~dp0" -cd ../.. - -REM Define service names and their corresponding Dockerfiles -set services[0]=uptime_client -set dockerfiles[0]=.\docker\dev\client.Dockerfile - -set services[1]=uptime_mongo -set dockerfiles[1]=.\docker\dev\mongoDB.Dockerfile - -set services[2]=uptime_server -set dockerfiles[2]=.\docker\dev\server.Dockerfile - -REM Loop through each service and build the corresponding image -for /L %%i in (0,1,2) do ( - set service=!services[%%i]! - set dockerfile=!dockerfiles[%%i]! - - docker build -f "!dockerfile!" -t "!service!" . - - REM Check if the build succeeded - if errorlevel 1 ( - echo Error building !service! image. Exiting... - exit /b 1 - ) -) - -echo All images built successfully -exit /b 0 \ No newline at end of file diff --git a/docker/dev/build_images.sh b/docker/dev/build_images.sh deleted file mode 100755 index eb43e31d8..000000000 --- a/docker/dev/build_images.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# Change directory to root Server directory for correct Docker Context -cd "$(dirname "$0")" -cd ../.. - -# Define service names and their corresponding Dockerfiles in parallel arrays -services=("uptime_client" "uptime_mongo" "uptime_server") -dockerfiles=( - "./docker/dev/client.Dockerfile" - "./docker/dev/mongoDB.Dockerfile" - "./docker/dev/server.Dockerfile" -) - -# Loop through each service and build the corresponding image -for i in "${!services[@]}"; do - service="${services[$i]}" - dockerfile="${dockerfiles[$i]}" - - docker build -f "$dockerfile" -t "$service" . - - # Check if the build succeeded - if [ $? -ne 0 ]; then - echo "Error building $service image. Exiting..." - exit 1 - fi -done - -echo "All images built successfully" \ No newline at end of file diff --git a/docker/dev/client.Dockerfile b/docker/dev/client.Dockerfile deleted file mode 100755 index df3ac7439..000000000 --- a/docker/dev/client.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:20-slim AS build - -ENV NODE_OPTIONS="--max-old-space-size=4096" - -WORKDIR /app - -COPY ./client/package*.json ./ - -RUN npm install - -COPY ./client ./ - -RUN npm run build - -FROM nginx:1.27.1-alpine - -COPY --from=build /app/dist /usr/share/nginx/html -COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh -RUN chmod +x /docker-entrypoint.d/env.sh -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docker/dev/docker-compose.custom-ca-example.yaml b/docker/dev/docker-compose.custom-ca-example.yaml index e1f185ae9..e086d6feb 100644 --- a/docker/dev/docker-compose.custom-ca-example.yaml +++ b/docker/dev/docker-compose.custom-ca-example.yaml @@ -1,29 +1,14 @@ # Dev/Test only: Not required in production # Example Docker Compose configuration for custom CA trust # This file demonstrates how to configure Checkmate to trust custom CAs -# -# Usage: docker-compose -f docker-compose.yaml -f docker-compose.custom-ca-example.yaml up +# +# Usage: docker compose -f docker-compose.yaml -f docker-compose.custom-ca-example.yaml up services: - server: - image: uptime_server:latest - restart: always - ports: - - "52345:52345" - env_file: - - server.env + checkmate: environment: # Mount your custom CA certificate NODE_EXTRA_CA_CERTS: /certs/custom-ca.pem volumes: # Mount the certs directory (read-only for security) - ./certs:/certs:ro - depends_on: - - mongodb - # Optional: Add healthcheck to ensure the server starts properly - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:52345/api/v1/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s diff --git a/docker/dev/docker-compose.yaml b/docker/dev/docker-compose.yaml index 09c12fe18..1dba10fae 100755 --- a/docker/dev/docker-compose.yaml +++ b/docker/dev/docker-compose.yaml @@ -1,27 +1,20 @@ services: - client: - image: uptime_client:latest - restart: always - ports: - - "80:80" - environment: - UPTIME_APP_API_BASE_URL: "http://localhost:52345/api/v1" - UPTIME_APP_CLIENT_HOST: "http://localhost" - volumes: - - ./nginx/conf.d:/etc/nginx/conf.d/ - depends_on: - - server - server: - image: uptime_server:latest + checkmate: + build: + context: ../.. + dockerfile: docker/Dockerfile restart: always ports: - "52345:52345" env_file: - server.env depends_on: - - mongodb + mongodb: + condition: service_healthy worker: - image: uptime_server:latest + build: + context: ../.. + dockerfile: docker/Dockerfile restart: always ports: - "52346:52346" @@ -43,9 +36,10 @@ services: timeout: 3s retries: 3 depends_on: - - mongodb + mongodb: + condition: service_healthy mongodb: - image: uptime_mongo:latest + image: mongo:8.0 restart: always ports: - "27017:27017" diff --git a/docker/dev/mongoDB.Dockerfile b/docker/dev/mongoDB.Dockerfile deleted file mode 100755 index 969a320c2..000000000 --- a/docker/dev/mongoDB.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mongo -EXPOSE 27017 -CMD ["mongod"] diff --git a/docker/dev/nginx/conf.d/default.conf b/docker/dev/nginx/conf.d/default.conf deleted file mode 100755 index e2c2a7268..000000000 --- a/docker/dev/nginx/conf.d/default.conf +++ /dev/null @@ -1,35 +0,0 @@ -server { - listen 80 default_server; - listen [::]:80 default_server; - - server_name localhost; # Set server name to localhost - server_tokens off; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - # location /api/ { - # proxy_pass http://server:52345/api/; - # proxy_http_version 1.1; - # proxy_set_header Host $host; - # proxy_set_header X-Real-IP $remote_addr; - # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # proxy_set_header X-Forwarded-Proto $scheme; - # proxy_set_header Connection ''; - # chunked_transfer_encoding off; - # proxy_buffering off; - # proxy_cache off; - # } - - # location /api-docs/ { - # proxy_pass http://server:52345/api-docs/; - # proxy_http_version 1.1; - # proxy_set_header Host $host; - # proxy_set_header X-Real-IP $remote_addr; - # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # proxy_set_header X-Forwarded-Proto $scheme; - # } -} \ No newline at end of file diff --git a/docker/dev/server.Dockerfile b/docker/dev/server.Dockerfile deleted file mode 100755 index 8a9af51d1..000000000 --- a/docker/dev/server.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:22-slim - -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -COPY ./server/package*.json ./ - -RUN npm install - -COPY ./server/ ./ - -RUN npm run build - -EXPOSE 52345 - -CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/docker/dist-arm/build_images.sh b/docker/dist-arm/build_images.sh deleted file mode 100755 index 2780e5359..000000000 --- a/docker/dist-arm/build_images.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -set -e # Exit on error - -# Change directory to root Server directory for correct Docker Context -cd "$(dirname "$0")" -cd ../.. - -# Ensure buildx builder exists and is used -if ! docker buildx inspect mybuilder &>/dev/null; then - docker buildx create --name mybuilder --use -else - docker buildx use mybuilder -fi - -# Define service names and Dockerfiles -services=("mono_server_arm") -dockerfiles=( - "./docker/dist-arm/server.Dockerfile" -) - -# Static image name for GitHub Container Registry (GHCR) -image="ghcr.io/bluewave-labs/checkmate:backend-dist-multi-arch" -platforms="linux/amd64,linux/arm64" -repo_url="https://github.com/bluewave-labs/checkmate" - -# Loop through each service and build -for i in "${!services[@]}"; do - service="${services[$i]}" - dockerfile="${dockerfiles[$i]}" - - echo "🚀 Building multi-arch image for $service..." - docker buildx build \ - --platform "$platforms" \ - -f "$dockerfile" \ - -t "$image" \ - --label "org.opencontainers.image.source=$repo_url" \ - --push \ - . - - echo "✅ $image pushed for platforms: $platforms" -done - -echo "🎉 All multi-arch images built and pushed successfully" diff --git a/docker/dist-arm/docker-compose.yaml b/docker/dist-arm/docker-compose.yaml deleted file mode 100644 index 49f81fe9a..000000000 --- a/docker/dist-arm/docker-compose.yaml +++ /dev/null @@ -1,28 +0,0 @@ -services: - server: - image: ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:latest - restart: always - pull_policy: always - ports: - - "52345:52345" - environment: - - UPTIME_APP_API_BASE_URL=http://localhost:52345/api/v1 - - UPTIME_APP_CLIENT_HOST=http://localhost - - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db - - CLIENT_HOST=http://localhost - - JWT_SECRET=my_secret - depends_on: - - mongodb - mongodb: - image: mongo:6.0 - restart: always - command: ["mongod", "--quiet", "--bind_ip_all"] - volumes: - - ./mongo/data:/data/db - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.runCommand({ ping: 1 })", "--quiet"] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 diff --git a/docker/dist-mono/build_images.bat b/docker/dist-mono/build_images.bat deleted file mode 100644 index 3e1d39fe1..000000000 --- a/docker/dist-mono/build_images.bat +++ /dev/null @@ -1,30 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Change directory to root Server directory for correct Docker Context -cd /d "%~dp0" -cd ../.. - -REM Define service names and their corresponding Dockerfiles -set services[0]=uptime_database_mongo -set dockerfiles[0]=.\docker\dist-mono\mongoDB.Dockerfile - -set services[1]=uptime_server -set dockerfiles[1]=.\docker\dist-mono\server.Dockerfile - -REM Loop through each service and build the corresponding image -for /L %%i in (0,1) do ( - set service=!services[%%i]! - set dockerfile=!dockerfiles[%%i]! - - docker build -f "!dockerfile!" -t "!service!" . - - REM Check if the build succeeded - if errorlevel 1 ( - echo Error building !service! image. Exiting... - exit /b 1 - ) -) - -echo All images built successfully -exit /b 0 diff --git a/docker/dist-mono/build_images.sh b/docker/dist-mono/build_images.sh deleted file mode 100755 index e3a0233d4..000000000 --- a/docker/dist-mono/build_images.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Change directory to root Server directory for correct Docker Context -cd "$(dirname "$0")" -cd ../.. - -# Define service names and their corresponding Dockerfiles in parallel arrays -services=("mono_mongo" "mono_server") -dockerfiles=( - "./docker/dist-mono/mongoDB.Dockerfile" - "./docker/dist-mono/server.Dockerfile" -) - -# Loop through each service and build the corresponding image -for i in "${!services[@]}"; do - service="${services[$i]}" - dockerfile="${dockerfiles[$i]}" - - docker build -f "$dockerfile" -t "$service" . - - # Check if the build succeeded - if [ $? -ne 0 ]; then - echo "Error building $service image. Exiting..." - exit 1 - fi -done - -echo "All images built successfully" \ No newline at end of file diff --git a/docker/dist-mono/docker-compose-local.yaml b/docker/dist-mono/docker-compose-local.yaml deleted file mode 100755 index a5616695b..000000000 --- a/docker/dist-mono/docker-compose-local.yaml +++ /dev/null @@ -1,27 +0,0 @@ -services: - server: - image: mono_server - restart: always - ports: - - "52345:52345" - environment: - - UPTIME_APP_API_BASE_URL=http://localhost:52345/api/v1 - - UPTIME_APP_CLIENT_HOST=http://localhost - - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db - - CLIENT_HOST=http://localhost - - JWT_SECRET=my_secret - depends_on: - - mongodb - mongodb: - image: mono_mongo - restart: always - command: ["mongod", "--quiet", "--bind_ip_all"] - volumes: - - ./mongo/data:/data/db - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 diff --git a/docker/dist-mono/docker-compose.yaml b/docker/dist-mono/docker-compose.yaml deleted file mode 100755 index 9553757b9..000000000 --- a/docker/dist-mono/docker-compose.yaml +++ /dev/null @@ -1,28 +0,0 @@ -services: - server: - image: ghcr.io/bluewave-labs/checkmate-backend-mono:latest - pull_policy: always - restart: always - ports: - - "52345:52345" - environment: - - UPTIME_APP_API_BASE_URL=http://localhost:52345/api/v1 - - UPTIME_APP_CLIENT_HOST=http://localhost - - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db - - CLIENT_HOST=http://localhost - - JWT_SECRET=my_secret - depends_on: - - mongodb - mongodb: - image: ghcr.io/bluewave-labs/checkmate-mongo:latest - restart: always - command: ["mongod", "--quiet", "--bind_ip_all"] - volumes: - - ./mongo/data:/data/db - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 diff --git a/docker/dist-mono/mongoDB.Dockerfile b/docker/dist-mono/mongoDB.Dockerfile deleted file mode 100755 index 969a320c2..000000000 --- a/docker/dist-mono/mongoDB.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mongo -EXPOSE 27017 -CMD ["mongod"] diff --git a/docker/dist-mono/server.Dockerfile b/docker/dist-mono/server.Dockerfile deleted file mode 100644 index 1e0eb303a..000000000 --- a/docker/dist-mono/server.Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM node:22-slim AS frontend-build - -WORKDIR /app/client - -COPY client/package*.json ./ -RUN npm install - -COPY client ./ -RUN npm run build - -FROM node:22-slim AS app - -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app/server - -COPY server ./ - -RUN npm install - -RUN npm run build -RUN cp -r src/templates dist/templates - -COPY --from=frontend-build /app/client/dist ./public - -RUN chmod +x ./scripts/inject-vars.sh \ - && chown -R node:node ./public \ - && chown -R node:node ./dist \ - && chown -R node:node ./scripts - - -EXPOSE 52345 - -CMD ["sh", "-c", "./scripts/inject-vars.sh && node ./dist/index.js"] diff --git a/docker/dist/build_images.bat b/docker/dist/build_images.bat deleted file mode 100644 index 92b5c4a9d..000000000 --- a/docker/dist/build_images.bat +++ /dev/null @@ -1,33 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Change directory to root Server directory for correct Docker Context -cd /d "%~dp0" -cd ../.. - -REM Define service names and their corresponding Dockerfiles -set services[0]=bluewaveuptime/uptime_client -set dockerfiles[0]=.\docker\dist\client.Dockerfile - -set services[1]=bluewaveuptime/uptime_database_mongo -set dockerfiles[1]=.\docker\dist\mongoDB.Dockerfile - -set services[2]=bluewaveuptime/uptime_server -set dockerfiles[2]=.\docker\dist\server.Dockerfile - -REM Loop through each service and build the corresponding image -for /L %%i in (0,1,2) do ( - set service=!services[%%i]! - set dockerfile=!dockerfiles[%%i]! - - docker build -f "!dockerfile!" -t "!service!" . - - REM Check if the build succeeded - if errorlevel 1 ( - echo Error building !service! image. Exiting... - exit /b 1 - ) -) - -echo All images built successfully -exit /b 0 diff --git a/docker/dist/build_images.sh b/docker/dist/build_images.sh deleted file mode 100755 index 84fbb7d5a..000000000 --- a/docker/dist/build_images.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# Change directory to root Server directory for correct Docker Context -cd "$(dirname "$0")" -cd ../../ - -# Define an array of services and their Dockerfiles -declare -A services=( - ["bluewaveuptime/uptime_client"]="./docker/dist/client.Dockerfile" - ["bluewaveuptime/uptime_database_mongo"]="./docker/dist/mongoDB.Dockerfile" - ["bluewaveuptime/uptime_server"]="./docker/dist/server.Dockerfile" -) - -for service in "${!services[@]}"; do - docker build \ - -f "${services[$service]}" \ - -t "$service" \ - . - - if [ $? -ne 0 ]; then - echo "Error building $service image. Exiting..." - exit 1 - fi -done - -echo "All images built successfully" diff --git a/docker/dist/client.Dockerfile b/docker/dist/client.Dockerfile deleted file mode 100755 index 9b42654b0..000000000 --- a/docker/dist/client.Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM node:20-slim AS build - -ENV NODE_OPTIONS="--max-old-space-size=4096" - -WORKDIR /app - -COPY ./client/package*.json ./ - -RUN npm install - -COPY ./client . - -RUN npm run build - -FROM nginx:1.27.1-alpine - -COPY ./docker/dist/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf -COPY --from=build /app/dist /usr/share/nginx/html -COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh -RUN chmod +x /docker-entrypoint.d/env.sh - -CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/dist/docker-compose.yaml b/docker/dist/docker-compose.yaml deleted file mode 100755 index 2e845daab..000000000 --- a/docker/dist/docker-compose.yaml +++ /dev/null @@ -1,38 +0,0 @@ -services: - client: - image: ghcr.io/bluewave-labs/checkmate-client:latest - restart: always - environment: - UPTIME_APP_API_BASE_URL: "http://localhost:52345/api/v1" - UPTIME_APP_CLIENT_HOST: "http://localhost" - ports: - - "80:80" - - "443:443" - depends_on: - - server - server: - image: ghcr.io/bluewave-labs/checkmate-backend:latest - restart: always - ports: - - "52345:52345" - depends_on: - - mongodb - environment: - - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db - - CLIENT_HOST=http://localhost - - JWT_SECRET=my_secret - # volumes: - # - /var/run/docker.sock:/var/run/docker.sock:ro - mongodb: - image: ghcr.io/bluewave-labs/checkmate-mongo:latest - restart: always - volumes: - - ./mongo/data:/data/db - command: ["mongod", "--quiet", "--bind_ip_all"] - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 diff --git a/docker/dist/mongoDB.Dockerfile b/docker/dist/mongoDB.Dockerfile deleted file mode 100755 index 969a320c2..000000000 --- a/docker/dist/mongoDB.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mongo -EXPOSE 27017 -CMD ["mongod"] diff --git a/docker/dist/nginx/conf.d/default.conf b/docker/dist/nginx/conf.d/default.conf deleted file mode 100755 index 30e51c71c..000000000 --- a/docker/dist/nginx/conf.d/default.conf +++ /dev/null @@ -1,35 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name checkmate-demo.bluewavelabs.ca; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - # location /api/ { - # proxy_pass http://server:5000/api/; - # proxy_http_version 1.1; - # proxy_set_header Host $host; - # proxy_set_header X-Real-IP $remote_addr; - # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # proxy_set_header X-Forwarded-Proto $scheme; - # } - - location /api-docs/ { - proxy_pass http://server:5000/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} \ No newline at end of file diff --git a/docker/dist/server.Dockerfile b/docker/dist/server.Dockerfile deleted file mode 100755 index 8a9af51d1..000000000 --- a/docker/dist/server.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:22-slim - -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -COPY ./server/package*.json ./ - -RUN npm install - -COPY ./server/ ./ - -RUN npm run build - -EXPOSE 52345 - -CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 000000000..e1a2d0e5a --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,45 @@ +services: + checkmate: + image: ghcr.io/bluewave-labs/checkmate:latest + pull_policy: always + restart: always + ports: + - "52345:52345" + environment: + - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db + - CLIENT_HOST=${CLIENT_HOST:-http://localhost:52345} + - JWT_SECRET=${JWT_SECRET:?set JWT_SECRET in your environment} + stop_grace_period: 60s + healthcheck: + test: + [ + "CMD", + "node", + "-e", + "require('http').get('http://localhost:52346/livez',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))", + ] + interval: 15s + timeout: 3s + start_period: 60s + start_interval: 2s + retries: 3 + depends_on: + mongodb: + condition: service_healthy + + mongodb: + image: mongo:8.0 + restart: always + command: ["mongod", "--quiet", "--bind_ip_all"] + volumes: + - mongo-data:/data/db + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] + interval: 5s + timeout: 30s + start_period: 0s + start_interval: 1s + retries: 30 + +volumes: + mongo-data: diff --git a/docker/prod/build_images.bat b/docker/prod/build_images.bat deleted file mode 100644 index 79403db3f..000000000 --- a/docker/prod/build_images.bat +++ /dev/null @@ -1,33 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Change directory to root Server directory for correct Docker Context -cd /d "%~dp0" -cd ../.. - -REM Define service names and their corresponding Dockerfiles -set services[0]=uptime_client -set dockerfiles[0]=.\docker\prod\client.Dockerfile - -set services[1]=uptime_database_mongo -set dockerfiles[1]=.\docker\prod\mongoDB.Dockerfile - -set services[2]=uptime_server -set dockerfiles[2]=.\docker\prod\server.Dockerfile - -REM Loop through each service and build the corresponding image -for /L %%i in (0,1,2) do ( - set service=!services[%%i]! - set dockerfile=!dockerfiles[%%i]! - - docker build -f "!dockerfile!" -t "!service!" . - - REM Check if the build succeeded - if errorlevel 1 ( - echo Error building !service! image. Exiting... - exit /b 1 - ) -) - -echo All images built successfully -exit /b 0 diff --git a/docker/prod/build_images.sh b/docker/prod/build_images.sh deleted file mode 100755 index 49f8c7841..000000000 --- a/docker/prod/build_images.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Change directory to root directory for correct Docker Context -cd "$(dirname "$0")" -cd ../../ - -# Define an array of services and their Dockerfiles -declare -A services=( - ["uptime_client"]="./docker/prod/client.Dockerfile" - ["uptime_database_mongo"]="./docker/prod/mongoDB.Dockerfile" - ["uptime_server"]="./docker/prod/server.Dockerfile" -) - -# Loop through each service and build the corresponding image -for service in "${!services[@]}"; do - docker build -f "${services[$service]}" -t "$service" . - - # Check if the build succeeded - if [ $? -ne 0 ]; then - echo "Error building $service image. Exiting..." - exit 1 - fi -done - -echo "All images built successfully" \ No newline at end of file diff --git a/docker/prod/client.Dockerfile b/docker/prod/client.Dockerfile deleted file mode 100755 index b4c685e3e..000000000 --- a/docker/prod/client.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:20-slim AS build - -ENV NODE_OPTIONS="--max-old-space-size=4096" - -WORKDIR /app - -COPY ./client/package*.json ./ - -RUN npm install - -COPY ./client . - -RUN npm run build - -FROM nginx:1.27.1-alpine - -COPY --from=build /app/dist /usr/share/nginx/html -COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh -RUN chmod +x /docker-entrypoint.d/env.sh -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docker/prod/docker-compose.yaml b/docker/prod/docker-compose.yaml deleted file mode 100755 index 02a9fd919..000000000 --- a/docker/prod/docker-compose.yaml +++ /dev/null @@ -1,53 +0,0 @@ -services: - client: - image: ghcr.io/bluewave-labs/checkmate:frontend-demo - restart: always - environment: - UPTIME_APP_API_BASE_URL: "https://demo.checkmate.so/api/v1" - UPTIME_APP_CLIENT_HOST: "https://demo.checkmate.so" - ports: - - "80:80" - - "443:443" - depends_on: - - server - volumes: - - ./nginx/conf.d:/etc/nginx/conf.d/:ro - - /etc/letsencrypt:/etc/nginx/ssl:ro - - server: - image: ghcr.io/bluewave-labs/checkmate:backend-demo - restart: always - ports: - - "52345:52345" - env_file: - - server.env - depends_on: - - mongodb - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - mongodb: - image: ghcr.io/bluewave-labs/checkmate:mongo-demo - restart: always - command: ["mongod", "--quiet", "--bind_ip_all"] - volumes: - - ./mongo/data:/data/db - env_file: - - mongo.env - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 - capture: - image: ghcr.io/bluewave-labs/capture:latest - container_name: capture - restart: unless-stopped - ports: - - "59232:59232" - environment: - - API_SECRET=my_secret - - GIN_MODE=release - volumes: - - /etc/os-release:/etc/os-release:ro diff --git a/docker/prod/mongo/init/init.js b/docker/prod/mongo/init/init.js deleted file mode 100644 index 34b90ced3..000000000 --- a/docker/prod/mongo/init/init.js +++ /dev/null @@ -1,41 +0,0 @@ -function initiateReplicaSet() { - try { - rs.initiate({ - _id: "rs0", - members: [{ _id: 0, host: "localhost:27017" }], - }); - } catch (e) { - print("Replica set already initiated or error occurred: " + e); - } -} - -function waitForPrimary() { - while (true) { - const isMaster = db.isMaster(); - if (isMaster.ismaster) { - print("This node is now PRIMARY."); - break; - } - print("Waiting to become PRIMARY..."); - sleep(2000); - } -} - -function createUser() { - db = db.getSiblingDB("uptime_db"); - - var username = process.env.USERNAME_ENV_VAR; - var password = process.env.PASSWORD_ENV_VAR; - - db.createUser({ - user: username, - pwd: password, - roles: [{ role: "readWrite", db: "uptime_db" }], - }); - - print("User '" + username + "' created successfully"); -} - -initiateReplicaSet(); -waitForPrimary(); -createUser(); diff --git a/docker/prod/mongoDB.Dockerfile b/docker/prod/mongoDB.Dockerfile deleted file mode 100755 index 969a320c2..000000000 --- a/docker/prod/mongoDB.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mongo -EXPOSE 27017 -CMD ["mongod"] diff --git a/docker/prod/nginx/conf.d/cerbot b/docker/prod/nginx/conf.d/cerbot deleted file mode 100755 index f26cfeb23..000000000 --- a/docker/prod/nginx/conf.d/cerbot +++ /dev/null @@ -1,15 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name checkmate-demo.bluewavelabs.ca www.checkmate-demo.bluewavelabs.ca; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - return 301 https://[domain-name]$request_uri; - } -} diff --git a/docker/prod/nginx/conf.d/default.conf b/docker/prod/nginx/conf.d/default.conf deleted file mode 100755 index 0e0e55a2b..000000000 --- a/docker/prod/nginx/conf.d/default.conf +++ /dev/null @@ -1,69 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name checkmate-demo.bluewavelabs.ca; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_pass http://server:52345/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api-docs/ { - proxy_pass http://server:52345/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} - -server { - listen 443 default_server ssl http2; - listen [::]:443 ssl http2; - - server_name checkmate-demo.bluewavelabs.ca; - - ssl_certificate /etc/nginx/ssl/live/demo.checkmate.so/fullchain.pem; - ssl_certificate_key /etc/nginx/ssl/live/demo.checkmate.so/privkey.pem; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_pass http://server:52345/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api-docs/ { - proxy_pass http://server:52345/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} diff --git a/docker/prod/server.Dockerfile b/docker/prod/server.Dockerfile deleted file mode 100755 index b093638f4..000000000 --- a/docker/prod/server.Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM node:22-slim - -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - -ENV NODE_OPTIONS="--max-old-space-size=2048" - -WORKDIR /app - -COPY ./server/package*.json ./ - -RUN npm install - -COPY ./server ./ - -RUN npm run build - -RUN cp -r src/templates dist/templates - -EXPOSE 52345 - -CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/docker/staging/build_images.bat b/docker/staging/build_images.bat deleted file mode 100644 index c8be0378a..000000000 --- a/docker/staging/build_images.bat +++ /dev/null @@ -1,33 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Change directory to root Server directory for correct Docker Context -cd /d "%~dp0" -cd ../.. - -REM Define service names and their corresponding Dockerfiles -set services[0]=uptime_client -set dockerfiles[0]=.\docker\staging\client.Dockerfile - -set services[1]=uptime_database_mongo -set dockerfiles[1]=.\docker\staging\mongoDB.Dockerfile - -set services[2]=uptime_server -set dockerfiles[2]=.\docker\staging\server.Dockerfile - -REM Loop through each service and build the corresponding image -for /L %%i in (0,1,2) do ( - set service=!services[%%i]! - set dockerfile=!dockerfiles[%%i]! - - docker build -f "!dockerfile!" -t "!service!" . - - REM Check if the build succeeded - if errorlevel 1 ( - echo Error building !service! image. Exiting... - exit /b 1 - ) -) - -echo All images built successfully -exit /b 0 diff --git a/docker/staging/build_images.sh b/docker/staging/build_images.sh deleted file mode 100755 index a11aebf7c..000000000 --- a/docker/staging/build_images.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Change directory to root directory for correct Docker Context -cd "$(dirname "$0")" -cd ../../ - -# Define an array of services and their Dockerfiles -declare -A services=( - ["uptime_client"]="./docker/staging/client.Dockerfile" - ["uptime_database_mongo"]="./docker/staging/mongoDB.Dockerfile" - ["uptime_server"]="./docker/staging/server.Dockerfile" -) - -# Loop through each service and build the corresponding image -for service in "${!services[@]}"; do - docker build -f "${services[$service]}" -t "$service" . - - # Check if the build succeeded - if [ $? -ne 0 ]; then - echo "Error building $service image. Exiting..." - exit 1 - fi -done - -echo "All images built successfully" \ No newline at end of file diff --git a/docker/staging/cerbot-compose.yaml b/docker/staging/cerbot-compose.yaml deleted file mode 100755 index 9220d07a1..000000000 --- a/docker/staging/cerbot-compose.yaml +++ /dev/null @@ -1,19 +0,0 @@ -version: "3" - -services: - webserver: - image: nginx:latest - ports: - - 80:80 - - 443:443 - restart: always - volumes: - - ./nginx/conf.d/:/etc/nginx/conf.d/:ro - - ./certbot/www/:/var/www/certbot/:ro - certbot: - image: certbot/certbot:latest - volumes: - - ./certbot/www/:/var/www/certbot/:rw - - ./certbot/conf/:/etc/letsencrypt/:rw - depends_on: - - webserver diff --git a/docker/staging/client.Dockerfile b/docker/staging/client.Dockerfile deleted file mode 100644 index df3ac7439..000000000 --- a/docker/staging/client.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:20-slim AS build - -ENV NODE_OPTIONS="--max-old-space-size=4096" - -WORKDIR /app - -COPY ./client/package*.json ./ - -RUN npm install - -COPY ./client ./ - -RUN npm run build - -FROM nginx:1.27.1-alpine - -COPY --from=build /app/dist /usr/share/nginx/html -COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh -RUN chmod +x /docker-entrypoint.d/env.sh -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docker/staging/docker-compose.yaml b/docker/staging/docker-compose.yaml deleted file mode 100755 index f4ed5ce6f..000000000 --- a/docker/staging/docker-compose.yaml +++ /dev/null @@ -1,62 +0,0 @@ -services: - client: - image: ghcr.io/bluewave-labs/checkmate:frontend-staging - restart: always - environment: - UPTIME_APP_API_BASE_URL: "https://checkmate-test.bluewavelabs.ca/api/v1" - UPTIME_APP_CLIENT_HOST: "https://checkmate-test.bluewavelabs.ca" - ports: - - "80:80" - - "443:443" - depends_on: - - server - volumes: - - ./nginx/conf.d:/etc/nginx/conf.d/:ro - - ./certbot/www:/var/www/certbot/:ro - - ./certbot/conf/:/etc/nginx/ssl/:ro - - certbot: - image: certbot/certbot:latest - restart: always - volumes: - - ./certbot/www/:/var/www/certbot/:rw - - ./certbot/conf/:/etc/letsencrypt/:rw - entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot; sleep 12h & wait $${!}; done;'" - - server: - image: ghcr.io/bluewave-labs/checkmate:backend-staging - restart: always - ports: - - "52345:52345" - env_file: - - server.env - depends_on: - - mongodb - mongodb: - image: ghcr.io/bluewave-labs/checkmate:mongo-staging - restart: always - command: ["mongod", "--quiet", "--bind_ip_all"] - ports: - - "27017:27017" - volumes: - - ./mongo/data:/data/db - env_file: - - mongo.env - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 - capture: - image: ghcr.io/bluewave-labs/capture:latest - container_name: capture - restart: unless-stopped - ports: - - "59232:59232" - environment: - - API_SECRET=my_secret - - GIN_MODE=release - volumes: - - /etc/os-release:/etc/os-release:ro diff --git a/docker/staging/mongo/init/01_init_replica_set.js b/docker/staging/mongo/init/01_init_replica_set.js deleted file mode 100644 index 878ce68a6..000000000 --- a/docker/staging/mongo/init/01_init_replica_set.js +++ /dev/null @@ -1,15 +0,0 @@ -try { - const status = rs.status(); - printjson(status); -} catch (e) { - if (e.codeName === "NotYetInitialized") { - print("Replica set not initialized. Initiating..."); - rs.initiate({ - _id: "rs0", - members: [{ _id: 0, host: "mongodb:27017" }], - }); - } else { - print("Unexpected error during rs.status():"); - printjson(e); - } -} diff --git a/docker/staging/mongo/init/02_create_users.js b/docker/staging/mongo/init/02_create_users.js deleted file mode 100755 index adb2bf078..000000000 --- a/docker/staging/mongo/init/02_create_users.js +++ /dev/null @@ -1,16 +0,0 @@ -var username = process.env.USERNAME_ENV_VAR; -var password = process.env.PASSWORD_ENV_VAR; - -db = db.getSiblingDB("uptime_db"); - -db.createUser({ - user: username, - pwd: password, - roles: [ - { - role: "readWrite", - db: "uptime_db", - }, - ], -}); -print("User uptime_user created successfully"); diff --git a/docker/staging/mongoDB.Dockerfile b/docker/staging/mongoDB.Dockerfile deleted file mode 100755 index 969a320c2..000000000 --- a/docker/staging/mongoDB.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mongo -EXPOSE 27017 -CMD ["mongod"] diff --git a/docker/staging/nginx/conf.d/certbot b/docker/staging/nginx/conf.d/certbot deleted file mode 100755 index eba24638c..000000000 --- a/docker/staging/nginx/conf.d/certbot +++ /dev/null @@ -1,15 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name checkmate-test.bluewavelabs.ca www.checkmate-test.bluewavelabs.ca; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - return 301 https://[domain-name]$request_uri; - } -} diff --git a/docker/staging/nginx/conf.d/default.conf b/docker/staging/nginx/conf.d/default.conf deleted file mode 100755 index c755ddef1..000000000 --- a/docker/staging/nginx/conf.d/default.conf +++ /dev/null @@ -1,77 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name checkmate-test.bluewavelabs.ca; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_pass http://server:52345/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Connection ''; - chunked_transfer_encoding off; - proxy_buffering off; - proxy_cache off; - } - - location /api-docs/ { - proxy_pass http://server:52345/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} - -server { - listen 443 default_server ssl http2; - listen [::]:443 ssl http2; - - server_name checkmate-test.bluewavelabs.ca; - - ssl_certificate /etc/nginx/ssl/live/checkmate-test.bluewavelabs.ca/fullchain.pem; - ssl_certificate_key /etc/nginx/ssl/live/checkmate-test.bluewavelabs.ca/privkey.pem; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_pass http://server:52345/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Connection ''; - chunked_transfer_encoding off; - proxy_buffering off; - proxy_cache off; - } - - location /api-docs/ { - proxy_pass http://server:52345/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} diff --git a/docker/staging/server.Dockerfile b/docker/staging/server.Dockerfile deleted file mode 100755 index b093638f4..000000000 --- a/docker/staging/server.Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM node:22-slim - -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - -ENV NODE_OPTIONS="--max-old-space-size=2048" - -WORKDIR /app - -COPY ./server/package*.json ./ - -RUN npm install - -COPY ./server ./ - -RUN npm run build - -RUN cp -r src/templates dist/templates - -EXPOSE 52345 - -CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/docker/unified/build_images.bat b/docker/unified/build_images.bat deleted file mode 100644 index a3d54e60d..000000000 --- a/docker/unified/build_images.bat +++ /dev/null @@ -1,33 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -REM Change directory to root Server directory for correct Docker Context -cd /d "%~dp0" -cd ../.. - -REM Define service names and their corresponding Dockerfiles -set services[0]=uptime_client -set dockerfiles[0]=.\docker\unified\client.Dockerfile - -set services[1]=uptime_database_mongo -set dockerfiles[1]=.\docker\unified\mongoDB.Dockerfile - -set services[2]=uptime_server -set dockerfiles[2]=.\docker\unified\server.Dockerfile - -REM Loop through each service and build the corresponding image -for /L %%i in (0,1,2) do ( - set service=!services[%%i]! - set dockerfile=!dockerfiles[%%i]! - - docker build -f "!dockerfile!" -t "!service!" . - - REM Check if the build succeeded - if errorlevel 1 ( - echo Error building !service! image. Exiting... - exit /b 1 - ) -) - -echo All images built successfully -exit /b 0 diff --git a/docker/unified/build_images.sh b/docker/unified/build_images.sh deleted file mode 100755 index 21b866bbe..000000000 --- a/docker/unified/build_images.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Change directory to root directory for correct Docker Context -cd "$(dirname "$0")" -cd ../../ - -# Define an array of services and their Dockerfiles -declare -A services=( - ["uptime_client"]="./docker/unified/client.Dockerfile" - ["uptime_database_mongo"]="./docker/unified/mongoDB.Dockerfile" - ["uptime_server"]="./docker/unified/server.Dockerfile" -) - -# Loop through each service and build the corresponding image -for service in "${!services[@]}"; do - docker build -f "${services[$service]}" -t "$service" . - - # Check if the build succeeded - if [ $? -ne 0 ]; then - echo "Error building $service image. Exiting..." - exit 1 - fi -done - -echo "All images built successfully" diff --git a/docker/unified/certbot-compose.yaml b/docker/unified/certbot-compose.yaml deleted file mode 100755 index 9220d07a1..000000000 --- a/docker/unified/certbot-compose.yaml +++ /dev/null @@ -1,19 +0,0 @@ -version: "3" - -services: - webserver: - image: nginx:latest - ports: - - 80:80 - - 443:443 - restart: always - volumes: - - ./nginx/conf.d/:/etc/nginx/conf.d/:ro - - ./certbot/www/:/var/www/certbot/:ro - certbot: - image: certbot/certbot:latest - volumes: - - ./certbot/www/:/var/www/certbot/:rw - - ./certbot/conf/:/etc/letsencrypt/:rw - depends_on: - - webserver diff --git a/docker/unified/client.Dockerfile b/docker/unified/client.Dockerfile deleted file mode 100755 index b4c685e3e..000000000 --- a/docker/unified/client.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:20-slim AS build - -ENV NODE_OPTIONS="--max-old-space-size=4096" - -WORKDIR /app - -COPY ./client/package*.json ./ - -RUN npm install - -COPY ./client . - -RUN npm run build - -FROM nginx:1.27.1-alpine - -COPY --from=build /app/dist /usr/share/nginx/html -COPY --from=build /app/env.sh /docker-entrypoint.d/env.sh -RUN chmod +x /docker-entrypoint.d/env.sh -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docker/unified/docker-compose.yaml b/docker/unified/docker-compose.yaml deleted file mode 100755 index 199d0da21..000000000 --- a/docker/unified/docker-compose.yaml +++ /dev/null @@ -1,114 +0,0 @@ -# sudo certbot certonly --standalone --http-01-port 8080 -# cerbot needs to run on 8080 since port 80 is used by the gateway - -services: - # Gateway (Single Entry Point) - gateway: - image: nginx:1.27.1-alpine - container_name: checkmate-gateway - restart: always - ports: - - "80:80" - - "443:443" - volumes: - - /etc/letsencrypt:/etc/nginx/ssl:ro - - ./gateway/nginx.conf:/etc/nginx/conf.d/default.conf:ro - - # Production Stack ----- - client-prod: - image: ghcr.io/bluewave-labs/checkmate:frontend-demo - container_name: client-prod - restart: always - environment: - UPTIME_APP_API_BASE_URL: "https://demo.checkmate.so/api/v1" - UPTIME_APP_CLIENT_HOST: "https://demo.checkmate.so" - expose: - - "80" - depends_on: - - server-prod - volumes: - - ./nginx/conf.d/prod.conf:/etc/nginx/conf.d/default.conf:ro - - server-prod: - image: ghcr.io/bluewave-labs/checkmate:backend-demo - container_name: server-prod - restart: always - env_file: - - server-prod.env - depends_on: - - mongodb-prod - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - mongodb-prod: - image: ghcr.io/bluewave-labs/checkmate:mongo-demo - container_name: mongodb-prod - restart: always - command: [ "mongod", "--quiet", "--bind_ip_all" ] - volumes: - - ./mongo/data-prod:/data/db - env_file: - - mongo-prod.env - healthcheck: - test: [ "CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet" ] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 - - # Staging Stack ----- - client-staging: - image: ghcr.io/bluewave-labs/checkmate:frontend-staging - container_name: client-staging - restart: always - environment: - UPTIME_APP_API_BASE_URL: "https://staging.checkmate.so/api/v1" - UPTIME_APP_CLIENT_HOST: "https://staging.checkmate.so" - expose: - - "80" - depends_on: - - server-staging - volumes: - - ./nginx/conf.d/staging.conf:/etc/nginx/conf.d/default.conf:ro - - server-staging: - image: ghcr.io/bluewave-labs/checkmate:backend-staging - container_name: server-staging - restart: always - env_file: - - server-staging.env - depends_on: - - mongodb-staging - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - mongodb-staging: - image: ghcr.io/bluewave-labs/checkmate:mongo-staging - container_name: mongodb-staging - restart: always - command: [ "mongod", "--quiet", "--bind_ip_all" ] - volumes: - - ./mongo/data-staging:/data/db - env_file: - - mongo-staging.env - healthcheck: - test: [ "CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet" ] - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 30 - - # Shared Utils - capture: - image: ghcr.io/bluewave-labs/capture:latest - container_name: capture - restart: unless-stopped - ports: - - "59232:59232" - environment: - - API_SECRET=my_secret - - GIN_MODE=release - volumes: - - /etc/os-release:/etc/os-release:ro diff --git a/docker/unified/mongoDB.Dockerfile b/docker/unified/mongoDB.Dockerfile deleted file mode 100755 index 969a320c2..000000000 --- a/docker/unified/mongoDB.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mongo -EXPOSE 27017 -CMD ["mongod"] diff --git a/docker/unified/nginx/conf.d/prod.conf b/docker/unified/nginx/conf.d/prod.conf deleted file mode 100755 index 63a42a401..000000000 --- a/docker/unified/nginx/conf.d/prod.conf +++ /dev/null @@ -1,31 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name _; - server_tokens off; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_pass http://server-prod:52345/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api-docs/ { - proxy_pass http://server-prod:52345/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} \ No newline at end of file diff --git a/docker/unified/nginx/conf.d/staging.conf b/docker/unified/nginx/conf.d/staging.conf deleted file mode 100755 index 6b640a06b..000000000 --- a/docker/unified/nginx/conf.d/staging.conf +++ /dev/null @@ -1,31 +0,0 @@ -server { - listen 80; - listen [::]:80; - - server_name _; - server_tokens off; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_pass http://server-staging:52345/api/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api-docs/ { - proxy_pass http://server-staging:52345/api-docs/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} \ No newline at end of file diff --git a/docker/unified/server.Dockerfile b/docker/unified/server.Dockerfile deleted file mode 100755 index 86a8c1f48..000000000 --- a/docker/unified/server.Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM node:22-slim - -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - -ENV NODE_OPTIONS="--max-old-space-size=2048" - -WORKDIR /app - -COPY ./server/package*.json ./ - -RUN npm install - -COPY ./server ./ - -RUN npm run build - -EXPOSE 52345 - -CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index b8904d1f9..09698b115 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,8 +19,6 @@ If you need to monitor internal HTTPS endpoints with certificates from private C ### Example Configurations The `docker/dev/` directory contains example configurations: - `docker-compose.custom-ca-example.yaml` - Example Docker Compose with custom CA -- `server-custom-ca.Dockerfile` - Example Dockerfile for OS-level CA trust -- `export-smallstep-ca.sh` - Helper script for Smallstep CA export ## Contributing to Documentation diff --git a/docs/custom-ca-trust.md b/docs/custom-ca-trust.md index b3a19831f..6e94894c0 100644 --- a/docs/custom-ca-trust.md +++ b/docs/custom-ca-trust.md @@ -18,8 +18,8 @@ Add a volume mount for your CA certificate and set the environment variable: ```yaml services: - server: - image: uptime_server:latest + checkmate: + image: ghcr.io/bluewave-labs/checkmate:latest restart: always ports: - "52345:52345" @@ -30,7 +30,6 @@ services: volumes: - ./certs:/certs:ro depends_on: - - redis - mongodb ``` @@ -52,67 +51,53 @@ For more comprehensive trust configuration, you can create a derived Dockerfile ### Custom Dockerfile -Create `docker/dev/server-custom-ca.Dockerfile`: +Create `checkmate-custom-ca.Dockerfile`: ```dockerfile -FROM node:20-alpine +FROM ghcr.io/bluewave-labs/checkmate:latest -# Install ca-certificates for Alpine -RUN apk add --no-cache ca-certificates +USER root -WORKDIR /app +RUN apt-get update \ + && apt-get install -y ca-certificates \ + && rm -rf /var/lib/apt/lists/* -COPY ./server/package*.json ./ - -RUN npm install - -COPY ./server/ ./ - -# Copy your custom CA certificate +# Copy your custom CA certificate (must have a .crt extension) COPY ./certs/custom-ca.crt /usr/local/share/ca-certificates/ # Update CA certificates RUN update-ca-certificates -EXPOSE 52345 +USER node -CMD ["node", "src/index.js"] +# Node uses its bundled CA store by default; make it read the system store +ENV NODE_OPTIONS="--use-system-ca" ``` ### Docker Compose Override -Create `docker/dev/docker-compose.custom-ca.yaml`: +Create `docker-compose.custom-ca.yaml`: ```yaml services: - server: + checkmate: build: context: . - dockerfile: server-custom-ca.Dockerfile + dockerfile: checkmate-custom-ca.Dockerfile restart: always ports: - "52345:52345" env_file: - server.env depends_on: - - redis - mongodb ``` -Run with: `docker-compose -f docker-compose.yaml -f docker-compose.custom-ca.yaml up` +Run with: `docker compose -f docker-compose.yaml -f docker-compose.custom-ca.yaml up` -## Alpine Linux Considerations +## Base Image Considerations -Since Checkmate uses Alpine Linux as the base image, you need to install the `ca-certificates` package: - -```dockerfile -# Install ca-certificates for Alpine -RUN apk add --no-cache ca-certificates - -# Copy and update CA certificates -COPY ./certs/custom-ca.crt /usr/local/share/ca-certificates/ -RUN update-ca-certificates -``` +The Checkmate image is Debian-based (`node:22-slim`), which does not ship the `ca-certificates` package — install it before running `update-ca-certificates` (see the Dockerfile above). Certificates dropped into `/usr/local/share/ca-certificates/` must have a `.crt` extension, and Node only consults the system store when started with `--use-system-ca`. For most setups the `NODE_EXTRA_CA_CERTS` compose approach is simpler. ## Smallstep CA Configuration diff --git a/docs/hosting/checkmate-so/README.md b/docs/hosting/checkmate-so/README.md new file mode 100644 index 000000000..0c06f6948 --- /dev/null +++ b/docs/hosting/checkmate-so/README.md @@ -0,0 +1,56 @@ +# checkmate.so hosting (demo + staging) + +Config for BlueWave's hosted instances at `demo.checkmate.so` and +`staging.checkmate.so`: one host, one nginx gateway terminating TLS, two +independent Checkmate stacks running the mono image +(`ghcr.io/bluewave-labs/checkmate`), plus a shared +[Capture](https://github.com/bluewave-labs/capture) agent. It replaces the old +`docker/unified/` setup (nginx client images + separate backend images + a +republished Mongo image) and doubles as a worked example of running the mono +image behind a TLS reverse proxy. + +## How deploys work + +`.github/workflows/build-image.yml` builds and pushes the image, then SSHes in +and runs `docker compose pull && docker compose up -d ` in +this directory: + +- merge to `develop` → image tagged `:develop` → `checkmate-staging` recreated +- merge to `master` → image tagged `:master` → `checkmate-demo` (prod) recreated + +The host keeps a git checkout of this repo (on `develop`); the deploy does +`git pull` first so compose changes land with the code. Note the deploy only +recreates the checkmate service — changes to `gateway/nginx.conf` or the mongo +services need a manual `docker compose up -d` / gateway restart on the host. + +## Host-local files (not committed) + +Kept next to this compose file on the host: + +- `server-prod.env` / `server-staging.env` — Checkmate server env + (`DB_CONNECTION_STRING`, `JWT_SECRET`, `CLIENT_HOST=https://demo.checkmate.so` + etc.). The old `UPTIME_APP_*` client vars are no-ops now and can be removed; + if a client override is ever needed, use the `CLIENT_CONFIG_*` vars (see the + Configuration section of the root README). +- `mongo-prod.env` / `mongo-staging.env` — Mongo credentials. +- `mongo/data-prod` / `mongo/data-staging` — Mongo data directories. + +TLS certs live in `/etc/letsencrypt` (see `certbot-compose.yaml`; certbot +answers ACME challenges on port 8080 behind the gateway). + +## One-time migration from docker/unified + +1. `cd ~/checkmate && git pull` (must be on a branch containing this directory). +2. Stop the old stack: `cd docker/unified && docker compose down`. +3. Move host-local files here: + `mv docker/unified/{server-prod.env,server-staging.env,mongo-prod.env,mongo-staging.env} docs/hosting/checkmate-so/` + `mv docker/unified/mongo docs/hosting/checkmate-so/mongo` +4. **Check the Mongo data version before first start.** The old + `checkmate:mongo-*` images were built `FROM mongo` (floating latest), so the + data files follow whatever version was current at their last image build. Run + `db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })` + against the old container first and make the compose's `mongo:` tag match + (checked 2026-07-09: both stacks report FCV 8.2, hence the `mongo:8.2` pin + here). A lower pin than the data's FCV will refuse to start; a higher one + should step up one release at a time. +5. `cd docs/hosting/checkmate-so && docker compose up -d`. diff --git a/docker/prod/certbot-compose.yaml b/docs/hosting/checkmate-so/certbot-compose.yaml similarity index 100% rename from docker/prod/certbot-compose.yaml rename to docs/hosting/checkmate-so/certbot-compose.yaml diff --git a/docs/hosting/checkmate-so/docker-compose.yaml b/docs/hosting/checkmate-so/docker-compose.yaml new file mode 100644 index 000000000..96849fecb --- /dev/null +++ b/docs/hosting/checkmate-so/docker-compose.yaml @@ -0,0 +1,122 @@ +# BlueWave's hosted demo + staging instances: one nginx gateway terminating TLS +# in front of two independent Checkmate stacks, both running the mono image +# (ghcr.io/bluewave-labs/checkmate). Doubles as a worked example of putting a +# TLS proxy in front of the image. +# +# Deployed by .github/workflows/build-image.yml (deploy-staging / deploy-demo +# jobs). Env files (server-prod.env, server-staging.env, mongo-prod.env, +# mongo-staging.env) live on the host next to this file and are not committed. + +services: + # Gateway (single entry point) + gateway: + image: nginx:1.27.1-alpine + container_name: checkmate-gateway + restart: always + ports: + - "80:80" + - "443:443" + volumes: + - /etc/letsencrypt:/etc/nginx/ssl:ro + - ./gateway/nginx.conf:/etc/nginx/conf.d/default.conf:ro + + # Prod: demo.checkmate.so ----- + checkmate-demo: + image: ghcr.io/bluewave-labs/checkmate:master + container_name: checkmate-demo + restart: always + env_file: + - server-prod.env + stop_grace_period: 60s + healthcheck: + test: + [ + "CMD", + "node", + "-e", + "require('http').get('http://localhost:52346/livez',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))", + ] + interval: 15s + timeout: 3s + start_period: 60s + start_interval: 2s + retries: 3 + depends_on: + mongodb-prod: + condition: service_healthy + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + + mongodb-prod: + image: mongo:8.2 + container_name: mongodb-prod + restart: always + command: ["mongod", "--quiet", "--bind_ip_all"] + volumes: + - ./mongo/data-prod:/data/db + env_file: + - mongo-prod.env + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] + interval: 5s + timeout: 30s + start_period: 0s + start_interval: 1s + retries: 30 + + # Staging: staging.checkmate.so ----- + checkmate-staging: + image: ghcr.io/bluewave-labs/checkmate:develop + container_name: checkmate-staging + restart: always + env_file: + - server-staging.env + stop_grace_period: 60s + healthcheck: + test: + [ + "CMD", + "node", + "-e", + "require('http').get('http://localhost:52346/livez',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))", + ] + interval: 15s + timeout: 3s + start_period: 60s + start_interval: 2s + retries: 3 + depends_on: + mongodb-staging: + condition: service_healthy + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + + mongodb-staging: + image: mongo:8.2 + container_name: mongodb-staging + restart: always + command: ["mongod", "--quiet", "--bind_ip_all"] + volumes: + - ./mongo/data-staging:/data/db + env_file: + - mongo-staging.env + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] + interval: 5s + timeout: 30s + start_period: 0s + start_interval: 1s + retries: 30 + + # Shared utils + capture: + image: ghcr.io/bluewave-labs/capture:latest + container_name: capture + restart: unless-stopped + ports: + - "59232:59232" + environment: + - API_SECRET=my_secret + - GIN_MODE=release + volumes: + - /etc/os-release:/etc/os-release:ro diff --git a/docker/unified/gateway/nginx.conf b/docs/hosting/checkmate-so/gateway/nginx.conf similarity index 59% rename from docker/unified/gateway/nginx.conf rename to docs/hosting/checkmate-so/gateway/nginx.conf index 47ad4695e..3523872c5 100644 --- a/docker/unified/gateway/nginx.conf +++ b/docs/hosting/checkmate-so/gateway/nginx.conf @@ -19,7 +19,7 @@ server { } -# --- PRODUCTION: demo.checkmate.so --- +# --- DEMO: demo.checkmate.so --- server { listen 443 ssl; server_name demo.checkmate.so; @@ -31,25 +31,24 @@ server { ssl_ciphers HIGH:!aNULL:!MD5; location / { - # Passes traffic to the prod client on port 80 - proxy_pass http://client-prod:80; + # The mono image serves both the SPA and the API + proxy_pass http://checkmate-demo:52345; } } # --- STAGING: staging.checkmate.so --- +server { + listen 443 ssl; + server_name staging.checkmate.so; - server { - listen 443 ssl; - server_name staging.checkmate.so; + ssl_certificate /etc/nginx/ssl/live/staging.checkmate.so/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/live/staging.checkmate.so/privkey.pem; - ssl_certificate /etc/nginx/ssl/live/staging.checkmate.so/fullchain.pem; - ssl_certificate_key /etc/nginx/ssl/live/staging.checkmate.so/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - - location / { - # Passes traffic to the staging client on port 80 - proxy_pass http://client-staging:80; - } - } + location / { + # The mono image serves both the SPA and the API + proxy_pass http://checkmate-staging:52345; + } +} diff --git a/server/.github/pull_request_template.md b/server/.github/pull_request_template.md deleted file mode 100755 index 4f4da5d80..000000000 --- a/server/.github/pull_request_template.md +++ /dev/null @@ -1,25 +0,0 @@ -**(Please remove this line only before submitting your PR. Ensure that all relevant items are checked before submission.)** - -## Describe your changes - -Briefly describe the changes you made and their purpose. - -## Write your issue number after "Fixes " - -Fixes #123 - -## Please ensure all items are checked off before requesting a review. "Checked off" means you need to add an "x" character between brackets so they turn into checkmarks. - -- [ ] (Do not skip this or your PR will be closed) I deployed the application locally. -- [ ] (Do not skip this or your PR will be closed) I have performed a self-review and testing of my code. -- [ ] I have included the issue # in the PR. -- [ ] I have added i18n support to visible strings (instead of `
Add
`, use): - -```Javascript -const { t } = useTranslation(); -
{t('add')}
-``` - -- [ ] The issue I am working on is assigned to me. -- [ ] I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application). -- [ ] My PR is granular and targeted to one specific feature. diff --git a/server/.github/workflows/staging-deploy.yml b/server/.github/workflows/staging-deploy.yml deleted file mode 100755 index 8fadf12d4..000000000 --- a/server/.github/workflows/staging-deploy.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Staging deploy - -on: - push: - branches: ["develop"] - -jobs: - docker-build-and-push: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-backend:staging \ - -f ./docker/staging/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate-backend \ - . - - - name: Push Server Docker image - run: docker push ghcr.io/bluewave-labs/checkmate-backend:staging - - - name: Build Mongo Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-mongo:staging \ - -f ./docker/staging/mongoDB.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate-backend \ - . - - - name: Push MongoDB Docker image - run: docker push ghcr.io/bluewave-labs/checkmate-mongo:staging - - - name: Build Redis Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate-redis:staging \ - -f ./docker/staging/redis.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate-backend \ - . - - - name: Push Redis Docker image - run: docker push ghcr.io/bluewave-labs/checkmate-redis:staging - - deploy-to-staging: - needs: docker-build-and-push - runs-on: ubuntu-latest - steps: - - name: SSH into server and restart container using Docker Compose - uses: appleboy/ssh-action@v1.0.0 - with: - host: ${{ secrets.STAGING_SERVER_HOST }} - username: ${{ secrets.STAGING_SERVER_USER }} - key: ${{ secrets.STAGING_SERVER_SSH_KEY }} - script: | - cd checkmate/server/docker/staging - docker compose down - docker compose pull - docker compose up -d diff --git a/server/package-lock.json b/server/package-lock.json index ce35150c4..b2f01cb2a 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -867,7 +867,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1500,7 +1499,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -1545,7 +1543,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1561,6 +1558,29 @@ "kuler": "^2.0.0" } }, + "node_modules/@emnapi/core": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", @@ -1568,6 +1588,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -4239,7 +4260,6 @@ "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -4400,7 +4420,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -4638,7 +4657,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -5339,7 +5357,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5747,7 +5764,6 @@ "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", "dev": true, "license": "Apache-2.0", - "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -6073,7 +6089,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001800", @@ -6959,7 +6974,6 @@ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.9.tgz", "integrity": "sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==", "license": "MIT", - "peer": true, "dependencies": { "cssnano-preset-default": "^7.0.17", "lilconfig": "^3.1.3" @@ -7746,7 +7760,6 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8124,7 +8137,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -9841,7 +9853,6 @@ "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.4.2", "@jest/types": "30.4.1", @@ -13146,7 +13157,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -15239,7 +15249,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -15409,7 +15418,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -15573,7 +15581,6 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16362,7 +16369,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/server/scripts/inject-vars.sh b/server/scripts/inject-vars.sh deleted file mode 100755 index 34365b0d3..000000000 --- a/server/scripts/inject-vars.sh +++ /dev/null @@ -1,7 +0,0 @@ -for i in $(env | grep UPTIME_APP_) -do - key=$(echo $i | cut -d '=' -f 1) - value=$(echo $i | cut -d '=' -f 2-) - echo $key=$value - find ./public/assets -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|${key}|${value}|g" '{}' + -done \ No newline at end of file diff --git a/server/src/app.ts b/server/src/app.ts index 12c88d5f6..f195d8140 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -52,6 +52,13 @@ export const createApp = ({ app.use(createStatusPageDocumentCsp(allowedOrigin)); + // Client runtime config; registered before express.static so it shadows the + // fallback config.js in the client build output + const clientConfigScript = `window.__CHECKMATE_CONFIG__ = ${JSON.stringify(envSettings.clientConfig).replace(/ { + res.set("Cache-Control", "no-cache").type("application/javascript").send(clientConfigScript); + }); + app.use(express.static(frontendPath)); app.use("/api/v1/monitors/import/json", express.json({ limit: "10mb" })); diff --git a/server/src/config/envValidation.ts b/server/src/config/envValidation.ts index 05471e690..a769a28c7 100644 --- a/server/src/config/envValidation.ts +++ b/server/src/config/envValidation.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { DbTypes, QueueModes } from "@/domain/app-settings/app-settings.type.js"; +import { DbTypes, LogLevels, QueueModes } from "@/domain/app-settings/app-settings.type.js"; import { booleanCoercion } from "@/api/validation/shared.js"; import { ILogger } from "@/utils/logger.js"; @@ -9,7 +9,7 @@ const envSchema = z.object({ // Port for worker health checks HEALTH_PORT: z.string().default("52346"), NODE_ENV: z.enum(["development", "production", "test"]).default("development"), - LOG_LEVEL: z.enum(["error", "warn", "info", "debug"]).default("debug"), + LOG_LEVEL: z.enum(LogLevels).default("debug"), // Database DB_CONNECTION_STRING: z.string().min(1, "Database connection string is required"), @@ -25,6 +25,12 @@ const envSchema = z.object({ // Client Configuration CLIENT_HOST: z.string().url("CLIENT_HOST must be a valid URL"), + // Client runtime config overrides, rendered into GET /config.js; unset keys are + // omitted and the client uses its same-origin defaults + CLIENT_CONFIG_API_BASE_URL: z.string().optional(), + CLIENT_CONFIG_CLIENT_HOST: z.string().url("CLIENT_CONFIG_CLIENT_HOST must be a valid URL").optional(), + CLIENT_CONFIG_LOG_LEVEL: z.enum(LogLevels).optional(), + // Optional ORIGIN: z.string().optional(), @@ -63,6 +69,15 @@ export const validateEnv = (logger: ILogger): ValidatedEnv => { process.exit(1); } + const legacyClientVars = Object.keys(process.env).filter((key) => key.startsWith("UPTIME_APP_")); + if (legacyClientVars.length > 0) { + logger.warn({ + message: `${legacyClientVars.join(", ")} no longer configure the client and will be ignored. The client defaults to the origin it is served from; to override, use CLIENT_CONFIG_API_BASE_URL, CLIENT_CONFIG_CLIENT_HOST, or CLIENT_CONFIG_LOG_LEVEL.`, + method: "validateEnv", + service: "Server", + }); + } + logger.info({ message: "Environment variables validated successfully", method: "validateEnv", diff --git a/server/src/domain/app-settings/app-settings.service.ts b/server/src/domain/app-settings/app-settings.service.ts index 8beb65d84..92a54702e 100755 --- a/server/src/domain/app-settings/app-settings.service.ts +++ b/server/src/domain/app-settings/app-settings.service.ts @@ -1,5 +1,5 @@ import { ISettingsRepository } from "@/domain/app-settings/app-settings-repository.interface.js"; -import { Settings, SettingsUpdate, type DbType, type QueueMode } from "@/domain/app-settings/app-settings.type.js"; +import { Settings, SettingsUpdate, type ClientRuntimeConfig, type DbType, type QueueMode } from "@/domain/app-settings/app-settings.type.js"; import { AppError } from "@/utils/AppError.js"; import { ValidatedEnv } from "@/config/envValidation.js"; import type { StringValue } from "ms"; @@ -16,6 +16,7 @@ export type EnvConfig = { queueMode: QueueMode; queuePrimaryProcesses: boolean; statusPageThemesEnabled: boolean; + clientConfig: ClientRuntimeConfig; }; export interface ISettingsService { @@ -44,6 +45,11 @@ export class SettingsService implements ISettingsService { queueMode: env.QUEUE_MODE, queuePrimaryProcesses: env.QUEUE_PRIMARY_PROCESSES, statusPageThemesEnabled: env.STATUS_PAGE_THEMES_ENABLED, + clientConfig: { + ...(env.CLIENT_CONFIG_API_BASE_URL && { apiBaseUrl: env.CLIENT_CONFIG_API_BASE_URL }), + ...(env.CLIENT_CONFIG_CLIENT_HOST && { clientHost: env.CLIENT_CONFIG_CLIENT_HOST }), + ...(env.CLIENT_CONFIG_LOG_LEVEL && { logLevel: env.CLIENT_CONFIG_LOG_LEVEL }), + }, }; } diff --git a/server/src/domain/app-settings/app-settings.type.ts b/server/src/domain/app-settings/app-settings.type.ts index 8cb52e93d..8233ced63 100644 --- a/server/src/domain/app-settings/app-settings.type.ts +++ b/server/src/domain/app-settings/app-settings.type.ts @@ -4,6 +4,17 @@ export type DbType = (typeof DbTypes)[number]; export const QueueModes = ["primary", "worker"] as const; export type QueueMode = (typeof QueueModes)[number]; +export const LogLevels = ["error", "warn", "info", "debug"] as const; +export type LogLevel = (typeof LogLevels)[number]; + +// Rendered into GET /config.js as window.__CHECKMATE_CONFIG__; keys left unset +// fall back to the client's same-origin defaults. +export type ClientRuntimeConfig = { + apiBaseUrl?: string; + clientHost?: string; + logLevel?: LogLevel; +}; + export interface SettingsThresholds { cpu?: number; memory?: number; diff --git a/server/src/index.ts b/server/src/index.ts index 44536ce8b..b5e870ded 100755 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -14,7 +14,7 @@ import Logger, { ILogger } from "@/utils/logger.js"; import { SettingsService } from "@/domain/app-settings/app-settings.service.js"; import { JobScheduler } from "@/worker/worker.job-scheduler.js"; import { IJobScheduler } from "@/worker/worker.interface.js"; -import { HealthServer } from "@/worker/worker.health-server.js"; +import { HealthServer, IHealthServer } from "@/worker/worker.health-server.js"; const SERVICE_NAME = "Server"; let logger: ILogger; @@ -63,12 +63,15 @@ const startApp = async () => { // Primary node path // *********************** let scheduler: IJobScheduler; + let healthServer: IHealthServer | undefined; // *********************** //Primary node processes jobs, need full worker // *********************** if (queuePrimaryProcesses === true) { const { worker } = await buildWorker(shared, envSettings); + healthServer = new HealthServer(logger, env.HEALTH_PORT, worker); + await healthServer.listen(); scheduler = worker; } // *********************** @@ -103,7 +106,7 @@ const startApp = async () => { logger.info({ message: `Server started on port:${env.PORT}` }); }); - initShutdownListener(server, services); + initShutdownListener(server, { ...services, healthServer }); }; startApp().catch((error) => { diff --git a/server/test/unit/services/settingsService.test.ts b/server/test/unit/services/settingsService.test.ts index af58b5705..43ea7fe18 100644 --- a/server/test/unit/services/settingsService.test.ts +++ b/server/test/unit/services/settingsService.test.ts @@ -83,6 +83,19 @@ describe("SettingsService", () => { dbConnectionString: "mongodb://localhost:27017/test_db", dbType: "mongodb", statusPageThemesEnabled: false, + clientConfig: {}, + }); + }); + + it("maps only set CLIENT_CONFIG_* env vars into clientConfig", () => { + const { service } = createService({ + CLIENT_CONFIG_API_BASE_URL: "https://api.example.com/api/v1", + CLIENT_CONFIG_LOG_LEVEL: "warn", + }); + + expect(service.loadSettings().clientConfig).toEqual({ + apiBaseUrl: "https://api.example.com/api/v1", + logLevel: "warn", }); });