From 55aa9f546a39e538645008cb7937b3a29d341974 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 13:51:33 +0200 Subject: [PATCH 01/54] pipeline upgrade --- .gitea/workflows/build.yaml | 127 ++++++++++++++++++++++++ .gitea/workflows/development-build.yaml | 66 ------------ .gitea/workflows/release.yaml | 62 ------------ 3 files changed, 127 insertions(+), 128 deletions(-) create mode 100644 .gitea/workflows/build.yaml delete mode 100644 .gitea/workflows/development-build.yaml delete mode 100644 .gitea/workflows/release.yaml diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..b748f4b --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,127 @@ +name: Gitea CI/CD + +on: + workflow_dispatch: + push: + branches: [ "development", "main", "feature/*", "bugfix/*", "enhancement/*" ] + tags: [ "v*.*.*" ] + pull_request: + branches: [ "development", "main" ] + +env: + DOTNET_VERSION: '8.0.x' + DOCKER_IMAGE_NAME: 'watcher-server' + REGISTRY_URL: 'git.triggermeelmo.com/watcher' + DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' + TAG: ${{ github.ref == 'refs/heads/main' && 'latest' || github.ref == 'refs/heads/development' && 'development' || github.ref_type == 'tag' && github.ref_name || 'pr' }} + +jobs: + build-and-test: + runs-on: ubuntu-latest + env: + RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal + continue-on-error: true + + - name: Publish + run: dotnet publish -c Release -o out + + set-tag: + name: Set Tag Name + runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.set_tag.outputs.tag_name }} + steps: + - name: Determine next semantic version tag + id: set_tag + run: | + git fetch --tags + + # Find latest tag matching vX.Y.Z + latest_tag=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n 1) + if [[ -z "$latest_tag" ]]; then + major=0 + minor=0 + patch=0 + else + version="${latest_tag#v}" + IFS='.' read -r major minor patch <<< "$version" + fi + + if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then + major=$((major + 1)) + minor=0 + patch=0 + elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then + minor=$((minor + 1)) + patch=0 + else + patch=$((patch + 1)) + fi + + new_tag="v${major}.${minor}.${patch}" + echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT + + docker-build-and-push: + runs-on: ubuntu-latest + env: + RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache + needs: build-and-test + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY_URL}} + username: ${{ secrets.AUTOMATION_USERNAME }} + password: ${{ secrets.AUTOMATION_PASSWORD }} + + - name: Build and Push Multi-Arch Docker Image + run: | + docker buildx build \ + --platform ${{ env.DOCKER_PLATFORMS }} \ + -t ${{ env.REGISTRY_URL }}/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set-tag.outputs.tag_name }} \ + --push . + + tag: + name: Create Tag for Gitea + needs: [docker-build-and-push, set-tag] + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Git user + run: | + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + - name: Create and push tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git tag ${{ needs.set-tag.outputs.tag_name }} + git push origin ${{ needs.set-tag.outputs.tag_name }} \ No newline at end of file diff --git a/.gitea/workflows/development-build.yaml b/.gitea/workflows/development-build.yaml deleted file mode 100644 index b3e7bac..0000000 --- a/.gitea/workflows/development-build.yaml +++ /dev/null @@ -1,66 +0,0 @@ -name: Development Build - -on: - push: - branches: - - development - -env: - DOTNET_VERSION: '8.0.x' - DOCKER_IMAGE_NAME: 'watcher-server' - REGISTRY_URL: 'git.triggermeelmo.com/watcher' - DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' - -jobs: - build-and-test: - runs-on: ubuntu-latest - env: - RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup .NET SDK - uses: actions/setup-dotnet@v3 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --configuration Release --no-restore - - - name: Test - run: dotnet test --no-build --verbosity normal - continue-on-error: true - - - name: Publish - run: dotnet publish -c Release -o out - - docker-build-and-push: - runs-on: ubuntu-latest - env: - RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache - needs: build-and-test - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to Gitea Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY_URL}} - username: ${{ secrets.AUTOMATION_USERNAME }} - password: ${{ secrets.AUTOMATION_PASSWORD }} - - - name: Build and Push Multi-Arch Docker Image - run: | - docker buildx build \ - --platform ${{ env.DOCKER_PLATFORMS }} \ - -t ${{ env.REGISTRY_URL }}/${{ env.DOCKER_IMAGE_NAME }}:development \ - -t ${{ env.REGISTRY_URL }}/${{ env.DOCKER_IMAGE_NAME }}:${{ github.sha }} \ - --push . diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml deleted file mode 100644 index bf599b5..0000000 --- a/.gitea/workflows/release.yaml +++ /dev/null @@ -1,62 +0,0 @@ -name: Release Build and Release - -on: - push: - branches: - - main - -env: - DOTNET_VERSION: '8.0.x' - DOCKER_IMAGE_NAME: 'watcher-server' - REGISTRY_URL: 'git.triggermeelmo.com/watcher' - DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' - -jobs: - build-and-test: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup .NET SDK - uses: actions/setup-dotnet@v3 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --configuration Release --no-restore - - - name: Test - run: dotnet test --no-build --verbosity normal - continue-on-error: true - - - name: Publish - run: dotnet publish -c Release -o out - - docker-build-and-push: - runs-on: ubuntu-latest - needs: build-and-test - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to Gitea Container Registry - uses: docker/login-action@v2 - with: - registry: git.triggermeelmo.com - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and Push Multi-Arch Docker Image - run: | - docker buildx build \ - --platform ${{ env.DOCKER_PLATFORMS }} \ - -t ${{ env.REGISTRY_URL }}/${{ env.DOCKER_IMAGE_NAME }}:v0.1.0 \ - -t ${{ env.REGISTRY_URL }}/${{ env.DOCKER_IMAGE_NAME }}:${{ github.sha }} \ - --push . -- 2.49.1 From 120374ebe14f675c4e0eb4daf21ac76ec54aaae4 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 13:57:15 +0200 Subject: [PATCH 02/54] url change --- .gitea/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index b748f4b..375b965 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -11,7 +11,7 @@ on: env: DOTNET_VERSION: '8.0.x' DOCKER_IMAGE_NAME: 'watcher-server' - REGISTRY_URL: 'git.triggermeelmo.com/watcher' + REGISTRY_URL: 'git.triggermeelmo.com' DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' TAG: ${{ github.ref == 'refs/heads/main' && 'latest' || github.ref == 'refs/heads/development' && 'development' || github.ref_type == 'tag' && github.ref_name || 'pr' }} @@ -101,7 +101,7 @@ jobs: run: | docker buildx build \ --platform ${{ env.DOCKER_PLATFORMS }} \ - -t ${{ env.REGISTRY_URL }}/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set-tag.outputs.tag_name }} \ + -t ${{ env.REGISTRY_URL }}/watcher/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set-tag.outputs.tag_name }} \ --push . tag: -- 2.49.1 From 8ffb220634b4d77d897c7577aeee1624c4cd6c12 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 14:00:22 +0200 Subject: [PATCH 03/54] url changes --- .gitea/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 375b965..ef1136e 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -10,8 +10,8 @@ on: env: DOTNET_VERSION: '8.0.x' - DOCKER_IMAGE_NAME: 'watcher-server' - REGISTRY_URL: 'git.triggermeelmo.com' + DOCKER_IMAGE_NAME: watcher-server + REGISTRY_URL: git.triggermeelmo.com DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' TAG: ${{ github.ref == 'refs/heads/main' && 'latest' || github.ref == 'refs/heads/development' && 'development' || github.ref_type == 'tag' && github.ref_name || 'pr' }} @@ -82,7 +82,7 @@ jobs: runs-on: ubuntu-latest env: RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache - needs: build-and-test + needs: [build-and-test, set-tag] steps: - name: Checkout code uses: actions/checkout@v3 @@ -106,7 +106,7 @@ jobs: tag: name: Create Tag for Gitea - needs: [docker-build-and-push, set-tag] + needs: [docker-build-and-push] if: github.ref == 'refs/heads/main' && github.event_name == 'push' runs-on: ubuntu-latest steps: -- 2.49.1 From 284175287006db65cd11c600173c717ada55ba53 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 14:02:33 +0200 Subject: [PATCH 04/54] chatgpt fix --- .gitea/workflows/build.yaml | 67 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index ef1136e..8e73e09 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -43,40 +43,45 @@ jobs: run: dotnet publish -c Release -o out set-tag: - name: Set Tag Name - runs-on: ubuntu-latest - outputs: - tag_name: ${{ steps.set_tag.outputs.tag_name }} - steps: - - name: Determine next semantic version tag - id: set_tag - run: | - git fetch --tags + name: Set Tag Name + runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.set_tag.outputs.tag_name }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # important: get full history and tags - # Find latest tag matching vX.Y.Z - latest_tag=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n 1) - if [[ -z "$latest_tag" ]]; then - major=0 - minor=0 - patch=0 - else - version="${latest_tag#v}" - IFS='.' read -r major minor patch <<< "$version" - fi + - name: Determine next semantic version tag + id: set_tag + run: | + git fetch --tags - if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then - major=$((major + 1)) - minor=0 - patch=0 - elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then - minor=$((minor + 1)) - patch=0 - else - patch=$((patch + 1)) - fi + # Find latest tag matching vX.Y.Z + latest_tag=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n 1) + if [[ -z "$latest_tag" ]]; then + major=0 + minor=0 + patch=0 + else + version="${latest_tag#v}" + IFS='.' read -r major minor patch <<< "$version" + fi - new_tag="v${major}.${minor}.${patch}" - echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT + if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then + major=$((major + 1)) + minor=0 + patch=0 + elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then + minor=$((minor + 1)) + patch=0 + else + patch=$((patch + 1)) + fi + + new_tag="v${major}.${minor}.${patch}" + echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT docker-build-and-push: runs-on: ubuntu-latest -- 2.49.1 From 015cdcb2025de842777c9118f1e09389262713ff Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 14:03:24 +0200 Subject: [PATCH 05/54] syntax error fix --- .gitea/workflows/build.yaml | 70 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 8e73e09..cb3992c 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -43,45 +43,45 @@ jobs: run: dotnet publish -c Release -o out set-tag: - name: Set Tag Name - runs-on: ubuntu-latest - outputs: - tag_name: ${{ steps.set_tag.outputs.tag_name }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # important: get full history and tags + name: Set Tag Name + runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.set_tag.outputs.tag_name }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # important: get full history and tags - - name: Determine next semantic version tag - id: set_tag - run: | - git fetch --tags + - name: Determine next semantic version tag + id: set_tag + run: | + git fetch --tags - # Find latest tag matching vX.Y.Z - latest_tag=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n 1) - if [[ -z "$latest_tag" ]]; then - major=0 - minor=0 - patch=0 - else - version="${latest_tag#v}" - IFS='.' read -r major minor patch <<< "$version" - fi + # Find latest tag matching vX.Y.Z + latest_tag=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n 1) + if [[ -z "$latest_tag" ]]; then + major=0 + minor=0 + patch=0 + else + version="${latest_tag#v}" + IFS='.' read -r major minor patch <<< "$version" + fi - if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then - major=$((major + 1)) - minor=0 - patch=0 - elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then - minor=$((minor + 1)) - patch=0 - else - patch=$((patch + 1)) - fi + if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then + major=$((major + 1)) + minor=0 + patch=0 + elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then + minor=$((minor + 1)) + patch=0 + else + patch=$((patch + 1)) + fi - new_tag="v${major}.${minor}.${patch}" - echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT + new_tag="v${major}.${minor}.${patch}" + echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT docker-build-and-push: runs-on: ubuntu-latest -- 2.49.1 From 8e0dcc34e75c8723e66fa82a936714ad20edc4fe Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 14:15:08 +0200 Subject: [PATCH 06/54] tag on every branch --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index cb3992c..013aa71 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -112,7 +112,7 @@ jobs: tag: name: Create Tag for Gitea needs: [docker-build-and-push] - if: github.ref == 'refs/heads/main' && github.event_name == 'push' + #if: github.ref == 'refs/heads/main' && github.event_name == 'push' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 -- 2.49.1 From db4587290800e76905b9a82865483a9d35341825 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 14:26:21 +0200 Subject: [PATCH 07/54] test --- .gitea/workflows/build.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 013aa71..7bad377 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -22,7 +22,7 @@ jobs: RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup .NET SDK uses: actions/setup-dotnet@v3 @@ -45,13 +45,12 @@ jobs: set-tag: name: Set Tag Name runs-on: ubuntu-latest + env: + RUNNER_TOOL_CACHE: /toolcache outputs: tag_name: ${{ steps.set_tag.outputs.tag_name }} steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # important: get full history and tags + - uses: actions/checkout@v4 - name: Determine next semantic version tag id: set_tag @@ -90,7 +89,7 @@ jobs: needs: [build-and-test, set-tag] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Docker Buildx uses: docker/setup-buildx-action@v2 @@ -110,10 +109,11 @@ jobs: --push . tag: - name: Create Tag for Gitea - needs: [docker-build-and-push] - #if: github.ref == 'refs/heads/main' && github.event_name == 'push' + name: Create Tag + needs: [docker-build, build, set-tag] runs-on: ubuntu-latest + env: + RUNNER_TOOL_CACHE: /toolcache steps: - uses: actions/checkout@v4 with: -- 2.49.1 From b3b97d4c09dc3a3ff91f9dd940fc387ac427ea4c Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 14:29:02 +0200 Subject: [PATCH 08/54] tag erst wenn push fertig --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 7bad377..08f9364 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -110,7 +110,7 @@ jobs: tag: name: Create Tag - needs: [docker-build, build, set-tag] + needs: [docker-build-and-push] runs-on: ubuntu-latest env: RUNNER_TOOL_CACHE: /toolcache -- 2.49.1 From d68022d831c2c50f9be390856f9d191ca1d2fa25 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 14:48:50 +0200 Subject: [PATCH 09/54] =?UTF-8?q?staging=20branch=20eingef=C3=BChrt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 08f9364..1604ead 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -3,10 +3,10 @@ name: Gitea CI/CD on: workflow_dispatch: push: - branches: [ "development", "main", "feature/*", "bugfix/*", "enhancement/*" ] + branches: [ "development", "main", "staging"] tags: [ "v*.*.*" ] pull_request: - branches: [ "development", "main" ] + branches: [ "development", "main", "staging" ] env: DOTNET_VERSION: '8.0.x' -- 2.49.1 From 7860d7e0f71e61fd8d0efadd158e8f9fca877e1c Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 16:37:00 +0200 Subject: [PATCH 10/54] Db Update, Endpoint eingerichtet, Ui angepasst --- Watcher/Controllers/MonitoringController.cs | 53 ++- ...20251003135555_ContainerUpdate.Designer.cs | 355 ++++++++++++++++++ .../20251003135555_ContainerUpdate.cs | 113 ++++++ .../Migrations/AppDbContextModelSnapshot.cs | 28 +- Watcher/Models/Container.cs | 19 +- Watcher/Views/Container/Overview.cshtml | 15 +- Watcher/Views/Home/_DashboardStats.cshtml | 8 +- 7 files changed, 533 insertions(+), 58 deletions(-) create mode 100644 Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs create mode 100644 Watcher/Migrations/20251003135555_ContainerUpdate.cs diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 5ab60ff..02d4b4a 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Watcher.Data; @@ -77,6 +78,13 @@ public class MetricDto public double NET_Out { get; set; } // Bytes/s } +public class ServiceDto +{ + public int ServerId { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts + public String? ContainerId { get; set; } + public String? Image { get; set; } + public String? Name { get; set; } +} [ApiController] [Route("[controller]")] @@ -128,10 +136,8 @@ public class MonitoringController : Controller _logger.LogInformation("Agent für '{server}' erfolgreich registriert.", server.Name); return Ok(); } - _logger.LogError("Kein Server für Registrierung gefunden"); return NotFound("No Matching Server found."); - } [HttpGet("server-id-by-ip")] @@ -215,6 +221,47 @@ public class MonitoringController : Controller } + // Endpoint, an dem Agents Ihre laufenden Services registrieren + public async Task ServiceDetection([FromBody] ServiceDto dto) + { + // Gültigkeit des Payloads prüfen + if (!ModelState.IsValid) + { + var errors = ModelState.Values + .SelectMany(v => v.Errors) + .Select(e => e.ErrorMessage) + .ToList(); + + _logger.LogError("Ungültiger ServiceDetection-Payload."); + return BadRequest(new { error = "Ungültiger Payload", details = errors }); + } + + // Metrics für Container, die zu ServerId gehören, löschen + + // Container, die zu ServerId gehören löschen + + // Container neu in Datenbank einlesen + try + { + Container container = new Container + { + ServerId = dto.ServerId, + ContainerId = dto.ContainerId, + Image = dto.Name, + Name = dto.Name + }; + + _context.Containers.Add(container); + await _context.SaveChangesAsync(); + } + catch (SqliteException e) + { + _logger.LogError("ServiceDiscovery failed: " + e.Message); + } + + return Ok(); + + } // Durchschnittliche Werte Berechnen [HttpGet("median")] @@ -317,6 +364,4 @@ public class MonitoringController : Controller return metric_input; } - - } \ No newline at end of file diff --git a/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs b/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs new file mode 100644 index 0000000..052eaa2 --- /dev/null +++ b/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs @@ -0,0 +1,355 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Watcher.Data; + +#nullable disable + +namespace Watcher.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20251003135555_ContainerUpdate")] + partial class ContainerUpdate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("TEXT"); + + b.Property("Image") + .HasColumnType("TEXT"); + + b.Property("ImageId") + .HasColumnType("INTEGER"); + + b.Property("IsRunning") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("TagId"); + + b.ToTable("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Tag") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("TEXT"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("ServerId"); + + b.ToTable("LogEvents"); + }); + + modelBuilder.Entity("Watcher.Models.Metric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Size") + .HasColumnType("REAL"); + + b.Property("DISK_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Usage") + .HasColumnType("REAL"); + + b.Property("GPU_Load") + .HasColumnType("REAL"); + + b.Property("GPU_Temp") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Size") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Usage") + .HasColumnType("REAL"); + + b.Property("NET_In") + .HasColumnType("REAL"); + + b.Property("NET_Out") + .HasColumnType("REAL"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Metrics"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DISK_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("DISK_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DiskSpace") + .HasColumnType("TEXT"); + + b.Property("Disk_Usage_Critical") + .HasColumnType("REAL"); + + b.Property("Disk_Usage_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("GpuType") + .HasColumnType("TEXT"); + + b.Property("IPAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsOnline") + .HasColumnType("INTEGER"); + + b.Property("IsVerified") + .HasColumnType("INTEGER"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RAM_Load_Critical") + .HasColumnType("REAL"); + + b.Property("RAM_Load_Warning") + .HasColumnType("REAL"); + + b.Property("RamSize") + .HasColumnType("REAL"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TagId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Watcher.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("IdentityProvider") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastLogin") + .HasColumnType("TEXT"); + + b.Property("OIDC_Id") + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.HasOne("Watcher.Models.Image", null) + .WithMany("Containers") + .HasForeignKey("ImageId"); + + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Containers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.HasOne("Watcher.Models.Container", "Container") + .WithMany() + .HasForeignKey("ContainerId"); + + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.Navigation("Container"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Servers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Navigation("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Navigation("Containers"); + + b.Navigation("Servers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Watcher/Migrations/20251003135555_ContainerUpdate.cs b/Watcher/Migrations/20251003135555_ContainerUpdate.cs new file mode 100644 index 0000000..8074ae7 --- /dev/null +++ b/Watcher/Migrations/20251003135555_ContainerUpdate.cs @@ -0,0 +1,113 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class ContainerUpdate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Containers_Servers_HostServerId", + table: "Containers"); + + migrationBuilder.DropIndex( + name: "IX_Containers_HostServerId", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "ExposedPort", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "Health", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "HostServerId", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "Status", + table: "Containers"); + + migrationBuilder.RenameColumn( + name: "InternalPort", + table: "Containers", + newName: "ServerId"); + + migrationBuilder.AddColumn( + name: "ContainerId", + table: "Containers", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ContainerId", + table: "Containers"); + + migrationBuilder.RenameColumn( + name: "ServerId", + table: "Containers", + newName: "InternalPort"); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "ExposedPort", + table: "Containers", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Health", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "HostServerId", + table: "Containers", + type: "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + name: "Status", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.CreateIndex( + name: "IX_Containers_HostServerId", + table: "Containers", + column: "HostServerId"); + + migrationBuilder.AddForeignKey( + name: "FK_Containers_Servers_HostServerId", + table: "Containers", + column: "HostServerId", + principalTable: "Servers", + principalColumn: "Id"); + } + } +} diff --git a/Watcher/Migrations/AppDbContextModelSnapshot.cs b/Watcher/Migrations/AppDbContextModelSnapshot.cs index 7fee30d..b642f34 100644 --- a/Watcher/Migrations/AppDbContextModelSnapshot.cs +++ b/Watcher/Migrations/AppDbContextModelSnapshot.cs @@ -23,45 +23,29 @@ namespace Watcher.Migrations .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("CreatedAt") + b.Property("ContainerId") .HasColumnType("TEXT"); - b.Property("ExposedPort") - .HasColumnType("INTEGER"); - - b.Property("Health") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("HostServerId") - .HasColumnType("INTEGER"); - b.Property("Image") .HasColumnType("TEXT"); b.Property("ImageId") .HasColumnType("INTEGER"); - b.Property("InternalPort") - .HasColumnType("INTEGER"); - b.Property("IsRunning") .HasColumnType("INTEGER"); b.Property("Name") .HasColumnType("TEXT"); - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); + b.Property("ServerId") + .HasColumnType("INTEGER"); b.Property("TagId") .HasColumnType("INTEGER"); b.HasKey("Id"); - b.HasIndex("HostServerId"); - b.HasIndex("ImageId"); b.HasIndex("TagId"); @@ -320,10 +304,6 @@ namespace Watcher.Migrations modelBuilder.Entity("Watcher.Models.Container", b => { - b.HasOne("Watcher.Models.Server", "HostServer") - .WithMany() - .HasForeignKey("HostServerId"); - b.HasOne("Watcher.Models.Image", null) .WithMany("Containers") .HasForeignKey("ImageId"); @@ -331,8 +311,6 @@ namespace Watcher.Migrations b.HasOne("Watcher.Models.Tag", null) .WithMany("Containers") .HasForeignKey("TagId"); - - b.Navigation("HostServer"); }); modelBuilder.Entity("Watcher.Models.LogEvent", b => diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index 0101b4f..2807d80 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -4,22 +4,11 @@ public class Container { public int Id { get; set; } - // Container Details - public string? Name { get; set; } + public int ServerId { get; set; } - public int ExposedPort { get; set; } - - public int InternalPort { get; set; } - - public string Status { get; set; } = string.Empty; - - public string Health { get; set; } = string.Empty; - - public string? Image { get; set; } - - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public String? ContainerId { get; set; } + public String? Image { get; set; } + public String? Name { get; set; } public Boolean IsRunning { get; set; } = true; - - public Server? HostServer { get; set; } } diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index 609b675..07cccb7 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -10,26 +10,21 @@
- - - - + - @for (int i = 0; i < 3; i++) + @foreach (Container container in Model.Containers) { - - - - - + + + } diff --git a/Watcher/Views/Home/_DashboardStats.cshtml b/Watcher/Views/Home/_DashboardStats.cshtml index dc201ff..dc011e4 100644 --- a/Watcher/Views/Home/_DashboardStats.cshtml +++ b/Watcher/Views/Home/_DashboardStats.cshtml @@ -139,12 +139,12 @@

Services

    - @foreach (var service in Model.Containers) + @foreach (var container in Model.Containers) {
  • - @service.Name - - @service.Status + @container.Name + + @container.Name
  • } -- 2.49.1 From 9a59e10b0c7a988f3ccb6ac513260882ab6e31c5 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 16:45:06 +0200 Subject: [PATCH 11/54] Endpoint Definition --- Watcher/Controllers/MonitoringController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 02d4b4a..2aa68a8 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -222,6 +222,7 @@ public class MonitoringController : Controller } // Endpoint, an dem Agents Ihre laufenden Services registrieren + [HttpPost("service-discovery")] public async Task ServiceDetection([FromBody] ServiceDto dto) { // Gültigkeit des Payloads prüfen -- 2.49.1 From 27792ff7f426cd31c5e787a520da89b74574cea2 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 17:17:28 +0200 Subject: [PATCH 12/54] Test und Log --- Tests/servicediscovery.py | 28 +++++++++++++++++++++ Watcher/Controllers/MonitoringController.cs | 2 ++ 2 files changed, 30 insertions(+) create mode 100644 Tests/servicediscovery.py diff --git a/Tests/servicediscovery.py b/Tests/servicediscovery.py new file mode 100644 index 0000000..d73ad5f --- /dev/null +++ b/Tests/servicediscovery.py @@ -0,0 +1,28 @@ +import json +import urllib.request + +url = "http://localhost:5000/monitoring/service-discovery" +payload = { +"ServerId": 1, +"ContainerId": "aaaaaaaaaaaa", +"Name": "test-Name", +"Image": "test-Image" +} + +data = json.dumps(payload).encode("utf-8") +req = urllib.request.Request( +url, +data=data, +headers={"Content-Type": "application/json"}, +method="POST" +) + +try: + with urllib.request.urlopen(req) as response: + resp_data = response.read().decode("utf-8") + print("Status Code:", response.status) + print("Response:", resp_data) +except Exception as e: + print("Fehler beim Senden der Request:", e) + + diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 2aa68a8..dc89756 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -252,6 +252,8 @@ public class MonitoringController : Controller Name = dto.Name }; + _logger.LogInformation(container.Name + " added for Host " + container.ServerId); + _context.Containers.Add(container); await _context.SaveChangesAsync(); } -- 2.49.1 From 2169b3d45fc3c9bdfc32d9809dd8c43af1e8dd67 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Fri, 3 Oct 2025 23:54:35 +0200 Subject: [PATCH 13/54] Changed Endpoint Names --- Watcher/Controllers/MonitoringController.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index dc89756..9a61003 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -12,7 +12,7 @@ using Watcher.ViewModels; namespace Watcher.Controllers; -public class RegistrationDto +public class HardwareDto { // Server Identity [Required] @@ -101,9 +101,9 @@ public class MonitoringController : Controller } - // Endpoint, an dem sich neue Agents registrieren - [HttpPost("register-agent-by-id")] - public async Task Register([FromBody] RegistrationDto dto) + // Endpoint, an den der Agent seine Hardwareinformationen schickt + [HttpPost("hardware-info")] + public async Task Register([FromBody] HardwareDto dto) { // Gültigkeit des Payloads prüfen if (!ModelState.IsValid) @@ -140,7 +140,8 @@ public class MonitoringController : Controller return NotFound("No Matching Server found."); } - [HttpGet("server-id-by-ip")] + // Endpoint, an dem sich ein Agent initial registriert + [HttpGet("register")] public async Task GetServerIdByIp([FromQuery] string IpAddress) { var server = await _context.Servers -- 2.49.1 From cb91ca3159e275520b4cd76bf23b92b2600b455c Mon Sep 17 00:00:00 2001 From: Daniel Habenicht Date: Sat, 4 Oct 2025 00:20:55 +0200 Subject: [PATCH 14/54] .gitea/workflows/build.yaml aktualisiert --- .gitea/workflows/build.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 1604ead..dc5b5d1 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -13,7 +13,6 @@ env: DOCKER_IMAGE_NAME: watcher-server REGISTRY_URL: git.triggermeelmo.com DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' - TAG: ${{ github.ref == 'refs/heads/main' && 'latest' || github.ref == 'refs/heads/development' && 'development' || github.ref_type == 'tag' && github.ref_name || 'pr' }} jobs: build-and-test: -- 2.49.1 From 7e5e295590068fc61df25186d1f2163d1f9c6301 Mon Sep 17 00:00:00 2001 From: Daniel Habenicht Date: Sat, 4 Oct 2025 00:34:47 +0200 Subject: [PATCH 15/54] .gitea/workflows/build.yaml aktualisiert --- .gitea/workflows/build.yaml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index dc5b5d1..0a15491 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -17,8 +17,6 @@ env: jobs: build-and-test: runs-on: ubuntu-latest - env: - RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache steps: - name: Checkout code uses: actions/checkout@v4 @@ -44,8 +42,6 @@ jobs: set-tag: name: Set Tag Name runs-on: ubuntu-latest - env: - RUNNER_TOOL_CACHE: /toolcache outputs: tag_name: ${{ steps.set_tag.outputs.tag_name }} steps: @@ -74,7 +70,7 @@ jobs: elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then minor=$((minor + 1)) patch=0 - else + elif [[ "${GITHUB_REF}" == "refs/heads/staging" ]]; then patch=$((patch + 1)) fi @@ -83,8 +79,6 @@ jobs: docker-build-and-push: runs-on: ubuntu-latest - env: - RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache needs: [build-and-test, set-tag] steps: - name: Checkout code @@ -111,8 +105,6 @@ jobs: name: Create Tag needs: [docker-build-and-push] runs-on: ubuntu-latest - env: - RUNNER_TOOL_CACHE: /toolcache steps: - uses: actions/checkout@v4 with: -- 2.49.1 From daed8c14620888b71db98dc2769d37f9d113fd2a Mon Sep 17 00:00:00 2001 From: Daniel Habenicht Date: Sat, 4 Oct 2025 00:52:35 +0200 Subject: [PATCH 16/54] .gitea/workflows/build.yaml aktualisiert --- .gitea/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 0a15491..be31c1a 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -5,8 +5,8 @@ on: push: branches: [ "development", "main", "staging"] tags: [ "v*.*.*" ] - pull_request: - branches: [ "development", "main", "staging" ] + #pull_request: + #branches: [ "development", "main", "staging" ] env: DOTNET_VERSION: '8.0.x' -- 2.49.1 From 12390031f9ead4ad15016bb0c6479ea9d13d6103 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 01:15:16 +0200 Subject: [PATCH 17/54] added permissions to write --- .gitea/workflows/build.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index be31c1a..de96879 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -8,6 +8,9 @@ on: #pull_request: #branches: [ "development", "main", "staging" ] +permissions: + contents: write + env: DOTNET_VERSION: '8.0.x' DOCKER_IMAGE_NAME: watcher-server -- 2.49.1 From 596baba5eff6fbcfece845b821a3fcf87373a5c3 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 01:41:06 +0200 Subject: [PATCH 18/54] added permissions for packages and moved token to actions checkout --- .gitea/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index de96879..3646c76 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -10,6 +10,7 @@ on: permissions: contents: write + packages: write env: DOTNET_VERSION: '8.0.x' @@ -112,6 +113,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - name: Set up Git user run: | @@ -119,8 +121,6 @@ jobs: git config user.email "actions@github.com" - name: Create and push tag - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git tag ${{ needs.set-tag.outputs.tag_name }} git push origin ${{ needs.set-tag.outputs.tag_name }} \ No newline at end of file -- 2.49.1 From 471767c4ed6fee518b575b366741bd2df8b0053a Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 01:43:57 +0200 Subject: [PATCH 19/54] added farther checking for existing tags --- .gitea/workflows/build.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 3646c76..dc69527 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -50,6 +50,8 @@ jobs: tag_name: ${{ steps.set_tag.outputs.tag_name }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Determine next semantic version tag id: set_tag -- 2.49.1 From 37468b678583e04cdde8b64979f74fd795ca0e21 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 01:51:56 +0200 Subject: [PATCH 20/54] added version tag output --- .gitea/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index dc69527..2cffcf6 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -81,6 +81,7 @@ jobs: fi new_tag="v${major}.${minor}.${patch}" + echo "Creating new version tag: ${new_tag}" echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT docker-build-and-push: -- 2.49.1 From ab1166566576ee2b384d3450002ad41a554d64eb Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 02:06:22 +0200 Subject: [PATCH 21/54] dani des is men push vorm pennen gehen --- .gitea/workflows/build.yaml | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 2cffcf6..babc9f9 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -118,12 +118,29 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - name: Set up Git user + - name: Check if tag already exists + id: check_tag run: | - git config user.name "GitHub Actions" - git config user.email "actions@github.com" + git fetch --tags + if git rev-parse "${{ needs.set-tag.outputs.tag_name }}" >/dev/null 2>&1; then + echo "Tag ${{ needs.set-tag.outputs.tag_name }} already exists!" + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "Tag ${{ needs.set-tag.outputs.tag_name }} does not exist" + echo "exists=false" >> $GITHUB_OUTPUT + fi - name: Create and push tag + if: steps.check_tag.outputs.exists == 'false' run: | + echo "Creating new tag: ${{ needs.set-tag.outputs.tag_name }}" + git config user.name "GitHub Actions" + git config user.email "actions@github.com" git tag ${{ needs.set-tag.outputs.tag_name }} - git push origin ${{ needs.set-tag.outputs.tag_name }} \ No newline at end of file + git push origin ${{ needs.set-tag.outputs.tag_name }} + echo "Tag created and pushed successfully" + + - name: Skip tag creation + if: steps.check_tag.outputs.exists == 'true' + run: | + echo "Skipping tag creation - ${{ needs.set-tag.outputs.tag_name }} already exists" \ No newline at end of file -- 2.49.1 From b7bc477d2ede32a53845568b65a522d2832a44ae Mon Sep 17 00:00:00 2001 From: Daniel Habenicht Date: Sat, 4 Oct 2025 13:02:11 +0200 Subject: [PATCH 22/54] .gitea/workflows/build.yaml aktualisiert --- .gitea/workflows/build.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index babc9f9..c79cc75 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -2,12 +2,13 @@ name: Gitea CI/CD on: workflow_dispatch: - push: - branches: [ "development", "main", "staging"] + #push: + #branches: [ "development", "main", "staging"] + #tags: [ "v*.*.*" ] + pull_request: + branches: [ "development", "main", "staging" ] tags: [ "v*.*.*" ] - #pull_request: - #branches: [ "development", "main", "staging" ] - + permissions: contents: write packages: write -- 2.49.1 From 9920c94a8b9c5b61ad582e48ad44e4d54b468f87 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 13:04:30 +0200 Subject: [PATCH 23/54] changed to pull request only --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index c79cc75..a437fd1 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -138,7 +138,7 @@ jobs: git config user.name "GitHub Actions" git config user.email "actions@github.com" git tag ${{ needs.set-tag.outputs.tag_name }} - git push origin ${{ needs.set-tag.outputs.tag_name }} + git push -u origin ${{ needs.set-tag.outputs.tag_name }} echo "Tag created and pushed successfully" - name: Skip tag creation -- 2.49.1 From 98754be1094799914137d027ed19c59ca5f49bca Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 13:06:43 +0200 Subject: [PATCH 24/54] a --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index a437fd1..f88ea08 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -77,7 +77,7 @@ jobs: elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then minor=$((minor + 1)) patch=0 - elif [[ "${GITHUB_REF}" == "refs/heads/staging" ]]; then + else patch=$((patch + 1)) fi -- 2.49.1 From 8771e1ee021351f394d087d5844b7b623954253a Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 13:11:18 +0200 Subject: [PATCH 25/54] b --- .gitea/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index f88ea08..86a5465 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -6,7 +6,7 @@ on: #branches: [ "development", "main", "staging"] #tags: [ "v*.*.*" ] pull_request: - branches: [ "development", "main", "staging" ] + #branches: [ "development", "main", "staging" ] tags: [ "v*.*.*" ] permissions: @@ -77,7 +77,7 @@ jobs: elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then minor=$((minor + 1)) patch=0 - else + elif [[ "${GITHUB_REF}" == "refs/heads/staging" ]]; then patch=$((patch + 1)) fi -- 2.49.1 From ba6e201adcdfe70c20fef498f1c0b56b9684c756 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 13:15:59 +0200 Subject: [PATCH 26/54] c --- .gitea/workflows/{build.yaml => build.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitea/workflows/{build.yaml => build.yml} (100%) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yml similarity index 100% rename from .gitea/workflows/build.yaml rename to .gitea/workflows/build.yml -- 2.49.1 From 285ff89cb081d3efbd600a411274d84507069060 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 13:20:58 +0200 Subject: [PATCH 27/54] d --- .gitea/workflows/build.yml | 71 +++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 86a5465..aa0df03 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -2,16 +2,9 @@ name: Gitea CI/CD on: workflow_dispatch: - #push: - #branches: [ "development", "main", "staging"] - #tags: [ "v*.*.*" ] - pull_request: - #branches: [ "development", "main", "staging" ] - tags: [ "v*.*.*" ] - -permissions: - contents: write - packages: write + push: + branches: [ "development", "main", "staging" ] + tags: [ "v*.*.*" ] env: DOTNET_VERSION: '8.0.x' @@ -19,6 +12,10 @@ env: REGISTRY_URL: git.triggermeelmo.com DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-and-test: runs-on: ubuntu-latest @@ -46,13 +43,14 @@ jobs: set-tag: name: Set Tag Name + needs: [detect-project, build] + #if: ${{ !failure() && !cancelled() && github.event_name != 'pull_request' }} runs-on: ubuntu-latest outputs: tag_name: ${{ steps.set_tag.outputs.tag_name }} + should_tag: ${{ steps.set_tag.outputs.should_tag }} steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Determine next semantic version tag id: set_tag @@ -74,17 +72,27 @@ jobs: major=$((major + 1)) minor=0 patch=0 + new_tag="v${major}.${minor}.${patch}" + echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT + echo "should_tag=true" >> $GITHUB_OUTPUT + echo "Creating new major version tag: ${new_tag}" + elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then minor=$((minor + 1)) patch=0 + new_tag="v${major}.${minor}.${patch}" + echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT + echo "should_tag=true" >> $GITHUB_OUTPUT + echo "Creating new minor version tag: ${new_tag}" + elif [[ "${GITHUB_REF}" == "refs/heads/staging" ]]; then patch=$((patch + 1)) + new_tag="v${major}.${minor}.${patch}" + echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT + echo "should_tag=true" >> $GITHUB_OUTPUT + echo "Creating new patch version tag: ${new_tag}" fi - new_tag="v${major}.${minor}.${patch}" - echo "Creating new version tag: ${new_tag}" - echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT - docker-build-and-push: runs-on: ubuntu-latest needs: [build-and-test, set-tag] @@ -111,37 +119,22 @@ jobs: tag: name: Create Tag - needs: [docker-build-and-push] + needs: [docker-build, build, set-tag] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - name: Check if tag already exists - id: check_tag + - name: Set up Git user run: | - git fetch --tags - if git rev-parse "${{ needs.set-tag.outputs.tag_name }}" >/dev/null 2>&1; then - echo "Tag ${{ needs.set-tag.outputs.tag_name }} already exists!" - echo "exists=true" >> $GITHUB_OUTPUT - else - echo "Tag ${{ needs.set-tag.outputs.tag_name }} does not exist" - echo "exists=false" >> $GITHUB_OUTPUT - fi - - - name: Create and push tag - if: steps.check_tag.outputs.exists == 'false' - run: | - echo "Creating new tag: ${{ needs.set-tag.outputs.tag_name }}" git config user.name "GitHub Actions" git config user.email "actions@github.com" - git tag ${{ needs.set-tag.outputs.tag_name }} - git push -u origin ${{ needs.set-tag.outputs.tag_name }} - echo "Tag created and pushed successfully" - - name: Skip tag creation - if: steps.check_tag.outputs.exists == 'true' + - name: Create and push tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - echo "Skipping tag creation - ${{ needs.set-tag.outputs.tag_name }} already exists" \ No newline at end of file + echo "Creating new tag: ${{ needs.set-tag.outputs.tag_name }}" + git tag ${{ needs.set-tag.outputs.tag_name }} + git push origin ${{ needs.set-tag.outputs.tag_name }} \ No newline at end of file -- 2.49.1 From e385eb94f4663c24c42057a29bbf4dc9dcb5d5d0 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 13:26:08 +0200 Subject: [PATCH 28/54] e --- .gitea/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index aa0df03..e98344c 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -43,7 +43,7 @@ jobs: set-tag: name: Set Tag Name - needs: [detect-project, build] + needs: [build-and-test] #if: ${{ !failure() && !cancelled() && github.event_name != 'pull_request' }} runs-on: ubuntu-latest outputs: @@ -114,12 +114,12 @@ jobs: run: | docker buildx build \ --platform ${{ env.DOCKER_PLATFORMS }} \ - -t ${{ env.REGISTRY_URL }}/watcher/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set-tag.outputs.tag_name }} \ + -t ${{ env.REGISTRY_URL }}/watcher/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set_tag.outputs.tag_name }} \ --push . tag: name: Create Tag - needs: [docker-build, build, set-tag] + needs: [docker-build-and-push] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 -- 2.49.1 From b012693c21a5aa47bbeb02ec113c10aa8a501da3 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 13:29:28 +0200 Subject: [PATCH 29/54] f --- .gitea/workflows/build.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index e98344c..3883da8 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -3,8 +3,8 @@ name: Gitea CI/CD on: workflow_dispatch: push: - branches: [ "development", "main", "staging" ] - tags: [ "v*.*.*" ] + branches: [ "development", "main", "staging" ] + tags: [ "v*.*.*" ] env: DOTNET_VERSION: '8.0.x' @@ -96,6 +96,9 @@ jobs: docker-build-and-push: runs-on: ubuntu-latest needs: [build-and-test, set-tag] + if: | + needs.set-tag.outputs.should_tag == 'true' && + github.event_name != 'pull_request' steps: - name: Checkout code uses: actions/checkout@v4 -- 2.49.1 From 286f72eac747ff459b371744fa5066a1946f4ed4 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 13:32:28 +0200 Subject: [PATCH 30/54] moved watcher-server to watcher --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index e98344c..dcfb3e0 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -8,7 +8,7 @@ on: env: DOTNET_VERSION: '8.0.x' - DOCKER_IMAGE_NAME: watcher-server + DOCKER_IMAGE_NAME: watcher REGISTRY_URL: git.triggermeelmo.com DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' -- 2.49.1 From 4c6635f9891f42f5a01bd9ec28bbd9530e9600a9 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 13:40:17 +0200 Subject: [PATCH 31/54] fixed misspelling set_tag to set-tag --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 2dacc08..90470c5 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -117,7 +117,7 @@ jobs: run: | docker buildx build \ --platform ${{ env.DOCKER_PLATFORMS }} \ - -t ${{ env.REGISTRY_URL }}/watcher/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set_tag.outputs.tag_name }} \ + -t ${{ env.REGISTRY_URL }}/watcher/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set-tag.outputs.tag_name }} \ --push . tag: -- 2.49.1 From 0e9555e3f71c5e179471db39a036f4795b81edec Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 14:00:58 +0200 Subject: [PATCH 32/54] added set-tag to tag needs --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 90470c5..e58dd90 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -122,7 +122,7 @@ jobs: tag: name: Create Tag - needs: [docker-build-and-push] + needs: [docker-build-and-push, set-tag] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 -- 2.49.1 From 921b4a9664450d817762b94c0562de29634c2fb2 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 14:02:12 +0200 Subject: [PATCH 33/54] added conditions to tag --- .gitea/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index e58dd90..dce5284 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -123,6 +123,9 @@ jobs: tag: name: Create Tag needs: [docker-build-and-push, set-tag] + if: | + needs.set-tag.outputs.should_tag == 'true' && + github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 -- 2.49.1 From b9d5ade0f1e833aa324f6f946eb1dff9e658b8c9 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 14:19:06 +0200 Subject: [PATCH 34/54] l --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index dce5284..f15109d 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -8,7 +8,7 @@ on: env: DOTNET_VERSION: '8.0.x' - DOCKER_IMAGE_NAME: watcher + DOCKER_IMAGE_NAME: watcher-server REGISTRY_URL: git.triggermeelmo.com DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' -- 2.49.1 From 37de21f06bc9ff5b6f46d375a2fe8c0e739e23c8 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 14:32:04 +0200 Subject: [PATCH 35/54] testing tagging --- .gitea/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index f15109d..ad2303a 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -141,6 +141,5 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - echo "Creating new tag: ${{ needs.set-tag.outputs.tag_name }}" git tag ${{ needs.set-tag.outputs.tag_name }} git push origin ${{ needs.set-tag.outputs.tag_name }} \ No newline at end of file -- 2.49.1 From 2249d1a7769350d87aaab3f1c6e72502b6a2c2e6 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Sat, 4 Oct 2025 14:43:02 +0200 Subject: [PATCH 36/54] adde debugging to build.yml --- .gitea/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index ad2303a..f15109d 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -141,5 +141,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + echo "Creating new tag: ${{ needs.set-tag.outputs.tag_name }}" git tag ${{ needs.set-tag.outputs.tag_name }} git push origin ${{ needs.set-tag.outputs.tag_name }} \ No newline at end of file -- 2.49.1 From 8f938f999e3bfa442a634a263b7705178f18fbc9 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Sat, 4 Oct 2025 19:07:49 +0200 Subject: [PATCH 37/54] pipeline job umbenannt --- .gitea/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 90470c5..4e909f7 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -17,7 +17,7 @@ concurrency: cancel-in-progress: true jobs: - build-and-test: + dotnet-build-and-test: runs-on: ubuntu-latest steps: - name: Checkout code @@ -43,7 +43,7 @@ jobs: set-tag: name: Set Tag Name - needs: [build-and-test] + needs: [dotnet-build-and-test] #if: ${{ !failure() && !cancelled() && github.event_name != 'pull_request' }} runs-on: ubuntu-latest outputs: @@ -95,7 +95,7 @@ jobs: docker-build-and-push: runs-on: ubuntu-latest - needs: [build-and-test, set-tag] + needs: [dotnet-build-and-test, set-tag] if: | needs.set-tag.outputs.should_tag == 'true' && github.event_name != 'pull_request' -- 2.49.1 From ef187f8750c53b86c5055fc54a3a71c7f45d20ca Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Thu, 9 Oct 2025 20:27:37 +0200 Subject: [PATCH 38/54] Basic Structure of new Background Service SystemManagement --- Watcher/Program.cs | 1 + Watcher/Services/ISystemStore.cs | 9 +++++++ Watcher/Services/SystemMangement.cs | 42 +++++++++++++++++++++++++++++ Watcher/Services/SystemStore.cs | 9 +++++++ 4 files changed, 61 insertions(+) create mode 100644 Watcher/Services/ISystemStore.cs create mode 100644 Watcher/Services/SystemMangement.cs create mode 100644 Watcher/Services/SystemStore.cs diff --git a/Watcher/Program.cs b/Watcher/Program.cs index 60e3133..b41a2de 100644 --- a/Watcher/Program.cs +++ b/Watcher/Program.cs @@ -36,6 +36,7 @@ builder.Services.AddHttpContextAccessor(); // Storage Singleton builder.Services.AddSingleton(); +builder.Services.AddSingleton(); // Background Services builder.Services.AddHostedService(); diff --git a/Watcher/Services/ISystemStore.cs b/Watcher/Services/ISystemStore.cs new file mode 100644 index 0000000..1ee6d22 --- /dev/null +++ b/Watcher/Services/ISystemStore.cs @@ -0,0 +1,9 @@ +namespace Watcher.Services; + +public interface ISystemStore +{ + Boolean NewVersionAvailable { get; set; } + + Double DatabaseSize { get; set; } + +} \ No newline at end of file diff --git a/Watcher/Services/SystemMangement.cs b/Watcher/Services/SystemMangement.cs new file mode 100644 index 0000000..efd8dcc --- /dev/null +++ b/Watcher/Services/SystemMangement.cs @@ -0,0 +1,42 @@ +using System.Net.Http; + +namespace Watcher.Services; + +public class SystemManagement : BackgroundService +{ + private readonly ILogger _logger; + + private ISystemStore _SystemStore; + + public SystemManagement(ILogger logger, ISystemStore SystemStore) + { + _logger = logger; + _SystemStore = SystemStore; + + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + // Todo: Umstellen auf einmal alle 24h + var timer = new PeriodicTimer(TimeSpan.FromSeconds(30)); + + while (await timer.WaitForNextTickAsync(stoppingToken)) + { + // Hintergrundprozess abwarten + await checkForNewDockerImageVersion(); + + // 5 Sekdunden Offset + await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); + } + } + + public Task checkForNewDockerImageVersion() + { + return Task.CompletedTask; + } + + public Task createDailySqliteBackup() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Watcher/Services/SystemStore.cs b/Watcher/Services/SystemStore.cs new file mode 100644 index 0000000..34fce64 --- /dev/null +++ b/Watcher/Services/SystemStore.cs @@ -0,0 +1,9 @@ +namespace Watcher.Services; + +public class SystemStore: ISystemStore +{ + public Boolean NewVersionAvailable { get; set; } + + public Double DatabaseSize { get; set; } + +} \ No newline at end of file -- 2.49.1 From f207440ae6fc86367bb3685e48ec5b03d769b2d0 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Fri, 10 Oct 2025 18:00:41 +0200 Subject: [PATCH 39/54] added swagger for automatic API Documentation --- Watcher/Program.cs | 15 +++++++++++++++ Watcher/Watcher.csproj | 1 + 2 files changed, 16 insertions(+) diff --git a/Watcher/Program.cs b/Watcher/Program.cs index b41a2de..9522004 100644 --- a/Watcher/Program.cs +++ b/Watcher/Program.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.EntityFrameworkCore; +using Microsoft.OpenApi.Models; using Serilog; @@ -42,6 +43,11 @@ builder.Services.AddSingleton(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); +// Swagger API-Dokumentation +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new OpenApiInfo { Title = "Watcher-Server API", Version = "v1" }); +}); // ---------- Konfiguration ---------- DotNetEnv.Env.Load(); @@ -213,6 +219,8 @@ if (!app.Environment.IsDevelopment()) app.UseHttpsRedirection(); app.UseRouting(); +app.UseSwagger(); + app.UseAuthentication(); app.UseAuthorization(); @@ -223,4 +231,11 @@ app.MapControllerRoute( pattern: "{controller=Home}/{action=Index}/{id?}" ); +app.MapSwagger(); + +app.UseSwaggerUI(options => +{ + options.SwaggerEndpoint("v1/swagger.json", "Watcher-Server API v1"); +}); + app.Run(); \ No newline at end of file diff --git a/Watcher/Watcher.csproj b/Watcher/Watcher.csproj index 7eb3182..4ec814e 100644 --- a/Watcher/Watcher.csproj +++ b/Watcher/Watcher.csproj @@ -23,6 +23,7 @@ + -- 2.49.1 From 6eed3c764e7470f0fb6fd8bdd62117ba5550a3ea Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Tue, 21 Oct 2025 19:15:16 +0200 Subject: [PATCH 40/54] swagger working /api/v1/swagger --- Watcher/Program.cs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Watcher/Program.cs b/Watcher/Program.cs index 9522004..466d1f1 100644 --- a/Watcher/Program.cs +++ b/Watcher/Program.cs @@ -211,31 +211,30 @@ using (var scope = app.Services.CreateScope()) if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } - app.UseHttpsRedirection(); +app.UseStaticFiles(); + app.UseRouting(); +// 🔹 Swagger aktivieren app.UseSwagger(); +app.UseSwaggerUI(options => +{ + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Watcher-Server API v1"); + options.RoutePrefix = "api/v1/swagger"; +}); +// 🔹 Authentifizierung & Autorisierung app.UseAuthentication(); app.UseAuthorization(); -app.UseStaticFiles(); - +// 🔹 MVC-Routing app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}" - ); +); -app.MapSwagger(); - -app.UseSwaggerUI(options => -{ - options.SwaggerEndpoint("v1/swagger.json", "Watcher-Server API v1"); -}); - -app.Run(); \ No newline at end of file +app.Run(); -- 2.49.1 From c01401eb072a910293469b63c42ae4a225e2676a Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Tue, 28 Oct 2025 15:25:08 +0100 Subject: [PATCH 41/54] Logik um ServiceDiscovery Payload zu verarbeiten soweit gemacht, JSON Input wird aber nocht nicht geparsed --- Watcher/Controllers/MonitoringController.cs | 80 ++++++++++++++------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 9a61003..e5963a7 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -80,10 +80,8 @@ public class MetricDto public class ServiceDto { - public int ServerId { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts - public String? ContainerId { get; set; } - public String? Image { get; set; } - public String? Name { get; set; } + public required int Server_id { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts + public required JsonContent Containers { get; set; } } [ApiController] @@ -234,33 +232,59 @@ public class MonitoringController : Controller .Select(e => e.ErrorMessage) .ToList(); - _logger.LogError("Ungültiger ServiceDetection-Payload."); - return BadRequest(new { error = "Ungültiger Payload", details = errors }); + _logger.LogError("Invalid ServiceDetection-Payload."); + return BadRequest(new { error = "Invalid Payload", details = errors }); } - // Metrics für Container, die zu ServerId gehören, löschen + // Container Objekte erstellen + List newContainers = ParseServiceDiscoveryInput(dto.Server_id, dto.Containers); - // Container, die zu ServerId gehören löschen + // Liste aller Container, die bereits der übergebenen ServerId zugewiesen sind + List existingContainers = _context.Containers + .Where(c => c.ServerId == dto.Server_id) + .ToList(); - // Container neu in Datenbank einlesen - try + + // Logik, um Container, die mit dem Payload kamen zu verarbeiten + foreach (Container c in newContainers) { - Container container = new Container + // Überprüfen, ob ein übergebener Container bereits für den Host registriert ist + if (existingContainers.Contains(c)) { - ServerId = dto.ServerId, - ContainerId = dto.ContainerId, - Image = dto.Name, - Name = dto.Name - }; - - _logger.LogInformation(container.Name + " added for Host " + container.ServerId); - - _context.Containers.Add(container); - await _context.SaveChangesAsync(); + _logger.LogInformation("Container with id " + c.ContainerId + " already exists."); + } + // Container auf einen Host/Server registrieren + else + { + // Container in Datenbank einlesen + try + { + _context.Containers.Add(c); + await _context.SaveChangesAsync(); + _logger.LogInformation(c.Name + " added for Host " + c.ServerId); + } + catch (SqliteException e) + { + _logger.LogError("Error writing new Containers to Database: " + e.Message); + } + } } - catch (SqliteException e) + + // Logik um abgeschaltene Container aus der Datenbank zu entfernen + foreach (Container c in existingContainers) { - _logger.LogError("ServiceDiscovery failed: " + e.Message); + // Abfrage, ob bereits vorhandener Container im Payload vorhanden war + if (!newContainers.Contains(c)) + { + // Container entfernen + _context.Containers.Remove(c); + await _context.SaveChangesAsync(); + + // Metrics für den Container entfernen + //Todo + + _logger.LogInformation("Container " + c.Name + " (" + c.Id + ") on Host-Id " + c.ServerId + " was successfully removed from the database."); + } } return Ok(); @@ -268,7 +292,6 @@ public class MonitoringController : Controller } // Durchschnittliche Werte Berechnen - [HttpGet("median")] public async Task CalculateMedian(string Metric, int HoursToMonitor, int ServerId) { // Aktuelle Zeit - X Stunden = letzter Wert, der berücksichtigt werden soll @@ -368,4 +391,13 @@ public class MonitoringController : Controller return metric_input; } + private List ParseServiceDiscoveryInput(int server_id, JsonContent containers) + { + List containerList = new List(); + + // JSON-Objekt auslesen und Container-Objekte erstellen + + + return containerList; + } } \ No newline at end of file -- 2.49.1 From 7c85e338fa349da2a6e8e431f6a04cb5c45336bb Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Wed, 29 Oct 2025 07:57:13 +0100 Subject: [PATCH 42/54] Json Parsing --- Watcher/Controllers/MonitoringController.cs | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index e5963a7..0f4e2a3 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -236,8 +237,26 @@ public class MonitoringController : Controller return BadRequest(new { error = "Invalid Payload", details = errors }); } + // Json zu was brauchbarem machen + string containersJson = await dto.Containers.ReadAsStringAsync(); + List newContainers = JsonSerializer.Deserialize>(containersJson)?? new List();; + + foreach (Container c in newContainers) + { + c.ServerId = dto.Server_id; + // Debug Logs + // TODO entfernen wenn fertig getestet + Console.WriteLine("---------"); + Console.WriteLine("ServerId: " + c.ServerId); + Console.WriteLine("ContainerId: " + c.ContainerId); + Console.WriteLine("Name: " + c.Name); + Console.WriteLine("Image: " + c.Image); + Console.WriteLine("---------"); + + } + // Container Objekte erstellen - List newContainers = ParseServiceDiscoveryInput(dto.Server_id, dto.Containers); + //List newContainers = ParseServiceDiscoveryInput(dto.Server_id, containers); // Liste aller Container, die bereits der übergebenen ServerId zugewiesen sind List existingContainers = _context.Containers @@ -391,7 +410,7 @@ public class MonitoringController : Controller return metric_input; } - private List ParseServiceDiscoveryInput(int server_id, JsonContent containers) + private List ParseServiceDiscoveryInput(int server_id, List containers) { List containerList = new List(); -- 2.49.1 From f1131479727d942fe4bcc68c1dd7fa2e0efe5f9d Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Wed, 29 Oct 2025 08:50:16 +0100 Subject: [PATCH 43/54] renamed variables --- Watcher/Controllers/MonitoringController.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 0f4e2a3..ab3f86b 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -81,8 +81,8 @@ public class MetricDto public class ServiceDto { - public required int Server_id { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts - public required JsonContent Containers { get; set; } + public required int server_id { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts + public required JsonContent containers { get; set; } } [ApiController] @@ -238,12 +238,12 @@ public class MonitoringController : Controller } // Json zu was brauchbarem machen - string containersJson = await dto.Containers.ReadAsStringAsync(); + string containersJson = await dto.containers.ReadAsStringAsync(); List newContainers = JsonSerializer.Deserialize>(containersJson)?? new List();; foreach (Container c in newContainers) { - c.ServerId = dto.Server_id; + c.ServerId = dto.server_id; // Debug Logs // TODO entfernen wenn fertig getestet Console.WriteLine("---------"); @@ -260,7 +260,7 @@ public class MonitoringController : Controller // Liste aller Container, die bereits der übergebenen ServerId zugewiesen sind List existingContainers = _context.Containers - .Where(c => c.ServerId == dto.Server_id) + .Where(c => c.ServerId == dto.server_id) .ToList(); -- 2.49.1 From e1684d4484e29078484c8ba4b93c8be130fcb881 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Wed, 29 Oct 2025 12:11:03 +0100 Subject: [PATCH 44/54] Frontend Changes + Model angepasst --- Tests/servicediscovery.py | 10 +- Watcher/Controllers/ContainerController.cs | 2 +- ...251029105954_container-changed.Designer.cs | 355 ++++++++++++++++++ .../20251029105954_container-changed.cs | 22 ++ Watcher/Models/Container.cs | 2 + Watcher/Views/Container/Overview.cshtml | 18 +- 6 files changed, 395 insertions(+), 14 deletions(-) create mode 100644 Watcher/Migrations/20251029105954_container-changed.Designer.cs create mode 100644 Watcher/Migrations/20251029105954_container-changed.cs diff --git a/Tests/servicediscovery.py b/Tests/servicediscovery.py index d73ad5f..b7634ae 100644 --- a/Tests/servicediscovery.py +++ b/Tests/servicediscovery.py @@ -1,12 +1,12 @@ import json import urllib.request -url = "http://localhost:5000/monitoring/service-discovery" +url = "http://localhost:5000/monitoring/ServiceDetection" payload = { -"ServerId": 1, -"ContainerId": "aaaaaaaaaaaa", -"Name": "test-Name", -"Image": "test-Image" + "server_id": 2, + "containers": [ + {"ContainerId": "hr1nm432143nkj", "Name": "Name des Containers", "Image": "ghcr.io/test/container:latest"} + ] } data = json.dumps(payload).encode("utf-8") diff --git a/Watcher/Controllers/ContainerController.cs b/Watcher/Controllers/ContainerController.cs index b18e4dd..12cf4be 100644 --- a/Watcher/Controllers/ContainerController.cs +++ b/Watcher/Controllers/ContainerController.cs @@ -20,7 +20,7 @@ public class ContainerController : Controller public async Task Overview() { var containers = await _context.Containers.ToListAsync(); - var servers = await _context.Servers.ToListAsync(); + var servers = await _context.Servers.ToListAsync(); var viewModel = new ContainerOverviewViewModel { diff --git a/Watcher/Migrations/20251029105954_container-changed.Designer.cs b/Watcher/Migrations/20251029105954_container-changed.Designer.cs new file mode 100644 index 0000000..4eda82f --- /dev/null +++ b/Watcher/Migrations/20251029105954_container-changed.Designer.cs @@ -0,0 +1,355 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Watcher.Data; + +#nullable disable + +namespace Watcher.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20251029105954_container-changed")] + partial class containerchanged + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("TEXT"); + + b.Property("Image") + .HasColumnType("TEXT"); + + b.Property("ImageId") + .HasColumnType("INTEGER"); + + b.Property("IsRunning") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("TagId"); + + b.ToTable("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Tag") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("TEXT"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("ServerId"); + + b.ToTable("LogEvents"); + }); + + modelBuilder.Entity("Watcher.Models.Metric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Size") + .HasColumnType("REAL"); + + b.Property("DISK_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Usage") + .HasColumnType("REAL"); + + b.Property("GPU_Load") + .HasColumnType("REAL"); + + b.Property("GPU_Temp") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Size") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Usage") + .HasColumnType("REAL"); + + b.Property("NET_In") + .HasColumnType("REAL"); + + b.Property("NET_Out") + .HasColumnType("REAL"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Metrics"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DISK_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("DISK_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DiskSpace") + .HasColumnType("TEXT"); + + b.Property("Disk_Usage_Critical") + .HasColumnType("REAL"); + + b.Property("Disk_Usage_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("GpuType") + .HasColumnType("TEXT"); + + b.Property("IPAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsOnline") + .HasColumnType("INTEGER"); + + b.Property("IsVerified") + .HasColumnType("INTEGER"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RAM_Load_Critical") + .HasColumnType("REAL"); + + b.Property("RAM_Load_Warning") + .HasColumnType("REAL"); + + b.Property("RamSize") + .HasColumnType("REAL"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TagId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Watcher.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("IdentityProvider") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastLogin") + .HasColumnType("TEXT"); + + b.Property("OIDC_Id") + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.HasOne("Watcher.Models.Image", null) + .WithMany("Containers") + .HasForeignKey("ImageId"); + + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Containers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.HasOne("Watcher.Models.Container", "Container") + .WithMany() + .HasForeignKey("ContainerId"); + + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.Navigation("Container"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Servers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Navigation("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Navigation("Containers"); + + b.Navigation("Servers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Watcher/Migrations/20251029105954_container-changed.cs b/Watcher/Migrations/20251029105954_container-changed.cs new file mode 100644 index 0000000..6f880a4 --- /dev/null +++ b/Watcher/Migrations/20251029105954_container-changed.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class containerchanged : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index 2807d80..a0a51a4 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -10,5 +10,7 @@ public class Container public String? Image { get; set; } public String? Name { get; set; } + // keine Variable, die vom Agent übergeben wird. Ein container ist immer Running, die Variable dient nur für die Übersicht + // auf dem Dashboard. public Boolean IsRunning { get; set; } = true; } diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index 07cccb7..e3edfc3 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -12,20 +12,22 @@
Service HostServer StatusSSL/TLSProxy Deployment
testtestOnlinetruetrue@container.Name@container.ServerIdnicht automatisiert Docker
- - - - - + + + + + @foreach (Container container in Model.Containers) { + - - - + + + + }
ServiceHostServerStatusDeploymentContainer-IDNameImageHost-IDAktionen
@container.ContainerId @container.Name@container.ServerIdnicht automatisiertDocker@container.Image@container.ServerIdnicht verfügbar
-- 2.49.1 From f1287314f414e23a10df1e43fe05c90c61939dd1 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Wed, 29 Oct 2025 20:51:04 +0100 Subject: [PATCH 45/54] Parse funktioniert mit List als Eingabe --- Tests/servicediscovery.py | 9 +++--- Watcher/Controllers/MonitoringController.cs | 34 +++++++++++++++++++-- Watcher/Models/Container.cs | 7 +++++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Tests/servicediscovery.py b/Tests/servicediscovery.py index b7634ae..dcf2df8 100644 --- a/Tests/servicediscovery.py +++ b/Tests/servicediscovery.py @@ -1,12 +1,11 @@ import json import urllib.request -url = "http://localhost:5000/monitoring/ServiceDetection" +url = "http://localhost:5000/monitoring/service-discovery" payload = { - "server_id": 2, - "containers": [ - {"ContainerId": "hr1nm432143nkj", "Name": "Name des Containers", "Image": "ghcr.io/test/container:latest"} - ] + "server_id": 7, + "containers": ["{\"id\":\"6621c5b67c25\",\"image\":\"git.triggermeelmo.com/donpat1to/watcher-agent:v0.1.26\",\"name\":\"watcher-agent\"}", + "{\"id\":\"b8c86fb260bd\",\"image\":\"git.triggermeelmo.com/watcher/watcher-server:v0.1.10\",\"name\":\"watcher\"}"] } data = json.dumps(payload).encode("utf-8") diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index ab3f86b..ddb6481 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -1,9 +1,12 @@ using System.ComponentModel.DataAnnotations; +using System.Net.Http.Json; using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; @@ -82,7 +85,7 @@ public class MetricDto public class ServiceDto { public required int server_id { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts - public required JsonContent containers { get; set; } + public required List containers { get; set; } } [ApiController] @@ -238,8 +241,33 @@ public class MonitoringController : Controller } // Json zu was brauchbarem machen - string containersJson = await dto.containers.ReadAsStringAsync(); - List newContainers = JsonSerializer.Deserialize>(containersJson)?? new List();; + //string containersJson = await dto.containers.ReadAsStringAsync(); + //List newContainers = JsonSerializer.Deserialize>(containersJson)?? new List();; + + List newContainers = new List(); + + foreach (String s in dto.containers) + { + // JSON zu einem JsonDocument parsen + using JsonDocument doc = JsonDocument.Parse(s); + JsonElement root = doc.RootElement; + + // Einzelne Werte extrahieren + string id = root.GetProperty("id").GetString() ?? ""; + string image = root.GetProperty("image").GetString() ?? ""; + string name = root.GetProperty("name").GetString() ?? ""; + + Container newContainer = new Container + { + ServerId = dto.server_id, + ContainerId = id, + Image = image, + Name = name, + }; + + + newContainers.Add(newContainer); + } foreach (Container c in newContainers) { diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index a0a51a4..8dc084f 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -1,13 +1,20 @@ +using System.Text.Json.Serialization; + namespace Watcher.Models; public class Container { + [JsonPropertyName("id")] public int Id { get; set; } public int ServerId { get; set; } public String? ContainerId { get; set; } + + [JsonPropertyName("image")] public String? Image { get; set; } + + [JsonPropertyName("name")] public String? Name { get; set; } // keine Variable, die vom Agent übergeben wird. Ein container ist immer Running, die Variable dient nur für die Übersicht -- 2.49.1 From 84750838971624f4e8bdf83250c0bcbf51496af4 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Wed, 29 Oct 2025 20:52:16 +0100 Subject: [PATCH 46/54] ServiceMetrics angefangen --- Watcher/Controllers/ContainerController.cs | 20 +- Watcher/Controllers/MonitoringController.cs | 48 ++- Watcher/Data/AppDbContext.cs | 2 + ...9125404_ContainerMetrics-Added.Designer.cs | 355 ++++++++++++++++++ .../20251029125404_ContainerMetrics-Added.cs | 22 ++ Watcher/Models/ContainerMetric.cs | 24 ++ 6 files changed, 460 insertions(+), 11 deletions(-) create mode 100644 Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs create mode 100644 Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs create mode 100644 Watcher/Models/ContainerMetric.cs diff --git a/Watcher/Controllers/ContainerController.cs b/Watcher/Controllers/ContainerController.cs index 12cf4be..f3f5c63 100644 --- a/Watcher/Controllers/ContainerController.cs +++ b/Watcher/Controllers/ContainerController.cs @@ -18,17 +18,17 @@ public class ContainerController : Controller } public async Task Overview() -{ - var containers = await _context.Containers.ToListAsync(); - var servers = await _context.Servers.ToListAsync(); - - var viewModel = new ContainerOverviewViewModel { - Servers = servers, - Containers = containers - }; + var containers = await _context.Containers.ToListAsync(); + var servers = await _context.Servers.ToListAsync(); - return View(viewModel); -} + var viewModel = new ContainerOverviewViewModel + { + Servers = servers, + Containers = containers + }; + + return View(viewModel); + } } diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index ab3f86b..ad7ef3f 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Net; using System.Text.Json; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -85,6 +86,12 @@ public class ServiceDto public required JsonContent containers { get; set; } } +public class ServiceMetricDto +{ + public required JsonContent metrics { get; set; } // Array an Json-Objekten mit den Daten aller Container + +} + [ApiController] [Route("[controller]")] public class MonitoringController : Controller @@ -239,7 +246,7 @@ public class MonitoringController : Controller // Json zu was brauchbarem machen string containersJson = await dto.containers.ReadAsStringAsync(); - List newContainers = JsonSerializer.Deserialize>(containersJson)?? new List();; + List newContainers = JsonSerializer.Deserialize>(containersJson) ?? new List(); ; foreach (Container c in newContainers) { @@ -310,6 +317,45 @@ public class MonitoringController : Controller } + // Endpoint, an den der Agent die Metrics der registrierten Container schickt + public async Task ServiceMetrics([FromBody] ServiceMetricDto dto) + { + // Gültigkeit des Payloads prüfen + if (!ModelState.IsValid) + { + var errors = ModelState.Values + .SelectMany(v => v.Errors) + .Select(e => e.ErrorMessage) + .ToList(); + + _logger.LogError("Invalid ServiceDetection-Payload."); + return BadRequest(new { error = "Invalid Payload", details = errors }); + } + + // Liste an Metrics aus der dto erstellen + List metrics = new List(); + + // Metrics in die Datenbank eintragen + try + { + foreach (ContainerMetric m in metrics) + { + _context.ContainerMetrics.Add(m); + await _context.SaveChangesAsync(); + // _logger.LogInformation(m. + " added for Host " + c.ServerId); + return Ok(); + } + } + catch (SqliteException e) + { + _logger.LogError(e.Message); + return StatusCode(500); + } + + return Ok(); + + } + // Durchschnittliche Werte Berechnen public async Task CalculateMedian(string Metric, int HoursToMonitor, int ServerId) { diff --git a/Watcher/Data/AppDbContext.cs b/Watcher/Data/AppDbContext.cs index c88df1b..1affd63 100644 --- a/Watcher/Data/AppDbContext.cs +++ b/Watcher/Data/AppDbContext.cs @@ -26,6 +26,8 @@ public class AppDbContext : DbContext public DbSet Tags { get; set; } + public DbSet ContainerMetrics { get; set; } + public DbSet Users { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) diff --git a/Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs b/Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs new file mode 100644 index 0000000..73b5076 --- /dev/null +++ b/Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs @@ -0,0 +1,355 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Watcher.Data; + +#nullable disable + +namespace Watcher.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20251029125404_ContainerMetrics-Added")] + partial class ContainerMetricsAdded + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("TEXT"); + + b.Property("Image") + .HasColumnType("TEXT"); + + b.Property("ImageId") + .HasColumnType("INTEGER"); + + b.Property("IsRunning") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("TagId"); + + b.ToTable("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Tag") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("TEXT"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("ServerId"); + + b.ToTable("LogEvents"); + }); + + modelBuilder.Entity("Watcher.Models.Metric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Size") + .HasColumnType("REAL"); + + b.Property("DISK_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Usage") + .HasColumnType("REAL"); + + b.Property("GPU_Load") + .HasColumnType("REAL"); + + b.Property("GPU_Temp") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Size") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Usage") + .HasColumnType("REAL"); + + b.Property("NET_In") + .HasColumnType("REAL"); + + b.Property("NET_Out") + .HasColumnType("REAL"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Metrics"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DISK_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("DISK_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DiskSpace") + .HasColumnType("TEXT"); + + b.Property("Disk_Usage_Critical") + .HasColumnType("REAL"); + + b.Property("Disk_Usage_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("GpuType") + .HasColumnType("TEXT"); + + b.Property("IPAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsOnline") + .HasColumnType("INTEGER"); + + b.Property("IsVerified") + .HasColumnType("INTEGER"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RAM_Load_Critical") + .HasColumnType("REAL"); + + b.Property("RAM_Load_Warning") + .HasColumnType("REAL"); + + b.Property("RamSize") + .HasColumnType("REAL"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TagId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Watcher.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("IdentityProvider") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastLogin") + .HasColumnType("TEXT"); + + b.Property("OIDC_Id") + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.HasOne("Watcher.Models.Image", null) + .WithMany("Containers") + .HasForeignKey("ImageId"); + + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Containers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.HasOne("Watcher.Models.Container", "Container") + .WithMany() + .HasForeignKey("ContainerId"); + + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.Navigation("Container"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Servers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Navigation("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Navigation("Containers"); + + b.Navigation("Servers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs b/Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs new file mode 100644 index 0000000..c084fda --- /dev/null +++ b/Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class ContainerMetricsAdded : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Watcher/Models/ContainerMetric.cs b/Watcher/Models/ContainerMetric.cs new file mode 100644 index 0000000..5c95d99 --- /dev/null +++ b/Watcher/Models/ContainerMetric.cs @@ -0,0 +1,24 @@ +namespace Watcher.Models; + +public class ContainerMetric +{ + // Metric Metadata + public int Id { get; set; } + public DateTime Timestamp { get; set; } + + + // Zuordnung zu einem Container -- Foreign Key + public int? ContainerId { get; set; } + + + // CPU-Daten + public double CPU_Load { get; set; } = 0.0; // % + + public double CPU_Temp { get; set; } = 0.0; // deg C + + // RAM-Daten + public double RAM_Size { get; set; } = 0.0; // GB + + public double RAM_Load { get; set; } = 0.0; // % + +} -- 2.49.1 From df1c4ca4b449b29a7b205c0fdc87ec701c5f299c Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Wed, 29 Oct 2025 20:57:57 +0100 Subject: [PATCH 47/54] Merge Errors fixed --- Watcher/Controllers/MonitoringController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index e956ed0..e8208a7 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -85,7 +85,7 @@ public class MetricDto public class ServiceDto { public required int server_id { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts - public required List containers { get; set; } + public required List containers { get; set; } } [ApiController] @@ -339,7 +339,7 @@ public class MonitoringController : Controller } // Endpoint, an den der Agent die Metrics der registrierten Container schickt - public async Task ServiceMetrics([FromBody] ServiceMetricDto dto) + /*public async Task ServiceMetrics([FromBody] ServiceMetricDto dto) { // Gültigkeit des Payloads prüfen if (!ModelState.IsValid) @@ -375,7 +375,7 @@ public class MonitoringController : Controller return Ok(); - } + }*/ // Durchschnittliche Werte Berechnen public async Task CalculateMedian(string Metric, int HoursToMonitor, int ServerId) -- 2.49.1 From 42a40e8b2e1ef998c1183fd9c81872a57e265ab6 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Thu, 30 Oct 2025 08:33:35 +0100 Subject: [PATCH 48/54] Fixed Json Parser --- Tests/servicediscovery.py | 36 +++++++++++-- Watcher/Controllers/MonitoringController.cs | 59 +++++++-------------- Watcher/Models/Container.cs | 4 +- 3 files changed, 53 insertions(+), 46 deletions(-) diff --git a/Tests/servicediscovery.py b/Tests/servicediscovery.py index dcf2df8..c114350 100644 --- a/Tests/servicediscovery.py +++ b/Tests/servicediscovery.py @@ -3,9 +3,39 @@ import urllib.request url = "http://localhost:5000/monitoring/service-discovery" payload = { - "server_id": 7, - "containers": ["{\"id\":\"6621c5b67c25\",\"image\":\"git.triggermeelmo.com/donpat1to/watcher-agent:v0.1.26\",\"name\":\"watcher-agent\"}", - "{\"id\":\"b8c86fb260bd\",\"image\":\"git.triggermeelmo.com/watcher/watcher-server:v0.1.10\",\"name\":\"watcher\"}"] + "Server_id": 2, + "Containers": [ + { + "id": "3e74abf5ce30", + "image": "hello-world:latest", + "name": "serene_nightingale" + }, + { + "id": "83cd9d461690", + "image": "postgres:latest", + "name": "distracted_feistel" + }, + { + "id": "b296c2ed1213", + "image": "postgres:latest", + "name": "mystifying_jackson" + }, + { + "id": "69568181d576", + "image": "hello-world:latest", + "name": "romantic_driscoll" + }, + { + "id": "67c37a2b1791", + "image": "hello-world:latest", + "name": "asdf" + }, + { + "id": "8f39bae1e316", + "image": "hello-world:latest", + "name": "distracted_mirzakhani" + } + ] } data = json.dumps(payload).encode("utf-8") diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index e8208a7..59f4fb9 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -82,10 +82,15 @@ public class MetricDto public double NET_Out { get; set; } // Bytes/s } -public class ServiceDto +public class DockerServiceDto { - public required int server_id { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts - public required List containers { get; set; } + public required int Server_id { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts + public required JsonElement Containers { get; set; } +} + +public class DockerServiceMetricDto +{ + } [ApiController] @@ -226,7 +231,7 @@ public class MonitoringController : Controller // Endpoint, an dem Agents Ihre laufenden Services registrieren [HttpPost("service-discovery")] - public async Task ServiceDetection([FromBody] ServiceDto dto) + public async Task ServiceDetection([FromBody] DockerServiceDto dto) { // Gültigkeit des Payloads prüfen if (!ModelState.IsValid) @@ -240,38 +245,13 @@ public class MonitoringController : Controller return BadRequest(new { error = "Invalid Payload", details = errors }); } - // Json zu was brauchbarem machen - //string containersJson = await dto.containers.ReadAsStringAsync(); - //List newContainers = JsonSerializer.Deserialize>(containersJson)?? new List();; - - List newContainers = new List(); - - foreach (String s in dto.containers) - { - // JSON zu einem JsonDocument parsen - using JsonDocument doc = JsonDocument.Parse(s); - JsonElement root = doc.RootElement; - - // Einzelne Werte extrahieren - string id = root.GetProperty("id").GetString() ?? ""; - string image = root.GetProperty("image").GetString() ?? ""; - string name = root.GetProperty("name").GetString() ?? ""; - - Container newContainer = new Container - { - ServerId = dto.server_id, - ContainerId = id, - Image = image, - Name = name, - }; - - - newContainers.Add(newContainer); - } + List newContainers = + JsonSerializer.Deserialize>(dto.Containers.GetRawText()) + ?? new List(); foreach (Container c in newContainers) { - c.ServerId = dto.server_id; + c.ServerId = dto.Server_id; // Debug Logs // TODO entfernen wenn fertig getestet Console.WriteLine("---------"); @@ -283,12 +263,9 @@ public class MonitoringController : Controller } - // Container Objekte erstellen - //List newContainers = ParseServiceDiscoveryInput(dto.Server_id, containers); - // Liste aller Container, die bereits der übergebenen ServerId zugewiesen sind List existingContainers = _context.Containers - .Where(c => c.ServerId == dto.server_id) + .Where(c => c.ServerId == dto.Server_id) .ToList(); @@ -339,7 +316,7 @@ public class MonitoringController : Controller } // Endpoint, an den der Agent die Metrics der registrierten Container schickt - /*public async Task ServiceMetrics([FromBody] ServiceMetricDto dto) + public async Task ServiceMetrics([FromBody] DockerServiceMetricDto dto) { // Gültigkeit des Payloads prüfen if (!ModelState.IsValid) @@ -374,8 +351,8 @@ public class MonitoringController : Controller } return Ok(); - - }*/ + + } // Durchschnittliche Werte Berechnen public async Task CalculateMedian(string Metric, int HoursToMonitor, int ServerId) @@ -482,7 +459,7 @@ public class MonitoringController : Controller List containerList = new List(); // JSON-Objekt auslesen und Container-Objekte erstellen - + return containerList; } diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index 8dc084f..13f1253 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -4,11 +4,11 @@ namespace Watcher.Models; public class Container { - [JsonPropertyName("id")] public int Id { get; set; } - + [JsonPropertyName("Server_id")] public int ServerId { get; set; } + [JsonPropertyName("id")] public String? ContainerId { get; set; } [JsonPropertyName("image")] -- 2.49.1 From cfb97536ca3051ffc0317f7bf9ce42942bb1b8ba Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Wed, 5 Nov 2025 21:15:10 +0100 Subject: [PATCH 49/54] KI shit haha --- .gitea/workflows/build.yml | 1 + Dockerfile | 7 + Watcher/.env.example | 25 +- Watcher/Controllers/AuthController.cs | 37 +- Watcher/Controllers/ContainerController.cs | 8 +- Watcher/Controllers/MonitoringController.cs | 1 - Watcher/Controllers/SystemController.cs | 31 +- Watcher/Controllers/UserController.cs | 2 - Watcher/Data/AppDbContext.cs | 23 +- ...0250617153602_InitialMigration.Designer.cs | 300 ------- .../20250617153602_InitialMigration.cs | 261 ------ ...0250617165126_ServerPrimaryKey.Designer.cs | 298 ------- .../20250617165126_ServerPrimaryKey.cs | 785 ------------------ ...250617174242_UserPasswordAdded.Designer.cs | 306 ------- .../20250617174242_UserPasswordAdded.cs | 40 - ...50621124832_DB-Update Issue#32.Designer.cs | 316 ------- .../20250621124832_DB-Update Issue#32.cs | 251 ------ ...te Issue#32 IsVerified-Servers.Designer.cs | 319 ------- ...7_DB-Update Issue#32 IsVerified-Servers.cs | 29 - ...0710090920_container-attribute.Designer.cs | 332 -------- .../20250710090920_container-attribute.cs | 119 --- ...3936_DiskSpace-ServerAttribute.Designer.cs | 335 -------- ...0250730113936_DiskSpace-ServerAttribute.cs | 28 - ...0730172010_MeasurementWarnings.Designer.cs | 377 --------- .../20250730172010_MeasurementWarnings.cs | 172 ---- .../20251003135555_ContainerUpdate.cs | 113 --- .../20251029105954_container-changed.cs | 22 - ...9125404_ContainerMetrics-Added.Designer.cs | 355 -------- .../20251029125404_ContainerMetrics-Added.cs | 22 - ...251105183329_InitialMigration.Designer.cs} | 53 +- .../20251105183329_InitialMigration.cs | 257 ++++++ ..._AddContainerServerNavigation.Designer.cs} | 63 +- ...1105201056_AddContainerServerNavigation.cs | 39 + .../Migrations/AppDbContextModelSnapshot.cs | 59 +- Watcher/Models/Container.cs | 4 + Watcher/Models/User.cs | 18 +- Watcher/Program.cs | 122 +-- Watcher/Services/IUpdateCheckStore.cs | 8 + Watcher/Services/IVersionService.cs | 6 + Watcher/Services/MetricCleanupService.cs | 127 +++ Watcher/Services/UpdateCheckService.cs | 159 ++++ Watcher/Services/UpdateCheckStore.cs | 8 + Watcher/Services/VersionService.cs | 19 + Watcher/Views/Container/Overview.cshtml | 5 +- Watcher/Views/Home/_DashboardStats.cshtml | 5 +- Watcher/Views/Shared/_Layout.cshtml | 18 +- Watcher/Views/System/Settings.cshtml | 8 +- Watcher/Watcher.csproj | 13 +- Watcher/appsettings.json | 16 +- docker-compose.yaml | 13 +- 50 files changed, 906 insertions(+), 5029 deletions(-) delete mode 100644 Watcher/Migrations/20250617153602_InitialMigration.Designer.cs delete mode 100644 Watcher/Migrations/20250617153602_InitialMigration.cs delete mode 100644 Watcher/Migrations/20250617165126_ServerPrimaryKey.Designer.cs delete mode 100644 Watcher/Migrations/20250617165126_ServerPrimaryKey.cs delete mode 100644 Watcher/Migrations/20250617174242_UserPasswordAdded.Designer.cs delete mode 100644 Watcher/Migrations/20250617174242_UserPasswordAdded.cs delete mode 100644 Watcher/Migrations/20250621124832_DB-Update Issue#32.Designer.cs delete mode 100644 Watcher/Migrations/20250621124832_DB-Update Issue#32.cs delete mode 100644 Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.Designer.cs delete mode 100644 Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.cs delete mode 100644 Watcher/Migrations/20250710090920_container-attribute.Designer.cs delete mode 100644 Watcher/Migrations/20250710090920_container-attribute.cs delete mode 100644 Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.Designer.cs delete mode 100644 Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.cs delete mode 100644 Watcher/Migrations/20250730172010_MeasurementWarnings.Designer.cs delete mode 100644 Watcher/Migrations/20250730172010_MeasurementWarnings.cs delete mode 100644 Watcher/Migrations/20251003135555_ContainerUpdate.cs delete mode 100644 Watcher/Migrations/20251029105954_container-changed.cs delete mode 100644 Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs delete mode 100644 Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs rename Watcher/Migrations/{20251003135555_ContainerUpdate.Designer.cs => 20251105183329_InitialMigration.Designer.cs} (88%) create mode 100644 Watcher/Migrations/20251105183329_InitialMigration.cs rename Watcher/Migrations/{20251029105954_container-changed.Designer.cs => 20251105201056_AddContainerServerNavigation.Designer.cs} (85%) create mode 100644 Watcher/Migrations/20251105201056_AddContainerServerNavigation.cs create mode 100644 Watcher/Services/IUpdateCheckStore.cs create mode 100644 Watcher/Services/IVersionService.cs create mode 100644 Watcher/Services/MetricCleanupService.cs create mode 100644 Watcher/Services/UpdateCheckService.cs create mode 100644 Watcher/Services/UpdateCheckStore.cs create mode 100644 Watcher/Services/VersionService.cs diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index ecd6e23..54ccced 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -117,6 +117,7 @@ jobs: run: | docker buildx build \ --platform ${{ env.DOCKER_PLATFORMS }} \ + --build-arg VERSION=${{ needs.set-tag.outputs.tag_name }} \ -t ${{ env.REGISTRY_URL }}/watcher/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.set-tag.outputs.tag_name }} \ --push . diff --git a/Dockerfile b/Dockerfile index a7adde3..0ef5a36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,10 @@ RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false # 2. Laufzeit-Phase: ASP.NET Core Runtime FROM mcr.microsoft.com/dotnet/aspnet:8.0 + +# Build-Argument für Version (wird zur Build-Zeit vom CI/CD gesetzt) +ARG VERSION=latest + WORKDIR /app COPY --from=build /app/publish . @@ -28,5 +32,8 @@ EXPOSE 5000 ENV ASPNETCORE_URLS=http://*:5000 ENV ASPNETCORE_ENVIRONMENT=Development +# Version als Environment Variable setzen +ENV WATCHER_VERSION=${VERSION} + # Anwendung starten ENTRYPOINT ["dotnet", "Watcher.dll"] diff --git a/Watcher/.env.example b/Watcher/.env.example index 032d787..87810d8 100644 --- a/Watcher/.env.example +++ b/Watcher/.env.example @@ -1,8 +1,19 @@ +# Application Version +# Bei lokalem Development wird "development" angezeigt, im Docker-Container die Image-Version +WATCHER_VERSION=development -# OIDC Einstellungen -AUTHENTICATION__USELOCAL=true -AUTHENTICATION__POCKETID__ENABLED=false -AUTHENTICATION__POCKETID__AUTHORITY=https://id.domain.app -AUTHENTICATION__POCKETID__CLIENTID= -AUTHENTICATION__POCKETID__CLIENTSECRET= -AUTHENTICATION__POCKETID__CALLBACKPATH=/signin-oidc +# Update Check +# Überprüft täglich, ob eine neue Version verfügbar ist +UPDATE_CHECK_ENABLED=true +UPDATE_CHECK_INTERVAL_HOURS=24 +UPDATE_CHECK_REPOSITORY_URL=https://git.triggermeelmo.com/api/v1/repos/Watcher/watcher/releases/latest + +# Data Retention Policy +# Wie lange sollen Metriken gespeichert werden (in Tagen)? +METRIC_RETENTION_DAYS=30 + +# Wie oft soll der Cleanup-Prozess laufen (in Stunden)? +METRIC_CLEANUP_INTERVAL_HOURS=24 + +# Soll der Cleanup-Service aktiviert sein? +METRIC_CLEANUP_ENABLED=true diff --git a/Watcher/Controllers/AuthController.cs b/Watcher/Controllers/AuthController.cs index b24110d..464a7c7 100644 --- a/Watcher/Controllers/AuthController.cs +++ b/Watcher/Controllers/AuthController.cs @@ -1,35 +1,20 @@ -using System.Net.Mail; using System.Security.Claims; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.Options; using Watcher.Data; using Watcher.ViewModels; namespace Watcher.Controllers; -public class AppSettings -{ - public Boolean oidc { get; set; } -} - public class AuthController : Controller { private readonly AppDbContext _context; - private readonly AppSettings _settings; - - // Logging einbinden private readonly ILogger _logger; - - public AuthController(AppDbContext context, IOptions options, ILogger logger) + public AuthController(AppDbContext context, ILogger logger) { _context = context; - _settings = options.Value; _logger = logger; } @@ -43,7 +28,6 @@ public class AuthController : Controller ReturnUrl = returnUrl }; - ViewBag.oidc = _settings.oidc; return View(model); } @@ -88,32 +72,15 @@ public class AuthController : Controller } - // Login mit OIDC-Provider - public IActionResult SignIn() - { - return Challenge(new AuthenticationProperties - { - RedirectUri = "/Home/Index" - }, "oidc"); - } - - - // Logout [HttpPost] [ValidateAntiForgeryToken] public async Task Logout() { - var props = new AuthenticationProperties - { - RedirectUri = Url.Action("Login", "Auth") - }; - await HttpContext.SignOutAsync("Cookies"); - await HttpContext.SignOutAsync("oidc", props); _logger.LogInformation("User abgemeldet"); - return Redirect("/"); // nur als Fallback + return RedirectToAction("Login", "Auth"); } } diff --git a/Watcher/Controllers/ContainerController.cs b/Watcher/Controllers/ContainerController.cs index f3f5c63..5b3ee5b 100644 --- a/Watcher/Controllers/ContainerController.cs +++ b/Watcher/Controllers/ContainerController.cs @@ -19,7 +19,13 @@ public class ContainerController : Controller public async Task Overview() { - var containers = await _context.Containers.ToListAsync(); + // Container mit Server-Informationen laden und nach Server sortieren + var containers = await _context.Containers + .Include(c => c.Server) + .OrderBy(c => c.Server.Name) + .ThenBy(c => c.Name) + .ToListAsync(); + var servers = await _context.Servers.ToListAsync(); var viewModel = new ContainerOverviewViewModel diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 59f4fb9..e7a2a22 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; using Watcher.Data; using Watcher.Models; using Watcher.ViewModels; diff --git a/Watcher/Controllers/SystemController.cs b/Watcher/Controllers/SystemController.cs index e39ff2c..0190055 100644 --- a/Watcher/Controllers/SystemController.cs +++ b/Watcher/Controllers/SystemController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Watcher.Data; +using Watcher.Services; using Watcher.ViewModels; namespace Watcher.Controllers; @@ -11,11 +12,13 @@ public class SystemController : Controller { private readonly AppDbContext _context; private readonly ILogger _logger; + private readonly IVersionService _versionService; - public SystemController(AppDbContext context, ILogger logger) + public SystemController(AppDbContext context, ILogger logger, IVersionService versionService) { _context = context; _logger = logger; + _versionService = versionService; } // Edit-Form anzeigen @@ -23,10 +26,32 @@ public class SystemController : Controller //public async Task Settings() public IActionResult Settings() { - ViewBag.DbProvider = "Microsoft.EntityFrameworkCore.Sqlite"; + ViewBag.DbProvider = "SQLite"; ViewBag.mail = "test@mail.com"; ViewBag.IdentityProvider = "Local"; - ViewBag.ServerVersion = "v0.1.0"; + ViewBag.ServerVersion = _versionService.GetVersion(); + + // Datenbankgröße ermitteln + try + { + var dbPath = "./persistence/watcher.db"; + if (System.IO.File.Exists(dbPath)) + { + var fileInfo = new System.IO.FileInfo(dbPath); + var sizeInMiB = fileInfo.Length / (1024.0 * 1024.0); + ViewBag.DatabaseSize = $"{sizeInMiB:F2} MiB"; + } + else + { + ViewBag.DatabaseSize = "Nicht gefunden"; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Fehler beim Ermitteln der Datenbankgröße."); + ViewBag.DatabaseSize = "Fehler beim Laden"; + } + return View(); } diff --git a/Watcher/Controllers/UserController.cs b/Watcher/Controllers/UserController.cs index b227618..b4ef71e 100644 --- a/Watcher/Controllers/UserController.cs +++ b/Watcher/Controllers/UserController.cs @@ -33,14 +33,12 @@ public class UserController : Controller var username = user.Username; var mail = user.Email; var Id = user.Id; - var IdProvider = user.IdentityProvider; // Anzeigedaten an View übergeben ViewBag.Claims = claims; ViewBag.Name = username; ViewBag.Mail = mail; ViewBag.Id = Id; - ViewBag.IdProvider = IdProvider; return View(); diff --git a/Watcher/Data/AppDbContext.cs b/Watcher/Data/AppDbContext.cs index 1affd63..36030f1 100644 --- a/Watcher/Data/AppDbContext.cs +++ b/Watcher/Data/AppDbContext.cs @@ -34,24 +34,11 @@ public class AppDbContext : DbContext { if (!optionsBuilder.IsConfigured) { - var provider = _configuration["Database:Provider"]; - - if (provider == "MySql") - { - var connStr = _configuration.GetConnectionString("MySql") - ?? _configuration["Database:ConnectionStrings:MySql"]; - optionsBuilder.UseMySql(connStr, ServerVersion.AutoDetect(connStr)); - } - else if (provider == "Sqlite") - { - var connStr = _configuration.GetConnectionString("Sqlite") - ?? _configuration["Database:ConnectionStrings:Sqlite"]; - optionsBuilder.UseSqlite(connStr); - } - else - { - throw new Exception("Unsupported database provider configured."); - } + // Nur SQLite wird unterstützt + var connStr = _configuration.GetConnectionString("Sqlite") + ?? _configuration["Database:ConnectionStrings:Sqlite"] + ?? "Data Source=./persistence/watcher.db"; + optionsBuilder.UseSqlite(connStr); } } } diff --git a/Watcher/Migrations/20250617153602_InitialMigration.Designer.cs b/Watcher/Migrations/20250617153602_InitialMigration.Designer.cs deleted file mode 100644 index 5d549f0..0000000 --- a/Watcher/Migrations/20250617153602_InitialMigration.Designer.cs +++ /dev/null @@ -1,300 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250617153602_InitialMigration")] - partial class InitialMigration - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ImageId") - .HasColumnType("int"); - - b.Property("IsRunning") - .HasColumnType("tinyint(1)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("TagId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("Tag") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("ContainerId") - .HasColumnType("int"); - - b.Property("Level") - .HasColumnType("longtext"); - - b.Property("Message") - .HasColumnType("longtext"); - - b.Property("ServerId") - .HasColumnType("int"); - - b.Property("Timestamp") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("ContainerId") - .HasColumnType("int"); - - b.Property("ServerId") - .HasColumnType("int"); - - b.Property("Timestamp") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("longtext"); - - b.Property("Value") - .HasColumnType("double"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("CpuCores") - .HasColumnType("int"); - - b.Property("CpuType") - .HasColumnType("longtext"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .HasColumnType("longtext"); - - b.Property("GpuType") - .HasColumnType("longtext"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsOnline") - .HasColumnType("tinyint(1)"); - - b.Property("LastSeen") - .HasColumnType("datetime(6)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("RamSize") - .HasColumnType("double"); - - b.Property("TagId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Email") - .HasColumnType("longtext"); - - b.Property("LastLogin") - .HasColumnType("datetime(6)"); - - b.Property("PocketId") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("PreferredUsername") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Image", "Image") - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("Image"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250617153602_InitialMigration.cs b/Watcher/Migrations/20250617153602_InitialMigration.cs deleted file mode 100644 index e8bc8fd..0000000 --- a/Watcher/Migrations/20250617153602_InitialMigration.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class InitialMigration : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Images", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Name = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Tag = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_Images", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Tags", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Name = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_Tags", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - PocketId = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - PreferredUsername = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - LastLogin = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Containers", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Status = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ImageId = table.Column(type: "int", nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - Hostname = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Type = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsRunning = table.Column(type: "tinyint(1)", nullable: false), - TagId = table.Column(type: "int", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Containers", x => x.Id); - table.ForeignKey( - name: "FK_Containers_Images_ImageId", - column: x => x.ImageId, - principalTable: "Images", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_Containers_Tags_TagId", - column: x => x.TagId, - principalTable: "Tags", - principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Servers", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IPAddress = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - Type = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsOnline = table.Column(type: "tinyint(1)", nullable: false), - LastSeen = table.Column(type: "datetime(6)", nullable: false), - Description = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - CpuType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - CpuCores = table.Column(type: "int", nullable: false), - GpuType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - RamSize = table.Column(type: "double", nullable: false), - TagId = table.Column(type: "int", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Servers", x => x.Id); - table.ForeignKey( - name: "FK_Servers_Tags_TagId", - column: x => x.TagId, - principalTable: "Tags", - principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "LogEvents", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Timestamp = table.Column(type: "datetime(6)", nullable: false), - Message = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Level = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ServerId = table.Column(type: "int", nullable: true), - ContainerId = table.Column(type: "int", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_LogEvents", x => x.Id); - table.ForeignKey( - name: "FK_LogEvents_Containers_ContainerId", - column: x => x.ContainerId, - principalTable: "Containers", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_LogEvents_Servers_ServerId", - column: x => x.ServerId, - principalTable: "Servers", - principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Metrics", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Timestamp = table.Column(type: "datetime(6)", nullable: false), - Type = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "double", nullable: false), - ServerId = table.Column(type: "int", nullable: true), - ContainerId = table.Column(type: "int", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Metrics", x => x.Id); - table.ForeignKey( - name: "FK_Metrics_Containers_ContainerId", - column: x => x.ContainerId, - principalTable: "Containers", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_Metrics_Servers_ServerId", - column: x => x.ServerId, - principalTable: "Servers", - principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_Containers_ImageId", - table: "Containers", - column: "ImageId"); - - migrationBuilder.CreateIndex( - name: "IX_Containers_TagId", - table: "Containers", - column: "TagId"); - - migrationBuilder.CreateIndex( - name: "IX_LogEvents_ContainerId", - table: "LogEvents", - column: "ContainerId"); - - migrationBuilder.CreateIndex( - name: "IX_LogEvents_ServerId", - table: "LogEvents", - column: "ServerId"); - - migrationBuilder.CreateIndex( - name: "IX_Metrics_ContainerId", - table: "Metrics", - column: "ContainerId"); - - migrationBuilder.CreateIndex( - name: "IX_Metrics_ServerId", - table: "Metrics", - column: "ServerId"); - - migrationBuilder.CreateIndex( - name: "IX_Servers_TagId", - table: "Servers", - column: "TagId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "LogEvents"); - - migrationBuilder.DropTable( - name: "Metrics"); - - migrationBuilder.DropTable( - name: "Users"); - - migrationBuilder.DropTable( - name: "Containers"); - - migrationBuilder.DropTable( - name: "Servers"); - - migrationBuilder.DropTable( - name: "Images"); - - migrationBuilder.DropTable( - name: "Tags"); - } - } -} diff --git a/Watcher/Migrations/20250617165126_ServerPrimaryKey.Designer.cs b/Watcher/Migrations/20250617165126_ServerPrimaryKey.Designer.cs deleted file mode 100644 index 14cf440..0000000 --- a/Watcher/Migrations/20250617165126_ServerPrimaryKey.Designer.cs +++ /dev/null @@ -1,298 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250617165126_ServerPrimaryKey")] - partial class ServerPrimaryKey - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("REAL"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("PocketId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("PreferredUsername") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Image", "Image") - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("Image"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250617165126_ServerPrimaryKey.cs b/Watcher/Migrations/20250617165126_ServerPrimaryKey.cs deleted file mode 100644 index f258b61..0000000 --- a/Watcher/Migrations/20250617165126_ServerPrimaryKey.cs +++ /dev/null @@ -1,785 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class ServerPrimaryKey : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "PreferredUsername", - table: "Users", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "PocketId", - table: "Users", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "LastLogin", - table: "Users", - type: "TEXT", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "datetime(6)"); - - migrationBuilder.AlterColumn( - name: "Email", - table: "Users", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Users", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Tags", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Tags", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Type", - table: "Servers", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "TagId", - table: "Servers", - type: "INTEGER", - nullable: true, - oldClrType: typeof(int), - oldType: "int", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "RamSize", - table: "Servers", - type: "REAL", - nullable: false, - oldClrType: typeof(double), - oldType: "double"); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Servers", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "LastSeen", - table: "Servers", - type: "TEXT", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "datetime(6)"); - - migrationBuilder.AlterColumn( - name: "IsOnline", - table: "Servers", - type: "INTEGER", - nullable: false, - oldClrType: typeof(bool), - oldType: "tinyint(1)"); - - migrationBuilder.AlterColumn( - name: "IPAddress", - table: "Servers", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "GpuType", - table: "Servers", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Description", - table: "Servers", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - table: "Servers", - type: "TEXT", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "datetime(6)"); - - migrationBuilder.AlterColumn( - name: "CpuType", - table: "Servers", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CpuCores", - table: "Servers", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int"); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Servers", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Value", - table: "Metrics", - type: "REAL", - nullable: false, - oldClrType: typeof(double), - oldType: "double"); - - migrationBuilder.AlterColumn( - name: "Type", - table: "Metrics", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Timestamp", - table: "Metrics", - type: "TEXT", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "datetime(6)"); - - migrationBuilder.AlterColumn( - name: "ServerId", - table: "Metrics", - type: "INTEGER", - nullable: true, - oldClrType: typeof(int), - oldType: "int", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "ContainerId", - table: "Metrics", - type: "INTEGER", - nullable: true, - oldClrType: typeof(int), - oldType: "int", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Metrics", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Timestamp", - table: "LogEvents", - type: "TEXT", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "datetime(6)"); - - migrationBuilder.AlterColumn( - name: "ServerId", - table: "LogEvents", - type: "INTEGER", - nullable: true, - oldClrType: typeof(int), - oldType: "int", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Message", - table: "LogEvents", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Level", - table: "LogEvents", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "ContainerId", - table: "LogEvents", - type: "INTEGER", - nullable: true, - oldClrType: typeof(int), - oldType: "int", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "LogEvents", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Tag", - table: "Images", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Images", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Images", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Type", - table: "Containers", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "TagId", - table: "Containers", - type: "INTEGER", - nullable: true, - oldClrType: typeof(int), - oldType: "int", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Status", - table: "Containers", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Containers", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "IsRunning", - table: "Containers", - type: "INTEGER", - nullable: false, - oldClrType: typeof(bool), - oldType: "tinyint(1)"); - - migrationBuilder.AlterColumn( - name: "ImageId", - table: "Containers", - type: "INTEGER", - nullable: true, - oldClrType: typeof(int), - oldType: "int", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Hostname", - table: "Containers", - type: "TEXT", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - table: "Containers", - type: "TEXT", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "datetime(6)"); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Containers", - type: "INTEGER", - nullable: false, - oldClrType: typeof(int), - oldType: "int") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "PreferredUsername", - table: "Users", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "PocketId", - table: "Users", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "LastLogin", - table: "Users", - type: "datetime(6)", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "Email", - table: "Users", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Users", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Tags", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Tags", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Type", - table: "Servers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "TagId", - table: "Servers", - type: "int", - nullable: true, - oldClrType: typeof(int), - oldType: "INTEGER", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "RamSize", - table: "Servers", - type: "double", - nullable: false, - oldClrType: typeof(double), - oldType: "REAL"); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Servers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "LastSeen", - table: "Servers", - type: "datetime(6)", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "IsOnline", - table: "Servers", - type: "tinyint(1)", - nullable: false, - oldClrType: typeof(bool), - oldType: "INTEGER"); - - migrationBuilder.AlterColumn( - name: "IPAddress", - table: "Servers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "GpuType", - table: "Servers", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Description", - table: "Servers", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - table: "Servers", - type: "datetime(6)", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "CpuType", - table: "Servers", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CpuCores", - table: "Servers", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER"); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Servers", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Value", - table: "Metrics", - type: "double", - nullable: false, - oldClrType: typeof(double), - oldType: "REAL"); - - migrationBuilder.AlterColumn( - name: "Type", - table: "Metrics", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Timestamp", - table: "Metrics", - type: "datetime(6)", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "ServerId", - table: "Metrics", - type: "int", - nullable: true, - oldClrType: typeof(int), - oldType: "INTEGER", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "ContainerId", - table: "Metrics", - type: "int", - nullable: true, - oldClrType: typeof(int), - oldType: "INTEGER", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Metrics", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Timestamp", - table: "LogEvents", - type: "datetime(6)", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "ServerId", - table: "LogEvents", - type: "int", - nullable: true, - oldClrType: typeof(int), - oldType: "INTEGER", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Message", - table: "LogEvents", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Level", - table: "LogEvents", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "ContainerId", - table: "LogEvents", - type: "int", - nullable: true, - oldClrType: typeof(int), - oldType: "INTEGER", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "LogEvents", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Tag", - table: "Images", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Images", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Images", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - - migrationBuilder.AlterColumn( - name: "Type", - table: "Containers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "TagId", - table: "Containers", - type: "int", - nullable: true, - oldClrType: typeof(int), - oldType: "INTEGER", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Status", - table: "Containers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Containers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "IsRunning", - table: "Containers", - type: "tinyint(1)", - nullable: false, - oldClrType: typeof(bool), - oldType: "INTEGER"); - - migrationBuilder.AlterColumn( - name: "ImageId", - table: "Containers", - type: "int", - nullable: true, - oldClrType: typeof(int), - oldType: "INTEGER", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Hostname", - table: "Containers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - table: "Containers", - type: "datetime(6)", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "TEXT"); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Containers", - type: "int", - nullable: false, - oldClrType: typeof(int), - oldType: "INTEGER") - .Annotation("Sqlite:Autoincrement", true) - .OldAnnotation("Sqlite:Autoincrement", true); - } - } -} diff --git a/Watcher/Migrations/20250617174242_UserPasswordAdded.Designer.cs b/Watcher/Migrations/20250617174242_UserPasswordAdded.Designer.cs deleted file mode 100644 index 123e1fa..0000000 --- a/Watcher/Migrations/20250617174242_UserPasswordAdded.Designer.cs +++ /dev/null @@ -1,306 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250617174242_UserPasswordAdded")] - partial class UserPasswordAdded - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("REAL"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("PocketId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("PreferredUsername") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Image", "Image") - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("Image"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250617174242_UserPasswordAdded.cs b/Watcher/Migrations/20250617174242_UserPasswordAdded.cs deleted file mode 100644 index 04ef332..0000000 --- a/Watcher/Migrations/20250617174242_UserPasswordAdded.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class UserPasswordAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "IdentityProvider", - table: "Users", - type: "TEXT", - nullable: false, - defaultValue: ""); - - migrationBuilder.AddColumn( - name: "Password", - table: "Users", - type: "TEXT", - nullable: false, - defaultValue: ""); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "IdentityProvider", - table: "Users"); - - migrationBuilder.DropColumn( - name: "Password", - table: "Users"); - } - } -} diff --git a/Watcher/Migrations/20250621124832_DB-Update Issue#32.Designer.cs b/Watcher/Migrations/20250621124832_DB-Update Issue#32.Designer.cs deleted file mode 100644 index e8cad36..0000000 --- a/Watcher/Migrations/20250621124832_DB-Update Issue#32.Designer.cs +++ /dev/null @@ -1,316 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250621124832_DB-Update Issue#32")] - partial class DBUpdateIssue32 - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load") - .HasColumnType("REAL"); - - b.Property("CPU_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Size") - .HasColumnType("REAL"); - - b.Property("DISK_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Usage") - .HasColumnType("REAL"); - - b.Property("GPU_Load") - .HasColumnType("REAL"); - - b.Property("GPU_Temp") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Size") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Usage") - .HasColumnType("REAL"); - - b.Property("NET_In") - .HasColumnType("REAL"); - - b.Property("NET_Out") - .HasColumnType("REAL"); - - b.Property("RAM_Load") - .HasColumnType("REAL"); - - b.Property("RAM_Size") - .HasColumnType("REAL"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Image", "Image") - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("Image"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250621124832_DB-Update Issue#32.cs b/Watcher/Migrations/20250621124832_DB-Update Issue#32.cs deleted file mode 100644 index b7644a8..0000000 --- a/Watcher/Migrations/20250621124832_DB-Update Issue#32.cs +++ /dev/null @@ -1,251 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class DBUpdateIssue32 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Metrics_Containers_ContainerId", - table: "Metrics"); - - migrationBuilder.DropForeignKey( - name: "FK_Metrics_Servers_ServerId", - table: "Metrics"); - - migrationBuilder.DropIndex( - name: "IX_Metrics_ContainerId", - table: "Metrics"); - - migrationBuilder.DropIndex( - name: "IX_Metrics_ServerId", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "PocketId", - table: "Users"); - - migrationBuilder.DropColumn( - name: "ContainerId", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "Type", - table: "Metrics"); - - migrationBuilder.RenameColumn( - name: "PreferredUsername", - table: "Users", - newName: "Username"); - - migrationBuilder.RenameColumn( - name: "Value", - table: "Metrics", - newName: "RAM_Size"); - - migrationBuilder.AddColumn( - name: "OIDC_Id", - table: "Users", - type: "TEXT", - nullable: true); - - migrationBuilder.AddColumn( - name: "CPU_Load", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "CPU_Temp", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "DISK_Size", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "DISK_Temp", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "DISK_Usage", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Load", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Temp", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Vram_Size", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Vram_Usage", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "NET_In", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "NET_Out", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "RAM_Load", - table: "Metrics", - type: "REAL", - nullable: false, - defaultValue: 0.0); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "OIDC_Id", - table: "Users"); - - migrationBuilder.DropColumn( - name: "CPU_Load", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "CPU_Temp", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "DISK_Size", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "DISK_Temp", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "DISK_Usage", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "GPU_Load", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "GPU_Temp", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "GPU_Vram_Size", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "GPU_Vram_Usage", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "NET_In", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "NET_Out", - table: "Metrics"); - - migrationBuilder.DropColumn( - name: "RAM_Load", - table: "Metrics"); - - migrationBuilder.RenameColumn( - name: "Username", - table: "Users", - newName: "PreferredUsername"); - - migrationBuilder.RenameColumn( - name: "RAM_Size", - table: "Metrics", - newName: "Value"); - - migrationBuilder.AddColumn( - name: "PocketId", - table: "Users", - type: "TEXT", - nullable: false, - defaultValue: ""); - - migrationBuilder.AddColumn( - name: "ContainerId", - table: "Metrics", - type: "INTEGER", - nullable: true); - - migrationBuilder.AddColumn( - name: "Type", - table: "Metrics", - type: "TEXT", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_Metrics_ContainerId", - table: "Metrics", - column: "ContainerId"); - - migrationBuilder.CreateIndex( - name: "IX_Metrics_ServerId", - table: "Metrics", - column: "ServerId"); - - migrationBuilder.AddForeignKey( - name: "FK_Metrics_Containers_ContainerId", - table: "Metrics", - column: "ContainerId", - principalTable: "Containers", - principalColumn: "Id"); - - migrationBuilder.AddForeignKey( - name: "FK_Metrics_Servers_ServerId", - table: "Metrics", - column: "ServerId", - principalTable: "Servers", - principalColumn: "Id"); - } - } -} diff --git a/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.Designer.cs b/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.Designer.cs deleted file mode 100644 index feb5c2e..0000000 --- a/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.Designer.cs +++ /dev/null @@ -1,319 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250621125157_DB-Update Issue#32 IsVerified-Servers")] - partial class DBUpdateIssue32IsVerifiedServers - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load") - .HasColumnType("REAL"); - - b.Property("CPU_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Size") - .HasColumnType("REAL"); - - b.Property("DISK_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Usage") - .HasColumnType("REAL"); - - b.Property("GPU_Load") - .HasColumnType("REAL"); - - b.Property("GPU_Temp") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Size") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Usage") - .HasColumnType("REAL"); - - b.Property("NET_In") - .HasColumnType("REAL"); - - b.Property("NET_Out") - .HasColumnType("REAL"); - - b.Property("RAM_Load") - .HasColumnType("REAL"); - - b.Property("RAM_Size") - .HasColumnType("REAL"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("IsVerified") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Image", "Image") - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("Image"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.cs b/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.cs deleted file mode 100644 index 974f389..0000000 --- a/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class DBUpdateIssue32IsVerifiedServers : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "IsVerified", - table: "Servers", - type: "INTEGER", - nullable: false, - defaultValue: false); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "IsVerified", - table: "Servers"); - } - } -} diff --git a/Watcher/Migrations/20250710090920_container-attribute.Designer.cs b/Watcher/Migrations/20250710090920_container-attribute.Designer.cs deleted file mode 100644 index deb4c2c..0000000 --- a/Watcher/Migrations/20250710090920_container-attribute.Designer.cs +++ /dev/null @@ -1,332 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250710090920_container-attribute")] - partial class containerattribute - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("ExposedPort") - .HasColumnType("INTEGER"); - - b.Property("Health") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("HostServerId") - .HasColumnType("INTEGER"); - - b.Property("Image") - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("InternalPort") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("HostServerId"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load") - .HasColumnType("REAL"); - - b.Property("CPU_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Size") - .HasColumnType("REAL"); - - b.Property("DISK_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Usage") - .HasColumnType("REAL"); - - b.Property("GPU_Load") - .HasColumnType("REAL"); - - b.Property("GPU_Temp") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Size") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Usage") - .HasColumnType("REAL"); - - b.Property("NET_In") - .HasColumnType("REAL"); - - b.Property("NET_Out") - .HasColumnType("REAL"); - - b.Property("RAM_Load") - .HasColumnType("REAL"); - - b.Property("RAM_Size") - .HasColumnType("REAL"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("IsVerified") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Server", "HostServer") - .WithMany() - .HasForeignKey("HostServerId"); - - b.HasOne("Watcher.Models.Image", null) - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("HostServer"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250710090920_container-attribute.cs b/Watcher/Migrations/20250710090920_container-attribute.cs deleted file mode 100644 index 7af4972..0000000 --- a/Watcher/Migrations/20250710090920_container-attribute.cs +++ /dev/null @@ -1,119 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class containerattribute : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Hostname", - table: "Containers"); - - migrationBuilder.RenameColumn( - name: "Type", - table: "Containers", - newName: "Health"); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Containers", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT"); - - migrationBuilder.AddColumn( - name: "ExposedPort", - table: "Containers", - type: "INTEGER", - nullable: false, - defaultValue: 0); - - migrationBuilder.AddColumn( - name: "HostServerId", - table: "Containers", - type: "INTEGER", - nullable: true); - - migrationBuilder.AddColumn( - name: "Image", - table: "Containers", - type: "TEXT", - nullable: true); - - migrationBuilder.AddColumn( - name: "InternalPort", - table: "Containers", - type: "INTEGER", - nullable: false, - defaultValue: 0); - - migrationBuilder.CreateIndex( - name: "IX_Containers_HostServerId", - table: "Containers", - column: "HostServerId"); - - migrationBuilder.AddForeignKey( - name: "FK_Containers_Servers_HostServerId", - table: "Containers", - column: "HostServerId", - principalTable: "Servers", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Containers_Servers_HostServerId", - table: "Containers"); - - migrationBuilder.DropIndex( - name: "IX_Containers_HostServerId", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "ExposedPort", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "HostServerId", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "Image", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "InternalPort", - table: "Containers"); - - migrationBuilder.RenameColumn( - name: "Health", - table: "Containers", - newName: "Type"); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Containers", - type: "TEXT", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - - migrationBuilder.AddColumn( - name: "Hostname", - table: "Containers", - type: "TEXT", - nullable: false, - defaultValue: ""); - } - } -} diff --git a/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.Designer.cs b/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.Designer.cs deleted file mode 100644 index d33eb83..0000000 --- a/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.Designer.cs +++ /dev/null @@ -1,335 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250730113936_DiskSpace-ServerAttribute")] - partial class DiskSpaceServerAttribute - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("ExposedPort") - .HasColumnType("INTEGER"); - - b.Property("Health") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("HostServerId") - .HasColumnType("INTEGER"); - - b.Property("Image") - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("InternalPort") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("HostServerId"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load") - .HasColumnType("REAL"); - - b.Property("CPU_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Size") - .HasColumnType("REAL"); - - b.Property("DISK_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Usage") - .HasColumnType("REAL"); - - b.Property("GPU_Load") - .HasColumnType("REAL"); - - b.Property("GPU_Temp") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Size") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Usage") - .HasColumnType("REAL"); - - b.Property("NET_In") - .HasColumnType("REAL"); - - b.Property("NET_Out") - .HasColumnType("REAL"); - - b.Property("RAM_Load") - .HasColumnType("REAL"); - - b.Property("RAM_Size") - .HasColumnType("REAL"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("DiskSpace") - .HasColumnType("TEXT"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("IsVerified") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Server", "HostServer") - .WithMany() - .HasForeignKey("HostServerId"); - - b.HasOne("Watcher.Models.Image", null) - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("HostServer"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.cs b/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.cs deleted file mode 100644 index a8fbd9f..0000000 --- a/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class DiskSpaceServerAttribute : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "DiskSpace", - table: "Servers", - type: "TEXT", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "DiskSpace", - table: "Servers"); - } - } -} diff --git a/Watcher/Migrations/20250730172010_MeasurementWarnings.Designer.cs b/Watcher/Migrations/20250730172010_MeasurementWarnings.Designer.cs deleted file mode 100644 index 25baf51..0000000 --- a/Watcher/Migrations/20250730172010_MeasurementWarnings.Designer.cs +++ /dev/null @@ -1,377 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250730172010_MeasurementWarnings")] - partial class MeasurementWarnings - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("ExposedPort") - .HasColumnType("INTEGER"); - - b.Property("Health") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("HostServerId") - .HasColumnType("INTEGER"); - - b.Property("Image") - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("InternalPort") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("HostServerId"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load") - .HasColumnType("REAL"); - - b.Property("CPU_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Size") - .HasColumnType("REAL"); - - b.Property("DISK_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Usage") - .HasColumnType("REAL"); - - b.Property("GPU_Load") - .HasColumnType("REAL"); - - b.Property("GPU_Temp") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Size") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Usage") - .HasColumnType("REAL"); - - b.Property("NET_In") - .HasColumnType("REAL"); - - b.Property("NET_Out") - .HasColumnType("REAL"); - - b.Property("RAM_Load") - .HasColumnType("REAL"); - - b.Property("RAM_Size") - .HasColumnType("REAL"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load_Critical") - .HasColumnType("REAL"); - - b.Property("CPU_Load_Warning") - .HasColumnType("REAL"); - - b.Property("CPU_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("CPU_Temp_Warning") - .HasColumnType("REAL"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("DISK_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("DISK_Temp_Warning") - .HasColumnType("REAL"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("DiskSpace") - .HasColumnType("TEXT"); - - b.Property("Disk_Usage_Critical") - .HasColumnType("REAL"); - - b.Property("Disk_Usage_Warning") - .HasColumnType("REAL"); - - b.Property("GPU_Load_Critical") - .HasColumnType("REAL"); - - b.Property("GPU_Load_Warning") - .HasColumnType("REAL"); - - b.Property("GPU_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("GPU_Temp_Warning") - .HasColumnType("REAL"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("IsVerified") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RAM_Load_Critical") - .HasColumnType("REAL"); - - b.Property("RAM_Load_Warning") - .HasColumnType("REAL"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Server", "HostServer") - .WithMany() - .HasForeignKey("HostServerId"); - - b.HasOne("Watcher.Models.Image", null) - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - - b.Navigation("HostServer"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250730172010_MeasurementWarnings.cs b/Watcher/Migrations/20250730172010_MeasurementWarnings.cs deleted file mode 100644 index f13e5c7..0000000 --- a/Watcher/Migrations/20250730172010_MeasurementWarnings.cs +++ /dev/null @@ -1,172 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class MeasurementWarnings : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "CPU_Load_Critical", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "CPU_Load_Warning", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "CPU_Temp_Critical", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "CPU_Temp_Warning", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "DISK_Temp_Critical", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "DISK_Temp_Warning", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "Disk_Usage_Critical", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "Disk_Usage_Warning", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Load_Critical", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Load_Warning", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Temp_Critical", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "GPU_Temp_Warning", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "RAM_Load_Critical", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "RAM_Load_Warning", - table: "Servers", - type: "REAL", - nullable: false, - defaultValue: 0.0); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "CPU_Load_Critical", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "CPU_Load_Warning", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "CPU_Temp_Critical", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "CPU_Temp_Warning", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "DISK_Temp_Critical", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "DISK_Temp_Warning", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "Disk_Usage_Critical", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "Disk_Usage_Warning", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "GPU_Load_Critical", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "GPU_Load_Warning", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "GPU_Temp_Critical", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "GPU_Temp_Warning", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "RAM_Load_Critical", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "RAM_Load_Warning", - table: "Servers"); - } - } -} diff --git a/Watcher/Migrations/20251003135555_ContainerUpdate.cs b/Watcher/Migrations/20251003135555_ContainerUpdate.cs deleted file mode 100644 index 8074ae7..0000000 --- a/Watcher/Migrations/20251003135555_ContainerUpdate.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class ContainerUpdate : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Containers_Servers_HostServerId", - table: "Containers"); - - migrationBuilder.DropIndex( - name: "IX_Containers_HostServerId", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "CreatedAt", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "ExposedPort", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "Health", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "HostServerId", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "Status", - table: "Containers"); - - migrationBuilder.RenameColumn( - name: "InternalPort", - table: "Containers", - newName: "ServerId"); - - migrationBuilder.AddColumn( - name: "ContainerId", - table: "Containers", - type: "TEXT", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "ContainerId", - table: "Containers"); - - migrationBuilder.RenameColumn( - name: "ServerId", - table: "Containers", - newName: "InternalPort"); - - migrationBuilder.AddColumn( - name: "CreatedAt", - table: "Containers", - type: "TEXT", - nullable: false, - defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); - - migrationBuilder.AddColumn( - name: "ExposedPort", - table: "Containers", - type: "INTEGER", - nullable: false, - defaultValue: 0); - - migrationBuilder.AddColumn( - name: "Health", - table: "Containers", - type: "TEXT", - nullable: false, - defaultValue: ""); - - migrationBuilder.AddColumn( - name: "HostServerId", - table: "Containers", - type: "INTEGER", - nullable: true); - - migrationBuilder.AddColumn( - name: "Status", - table: "Containers", - type: "TEXT", - nullable: false, - defaultValue: ""); - - migrationBuilder.CreateIndex( - name: "IX_Containers_HostServerId", - table: "Containers", - column: "HostServerId"); - - migrationBuilder.AddForeignKey( - name: "FK_Containers_Servers_HostServerId", - table: "Containers", - column: "HostServerId", - principalTable: "Servers", - principalColumn: "Id"); - } - } -} diff --git a/Watcher/Migrations/20251029105954_container-changed.cs b/Watcher/Migrations/20251029105954_container-changed.cs deleted file mode 100644 index 6f880a4..0000000 --- a/Watcher/Migrations/20251029105954_container-changed.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class containerchanged : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs b/Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs deleted file mode 100644 index 73b5076..0000000 --- a/Watcher/Migrations/20251029125404_ContainerMetrics-Added.Designer.cs +++ /dev/null @@ -1,355 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Watcher.Data; - -#nullable disable - -namespace Watcher.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20251029125404_ContainerMetrics-Added")] - partial class ContainerMetricsAdded - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("TEXT"); - - b.Property("Image") - .HasColumnType("TEXT"); - - b.Property("ImageId") - .HasColumnType("INTEGER"); - - b.Property("IsRunning") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ImageId"); - - b.HasIndex("TagId"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Tag") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Images"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ContainerId") - .HasColumnType("INTEGER"); - - b.Property("Level") - .HasColumnType("TEXT"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - - b.ToTable("LogEvents"); - }); - - modelBuilder.Entity("Watcher.Models.Metric", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load") - .HasColumnType("REAL"); - - b.Property("CPU_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Size") - .HasColumnType("REAL"); - - b.Property("DISK_Temp") - .HasColumnType("REAL"); - - b.Property("DISK_Usage") - .HasColumnType("REAL"); - - b.Property("GPU_Load") - .HasColumnType("REAL"); - - b.Property("GPU_Temp") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Size") - .HasColumnType("REAL"); - - b.Property("GPU_Vram_Usage") - .HasColumnType("REAL"); - - b.Property("NET_In") - .HasColumnType("REAL"); - - b.Property("NET_Out") - .HasColumnType("REAL"); - - b.Property("RAM_Load") - .HasColumnType("REAL"); - - b.Property("RAM_Size") - .HasColumnType("REAL"); - - b.Property("ServerId") - .HasColumnType("INTEGER"); - - b.Property("Timestamp") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Metrics"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CPU_Load_Critical") - .HasColumnType("REAL"); - - b.Property("CPU_Load_Warning") - .HasColumnType("REAL"); - - b.Property("CPU_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("CPU_Temp_Warning") - .HasColumnType("REAL"); - - b.Property("CpuCores") - .HasColumnType("INTEGER"); - - b.Property("CpuType") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("DISK_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("DISK_Temp_Warning") - .HasColumnType("REAL"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("DiskSpace") - .HasColumnType("TEXT"); - - b.Property("Disk_Usage_Critical") - .HasColumnType("REAL"); - - b.Property("Disk_Usage_Warning") - .HasColumnType("REAL"); - - b.Property("GPU_Load_Critical") - .HasColumnType("REAL"); - - b.Property("GPU_Load_Warning") - .HasColumnType("REAL"); - - b.Property("GPU_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("GPU_Temp_Warning") - .HasColumnType("REAL"); - - b.Property("GpuType") - .HasColumnType("TEXT"); - - b.Property("IPAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IsOnline") - .HasColumnType("INTEGER"); - - b.Property("IsVerified") - .HasColumnType("INTEGER"); - - b.Property("LastSeen") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RAM_Load_Critical") - .HasColumnType("REAL"); - - b.Property("RAM_Load_Warning") - .HasColumnType("REAL"); - - b.Property("RamSize") - .HasColumnType("REAL"); - - b.Property("TagId") - .HasColumnType("INTEGER"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("TagId"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Watcher.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastLogin") - .HasColumnType("TEXT"); - - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Watcher.Models.Container", b => - { - b.HasOne("Watcher.Models.Image", null) - .WithMany("Containers") - .HasForeignKey("ImageId"); - - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Containers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.LogEvent", b => - { - b.HasOne("Watcher.Models.Container", "Container") - .WithMany() - .HasForeignKey("ContainerId"); - - b.HasOne("Watcher.Models.Server", "Server") - .WithMany() - .HasForeignKey("ServerId"); - - b.Navigation("Container"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.HasOne("Watcher.Models.Tag", null) - .WithMany("Servers") - .HasForeignKey("TagId"); - }); - - modelBuilder.Entity("Watcher.Models.Image", b => - { - b.Navigation("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Tag", b => - { - b.Navigation("Containers"); - - b.Navigation("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs b/Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs deleted file mode 100644 index c084fda..0000000 --- a/Watcher/Migrations/20251029125404_ContainerMetrics-Added.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class ContainerMetricsAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs b/Watcher/Migrations/20251105183329_InitialMigration.Designer.cs similarity index 88% rename from Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs rename to Watcher/Migrations/20251105183329_InitialMigration.Designer.cs index 052eaa2..539298f 100644 --- a/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs +++ b/Watcher/Migrations/20251105183329_InitialMigration.Designer.cs @@ -11,8 +11,8 @@ using Watcher.Data; namespace Watcher.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20251003135555_ContainerUpdate")] - partial class ContainerUpdate + [Migration("20251105183329_InitialMigration")] + partial class InitialMigration { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -27,10 +27,12 @@ namespace Watcher.Migrations .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "id"); b.Property("Image") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "image"); b.Property("ImageId") .HasColumnType("INTEGER"); @@ -39,10 +41,12 @@ namespace Watcher.Migrations .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "name"); b.Property("ServerId") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "Server_id"); b.Property("TagId") .HasColumnType("INTEGER"); @@ -56,6 +60,35 @@ namespace Watcher.Migrations b.ToTable("Containers"); }); + modelBuilder.Entity("Watcher.Models.ContainerMetric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ContainerMetrics"); + }); + modelBuilder.Entity("Watcher.Models.Image", b => { b.Property("Id") @@ -282,22 +315,16 @@ namespace Watcher.Migrations b.Property("Email") .HasColumnType("TEXT"); - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - b.Property("LastLogin") .HasColumnType("TEXT"); - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - b.Property("Password") .IsRequired() .HasColumnType("TEXT"); b.Property("Username") .IsRequired() + .HasMaxLength(50) .HasColumnType("TEXT"); b.HasKey("Id"); diff --git a/Watcher/Migrations/20251105183329_InitialMigration.cs b/Watcher/Migrations/20251105183329_InitialMigration.cs new file mode 100644 index 0000000..253bf68 --- /dev/null +++ b/Watcher/Migrations/20251105183329_InitialMigration.cs @@ -0,0 +1,257 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ContainerMetrics", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Timestamp = table.Column(type: "TEXT", nullable: false), + ContainerId = table.Column(type: "INTEGER", nullable: true), + CPU_Load = table.Column(type: "REAL", nullable: false), + CPU_Temp = table.Column(type: "REAL", nullable: false), + RAM_Size = table.Column(type: "REAL", nullable: false), + RAM_Load = table.Column(type: "REAL", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ContainerMetrics", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Images", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: true), + Tag = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Images", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Metrics", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Timestamp = table.Column(type: "TEXT", nullable: false), + ServerId = table.Column(type: "INTEGER", nullable: true), + CPU_Load = table.Column(type: "REAL", nullable: false), + CPU_Temp = table.Column(type: "REAL", nullable: false), + GPU_Load = table.Column(type: "REAL", nullable: false), + GPU_Temp = table.Column(type: "REAL", nullable: false), + GPU_Vram_Size = table.Column(type: "REAL", nullable: false), + GPU_Vram_Usage = table.Column(type: "REAL", nullable: false), + RAM_Size = table.Column(type: "REAL", nullable: false), + RAM_Load = table.Column(type: "REAL", nullable: false), + DISK_Size = table.Column(type: "REAL", nullable: false), + DISK_Usage = table.Column(type: "REAL", nullable: false), + DISK_Temp = table.Column(type: "REAL", nullable: false), + NET_In = table.Column(type: "REAL", nullable: false), + NET_Out = table.Column(type: "REAL", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Metrics", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Tags", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tags", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Username = table.Column(type: "TEXT", maxLength: 50, nullable: false), + Email = table.Column(type: "TEXT", nullable: true), + LastLogin = table.Column(type: "TEXT", nullable: false), + Password = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Containers", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ServerId = table.Column(type: "INTEGER", nullable: false), + ContainerId = table.Column(type: "TEXT", nullable: true), + Image = table.Column(type: "TEXT", nullable: true), + Name = table.Column(type: "TEXT", nullable: true), + IsRunning = table.Column(type: "INTEGER", nullable: false), + ImageId = table.Column(type: "INTEGER", nullable: true), + TagId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Containers", x => x.Id); + table.ForeignKey( + name: "FK_Containers_Images_ImageId", + column: x => x.ImageId, + principalTable: "Images", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Containers_Tags_TagId", + column: x => x.TagId, + principalTable: "Tags", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Servers", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + IPAddress = table.Column(type: "TEXT", nullable: false), + Type = table.Column(type: "TEXT", nullable: false), + Description = table.Column(type: "TEXT", nullable: true), + CpuType = table.Column(type: "TEXT", nullable: true), + CpuCores = table.Column(type: "INTEGER", nullable: false), + GpuType = table.Column(type: "TEXT", nullable: true), + RamSize = table.Column(type: "REAL", nullable: false), + DiskSpace = table.Column(type: "TEXT", nullable: true), + CPU_Load_Warning = table.Column(type: "REAL", nullable: false), + CPU_Load_Critical = table.Column(type: "REAL", nullable: false), + CPU_Temp_Warning = table.Column(type: "REAL", nullable: false), + CPU_Temp_Critical = table.Column(type: "REAL", nullable: false), + RAM_Load_Warning = table.Column(type: "REAL", nullable: false), + RAM_Load_Critical = table.Column(type: "REAL", nullable: false), + GPU_Load_Warning = table.Column(type: "REAL", nullable: false), + GPU_Load_Critical = table.Column(type: "REAL", nullable: false), + GPU_Temp_Warning = table.Column(type: "REAL", nullable: false), + GPU_Temp_Critical = table.Column(type: "REAL", nullable: false), + Disk_Usage_Warning = table.Column(type: "REAL", nullable: false), + Disk_Usage_Critical = table.Column(type: "REAL", nullable: false), + DISK_Temp_Warning = table.Column(type: "REAL", nullable: false), + DISK_Temp_Critical = table.Column(type: "REAL", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + IsOnline = table.Column(type: "INTEGER", nullable: false), + LastSeen = table.Column(type: "TEXT", nullable: false), + IsVerified = table.Column(type: "INTEGER", nullable: false), + TagId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Servers", x => x.Id); + table.ForeignKey( + name: "FK_Servers_Tags_TagId", + column: x => x.TagId, + principalTable: "Tags", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "LogEvents", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Timestamp = table.Column(type: "TEXT", nullable: false), + Message = table.Column(type: "TEXT", nullable: true), + Level = table.Column(type: "TEXT", nullable: true), + ServerId = table.Column(type: "INTEGER", nullable: true), + ContainerId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LogEvents", x => x.Id); + table.ForeignKey( + name: "FK_LogEvents_Containers_ContainerId", + column: x => x.ContainerId, + principalTable: "Containers", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_LogEvents_Servers_ServerId", + column: x => x.ServerId, + principalTable: "Servers", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Containers_ImageId", + table: "Containers", + column: "ImageId"); + + migrationBuilder.CreateIndex( + name: "IX_Containers_TagId", + table: "Containers", + column: "TagId"); + + migrationBuilder.CreateIndex( + name: "IX_LogEvents_ContainerId", + table: "LogEvents", + column: "ContainerId"); + + migrationBuilder.CreateIndex( + name: "IX_LogEvents_ServerId", + table: "LogEvents", + column: "ServerId"); + + migrationBuilder.CreateIndex( + name: "IX_Servers_TagId", + table: "Servers", + column: "TagId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ContainerMetrics"); + + migrationBuilder.DropTable( + name: "LogEvents"); + + migrationBuilder.DropTable( + name: "Metrics"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Containers"); + + migrationBuilder.DropTable( + name: "Servers"); + + migrationBuilder.DropTable( + name: "Images"); + + migrationBuilder.DropTable( + name: "Tags"); + } + } +} diff --git a/Watcher/Migrations/20251029105954_container-changed.Designer.cs b/Watcher/Migrations/20251105201056_AddContainerServerNavigation.Designer.cs similarity index 85% rename from Watcher/Migrations/20251029105954_container-changed.Designer.cs rename to Watcher/Migrations/20251105201056_AddContainerServerNavigation.Designer.cs index 4eda82f..bc2eae9 100644 --- a/Watcher/Migrations/20251029105954_container-changed.Designer.cs +++ b/Watcher/Migrations/20251105201056_AddContainerServerNavigation.Designer.cs @@ -11,8 +11,8 @@ using Watcher.Data; namespace Watcher.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20251029105954_container-changed")] - partial class containerchanged + [Migration("20251105201056_AddContainerServerNavigation")] + partial class AddContainerServerNavigation { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -27,10 +27,12 @@ namespace Watcher.Migrations .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "id"); b.Property("Image") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "image"); b.Property("ImageId") .HasColumnType("INTEGER"); @@ -39,10 +41,12 @@ namespace Watcher.Migrations .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "name"); b.Property("ServerId") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "Server_id"); b.Property("TagId") .HasColumnType("INTEGER"); @@ -51,11 +55,42 @@ namespace Watcher.Migrations b.HasIndex("ImageId"); + b.HasIndex("ServerId"); + b.HasIndex("TagId"); b.ToTable("Containers"); }); + modelBuilder.Entity("Watcher.Models.ContainerMetric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ContainerMetrics"); + }); + modelBuilder.Entity("Watcher.Models.Image", b => { b.Property("Id") @@ -282,22 +317,16 @@ namespace Watcher.Migrations b.Property("Email") .HasColumnType("TEXT"); - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - b.Property("LastLogin") .HasColumnType("TEXT"); - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - b.Property("Password") .IsRequired() .HasColumnType("TEXT"); b.Property("Username") .IsRequired() + .HasMaxLength(50) .HasColumnType("TEXT"); b.HasKey("Id"); @@ -311,9 +340,17 @@ namespace Watcher.Migrations .WithMany("Containers") .HasForeignKey("ImageId"); + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + b.HasOne("Watcher.Models.Tag", null) .WithMany("Containers") .HasForeignKey("TagId"); + + b.Navigation("Server"); }); modelBuilder.Entity("Watcher.Models.LogEvent", b => diff --git a/Watcher/Migrations/20251105201056_AddContainerServerNavigation.cs b/Watcher/Migrations/20251105201056_AddContainerServerNavigation.cs new file mode 100644 index 0000000..b03c260 --- /dev/null +++ b/Watcher/Migrations/20251105201056_AddContainerServerNavigation.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class AddContainerServerNavigation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_Containers_ServerId", + table: "Containers", + column: "ServerId"); + + migrationBuilder.AddForeignKey( + name: "FK_Containers_Servers_ServerId", + table: "Containers", + column: "ServerId", + principalTable: "Servers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Containers_Servers_ServerId", + table: "Containers"); + + migrationBuilder.DropIndex( + name: "IX_Containers_ServerId", + table: "Containers"); + } + } +} diff --git a/Watcher/Migrations/AppDbContextModelSnapshot.cs b/Watcher/Migrations/AppDbContextModelSnapshot.cs index b642f34..99d7352 100644 --- a/Watcher/Migrations/AppDbContextModelSnapshot.cs +++ b/Watcher/Migrations/AppDbContextModelSnapshot.cs @@ -24,10 +24,12 @@ namespace Watcher.Migrations .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "id"); b.Property("Image") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "image"); b.Property("ImageId") .HasColumnType("INTEGER"); @@ -36,10 +38,12 @@ namespace Watcher.Migrations .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "name"); b.Property("ServerId") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "Server_id"); b.Property("TagId") .HasColumnType("INTEGER"); @@ -48,11 +52,42 @@ namespace Watcher.Migrations b.HasIndex("ImageId"); + b.HasIndex("ServerId"); + b.HasIndex("TagId"); b.ToTable("Containers"); }); + modelBuilder.Entity("Watcher.Models.ContainerMetric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ContainerMetrics"); + }); + modelBuilder.Entity("Watcher.Models.Image", b => { b.Property("Id") @@ -279,22 +314,16 @@ namespace Watcher.Migrations b.Property("Email") .HasColumnType("TEXT"); - b.Property("IdentityProvider") - .IsRequired() - .HasColumnType("TEXT"); - b.Property("LastLogin") .HasColumnType("TEXT"); - b.Property("OIDC_Id") - .HasColumnType("TEXT"); - b.Property("Password") .IsRequired() .HasColumnType("TEXT"); b.Property("Username") .IsRequired() + .HasMaxLength(50) .HasColumnType("TEXT"); b.HasKey("Id"); @@ -308,9 +337,17 @@ namespace Watcher.Migrations .WithMany("Containers") .HasForeignKey("ImageId"); + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + b.HasOne("Watcher.Models.Tag", null) .WithMany("Containers") .HasForeignKey("TagId"); + + b.Navigation("Server"); }); modelBuilder.Entity("Watcher.Models.LogEvent", b => diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index 13f1253..826dcbe 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -5,9 +5,13 @@ namespace Watcher.Models; public class Container { public int Id { get; set; } + [JsonPropertyName("Server_id")] public int ServerId { get; set; } + // Navigation Property + public Server Server { get; set; } = null!; + [JsonPropertyName("id")] public String? ContainerId { get; set; } diff --git a/Watcher/Models/User.cs b/Watcher/Models/User.cs index 13d366a..ce774d7 100644 --- a/Watcher/Models/User.cs +++ b/Watcher/Models/User.cs @@ -7,16 +7,18 @@ public class User { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; set; } // PK - public string? OIDC_Id { get; set; } = null!; - public string Username { get; set; } = null!; - public string? Email { get; set; } - public DateTime LastLogin { get; set; } + public int Id { get; set; } [Required] - public string IdentityProvider { get; set; } = "local"; - + [StringLength(50)] + public string Username { get; set; } = null!; + + [EmailAddress] + public string? Email { get; set; } + + public DateTime LastLogin { get; set; } = DateTime.UtcNow; + [Required] [DataType(DataType.Password)] - public String? Password { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; } diff --git a/Watcher/Program.cs b/Watcher/Program.cs index 466d1f1..d6fab21 100644 --- a/Watcher/Program.cs +++ b/Watcher/Program.cs @@ -1,4 +1,3 @@ -using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.EntityFrameworkCore; using Microsoft.OpenApi.Models; @@ -7,8 +6,6 @@ using Serilog; using Watcher.Data; using Watcher.Models; using Watcher.Services; -//using Watcher.Services; -//using Watcher.Workers; var builder = WebApplication.CreateBuilder(args); @@ -38,10 +35,14 @@ builder.Services.AddHttpContextAccessor(); // Storage Singleton builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); // Background Services builder.Services.AddHostedService(); builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); // Swagger API-Dokumentation builder.Services.AddSwaggerGen(options => @@ -62,106 +63,25 @@ var configuration = builder.Configuration; // ---------- DB-Kontext ---------- -var dbProvider = configuration["Database:Provider"] ?? "MySQL"; -var connectionString = configuration["Database:ConnectionString"]; +// Nur SQLite wird unterstützt +var sqliteConnectionString = configuration.GetConnectionString("Sqlite") + ?? configuration["Database:ConnectionStrings:Sqlite"] + ?? "Data Source=./persistence/watcher.db"; + builder.Services.AddDbContext((serviceProvider, options) => { - var config = serviceProvider.GetRequiredService(); - var provider = dbProvider; - - if (provider == "MySql") - { - var connStr = config.GetConnectionString("MySql") ?? config["Database:ConnectionStrings:MySql"]; - options.UseMySql(connStr, ServerVersion.AutoDetect(connStr)); - } - else if (provider == "Sqlite") - { - var connStr = config.GetConnectionString("Sqlite") ?? config["Database:ConnectionStrings:Sqlite"]; - options.UseSqlite(connStr); - } - else - { - throw new Exception("Unsupported database provider configured."); - } + options.UseSqlite(sqliteConnectionString); }); // ---------- Authentifizierung konfigurieren ---------- -// PocketID nur konfigurieren, wenn aktiviert -var pocketIdSection = builder.Configuration.GetSection("Authentication:PocketID"); -var pocketIdEnabled = pocketIdSection.GetValue("Enabled"); - - -var auth = builder.Services.AddAuthentication("Cookies"); -auth.AddCookie("Cookies", options => -{ - options.LoginPath = "/Auth/Login"; - options.AccessDeniedPath = "/Auth/AccessDenied"; -}); - - -builder.Services.AddAuthentication() -.AddOpenIdConnect("oidc", options => -{ - options.Authority = pocketIdSection["Authority"]; - options.ClientId = pocketIdSection["ClientId"]; - options.ClientSecret = pocketIdSection["ClientSecret"]; - options.ResponseType = "code"; - options.CallbackPath = pocketIdSection["CallbackPath"]; - options.SaveTokens = true; - - options.GetClaimsFromUserInfoEndpoint = true; - - options.Scope.Clear(); - options.Scope.Add("openid"); - options.Scope.Add("profile"); - options.Scope.Add("email"); - - options.Events = new OpenIdConnectEvents +// Nur Cookie-basierte lokale Authentifizierung +builder.Services.AddAuthentication("Cookies") + .AddCookie("Cookies", options => { - OnTokenValidated = async ctx => - { - var db = ctx.HttpContext.RequestServices.GetRequiredService(); - - var principal = ctx.Principal; -#pragma warning disable CS8602 // Dereference of a possibly null reference. - - var pocketId = principal.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; -#pragma warning restore CS8602 // Dereference of a possibly null reference. - - var preferredUsername = principal.FindFirst("preferred_username")?.Value; - var email = principal.FindFirst("email")?.Value; - - if (string.IsNullOrEmpty(pocketId)) - return; - - var user = await db.Users.FirstOrDefaultAsync(u => u.OIDC_Id == pocketId); - - if (user == null) - { - user = new User - { - OIDC_Id = pocketId, - Username = preferredUsername ?? "", - Email = email, - LastLogin = DateTime.UtcNow, - IdentityProvider = "oidc", - Password = string.Empty - }; - db.Users.Add(user); - } - else - { - user.LastLogin = DateTime.UtcNow; - user.Username = preferredUsername ?? user.Username; - user.Email = email ?? user.Email; - db.Users.Update(user); - } - - await db.SaveChangesAsync(); - } - }; -}); + options.LoginPath = "/Auth/Login"; + options.AccessDeniedPath = "/Auth/AccessDenied"; + }); var app = builder.Build(); @@ -175,7 +95,7 @@ using (var scope = app.Services.CreateScope()) } -// Standart-User in Datenbank schreiben +// Standard-Admin-User erstellen using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); @@ -184,21 +104,19 @@ using (var scope = app.Services.CreateScope()) if (!db.Users.Any()) { - Console.WriteLine("No users found, creating default user..."); + Console.WriteLine("No users found, creating default admin user..."); var defaultUser = new User { - OIDC_Id = string.Empty, Username = "admin", - Email = string.Empty, + Email = "admin@localhost", LastLogin = DateTime.UtcNow, - IdentityProvider = "local", Password = BCrypt.Net.BCrypt.HashPassword("changeme") }; db.Users.Add(defaultUser); db.SaveChanges(); - Console.WriteLine("Default user created."); + Console.WriteLine("Default admin user created (username: admin, password: changeme)"); } else { diff --git a/Watcher/Services/IUpdateCheckStore.cs b/Watcher/Services/IUpdateCheckStore.cs new file mode 100644 index 0000000..ead0e00 --- /dev/null +++ b/Watcher/Services/IUpdateCheckStore.cs @@ -0,0 +1,8 @@ +namespace Watcher.Services; + +public interface IUpdateCheckStore +{ + bool IsUpdateAvailable { get; set; } + string? LatestVersion { get; set; } + DateTime LastChecked { get; set; } +} diff --git a/Watcher/Services/IVersionService.cs b/Watcher/Services/IVersionService.cs new file mode 100644 index 0000000..673754f --- /dev/null +++ b/Watcher/Services/IVersionService.cs @@ -0,0 +1,6 @@ +namespace Watcher.Services; + +public interface IVersionService +{ + string GetVersion(); +} diff --git a/Watcher/Services/MetricCleanupService.cs b/Watcher/Services/MetricCleanupService.cs new file mode 100644 index 0000000..a7cafa6 --- /dev/null +++ b/Watcher/Services/MetricCleanupService.cs @@ -0,0 +1,127 @@ +using Microsoft.EntityFrameworkCore; +using Watcher.Data; + +namespace Watcher.Services; + +public class MetricCleanupService : BackgroundService +{ + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private readonly int _retentionDays; + private readonly int _checkIntervalHours; + private readonly bool _enabled; + + public MetricCleanupService( + ILogger logger, + IServiceProvider serviceProvider, + IConfiguration configuration) + { + _logger = logger; + _serviceProvider = serviceProvider; + + // Konfiguration aus Environment Variablen laden + _retentionDays = int.TryParse( + configuration["DataRetention:MetricRetentionDays"] ?? + Environment.GetEnvironmentVariable("METRIC_RETENTION_DAYS"), + out var days) ? days : 30; // Default: 30 Tage + + _checkIntervalHours = int.TryParse( + configuration["DataRetention:CleanupIntervalHours"] ?? + Environment.GetEnvironmentVariable("METRIC_CLEANUP_INTERVAL_HOURS"), + out var hours) ? hours : 24; // Default: 24 Stunden + + _enabled = bool.TryParse( + configuration["DataRetention:Enabled"] ?? + Environment.GetEnvironmentVariable("METRIC_CLEANUP_ENABLED"), + out var enabled) ? enabled : true; // Default: aktiviert + + _logger.LogInformation( + "MetricCleanupService konfiguriert: Enabled={Enabled}, RetentionDays={Days}, IntervalHours={Hours}", + _enabled, _retentionDays, _checkIntervalHours); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + if (!_enabled) + { + _logger.LogInformation("MetricCleanupService ist deaktiviert."); + return; + } + + // Warte 1 Minute nach Start, bevor der erste Cleanup läuft + await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); + + var timer = new PeriodicTimer(TimeSpan.FromHours(_checkIntervalHours)); + + while (await timer.WaitForNextTickAsync(stoppingToken)) + { + try + { + await CleanupOldMetricsAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Fehler beim Cleanup alter Metriken."); + } + + // Offset nach Cleanup + await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); + } + } + + private async Task CleanupOldMetricsAsync() + { + _logger.LogInformation("Starte Metric Cleanup für Daten älter als {Days} Tage...", _retentionDays); + + using var scope = _serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + var cutoffDate = DateTime.UtcNow.AddDays(-_retentionDays); + + try + { + // Anzahl der zu löschenden Einträge ermitteln + var countToDelete = await context.Metrics + .Where(m => m.Timestamp < cutoffDate) + .CountAsync(); + + if (countToDelete == 0) + { + _logger.LogInformation("Keine alten Metriken zum Löschen gefunden."); + return; + } + + _logger.LogInformation("Lösche {Count} Metriken vor {Date}...", countToDelete, cutoffDate); + + // Metriken löschen + var deletedCount = await context.Metrics + .Where(m => m.Timestamp < cutoffDate) + .ExecuteDeleteAsync(); + + _logger.LogInformation( + "Metric Cleanup abgeschlossen: {DeletedCount} Einträge gelöscht.", + deletedCount); + + // Optional: ContainerMetrics auch bereinigen + var containerMetricsCount = await context.ContainerMetrics + .Where(cm => cm.Timestamp < cutoffDate) + .CountAsync(); + + if (containerMetricsCount > 0) + { + var deletedContainerMetrics = await context.ContainerMetrics + .Where(cm => cm.Timestamp < cutoffDate) + .ExecuteDeleteAsync(); + + _logger.LogInformation( + "ContainerMetrics Cleanup: {DeletedCount} Einträge gelöscht.", + deletedContainerMetrics); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Fehler beim Löschen alter Metriken aus der Datenbank."); + throw; + } + } +} diff --git a/Watcher/Services/UpdateCheckService.cs b/Watcher/Services/UpdateCheckService.cs new file mode 100644 index 0000000..27377cb --- /dev/null +++ b/Watcher/Services/UpdateCheckService.cs @@ -0,0 +1,159 @@ +using System.Text.Json; + +namespace Watcher.Services; + +public class UpdateCheckService : BackgroundService +{ + private readonly ILogger _logger; + private readonly IUpdateCheckStore _updateCheckStore; + private readonly IVersionService _versionService; + private readonly string _repositoryUrl; + private readonly int _checkIntervalHours; + private readonly bool _enabled; + private readonly HttpClient _httpClient; + + public UpdateCheckService( + ILogger logger, + IUpdateCheckStore updateCheckStore, + IVersionService versionService, + IConfiguration configuration) + { + _logger = logger; + _updateCheckStore = updateCheckStore; + _versionService = versionService; + _httpClient = new HttpClient(); + + // Konfiguration aus Environment Variablen laden + _repositoryUrl = configuration["UpdateCheck:RepositoryUrl"] + ?? Environment.GetEnvironmentVariable("UPDATE_CHECK_REPOSITORY_URL") + ?? "https://git.triggermeelmo.com/api/v1/repos/Watcher/watcher/releases/latest"; + + _checkIntervalHours = int.TryParse( + configuration["UpdateCheck:CheckIntervalHours"] + ?? Environment.GetEnvironmentVariable("UPDATE_CHECK_INTERVAL_HOURS"), + out var hours) ? hours : 24; // Default: 24 Stunden + + _enabled = bool.TryParse( + configuration["UpdateCheck:Enabled"] + ?? Environment.GetEnvironmentVariable("UPDATE_CHECK_ENABLED"), + out var enabled) ? enabled : true; // Default: aktiviert + + _logger.LogInformation( + "UpdateCheckService konfiguriert: Enabled={Enabled}, Repository={Repo}, IntervalHours={Hours}", + _enabled, _repositoryUrl, _checkIntervalHours); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + if (!_enabled) + { + _logger.LogInformation("UpdateCheckService ist deaktiviert."); + return; + } + + // Warte 2 Minuten nach Start, bevor der erste Check läuft + await Task.Delay(TimeSpan.FromMinutes(2), stoppingToken); + + var timer = new PeriodicTimer(TimeSpan.FromHours(_checkIntervalHours)); + + while (await timer.WaitForNextTickAsync(stoppingToken)) + { + try + { + await CheckForUpdatesAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Fehler beim Update-Check."); + } + + await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); + } + } + + private async Task CheckForUpdatesAsync() + { + var currentVersion = _versionService.GetVersion(); + + _logger.LogInformation("Starte Update-Check. Aktuelle Version: {Version}", currentVersion); + + // Bei "development" oder "latest" immer als aktuell markieren + if (currentVersion == "development" || currentVersion == "latest") + { + _updateCheckStore.IsUpdateAvailable = false; + _updateCheckStore.LatestVersion = currentVersion; + _updateCheckStore.LastChecked = DateTime.UtcNow; + _logger.LogInformation("Development/Latest Build - keine Update-Prüfung nötig."); + return; + } + + try + { + // Gitea API abfragen + var response = await _httpClient.GetAsync(_repositoryUrl); + + if (!response.IsSuccessStatusCode) + { + _logger.LogWarning("Gitea API Fehler: {StatusCode}", response.StatusCode); + return; + } + + var jsonContent = await response.Content.ReadAsStringAsync(); + var releaseInfo = JsonSerializer.Deserialize(jsonContent); + + if (releaseInfo?.TagName == null) + { + _logger.LogWarning("Keine Release-Information gefunden."); + return; + } + + var latestVersion = releaseInfo.TagName; + _updateCheckStore.LatestVersion = latestVersion; + _updateCheckStore.LastChecked = DateTime.UtcNow; + + // Versionsvergleich + var isNewer = CompareVersions(latestVersion, currentVersion); + _updateCheckStore.IsUpdateAvailable = isNewer; + + if (isNewer) + { + _logger.LogInformation( + "Neue Version verfügbar: {Latest} (aktuell: {Current})", + latestVersion, currentVersion); + } + else + { + _logger.LogInformation( + "System ist auf dem neuesten Stand: {Version}", + currentVersion); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Fehler beim Abrufen der Release-Informationen von Gitea."); + } + } + + private bool CompareVersions(string latestVersion, string currentVersion) + { + // Entferne "v" Prefix falls vorhanden + latestVersion = latestVersion.TrimStart('v'); + currentVersion = currentVersion.TrimStart('v'); + + // Versuche semantic versioning zu parsen + if (Version.TryParse(latestVersion, out var latest) && + Version.TryParse(currentVersion, out var current)) + { + return latest > current; + } + + // Fallback: String-Vergleich + return string.Compare(latestVersion, currentVersion, StringComparison.Ordinal) > 0; + } + + // DTO für Gitea API Response + private class GiteaReleaseResponse + { + public string? TagName { get; set; } + } +} diff --git a/Watcher/Services/UpdateCheckStore.cs b/Watcher/Services/UpdateCheckStore.cs new file mode 100644 index 0000000..5cca773 --- /dev/null +++ b/Watcher/Services/UpdateCheckStore.cs @@ -0,0 +1,8 @@ +namespace Watcher.Services; + +public class UpdateCheckStore : IUpdateCheckStore +{ + public bool IsUpdateAvailable { get; set; } = false; + public string? LatestVersion { get; set; } = null; + public DateTime LastChecked { get; set; } = DateTime.MinValue; +} diff --git a/Watcher/Services/VersionService.cs b/Watcher/Services/VersionService.cs new file mode 100644 index 0000000..a7c13b6 --- /dev/null +++ b/Watcher/Services/VersionService.cs @@ -0,0 +1,19 @@ +namespace Watcher.Services; + +public class VersionService : IVersionService +{ + private readonly string _version; + + public VersionService(IConfiguration configuration) + { + // Priorität: Environment Variable > Configuration > Default + _version = Environment.GetEnvironmentVariable("WATCHER_VERSION") + ?? configuration["Application:Version"] + ?? "development"; + } + + public string GetVersion() + { + return _version; + } +} diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index e3edfc3..7e818fb 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -15,7 +15,7 @@ Container-ID Name Image - Host-ID + Host Aktionen @@ -25,8 +25,7 @@ @container.ContainerId @container.Name @container.Image - - @container.ServerId + @container.Server?.Name nicht verfügbar } diff --git a/Watcher/Views/Home/_DashboardStats.cshtml b/Watcher/Views/Home/_DashboardStats.cshtml index dc011e4..4b0a81a 100644 --- a/Watcher/Views/Home/_DashboardStats.cshtml +++ b/Watcher/Views/Home/_DashboardStats.cshtml @@ -1,4 +1,3 @@ -@using Microsoft.IdentityModel.Tokens @model Watcher.ViewModels.DashboardViewModel @{ @@ -58,7 +57,7 @@
Systemstatus
- @if (!Model.NetworkStatus.IsNullOrEmpty()) + @if (!string.IsNullOrEmpty(Model.NetworkStatus)) { @if (Model.NetworkStatus == "online") { @@ -81,7 +80,7 @@ } } - @if (!Model.DatabaseStatus.IsNullOrEmpty()) + @if (!string.IsNullOrEmpty(Model.DatabaseStatus)) { @if (Model.DatabaseStatus == "ok") { diff --git a/Watcher/Views/Shared/_Layout.cshtml b/Watcher/Views/Shared/_Layout.cshtml index a252560..23e61bf 100644 --- a/Watcher/Views/Shared/_Layout.cshtml +++ b/Watcher/Views/Shared/_Layout.cshtml @@ -1,7 +1,10 @@ @using Microsoft.AspNetCore.Authentication @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Http +@using Watcher.Services @inject IHttpContextAccessor HttpContextAccessor +@inject IVersionService VersionService +@inject IUpdateCheckStore UpdateCheckStore @{ var pictureUrl = User.FindFirst("picture")?.Value; @@ -98,7 +101,7 @@
- +
@User.Identity?.Name
Profil ansehen @@ -110,6 +113,19 @@ { Login } + +
+ + @{ + var statusColor = UpdateCheckStore.IsUpdateAvailable ? "#ffc107" : "#28a745"; + var statusTitle = UpdateCheckStore.IsUpdateAvailable + ? $"Update verfügbar: {UpdateCheckStore.LatestVersion}" + : "System ist aktuell"; + } + + Version: @VersionService.GetVersion() + +
diff --git a/Watcher/Views/System/Settings.cshtml b/Watcher/Views/System/Settings.cshtml index 23c0ef3..aa4b0c5 100644 --- a/Watcher/Views/System/Settings.cshtml +++ b/Watcher/Views/System/Settings.cshtml @@ -29,10 +29,14 @@
Datenbank-Engine: @(DbEngine ?? "nicht gefunden")
- + + @if (ViewBag.DatabaseSize != null) + { +
Datenbankgröße: @ViewBag.DatabaseSize
+ } - @if (DbEngine == "Microsoft.EntityFrameworkCore.Sqlite") + @if (DbEngine == "SQLite" || DbEngine == "Microsoft.EntityFrameworkCore.Sqlite") {
diff --git a/Watcher/Watcher.csproj b/Watcher/Watcher.csproj index 4ec814e..b150629 100644 --- a/Watcher/Watcher.csproj +++ b/Watcher/Watcher.csproj @@ -7,20 +7,13 @@ - - - - - - - - - + + + - diff --git a/Watcher/appsettings.json b/Watcher/appsettings.json index dff88f0..1cfd965 100644 --- a/Watcher/appsettings.json +++ b/Watcher/appsettings.json @@ -9,22 +9,8 @@ "AllowedHosts": "*", "Database": { - "Provider": "Sqlite", "ConnectionStrings": { - "MySql": "server=0.0.0.0;port=3306;database=db;user=user;password=password;", "Sqlite": "Data Source=./persistence/watcher.db" } - }, - - "Authentication": { - "UseLocal": true, - "PocketIDEnabled": false, - "PocketID": { - "Authority": "https://pocketid.triggermeelmo.com", - "ClientId": "629a5f42-ab02-4905-8311-cc7b64165cc0", - "ClientSecret": "QHUNaRyK2VVYdZVz1cQqv8FEf2qtL6QH", - "CallbackPath": "/signin-oidc", - "ResponseType": "code" - } - } + } } diff --git a/docker-compose.yaml b/docker-compose.yaml index 6eacc9c..347348f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,6 @@ services: watcher: - image: git.triggermeelmo.com/watcher/watcher-server:v0.1.0 + image: git.triggermeelmo.com/watcher/watcher-server:${IMAGE_VERSION:-latest} container_name: watcher deploy: resources: @@ -8,6 +8,17 @@ services: memory: 200M restart: unless-stopped env_file: .env + environment: + # Application Version (wird aus Image-Tag übernommen) + - WATCHER_VERSION=${IMAGE_VERSION:-latest} + # Update Check + - UPDATE_CHECK_ENABLED=true + - UPDATE_CHECK_INTERVAL_HOURS=24 + - UPDATE_CHECK_REPOSITORY_URL=https://git.triggermeelmo.com/api/v1/repos/Watcher/watcher/releases/latest + # Data Retention Policy + - METRIC_RETENTION_DAYS=30 + - METRIC_CLEANUP_INTERVAL_HOURS=24 + - METRIC_CLEANUP_ENABLED=true ports: - "5000:5000" volumes: -- 2.49.1 From 65c3ae2a406ccb30daeb0fea7546c8f46f30135b Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Wed, 5 Nov 2025 22:01:34 +0100 Subject: [PATCH 50/54] database anpassung --- Watcher/Controllers/MonitoringController.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index e7a2a22..ecae4f2 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -244,6 +244,14 @@ public class MonitoringController : Controller return BadRequest(new { error = "Invalid Payload", details = errors }); } + // Prüfen, ob der Server existiert + var serverExists = await _context.Servers.AnyAsync(s => s.Id == dto.Server_id); + if (!serverExists) + { + _logger.LogError($"Server with ID {dto.Server_id} does not exist."); + return BadRequest(new { error = "Server not found", details = $"Server with ID {dto.Server_id} does not exist. Please register the server first." }); + } + List newContainers = JsonSerializer.Deserialize>(dto.Containers.GetRawText()) ?? new List(); -- 2.49.1 From 3d375f479255fe351724cc9843983d9c6d086b40 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Thu, 6 Nov 2025 16:53:00 +0100 Subject: [PATCH 51/54] Graph fix --- Watcher/Controllers/MonitoringController.cs | 63 ++++++----- Watcher/Views/Server/Details.cshtml | 112 +++++++++++++++++--- 2 files changed, 136 insertions(+), 39 deletions(-) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index ecae4f2..faf6df9 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -378,53 +378,62 @@ public class MonitoringController : Controller } [HttpGet("cpu-usage")] - public async Task GetCpuUsageData(int serverId) + public async Task GetCpuUsageData(int serverId, int hours = 1) { - var oneDayAgo = DateTime.UtcNow.AddDays(-1); - var data = await _context.Metrics - .Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId) + var startTime = DateTime.UtcNow.AddHours(-hours); + var metrics = await _context.Metrics + .Where(m => m.Timestamp >= startTime && m.ServerId == serverId) .OrderBy(m => m.Timestamp) - .Select(m => new - { - label = m.Timestamp.ToUniversalTime().ToString("o"), - data = m.CPU_Load - }) .ToListAsync(); + // Timestamp-Format basierend auf Zeitbereich anpassen + string format = hours > 1 ? "dd.MM HH:mm" : "HH:mm"; + var data = metrics.Select(m => new + { + label = m.Timestamp.ToLocalTime().ToString(format), + data = m.CPU_Load + }).ToList(); + return Ok(data); } [HttpGet("ram-usage")] - public async Task GetRamUsageData(int serverId) + public async Task GetRamUsageData(int serverId, int hours = 1) { - var oneDayAgo = DateTime.UtcNow.AddDays(-1); - var data = await _context.Metrics - .Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId) + var startTime = DateTime.UtcNow.AddHours(-hours); + var metrics = await _context.Metrics + .Where(m => m.Timestamp >= startTime && m.ServerId == serverId) .OrderBy(m => m.Timestamp) - .Select(m => new - { - label = m.Timestamp.ToUniversalTime().ToString("o"), - data = m.RAM_Load - }) .ToListAsync(); + // Timestamp-Format basierend auf Zeitbereich anpassen + string format = hours > 1 ? "dd.MM HH:mm" : "HH:mm"; + var data = metrics.Select(m => new + { + label = m.Timestamp.ToLocalTime().ToString(format), + data = m.RAM_Load + }).ToList(); + return Ok(data); } [HttpGet("gpu-usage")] - public async Task GetGpuUsageData(int serverId) + public async Task GetGpuUsageData(int serverId, int hours = 1) { - var oneDayAgo = DateTime.UtcNow.AddDays(-1); - var data = await _context.Metrics - .Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId) + var startTime = DateTime.UtcNow.AddHours(-hours); + var metrics = await _context.Metrics + .Where(m => m.Timestamp >= startTime && m.ServerId == serverId) .OrderBy(m => m.Timestamp) - .Select(m => new - { - label = m.Timestamp.ToUniversalTime().ToString("o"), - data = m.GPU_Load - }) .ToListAsync(); + // Timestamp-Format basierend auf Zeitbereich anpassen + string format = hours > 1 ? "dd.MM HH:mm" : "HH:mm"; + var data = metrics.Select(m => new + { + label = m.Timestamp.ToLocalTime().ToString(format), + data = m.GPU_Load + }).ToList(); + return Ok(data); } diff --git a/Watcher/Views/Server/Details.cshtml b/Watcher/Views/Server/Details.cshtml index fb903a7..715bdb8 100644 --- a/Watcher/Views/Server/Details.cshtml +++ b/Watcher/Views/Server/Details.cshtml @@ -58,6 +58,17 @@
+
+
+
Metriken
+
+ + + +
+
+
+
CPU Last
@@ -94,10 +105,13 @@ datasets: [{ label: 'CPU Last (%)', data: [], - backgroundColor: 'rgba(255, 99, 132, 0.2)', + borderColor: 'rgba(54, 162, 235, 1)', + backgroundColor: 'rgba(54, 162, 235, 0.1)', + borderWidth: 2, fill: true, - tension: 0.3, - pointRadius: 3 + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 4 }] }, options: { @@ -110,14 +124,29 @@ title: { display: true, text: 'Auslastung in %' + }, + grid: { + color: 'rgba(255, 255, 255, 0.1)' } }, x: { title: { display: false, text: 'Zeit' + }, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + maxTicksLimit: 12, + autoSkip: true } } + }, + plugins: { + legend: { + display: false + } } } }); @@ -129,10 +158,13 @@ datasets: [{ label: 'RAM Last (%)', data: [], - backgroundColor: 'rgba(255, 99, 132, 0.2)', + borderColor: 'rgba(75, 192, 192, 1)', + backgroundColor: 'rgba(75, 192, 192, 0.1)', + borderWidth: 2, fill: true, - tension: 0.3, - pointRadius: 3 + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 4 }] }, options: { @@ -145,14 +177,29 @@ title: { display: true, text: 'Auslastung in %' + }, + grid: { + color: 'rgba(255, 255, 255, 0.1)' } }, x: { title: { display: false, text: 'Zeit' + }, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + maxTicksLimit: 12, + autoSkip: true } } + }, + plugins: { + legend: { + display: false + } } } }); @@ -164,10 +211,13 @@ datasets: [{ label: 'GPU Last (%)', data: [], - backgroundColor: 'rgba(255, 99, 132, 0.2)', + borderColor: 'rgba(153, 102, 255, 1)', + backgroundColor: 'rgba(153, 102, 255, 0.1)', + borderWidth: 2, fill: true, - tension: 0.3, - pointRadius: 3 + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 4 }] }, options: { @@ -180,21 +230,38 @@ title: { display: true, text: 'Auslastung in %' + }, + grid: { + color: 'rgba(255, 255, 255, 0.1)' } }, x: { title: { display: false, text: 'Zeit' + }, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + maxTicksLimit: 12, + autoSkip: true } } + }, + plugins: { + legend: { + display: false + } } } }); + let currentHours = 1; // Standard: 1 Stunde + async function loadCpuData() { try { - const response = await fetch('/monitoring/cpu-usage?serverId=@Model.Id'); + const response = await fetch(`/monitoring/cpu-usage?serverId=@Model.Id&hours=${currentHours}`); if (!response.ok) { throw new Error(`HTTP-Fehler: ${response.status}`); } @@ -217,7 +284,7 @@ async function loadRamData() { try { - const response = await fetch('/monitoring/ram-usage?serverId=@Model.Id'); + const response = await fetch(`/monitoring/ram-usage?serverId=@Model.Id&hours=${currentHours}`); if (!response.ok) { throw new Error(`HTTP-Fehler: ${response.status}`); } @@ -240,7 +307,7 @@ async function loadGpuData() { try { - const response = await fetch('/monitoring/gpu-usage?serverId=@Model.Id'); + const response = await fetch(`/monitoring/gpu-usage?serverId=@Model.Id&hours=${currentHours}`); if (!response.ok) { throw new Error(`HTTP-Fehler: ${response.status}`); } @@ -261,6 +328,27 @@ } } + // Button-Handler für Zeitbereich-Wechsel + document.querySelectorAll('[data-range]').forEach(button => { + button.addEventListener('click', function() { + // Alle Buttons deaktivieren + document.querySelectorAll('[data-range]').forEach(btn => { + btn.classList.remove('active'); + }); + + // Aktuellen Button aktivieren + this.classList.add('active'); + + // Zeitbereich aktualisieren + currentHours = parseInt(this.getAttribute('data-range')); + + // Daten neu laden + loadCpuData(); + loadRamData(); + loadGpuData(); + }); + }); + // Initiales Laden loadCpuData(); loadRamData(); -- 2.49.1 From da0c5d9efbfcec03849bf9a659fac1fe6d05f8ff Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Thu, 6 Nov 2025 17:29:54 +0100 Subject: [PATCH 52/54] =?UTF-8?q?Server=20Card=20sch=C3=B6ner=20gemacht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Watcher/Views/Container/Overview.cshtml | 196 ++++++++++++++++++--- Watcher/Views/Server/Details.cshtml | 198 ++++++++++++++++++---- Watcher/wwwroot/css/server-detail.css | 69 +++++++- Watcher/wwwroot/css/services-overview.css | 117 ++++++++++++- 4 files changed, 508 insertions(+), 72 deletions(-) diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index 7e818fb..176d793 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -8,28 +8,180 @@ -
-
- - - - - - - - - - @foreach (Container container in Model.Containers) - { - - - - - - - - } -
Container-IDNameImageHostAktionen
@container.ContainerId@container.Name@container.Image@container.Server?.Namenicht verfügbar
+
+
+

Container & Services

+ @Model.Containers.Count Container
+ @if (!Model.Containers.Any()) + { +
+ Keine Container gefunden. Container werden automatisch von den Agents erkannt. +
+ } + else + { + var groupedContainers = Model.Containers.GroupBy(c => c.Server?.Name ?? "Unbekannt"); + + foreach (var serverGroup in groupedContainers.OrderBy(g => g.Key)) + { +
+
+ @serverGroup.Key + @serverGroup.Count() +
+ +
+ @foreach (var container in serverGroup) + { +
+
+
+
+ + @container.Name +
+ + + @(container.IsRunning ? "Running" : "Stopped") + +
+
+
+
+ ID: + @(container.ContainerId != null && container.ContainerId.Length > 12 ? container.ContainerId.Substring(0, 12) : container.ContainerId) +
+
+ Image: + @container.Image +
+ +
+ + +
+ + + +
+ + +
+ +
+
+
+
+ CPU + --% +
+
+
+
+
+
+
+ RAM + -- MB +
+
+
+
+
+
+
+ Network + + -- MB/s + -- MB/s + +
+
+
+ + + Metriken werden in Echtzeit aktualisiert + +
+
+
+
+
+
+
+ } +
+
+ } + }
+ +@section Scripts { + +} diff --git a/Watcher/Views/Server/Details.cshtml b/Watcher/Views/Server/Details.cshtml index 715bdb8..a2b9759 100644 --- a/Watcher/Views/Server/Details.cshtml +++ b/Watcher/Views/Server/Details.cshtml @@ -11,50 +11,176 @@
-
-
+ +
+
@Model.Name
- - - @(Model.IsOnline ? "Online" : "Offline") - +
+ + + @(Model.IsOnline ? "Online" : "Offline") + + + Bearbeiten + + + + +
-
-
-
IP: @Model.IPAddress
-
Typ: @Model.Type
-
Erstellt: - @Model.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
-
Last-Seen: - @Model.LastSeen.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
-
-
-
CPU: @(Model.CpuType ?? "not found")
-
CPU-Kerne: @Model.CpuCores
-
GPU: @(Model.GpuType ?? "not found") +
+
+ +
+
+ System +
+
+
+ + IP-Adresse + + @Model.IPAddress +
+
+ + Typ + + @Model.Type +
+
+ + Erstellt + + @Model.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy") +
+
+ + Last Seen + + + @{ + var timeSinceLastSeen = DateTime.UtcNow - Model.LastSeen; + if (timeSinceLastSeen.TotalMinutes < 1) + { + Gerade eben + } + else if (timeSinceLastSeen.TotalMinutes < 60) + { + vor @((int)timeSinceLastSeen.TotalMinutes) Min + } + else if (timeSinceLastSeen.TotalHours < 24) + { + vor @((int)timeSinceLastSeen.TotalHours) Std + } + else + { + @Model.LastSeen.ToLocalTime().ToString("dd.MM.yyyy HH:mm") + } + } + +
+
-
RAM: @(Model.RamSize)
-
Disk Space: ...
-
-
- -
+ +
+
+ Prozessor & Grafik +
+
+
+ + CPU + + + @if (!string.IsNullOrWhiteSpace(Model.CpuType)) + { + @Model.CpuType + } + else + { + Unbekannt + } + +
+
+ + CPU-Kerne + + + @if (Model.CpuCores > 0) + { + @Model.CpuCores + Cores + } + else + { + Unbekannt + } + +
+
+ + GPU + + + @if (!string.IsNullOrWhiteSpace(Model.GpuType)) + { + @Model.GpuType + } + else + { + Keine / Unbekannt + } + +
+
+
+ + +
+
+ Speicher +
+
+
+ + RAM + + + @if (Model.RamSize > 0) + { + var ramGB = Model.RamSize / (1024.0 * 1024.0 * 1024.0); + @($"{ramGB:F1} GB") + } + else + { + Unbekannt + } + +
+
+
+ + + @if (!string.IsNullOrWhiteSpace(Model.Description)) + { +
+
+ Beschreibung +
+

@Model.Description

+
+ } +
-
diff --git a/Watcher/wwwroot/css/server-detail.css b/Watcher/wwwroot/css/server-detail.css index f531a0c..7f9683a 100644 --- a/Watcher/wwwroot/css/server-detail.css +++ b/Watcher/wwwroot/css/server-detail.css @@ -1,20 +1,75 @@ -.info { - margin: 2rem; - margin-top: 3rem; +/* Server Details - Info Card */ +.info-list { + display: flex; + flex-direction: column; + gap: 0.75rem; } -.hardware { - margin: 2rem; - margin-top: 3rem; +.info-item { + display: flex; + flex-direction: column; + gap: 0.25rem; } +.info-label { + font-size: 0.85rem; + font-weight: 500; + color: #6c757d; + display: flex; + align-items: center; +} + +.info-value { + font-size: 0.95rem; + color: var(--color-text, #212529); + font-weight: 400; + padding-left: 1.25rem; +} + +.info-value .text-muted { + font-style: italic; + font-size: 0.9rem; +} + +/* Section Headers in Info Card */ +.card-body h6.text-muted { + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; +} + +.card-body h6.text-muted i { + font-size: 1rem; +} + +/* Graph Container */ .graphcontainer { height: 25rem; width: 100%; - background-color: var(--color-surface); + background-color: var(--color-surface, #f8f9fa); + border-radius: 0.375rem; } .graph { width: 100%; height: 22rem; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .info-item { + margin-bottom: 0.5rem; + } +} + +@media (max-width: 768px) { + .card-header .d-flex.gap-2 { + flex-wrap: wrap; + } + + .card-header .btn-sm { + font-size: 0.75rem; + padding: 0.25rem 0.5rem; + } } \ No newline at end of file diff --git a/Watcher/wwwroot/css/services-overview.css b/Watcher/wwwroot/css/services-overview.css index f2a983e..cc0779a 100644 --- a/Watcher/wwwroot/css/services-overview.css +++ b/Watcher/wwwroot/css/services-overview.css @@ -1,12 +1,115 @@ -.ServiceList { - width: 80%; +/* Container Card Styling */ +.container-card { + transition: transform 0.2s ease, box-shadow 0.2s ease; + border: 1px solid rgba(0, 0, 0, 0.125); + background: var(--color-background-secondary, #fff); } -.ServiceRow { - border-style: solid; - border-color: var(--color-text); +.container-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; } -.ServiceEntry { - text-decoration: none; +.container-card .card-header { + background-color: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); + padding: 0.75rem 1rem; +} + +.container-card .card-body { + padding: 1rem; +} + +/* Container Info Rows */ +.container-info { + font-size: 0.9rem; +} + +.info-row { + display: flex; + justify-content: space-between; + padding: 0.4rem 0; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); +} + +.info-row:last-child { + border-bottom: none; +} + +.info-label { + color: #6c757d; + font-weight: 500; +} + +.info-value { + color: var(--color-text, #212529); + word-break: break-all; +} + +/* Action Buttons */ +.action-buttons .btn { + font-size: 0.85rem; + padding: 0.4rem 0.6rem; +} + +/* Metrics Panel */ +.metrics-panel { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease-out; +} + +.metrics-panel.show { + max-height: 500px; + transition: max-height 0.4s ease-in; +} + +.metrics-content { + background-color: #f8f9fa !important; + border: 1px solid #dee2e6; +} + +.metric-item { + margin-bottom: 0.5rem; +} + +.metric-item:last-child { + margin-bottom: 0; +} + +.progress { + background-color: rgba(0, 0, 0, 0.1); +} + +/* Toggle Button */ +.toggle-metrics { + transition: all 0.2s ease; +} + +.toggle-metrics:hover { + transform: scale(1.02); +} + +/* Responsive Adjustments */ +@media (max-width: 768px) { + .action-buttons { + flex-direction: column; + } + + .action-buttons .btn { + width: 100%; + } +} + +/* Badge Styling */ +.badge { + font-size: 0.75rem; + padding: 0.35em 0.65em; +} + +/* Server Group Header */ +h5.text-muted { + font-weight: 600; + border-bottom: 2px solid #dee2e6; + padding-bottom: 0.5rem; } \ No newline at end of file -- 2.49.1 From 70eec043271022a89d3e9e501dfe32423c41cb7b Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Thu, 6 Nov 2025 20:09:51 +0100 Subject: [PATCH 53/54] viele Fixes --- Tests/populate_testdata.py | 412 ++++++++++++++++++++++ Watcher/Views/Container/Overview.cshtml | 2 +- Watcher/Views/Server/Details.cshtml | 14 +- Watcher/Views/Shared/_Layout.cshtml | 6 +- Watcher/wwwroot/css/Login.css | 2 +- Watcher/wwwroot/css/server-detail.css | 7 +- Watcher/wwwroot/css/services-overview.css | 25 +- Watcher/wwwroot/css/site.css | 24 +- Watcher/wwwroot/css/user-info.css | 3 +- 9 files changed, 469 insertions(+), 26 deletions(-) create mode 100644 Tests/populate_testdata.py diff --git a/Tests/populate_testdata.py b/Tests/populate_testdata.py new file mode 100644 index 0000000..b87941c --- /dev/null +++ b/Tests/populate_testdata.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python3 +""" +Watcher Database Test Data Population Script +Füllt die SQLite-Datenbank mit realistischen Testdaten für lokale Entwicklung +""" + +import sqlite3 +import random +from datetime import datetime, timedelta +import os + +# Datenbankpfad +DB_PATH = "Watcher\persistence\watcher.db" + +# Prüfe ob Datenbank existiert +if not os.path.exists(DB_PATH): + print(f"❌ Datenbank nicht gefunden: {DB_PATH}") + print("Bitte stelle sicher, dass die Anwendung einmal gestartet wurde, um die Datenbank zu erstellen.") + exit(1) + +print(f"📊 Verbinde mit Datenbank: {DB_PATH}") +conn = sqlite3.connect(DB_PATH) +cursor = conn.cursor() + +# Lösche vorhandene Daten (optional - auskommentieren wenn bestehende Daten behalten werden sollen) +print("\n🗑️ Lösche vorhandene Testdaten...") +cursor.execute("DELETE FROM ContainerMetrics") +cursor.execute("DELETE FROM Metrics") +cursor.execute("DELETE FROM LogEvents") +cursor.execute("DELETE FROM Containers") +cursor.execute("DELETE FROM Images") +cursor.execute("DELETE FROM Tags") +cursor.execute("DELETE FROM Servers") +# Users werden NICHT gelöscht +conn.commit() + +print("✅ Alte Daten gelöscht (Users bleiben erhalten)\n") + +# ============================================================================ +# 2. SERVERS - Test-Server erstellen +# ============================================================================ +print("\n🖥️ Erstelle Server...") + +servers_data = [ + { + "name": "Production-Web-01", + "ip": "192.168.1.10", + "type": "Ubuntu 22.04", + "description": "Haupt-Webserver für Production", + "cpu_type": "Intel Core i7-12700K", + "cpu_cores": 12, + "gpu_type": None, + "ram_size": 34359738368, # 32 GB in Bytes + "disk_space": "512 GB NVMe SSD", + "is_online": True + }, + { + "name": "Dev-Server", + "ip": "192.168.1.20", + "type": "Debian 12", + "description": "Entwicklungs- und Testserver", + "cpu_type": "AMD Ryzen 9 5900X", + "cpu_cores": 12, + "gpu_type": None, + "ram_size": 68719476736, # 64 GB in Bytes + "disk_space": "1 TB NVMe SSD", + "is_online": True + }, + { + "name": "GPU-Server-ML", + "ip": "192.168.1.30", + "type": "Ubuntu 22.04 LTS", + "description": "Machine Learning Training Server", + "cpu_type": "AMD Ryzen Threadripper 3970X", + "cpu_cores": 32, + "gpu_type": "NVIDIA RTX 4090", + "ram_size": 137438953472, # 128 GB in Bytes + "disk_space": "2 TB NVMe SSD", + "is_online": True + }, + { + "name": "Backup-Server", + "ip": "192.168.1.40", + "type": "Ubuntu 20.04", + "description": "Backup und Storage Server", + "cpu_type": "Intel Xeon E5-2680 v4", + "cpu_cores": 14, + "gpu_type": None, + "ram_size": 17179869184, # 16 GB in Bytes + "disk_space": "10 TB HDD RAID5", + "is_online": False + }, + { + "name": "Docker-Host-01", + "ip": "192.168.1.50", + "type": "Ubuntu 22.04", + "description": "Docker Container Host", + "cpu_type": "Intel Xeon Gold 6248R", + "cpu_cores": 24, + "gpu_type": None, + "ram_size": 68719476736, # 64 GB in Bytes + "disk_space": "2 TB NVMe SSD", + "is_online": True + } +] + +server_ids = [] +for server in servers_data: + last_seen = datetime.utcnow() - timedelta(minutes=random.randint(0, 30)) if server["is_online"] else datetime.utcnow() - timedelta(hours=random.randint(2, 48)) + + cursor.execute(""" + INSERT INTO Servers ( + Name, IPAddress, Type, Description, + CpuType, CpuCores, GpuType, RamSize, DiskSpace, + CPU_Load_Warning, CPU_Load_Critical, + CPU_Temp_Warning, CPU_Temp_Critical, + RAM_Load_Warning, RAM_Load_Critical, + GPU_Load_Warning, GPU_Load_Critical, + GPU_Temp_Warning, GPU_Temp_Critical, + Disk_Usage_Warning, Disk_Usage_Critical, + DISK_Temp_Warning, DISK_Temp_Critical, + CreatedAt, IsOnline, LastSeen, IsVerified + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, ( + server["name"], server["ip"], server["type"], server["description"], + server["cpu_type"], server["cpu_cores"], server["gpu_type"], server["ram_size"], server["disk_space"], + 75.0, 90.0, # CPU Load + 80.0, 90.0, # CPU Temp + 85.0, 95.0, # RAM Load + 75.0, 90.0, # GPU Load + 70.0, 80.0, # GPU Temp + 75.0, 90.0, # Disk Usage + 34.0, 36.0, # Disk Temp + datetime.utcnow() - timedelta(days=random.randint(30, 365)), + server["is_online"], last_seen, True + )) + server_ids.append(cursor.lastrowid) + print(f" ✓ Server '{server['name']}' erstellt (ID: {cursor.lastrowid})") + +conn.commit() + +# ============================================================================ +# 3. METRICS - Server-Metriken erstellen (letzte 48 Stunden) +# ============================================================================ +print("\n📈 Erstelle Server-Metriken (letzte 48 Stunden)...") + +metrics_count = 0 +for server_id in server_ids: + # Finde den Server + cursor.execute("SELECT IsOnline FROM Servers WHERE Id = ?", (server_id,)) + is_online = cursor.fetchone()[0] + + if not is_online: + continue # Keine Metriken für offline Server + + # Erstelle Metriken für die letzten 48 Stunden (alle 5 Minuten) + start_time = datetime.utcnow() - timedelta(hours=48) + current_time = start_time + + # Basis-Werte für realistische Schwankungen + base_cpu = random.uniform(20, 40) + base_ram = random.uniform(40, 60) + base_gpu = random.uniform(10, 30) if server_id == server_ids[2] else 0 # Nur GPU-Server + + while current_time <= datetime.utcnow(): + # Realistische Schwankungen + cpu_load = max(0, min(100, base_cpu + random.gauss(0, 15))) + cpu_temp = 30 + (cpu_load * 0.5) + random.gauss(0, 3) + + ram_load = max(0, min(100, base_ram + random.gauss(0, 10))) + + gpu_load = max(0, min(100, base_gpu + random.gauss(0, 20))) if base_gpu > 0 else 0 + gpu_temp = 25 + (gpu_load * 0.6) + random.gauss(0, 3) if gpu_load > 0 else 0 + gpu_vram_usage = gpu_load * 0.8 if gpu_load > 0 else 0 + + disk_usage = random.uniform(40, 75) + disk_temp = random.uniform(28, 35) + + net_in = random.uniform(1000000, 10000000) # 1-10 Mbps in Bits + net_out = random.uniform(500000, 5000000) # 0.5-5 Mbps in Bits + + cursor.execute(""" + INSERT INTO Metrics ( + ServerId, Timestamp, + CPU_Load, CPU_Temp, + GPU_Load, GPU_Temp, GPU_Vram_Size, GPU_Vram_Usage, + RAM_Size, RAM_Load, + DISK_Size, DISK_Usage, DISK_Temp, + NET_In, NET_Out + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, ( + server_id, current_time, + cpu_load, cpu_temp, + gpu_load, gpu_temp, 24.0, gpu_vram_usage, # 24 GB VRAM + 64.0, ram_load, + 512.0, disk_usage, disk_temp, + net_in, net_out + )) + + metrics_count += 1 + current_time += timedelta(minutes=5) + + print(f" ✓ Server {server_id}: {metrics_count} Metriken erstellt") + +conn.commit() +print(f"✅ Insgesamt {metrics_count} Metriken erstellt") + +# ============================================================================ +# 4. IMAGES - Docker Images +# ============================================================================ +print("\n🐳 Erstelle Docker Images...") + +images_data = [ + ("nginx", "latest"), + ("nginx", "alpine"), + ("postgres", "15"), + ("postgres", "14-alpine"), + ("redis", "7-alpine"), + ("node", "18-alpine"), + ("python", "3.11-slim"), + ("mysql", "8.0"), + ("traefik", "v2.10"), + ("portainer", "latest") +] + +image_ids = {} +for name, tag in images_data: + cursor.execute(""" + INSERT INTO Images (Name, Tag) + VALUES (?, ?) + """, (name, tag)) + image_ids[f"{name}:{tag}"] = cursor.lastrowid + print(f" ✓ Image '{name}:{tag}' erstellt") + +conn.commit() + +# ============================================================================ +# 5. CONTAINERS - Docker Container +# ============================================================================ +print("\n📦 Erstelle Docker Container...") + +# Nur für Server, die online sind +online_server_ids = [sid for sid in server_ids[:2]] # Erste 2 Server haben Container + +containers_data = [ + # Production-Web-01 + ("nginx-web", "abc123def456", "nginx:latest", online_server_ids[0], True), + ("postgres-db", "def456ghi789", "postgres:15", online_server_ids[0], True), + ("redis-cache", "ghi789jkl012", "redis:7-alpine", online_server_ids[0], True), + ("traefik-proxy", "jkl012mno345", "traefik:v2.10", online_server_ids[0], True), + + # Dev-Server + ("dev-nginx", "mno345pqr678", "nginx:alpine", online_server_ids[1], True), + ("dev-postgres", "pqr678stu901", "postgres:14-alpine", online_server_ids[1], False), + ("dev-redis", "stu901vwx234", "redis:7-alpine", online_server_ids[1], True), + ("test-app", "vwx234yz567", "node:18-alpine", online_server_ids[1], True), + ("portainer", "yz567abc890", "portainer:latest", online_server_ids[1], True), +] + +container_ids = [] +for name, container_id, image, server_id, is_running in containers_data: + cursor.execute(""" + INSERT INTO Containers (Name, ContainerId, Image, ServerId, IsRunning) + VALUES (?, ?, ?, ?, ?) + """, (name, container_id, image, server_id, is_running)) + container_ids.append(cursor.lastrowid) + status = "🟢 Running" if is_running else "🔴 Stopped" + print(f" ✓ Container '{name}' erstellt - {status}") + +conn.commit() + +# ============================================================================ +# 6. CONTAINER METRICS - Container-Metriken (letzte 24 Stunden) +# ============================================================================ +print("\n📊 Erstelle Container-Metriken (letzte 24 Stunden)...") + +container_metrics_count = 0 +for container_id in container_ids: + # Prüfe ob Container läuft + cursor.execute("SELECT IsRunning FROM Containers WHERE Id = ?", (container_id,)) + is_running = cursor.fetchone()[0] + + if not is_running: + continue # Keine Metriken für gestoppte Container + + # Erstelle Metriken für die letzten 24 Stunden (alle 5 Minuten) + start_time = datetime.utcnow() - timedelta(hours=24) + current_time = start_time + + # Basis-Werte für Container (meist niedriger als Host) + base_cpu = random.uniform(5, 15) + base_ram = random.uniform(10, 30) + + while current_time <= datetime.utcnow(): + cpu_load = max(0, min(100, base_cpu + random.gauss(0, 8))) + cpu_temp = 30 + (cpu_load * 0.5) + random.gauss(0, 2) + + ram_load = max(0, min(100, base_ram + random.gauss(0, 5))) + ram_size = random.uniform(0.5, 4.0) # Container nutzen weniger RAM + + cursor.execute(""" + INSERT INTO ContainerMetrics ( + ContainerId, Timestamp, + CPU_Load, CPU_Temp, + RAM_Size, RAM_Load + ) VALUES (?, ?, ?, ?, ?, ?) + """, ( + container_id, current_time, + cpu_load, cpu_temp, + ram_size, ram_load + )) + + container_metrics_count += 1 + current_time += timedelta(minutes=5) + +conn.commit() +print(f"✅ Insgesamt {container_metrics_count} Container-Metriken erstellt") + +# ============================================================================ +# 7. LOG EVENTS - Log-Einträge erstellen +# ============================================================================ +print("\n📝 Erstelle Log Events...") + +log_messages = [ + ("Info", "Server erfolgreich gestartet", None, None), + ("Info", "Backup abgeschlossen", server_ids[3], None), + ("Warning", "CPU-Auslastung über 80%", server_ids[0], None), + ("Info", "Container gestartet", server_ids[0], container_ids[0]), + ("Error", "Datenbank-Verbindung fehlgeschlagen", server_ids[1], container_ids[5]), + ("Warning", "Speicherplatz unter 25%", server_ids[1], None), + ("Info", "Update installiert", server_ids[2], None), + ("Info", "Container neu gestartet", server_ids[0], container_ids[2]), + ("Warning", "GPU-Temperatur über 75°C", server_ids[2], None), + ("Info", "Netzwerk-Check erfolgreich", server_ids[0], None), +] + +for level, message, server_id, container_id in log_messages: + timestamp = datetime.utcnow() - timedelta(hours=random.randint(0, 48)) + cursor.execute(""" + INSERT INTO LogEvents (Timestamp, Message, Level, ServerId, ContainerId) + VALUES (?, ?, ?, ?, ?) + """, (timestamp, message, level, server_id, container_id)) + print(f" ✓ Log: [{level}] {message}") + +conn.commit() + +# ============================================================================ +# 8. TAGS - Tags für Server/Container +# ============================================================================ +print("\n🏷️ Erstelle Tags...") + +tags_data = ["production", "development", "backup", "docker", "monitoring", "critical"] + +for tag_name in tags_data: + cursor.execute(""" + INSERT INTO Tags (Name) + VALUES (?) + """, (tag_name,)) + print(f" ✓ Tag '{tag_name}' erstellt") + +conn.commit() + +# ============================================================================ +# Abschluss +# ============================================================================ +print("\n" + "="*60) +print("✅ Testdaten erfolgreich erstellt!") +print("="*60) + +# Statistiken ausgeben +cursor.execute("SELECT COUNT(*) FROM Servers") +server_count = cursor.fetchone()[0] + +cursor.execute("SELECT COUNT(*) FROM Containers") +container_count = cursor.fetchone()[0] + +cursor.execute("SELECT COUNT(*) FROM Metrics") +metrics_count = cursor.fetchone()[0] + +cursor.execute("SELECT COUNT(*) FROM ContainerMetrics") +container_metrics_count = cursor.fetchone()[0] + +cursor.execute("SELECT COUNT(*) FROM LogEvents") +log_count = cursor.fetchone()[0] + +cursor.execute("SELECT COUNT(*) FROM Images") +image_count = cursor.fetchone()[0] + +cursor.execute("SELECT COUNT(*) FROM Tags") +tag_count = cursor.fetchone()[0] + +print(f""" +📊 STATISTIK: + 🖥️ Server: {server_count} + 📦 Container: {container_count} + 📈 Server-Metriken: {metrics_count} + 📊 Container-Metriken: {container_metrics_count} + 📝 Log Events: {log_count} + 🐳 Images: {image_count} + 🏷️ Tags: {tag_count} + +💡 HINWEIS: + - User-Tabelle wurde nicht verändert + - Metriken wurden für die letzten 48 Stunden generiert + - Server 'Backup-Server' ist offline (für Tests) + - Container 'dev-postgres' ist gestoppt (für Tests) + - Die Datenbank befindet sich unter: {DB_PATH} +""") + +# Verbindung schließen +conn.close() +print("🔒 Datenbankverbindung geschlossen\n") diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index 176d793..63694e4 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -90,7 +90,7 @@
-
+
CPU diff --git a/Watcher/Views/Server/Details.cshtml b/Watcher/Views/Server/Details.cshtml index a2b9759..ffb2aa1 100644 --- a/Watcher/Views/Server/Details.cshtml +++ b/Watcher/Views/Server/Details.cshtml @@ -13,7 +13,7 @@
-
+
@Model.Name
@@ -231,8 +231,8 @@ datasets: [{ label: 'CPU Last (%)', data: [], - borderColor: 'rgba(54, 162, 235, 1)', - backgroundColor: 'rgba(54, 162, 235, 0.1)', + borderColor: 'rgba(13, 202, 240, 1)', + backgroundColor: 'rgba(13, 202, 240, 0.2)', borderWidth: 2, fill: true, tension: 0.4, @@ -284,8 +284,8 @@ datasets: [{ label: 'RAM Last (%)', data: [], - borderColor: 'rgba(75, 192, 192, 1)', - backgroundColor: 'rgba(75, 192, 192, 0.1)', + borderColor: 'rgba(25, 135, 84, 1)', + backgroundColor: 'rgba(25, 135, 84, 0.2)', borderWidth: 2, fill: true, tension: 0.4, @@ -337,8 +337,8 @@ datasets: [{ label: 'GPU Last (%)', data: [], - borderColor: 'rgba(153, 102, 255, 1)', - backgroundColor: 'rgba(153, 102, 255, 0.1)', + borderColor: 'rgba(220, 53, 69, 1)', + backgroundColor: 'rgba(220, 53, 69, 0.2)', borderWidth: 2, fill: true, tension: 0.4, diff --git a/Watcher/Views/Shared/_Layout.cshtml b/Watcher/Views/Shared/_Layout.cshtml index 23e61bf..226fc88 100644 --- a/Watcher/Views/Shared/_Layout.cshtml +++ b/Watcher/Views/Shared/_Layout.cshtml @@ -104,7 +104,7 @@
@User.Identity?.Name
- Profil ansehen + Profil ansehen
@@ -115,7 +115,7 @@ }
- + @{ var statusColor = UpdateCheckStore.IsUpdateAvailable ? "#ffc107" : "#28a745"; var statusTitle = UpdateCheckStore.IsUpdateAvailable @@ -123,7 +123,7 @@ : "System ist aktuell"; } - Version: @VersionService.GetVersion() + Version: @VersionService.GetVersion()
diff --git a/Watcher/wwwroot/css/Login.css b/Watcher/wwwroot/css/Login.css index 4960097..e02fd04 100644 --- a/Watcher/wwwroot/css/Login.css +++ b/Watcher/wwwroot/css/Login.css @@ -19,6 +19,6 @@ } .form-error { - color: #ff6b6b; + color: var(--color-danger); font-size: 0.875rem; } \ No newline at end of file diff --git a/Watcher/wwwroot/css/server-detail.css b/Watcher/wwwroot/css/server-detail.css index 7f9683a..2633530 100644 --- a/Watcher/wwwroot/css/server-detail.css +++ b/Watcher/wwwroot/css/server-detail.css @@ -14,14 +14,14 @@ .info-label { font-size: 0.85rem; font-weight: 500; - color: #6c757d; + color: var(--color-muted); display: flex; align-items: center; } .info-value { font-size: 0.95rem; - color: var(--color-text, #212529); + color: var(--color-text, #f9feff); font-weight: 400; padding-left: 1.25rem; } @@ -37,6 +37,7 @@ text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; + color: var(--color-text) !important; } .card-body h6.text-muted i { @@ -47,7 +48,7 @@ .graphcontainer { height: 25rem; width: 100%; - background-color: var(--color-surface, #f8f9fa); + background-color: var(--color-surface, #212121); border-radius: 0.375rem; } diff --git a/Watcher/wwwroot/css/services-overview.css b/Watcher/wwwroot/css/services-overview.css index cc0779a..c31ebb4 100644 --- a/Watcher/wwwroot/css/services-overview.css +++ b/Watcher/wwwroot/css/services-overview.css @@ -2,7 +2,7 @@ .container-card { transition: transform 0.2s ease, box-shadow 0.2s ease; border: 1px solid rgba(0, 0, 0, 0.125); - background: var(--color-background-secondary, #fff); + background: var(--color-surface); } .container-card:hover { @@ -11,8 +11,8 @@ } .container-card .card-header { - background-color: rgba(0, 0, 0, 0.03); - border-bottom: 1px solid rgba(0, 0, 0, 0.125); + background-color: var(--color-bg); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding: 0.75rem 1rem; } @@ -37,15 +37,24 @@ } .info-label { - color: #6c757d; + color: var(--color-muted); font-weight: 500; } .info-value { - color: var(--color-text, #212529); + color: var(--color-text, #f9feff); word-break: break-all; } +.info-value a { + color: var(--color-text); + transition: color 0.2s ease; +} + +.info-value a:hover { + color: var(--color-primary); +} + /* Action Buttons */ .action-buttons .btn { font-size: 0.85rem; @@ -65,8 +74,8 @@ } .metrics-content { - background-color: #f8f9fa !important; - border: 1px solid #dee2e6; + background-color: var(--color-bg) !important; + border: 1px solid rgba(255, 255, 255, 0.1); } .metric-item { @@ -110,6 +119,6 @@ /* Server Group Header */ h5.text-muted { font-weight: 600; - border-bottom: 2px solid #dee2e6; + border-bottom: 2px solid var(--color-accent); padding-bottom: 0.5rem; } \ No newline at end of file diff --git a/Watcher/wwwroot/css/site.css b/Watcher/wwwroot/css/site.css index 071d263..304ea49 100644 --- a/Watcher/wwwroot/css/site.css +++ b/Watcher/wwwroot/css/site.css @@ -6,6 +6,7 @@ --color-text: #f9feff; --color-muted: #c0c0c0; --color-success: #14a44d; + --color-success-hover: #0f8c3c; --color-danger: #ff6b6b; } @@ -54,9 +55,30 @@ a { } .btn-pocketid:hover { - background-color: #0f8c3c; + background-color: var(--color-success-hover); } hr { border-top: 1px solid var(--color-accent); } + +/* Bootstrap Overrides für Dark Theme */ +.text-muted { + color: var(--color-muted) !important; +} + +.bg-light { + background-color: var(--color-surface) !important; +} + +.text-text { + color: var(--color-text) !important; +} + +.text-primary-emphasis { + color: var(--color-primary) !important; +} + +.border-secondary { + border-color: rgba(255, 255, 255, 0.2) !important; +} diff --git a/Watcher/wwwroot/css/user-info.css b/Watcher/wwwroot/css/user-info.css index 89c5118..ebbca38 100644 --- a/Watcher/wwwroot/css/user-info.css +++ b/Watcher/wwwroot/css/user-info.css @@ -1,6 +1,5 @@ .table { - color: red; - + color: var(--color-text); } .picture { -- 2.49.1 From 454d651d4d9dff07c8f89343017e0c999c83ad23 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Thu, 6 Nov 2025 21:15:13 +0100 Subject: [PATCH 54/54] UI stuff --- Watcher/Views/Server/_ServerCard.cshtml | 196 +++++++++++++++++++----- Watcher/Views/System/Settings.cshtml | 193 +++++++++++------------ Watcher/wwwroot/css/server-detail.css | 18 ++- Watcher/wwwroot/css/server-overview.css | 106 +++++++++++++ Watcher/wwwroot/css/settings.css | 162 ++++++++++++++++++-- 5 files changed, 518 insertions(+), 157 deletions(-) diff --git a/Watcher/Views/Server/_ServerCard.cshtml b/Watcher/Views/Server/_ServerCard.cshtml index 447a6f2..87ecf7d 100644 --- a/Watcher/Views/Server/_ServerCard.cshtml +++ b/Watcher/Views/Server/_ServerCard.cshtml @@ -1,51 +1,169 @@ @model IEnumerable -
-
- @foreach (var s in Model) - { -
-
-
-
-
- (#@s.Id) @s.Name -
- -
-
IP: @s.IPAddress
-
Typ: @s.Type
-
- - - - @(s.IsOnline ? "Online" : "Offline") +
+ @foreach (var server in Model) + { +
+
+
+
+ + @server.Name +
+ + + @(server.IsOnline ? "Online" : "Offline") + +
+
+ +
+
+ IP: + @server.IPAddress +
+
+ Typ: + @server.Type +
+
+ CPU: + @(server.CpuCores > 0 ? $"{server.CpuCores} Cores" : "N/A") +
+
+ RAM: + + @if (server.RamSize > 0) + { + var ramGB = server.RamSize / (1024.0 * 1024.0 * 1024.0); + @($"{ramGB:F1} GB") + } + else + { + N/A + } -
- - - Bearbeiten - - - - Metrics - - -
- -
+
+
+ + @if (server.IsOnline) + { +
+
Aktuelle Last
+
+
+
+ CPU + -- +
+
+
+
+
+
+
+ RAM + -- +
+
+
+
+
+ @if (!string.IsNullOrEmpty(server.GpuType)) + { +
+
+ GPU + -- +
+
+
+
+
+ }
+ } + +
+ + Details + + + + Edit + +
+ +
+
+ +
+
+ } +
+ + \ No newline at end of file diff --git a/Watcher/Views/System/Settings.cshtml b/Watcher/Views/System/Settings.cshtml index aa4b0c5..256016d 100644 --- a/Watcher/Views/System/Settings.cshtml +++ b/Watcher/Views/System/Settings.cshtml @@ -12,107 +12,96 @@ -
+
+

+ Einstellungen +

+ + +
+
+
+ Systemeinformationen +
+
+
+ +
+
+
+ Version +
+
@ServerVersion
+
+ +
+
+ Authentifizierung +
+
@(ViewBag.IdentityProvider ?? "nicht gefunden")
+
+ +
+
+ Datenbank-Engine +
+
@(DbEngine ?? "nicht gefunden")
+
+ + @if (ViewBag.DatabaseSize != null) + { +
+
+ Datenbankgröße +
+
@ViewBag.DatabaseSize
+
+ } +
+ + + @if (DbEngine == "SQLite" || DbEngine == "Microsoft.EntityFrameworkCore.Sqlite") + { +
+
+ Datenbank-Backups +
+
+
+ +
+
+ +
+
+ } + else if (DbEngine == "Microsoft.EntityFrameworkCore.MySQL") + { +
+
+ MySQL Dump aktuell nicht möglich +
+ } + + + @if (TempData["DumpMessage"] != null) + { +
+ @TempData["DumpMessage"] +
+ } + @if (TempData["DumpError"] != null) + { +
+ @TempData["DumpError"] +
+ } +
+
+ -
-

Systemeinformationen

- -
- -
Watcher Version: @ServerVersion
- -
- -
Authentifizierungsmethode: @(ViewBag.IdentityProvider ?? "nicht gefunden")
- - -
- -
Datenbank-Engine: @(DbEngine ?? "nicht gefunden")
- - @if (ViewBag.DatabaseSize != null) - { -
Datenbankgröße: @ViewBag.DatabaseSize
- } - - - @if (DbEngine == "SQLite" || DbEngine == "Microsoft.EntityFrameworkCore.Sqlite") - { -
-
- -
- -
- -
-
- - } - else if (DbEngine == "Microsoft.EntityFrameworkCore.MySQL") - { -

MySQL Dump aktuell nicht möglich

- } - - - @if (TempData["DumpMessage"] != null) - { -
- @TempData["DumpMessage"] -
- } - @if (TempData["DumpError"] != null) - { -
- @TempData["DumpError"] -
- } - -
- -
-

Benachrichtungen

- -

Registrierte E-Mail Adresse: @(ViewBag.mail ?? "nicht gefunden")

- - -
-
-
- Benachrichtigungseinstellungen -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- - -
-
- -
- -
diff --git a/Watcher/wwwroot/css/server-detail.css b/Watcher/wwwroot/css/server-detail.css index 2633530..49c7e4b 100644 --- a/Watcher/wwwroot/css/server-detail.css +++ b/Watcher/wwwroot/css/server-detail.css @@ -14,16 +14,25 @@ .info-label { font-size: 0.85rem; font-weight: 500; - color: var(--color-muted); + color: var(--color-muted) !important; display: flex; align-items: center; } .info-value { font-size: 0.95rem; - color: var(--color-text, #f9feff); font-weight: 400; padding-left: 1.25rem; + color: var(--color-text, #f9feff) !important; +} + +/* All text within info-value should be visible */ +.info-value, +.info-value *, +.info-value span, +.info-value .text-muted, +.info-value .text-success { + color: var(--color-text, #f9feff) !important; } .info-value .text-muted { @@ -44,6 +53,11 @@ font-size: 1rem; } +/* Description and other text-muted paragraphs */ +.card-body p.text-muted { + color: var(--color-text) !important; +} + /* Graph Container */ .graphcontainer { height: 25rem; diff --git a/Watcher/wwwroot/css/server-overview.css b/Watcher/wwwroot/css/server-overview.css index e69de29..dc7ec97 100644 --- a/Watcher/wwwroot/css/server-overview.css +++ b/Watcher/wwwroot/css/server-overview.css @@ -0,0 +1,106 @@ +/* Server Card Styling */ +.server-card { + transition: transform 0.2s ease, box-shadow 0.2s ease; + border: 1px solid rgba(0, 0, 0, 0.125); + background: var(--color-surface); + color: var(--color-text); +} + +.server-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; +} + +.server-card .card-header { + background-color: var(--color-bg); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding: 0.75rem 1rem; +} + +.server-card .card-body { + padding: 1rem; +} + +/* Ensure all text in server-card uses light color */ +.server-card, +.server-card * { + color: var(--color-text); +} + +/* Allow Bootstrap badge colors to work */ +.server-card .badge { + color: white; +} + +/* Allow button colors to work */ +.server-card .btn { + color: inherit; +} + +/* Server Info Rows */ +.server-info { + font-size: 0.9rem; +} + +.info-row { + display: flex; + justify-content: space-between; + padding: 0.4rem 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); +} + +.info-row:last-child { + border-bottom: none; +} + +.info-label { + color: var(--color-muted); + font-weight: 500; +} + +.info-value { + word-break: break-all; +} + +/* Current Metrics Section */ +.current-metrics h6 { + font-size: 0.9rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.metric-bar-item { + margin-bottom: 0.5rem; +} + +.metric-bar-item:last-child { + margin-bottom: 0; +} + +.progress { + background-color: rgba(255, 255, 255, 0.1); +} + +/* Action Buttons */ +.action-buttons .btn { + font-size: 0.85rem; + padding: 0.4rem 0.6rem; +} + +/* Responsive Adjustments */ +@media (max-width: 768px) { + .action-buttons { + flex-direction: column; + } + + .action-buttons .btn { + width: 100%; + } +} + +/* Badge Styling */ +.badge { + font-size: 0.75rem; + padding: 0.35em 0.65em; +} diff --git a/Watcher/wwwroot/css/settings.css b/Watcher/wwwroot/css/settings.css index 871de0b..c870171 100644 --- a/Watcher/wwwroot/css/settings.css +++ b/Watcher/wwwroot/css/settings.css @@ -1,24 +1,158 @@ -.Settingscontainer { +/* Settings Card Styling - gleicher Stil wie Server Cards */ +.settings-card { + background: var(--color-surface); + border: 1px solid rgba(255, 255, 255, 0.1); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.settings-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; +} + +.settings-card .card-header { + background-color: var(--color-bg); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding: 0.75rem 1rem; +} + +.settings-card .card-header h5 { + color: var(--color-text); + font-weight: 600; + font-size: 1rem; +} + +.settings-card .card-body { + padding: 1rem; +} + +/* Info Table Layout - ähnlich wie Server Info Rows */ +.info-table { display: flex; + flex-direction: column; + gap: 0; +} + +.info-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + transition: background-color 0.2s ease; +} + +.info-row:last-child { + border-bottom: none; +} + +.info-row:hover { + background-color: rgba(255, 255, 255, 0.02); +} + +.info-label { + color: var(--color-muted); + font-weight: 500; + font-size: 0.9rem; + display: flex; + align-items: center; +} + +.info-label i { + color: var(--color-accent); +} + +.info-value { + color: var(--color-text); + font-weight: 600; + font-size: 0.9rem; + text-align: right; +} + +/* Subsection Headers */ +.settings-card h6 { + color: var(--color-text); + font-weight: 600; + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Backup Buttons */ +.backup-buttons { + display: flex; + gap: 0.75rem; flex-wrap: wrap; - /* Wichtig: erlaubt Umbruch */ - gap: 1rem; - /* optionaler Abstand */ } -.Settingscontainer>* { - flex: 1 1 calc(50% - 0.5rem); - /* 2 Elemente pro Zeile, inkl. Gap */ - box-sizing: border-box; +.backup-buttons form { + flex: 1; + min-width: 200px; } -.btn-db { - background-color: var(--color-primary); - border: none; +.backup-buttons .btn { + width: 100%; } -.btn-db:hover { - background-color: var(--color-accent); - border: none; +/* Button Styling */ +.settings-card .btn { + font-weight: 500; + font-size: 0.85rem; + padding: 0.5rem 0.75rem; + transition: all 0.2s ease; } +.settings-card .btn-outline-primary { + border-color: var(--color-accent) !important; + color: var(--color-accent) !important; + background-color: transparent !important; +} + +.settings-card .btn-outline-primary:hover { + background-color: var(--color-accent) !important; + border-color: var(--color-accent) !important; + color: white !important; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); +} + +.settings-card .btn-outline-primary:active, +.settings-card .btn-outline-primary:focus, +.settings-card .btn-outline-primary:focus-visible { + background-color: var(--color-accent) !important; + border-color: var(--color-accent) !important; + color: white !important; +} + +/* HR Styling */ +.settings-card hr { + border-top: 1px solid rgba(255, 255, 255, 0.1); + margin: 1.5rem 0; +} + +/* Alert Styling */ +.alert { + border-radius: 0.375rem; +} + +/* Responsive */ +@media (max-width: 768px) { + .info-row { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + padding: 0.75rem; + } + + .info-value { + text-align: left; + } + + .backup-buttons { + flex-direction: column; + } + + .backup-buttons form { + width: 100%; + } +} -- 2.49.1