diff --git a/.gitea/workflows/build-and-deploy.yaml b/.gitea/workflows/build-and-deploy.yaml deleted file mode 100644 index dbdc8af..0000000 --- a/.gitea/workflows/build-and-deploy.yaml +++ /dev/null @@ -1,46 +0,0 @@ -name: Build and Deploy - -on: - push: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup .NET 8 SDK - uses: actions/setup-dotnet@v3 - with: - dotnet-version: '8.0.x' - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --configuration Release --no-restore - - - name: Test (optional) - run: dotnet test --no-build --verbosity normal - - - name: Publish - run: dotnet publish -c Release -o out - - - name: Build Docker Image - run: docker build -t watcher:latest . - - - name: Docker Login - run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - - - name: Push Docker Image - run: docker push watcher:latest - - - name: Deploy (Beispiel - via SSH) - run: | - ssh daniel-hbn@git.triggermeelmo.com "docker pull deine-app:latest && docker stop watcher || true && docker rm watcher || true && docker run -d --name watcher -p 5000:5000 deine-app:latest" - env: - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} diff --git a/.gitea/workflows/development-build.yaml b/.gitea/workflows/development-build.yaml new file mode 100644 index 0000000..97e5dcb --- /dev/null +++ b/.gitea/workflows/development-build.yaml @@ -0,0 +1,62 @@ +name: Development Build and Release + +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 + 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 }}:development \ + -t ${{ env.REGISTRY_URL }}/${{ env.DOCKER_IMAGE_NAME }}:${{ github.sha }} \ + --push . diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml new file mode 100644 index 0000000..5d530ab --- /dev/null +++ b/.gitea/workflows/release.yaml @@ -0,0 +1,49 @@ +name: Development Build + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup .NET 8 SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test (optional) + run: dotnet test --no-build --verbosity normal + + - name: Publish + run: dotnet publish -c Release -o out + + - name: Build Docker Image + run: docker build -t watcher:v0.1.0 . + + - name: Login to Gitea Container Registry + run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.triggermeelmo.com -u ${{ secrets.DOCKER_USERNAME }} --password-stdin + + - name: Tag image + run: docker tag watcher:v0.1.0 git.triggermeelmo.com/daniel-hbn/watcher/watcher:v0.1.0 + + - name: Push image + run: docker push git.triggermeelmo.com/daniel-hbn/watcher/watcher:v0.1.0 + + - name: Tag image + run: docker tag watcher:v0.1.0 git.triggermeelmo.com/daniel-hbn/watcher/watcher:latest + + - name: Push image + run: docker push git.triggermeelmo.com/daniel-hbn/watcher/watcher:latest \ No newline at end of file diff --git a/.gitignore b/.gitignore index cc031e3..940422c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ +# Per-Use special Files and Directories +/persistance/*.db +/persistance/*.db-shm +/persistance/*.db-wal +/logs/*.log +*.env +/wwwroot/downloads/sqlite/*.sql + +/persistence/*.db-shm +/persistence/*.db-wal + # Build-Ordner bin/ obj/ diff --git a/Dockerfile b/Dockerfile index fad3ec9..a7adde3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,13 +13,20 @@ WORKDIR /app/Watcher RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false # 2. Laufzeit-Phase: ASP.NET Core Runtime -FROM mcr.microsoft.com/dotnet/aspnet:9.0 +FROM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app COPY --from=build /app/publish . -# Exponiere Port 80 und 443 (HTTP + HTTPS) -EXPOSE 80 -EXPOSE 443 +# Stelle sicher, dass Verzeichnisse existieren +RUN mkdir -p /app/persistence /app/wwwroot/downloads/sqlite /app/logs + +# Volumes +VOLUME ["/app/persistence", "/app/wwwroot/downloads/sqlite", "/app/logs"] + +# Expose Port 5000 +EXPOSE 5000 +ENV ASPNETCORE_URLS=http://*:5000 +ENV ASPNETCORE_ENVIRONMENT=Development # Anwendung starten ENTRYPOINT ["dotnet", "Watcher.dll"] diff --git a/README.md b/README.md index a48d1cc..7fc7e09 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,34 @@ -# Watcher +# 🖥️ Server Monitoring Software -Monitoring Tool für Server, VMs und Docker Container. \ No newline at end of file +Dieses Projekt ist eine umfassende Monitoring-Lösung für Server und Host-Geräte. +Die Software besteht aus zwei Teilen: +- **Host Agent**: Sammelt Hardware-Daten von den Hosts +- **Zentrale Monitoring-Software**: Visualisiert und verwaltet die gesammelten Daten + +Die Lösung unterstützt moderne Authentifizierungsverfahren und flexible Datenbankanbindungen – alles bequem verteilt als Docker Image. + +--- + +## ✨ Features + +- Erfassung von Hardware-Daten aller wichtigen Komponenten +- Lokale Authentifizierung oder OIDC (OpenID Connect) +- Wahlweise Speicherung der Daten in: + - Lokaler SQLite-Datenbank + - Remote MySQL-Datenbank +- Einfache Bereitstellung via Docker & docker-compose + +--- + +## ⚙️ Installation & Start + +Voraussetzung: +- [Docker](https://www.docker.com/) +- [docker-compose](https://docs.docker.com/compose/) + +1. Image herunterladen: + ```bash + docker pull git.triggermeelmo.com/daniel-hbn/watcher/watcher:latest +2. Docker Container starten + ```bash + docker compose up -d \ No newline at end of file diff --git a/Watcher/.env.example b/Watcher/.env.example new file mode 100644 index 0000000..032d787 --- /dev/null +++ b/Watcher/.env.example @@ -0,0 +1,8 @@ + +# 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 diff --git a/Watcher/Controllers/AuthController.cs b/Watcher/Controllers/AuthController.cs index 971a3ff..b24110d 100644 --- a/Watcher/Controllers/AuthController.cs +++ b/Watcher/Controllers/AuthController.cs @@ -1,41 +1,119 @@ +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 { - public IActionResult Login() + private readonly AppDbContext _context; + private readonly AppSettings _settings; + + // Logging einbinden + private readonly ILogger _logger; + + + public AuthController(AppDbContext context, IOptions options, ILogger logger) { - return View(); + _context = context; + _settings = options.Value; + _logger = logger; } + + // Login Seite anzeigen + [HttpGet("/Auth/Login")] + public IActionResult Login(string? returnUrl = null) + { + var model = new LoginViewModel + { + ReturnUrl = returnUrl + }; + + ViewBag.oidc = _settings.oidc; + return View(model); + } + + + // Login mit lokalem User + [HttpPost] + public async Task Login(LoginViewModel model) + { + if (!ModelState.IsValid) + return View(model); + + try + { + var user = await _context.Users.FirstOrDefaultAsync(u => u.Username == model.Username); + if (user == null || !BCrypt.Net.BCrypt.Verify(model.Password, user.Password)) + { + ModelState.AddModelError("", "Benutzername oder Passwort ist falsch."); + return View(model); + } + + var claims = new List + { + new Claim(ClaimTypes.Name, user.Username), + new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), + }; + + var identity = new ClaimsIdentity(claims, "local"); + var principal = new ClaimsPrincipal(identity); + + await HttpContext.SignInAsync("Cookies", principal); + + _logger.LogInformation("lokaler User angemeldet: " + user.Username); + + return Redirect("/"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Fehler beim Login. User: {UserName}", model.Username); + ModelState.AddModelError("", "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut."); + return View(model); + } + } + + + // Login mit OIDC-Provider public IActionResult SignIn() { return Challenge(new AuthenticationProperties { - RedirectUri = "/" + RedirectUri = "/Home/Index" }, "oidc"); } + + + // Logout [HttpPost] [ValidateAntiForgeryToken] public async Task Logout() { - await HttpContext.SignOutAsync(); - return RedirectToAction("Index", "Home"); - } + var props = new AuthenticationProperties + { + RedirectUri = Url.Action("Login", "Auth") + }; - [Authorize] - public IActionResult Info() - { - var name = User.Identity?.Name; - var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList(); + await HttpContext.SignOutAsync("Cookies"); + await HttpContext.SignOutAsync("oidc", props); - ViewBag.Name = name; - ViewBag.Claims = claims; + _logger.LogInformation("User abgemeldet"); - return View(); + return Redirect("/"); // nur als Fallback } } diff --git a/Watcher/Controllers/ContainerController.cs b/Watcher/Controllers/ContainerController.cs index 624a1a8..b18e4dd 100644 --- a/Watcher/Controllers/ContainerController.cs +++ b/Watcher/Controllers/ContainerController.cs @@ -7,7 +7,7 @@ using Watcher.ViewModels; namespace Watcher.Controllers; - +[Authorize] public class ContainerController : Controller { private readonly AppDbContext _context; @@ -20,41 +20,15 @@ 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 }; return View(viewModel); } - - [HttpGet] - public IActionResult AddContainer() - { - return View(); - } - - [HttpPost] - public async Task AddContainer(AddContainerViewModel vm) - { - if (!ModelState.IsValid) - return View(vm); - - var container = new Container - { - Name = vm.Name, - Image = vm.Image, - Status = vm.Status, - Hostname = vm.Hostname, - IsRunning = vm.IsRunning, - CreatedAt = DateTime.UtcNow - }; - - _context.Containers.Add(container); - await _context.SaveChangesAsync(); - - return RedirectToAction("Index"); - } } diff --git a/Watcher/Controllers/DatabaseController.cs b/Watcher/Controllers/DatabaseController.cs new file mode 100644 index 0000000..d6cb17f --- /dev/null +++ b/Watcher/Controllers/DatabaseController.cs @@ -0,0 +1,200 @@ +using System.Runtime.InteropServices; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using System.IO; +using System.Diagnostics; + +namespace Watcher.Controllers +{ + [Authorize] + public class DatabaseController : Controller + { + // Backup Infos + private readonly string _dbPath = Path.Combine(Directory.GetCurrentDirectory(), "persistence", "watcher.db"); + private readonly string _backupFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "downloads", "sqlite"); + + // Services + private readonly ILogger _logger; + + public class DumpFileInfo + { + public string? FileName { get; set; } + public long SizeKb { get; set; } + public DateTime Created { get; set; } + } + + + public DatabaseController(IWebHostEnvironment env, ILogger logger) + { + _logger = logger; + + _backupFolder = Path.Combine(env.WebRootPath, "downloads", "sqlite"); + + if (!Directory.Exists(_backupFolder)) + { + _logger.LogInformation("Backup-Directory neu erstellt"); + Directory.CreateDirectory(_backupFolder); + } + else + { + _logger.LogInformation("Backup-Directory existiert bereits"); + } + + } + + [HttpPost("/maintenance/sqlite-dump")] + public IActionResult CreateSqlDump() + { + try + { + // Zielordner sicherstellen + if (!Directory.Exists(_backupFolder)) + Directory.CreateDirectory(_backupFolder); + + // Ziel-Dateiname z. B. mit Zeitstempel + var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var dumpFileName = $"watcher_dump_{timestamp}.sql"; + var dumpFilePath = Path.Combine(_backupFolder, dumpFileName); + + using var connection = new SqliteConnection($"Data Source={_dbPath}"); + connection.Open(); + + using var command = connection.CreateCommand(); + command.CommandText = "SELECT sql FROM sqlite_master WHERE type='table' AND sql IS NOT NULL;"; + using var writer = new StreamWriter(dumpFilePath); + + // Write schema + using (var schemaCmd = connection.CreateCommand()) + { + schemaCmd.CommandText = "SELECT sql FROM sqlite_master WHERE type='table'"; + using var reader = schemaCmd.ExecuteReader(); + while (reader.Read()) + { + writer.WriteLine(reader.GetString(0) + ";"); + } + } + + // Write content + using (var tableCmd = connection.CreateCommand()) + { + tableCmd.CommandText = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"; + using var tableReader = tableCmd.ExecuteReader(); + while (tableReader.Read()) + { + var tableName = tableReader.GetString(0); + using var dataCmd = connection.CreateCommand(); + dataCmd.CommandText = $"SELECT * FROM {tableName}"; + using var dataReader = dataCmd.ExecuteReader(); + + while (dataReader.Read()) + { + var columns = new string[dataReader.FieldCount]; + var values = new string[dataReader.FieldCount]; + + for (int i = 0; i < dataReader.FieldCount; i++) + { + columns[i] = dataReader.GetName(i); + var val = dataReader.GetValue(i); + values[i] = val == null || val == DBNull.Value + ? "NULL" + : $"'{val.ToString().Replace("'", "''")}'"; + } + + writer.WriteLine($"INSERT INTO {tableName} ({string.Join(",", columns)}) VALUES ({string.Join(",", values)});"); + } + } + } + + writer.Flush(); + + //return Ok($"Dump erfolgreich erstellt: {dumpFileName}"); + + TempData["DumpMessage"] = "SQLite-Dump erfolgreich erstellt."; + _logger.LogInformation("SQLite-Dump erfolgreich erstellt."); + return RedirectToAction("UserSettings", "User"); + } + catch (Exception ex) + { + //return StatusCode(500, $"Fehler beim Erstellen des Dumps: {ex.Message}"); + TempData["DumpError"] = $"Fehler beim Erstellen des Dumps: {ex.Message}"; + _logger.LogError("Fehler beim Erstellen des Dumps: {message}", ex.Message); + return RedirectToAction("UserSettings", "User"); + } + } + + public IActionResult ManageSqlDumps() + { + var files = Directory.GetFiles(_backupFolder, "*.sql") + .Select(f => new DumpFileInfo + { + FileName = Path.GetFileName(f), + SizeKb = new FileInfo(f).Length / 1024, + Created = System.IO.File.GetCreationTime(f) + }) + .OrderByDescending(f => f.Created) + .ToList(); + + return View(files); + } + + [HttpPost] + public IActionResult Delete(string fileName) + { + var filePath = Path.Combine(_backupFolder, fileName); + if (System.IO.File.Exists(filePath)) + { + System.IO.File.Delete(filePath); + TempData["Success"] = $"Backup {fileName} wurde gelöscht."; + } + + return RedirectToAction("ManageSqlDumps"); + } + + // 🔹 4. Dump wiederherstellen + [HttpPost] + public IActionResult Restore(string fileName) + { + var filePath = Path.Combine(_backupFolder, fileName); + if (!System.IO.File.Exists(filePath)) + { + TempData["Error"] = "Dump nicht gefunden."; + return RedirectToAction("ManageSqlDumps"); + } + + try + { + var dbPath = Path.Combine(Directory.GetCurrentDirectory(), "persistence", "watcher.db"); // anpassen + + // Leere Datenbank + System.IO.File.WriteAllText(dbPath, ""); + + var psi = new ProcessStartInfo + { + FileName = "sqlite3", + Arguments = $"\"{dbPath}\" \".read \\\"{filePath}\\\"\"", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + using var proc = Process.Start(psi); + proc.WaitForExit(); + + TempData["Success"] = $"Backup {fileName} wurde wiederhergestellt."; + } + catch (Exception ex) + { + TempData["Error"] = $"Fehler beim Wiederherstellen: {ex.Message}"; + } + + return RedirectToAction("ManageSqlDumps"); + } + } + +} \ No newline at end of file diff --git a/Watcher/Controllers/DownloadController.cs b/Watcher/Controllers/DownloadController.cs new file mode 100644 index 0000000..b13d765 --- /dev/null +++ b/Watcher/Controllers/DownloadController.cs @@ -0,0 +1,52 @@ +using System.Runtime.InteropServices; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace Watcher.Controllers; + +[Authorize] +public class DownloadController : Controller +{ + // Logging einbinden + private readonly ILogger _logger; + + public DownloadController(ILogger logger) + { + _logger = logger; + } + + // Allgemeiner Download von Dateien + [Authorize] + [HttpGet("Download/File/{type}/{filename}")] + public IActionResult FileDownload(string type, string filename) + { + // Nur erlaubte Endungen zulassen (Sicherheit!) + // TODO: aktuelles "" für Binaries ist das absolute Gegenteil von sicher, ich hab aber keine bessere Idee + var allowedExtensions = new[] { ".exe", "", ".sql" }; + var extension = Path.GetExtension(filename).ToLowerInvariant(); + + if (!allowedExtensions.Contains(extension)) + return BadRequest("Dateityp nicht erlaubt"); + + var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "downloads", type, filename); + + if (!System.IO.File.Exists(path)) + { + _logger.LogError("Download fehlgeschlagen: Datei nicht gefunden ({path})", path); + return NotFound("Datei nicht gefunden"); + } + + // .exe MIME-Typ: application/octet-stream + var mimeType = "application/octet-stream"; + + var fileBytes = System.IO.File.ReadAllBytes(path); + + _logger.LogTrace("Download ausgeführt: {filename}", filename); + return File(fileBytes, mimeType, filename); + //return File(fileBytes, filename); + } +} diff --git a/Watcher/Controllers/HeartbeatController.cs b/Watcher/Controllers/HeartbeatController.cs index 10015a9..1203b4f 100644 --- a/Watcher/Controllers/HeartbeatController.cs +++ b/Watcher/Controllers/HeartbeatController.cs @@ -1,13 +1,10 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; using Watcher.Data; using Watcher.Models; -using Watcher.ViewModels; +// eingehender Heartbeat-Payload public class HeartbeatDto { public string? IpAddress { get; set; } @@ -18,18 +15,27 @@ public class HeartbeatDto public class HeartbeatController : Controller { + // Datenbankverbindung private readonly AppDbContext _context; - public HeartbeatController(AppDbContext context) + // Logging einbinden + private readonly ILogger _logger; + + public HeartbeatController(AppDbContext context, ILogger logger) { _context = context; + _logger = logger; } + // Endpoint um Heartbeat-Payloads zu empfangen [HttpPost("receive")] public async Task Receive([FromBody] HeartbeatDto dto) { + _logger.LogInformation("Heartbeat empfangen"); + if (string.IsNullOrWhiteSpace(dto.IpAddress)) { + _logger.LogWarning("Eingehender Payload enthält keine IP-Adresse"); return BadRequest("Missing IP address."); } @@ -40,9 +46,11 @@ public class HeartbeatController : Controller { server.LastSeen = DateTime.UtcNow; await _context.SaveChangesAsync(); + _logger.LogInformation("Heartbeat von {server} in Datenbank eingetragen.", server.Name); return Ok(); } + _logger.LogWarning("Eingehender Payload kann keiner bekannten IP-Adresse zugewiesen werden."); return NotFound("No matching server found."); } } diff --git a/Watcher/Controllers/HomeController.cs b/Watcher/Controllers/HomeController.cs index 83df394..f043b7c 100644 --- a/Watcher/Controllers/HomeController.cs +++ b/Watcher/Controllers/HomeController.cs @@ -1,9 +1,9 @@ +// .NET Packages using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using System.Security.Claims; +// Local Namespaces using Watcher.Data; -using Watcher.Models; using Watcher.ViewModels; namespace Watcher.Controllers @@ -11,19 +11,28 @@ namespace Watcher.Controllers [Authorize] public class HomeController : Controller { + // Datenbankverbindung private readonly AppDbContext _context; - public HomeController(AppDbContext context) + // Logging einbinden + private readonly ILogger _logger; + + // HomeController Constructor + public HomeController(AppDbContext context, ILogger logger) { _context = context; + _logger = logger; } + // Dashboard unter /home/index + [HttpGet("/")] public async Task Index() { - var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var preferredUserName = User.FindFirst("preferred_username")?.Value; + var user = await _context.Users - .Where(u => u.PocketId == userId) + .Where(u => u.Username == preferredUserName) .FirstOrDefaultAsync(); var viewModel = new DashboardViewModel @@ -37,5 +46,30 @@ namespace Watcher.Controllers return View(viewModel); } + + // Funktion für /Views/Home/Index.cshtml um das DashboardStats-Partial neu zu laden. + // Die Funktion wird nicht direkt aufgerufen, sondern nur der /Home/DashboardStats Endpoint angefragt. + public IActionResult DashboardStats() + { + var servers = _context.Servers.ToList(); + var containers = _context.Containers.ToList(); + + var now = DateTime.UtcNow; + + var model = new DashboardViewModel + { + ActiveServers = servers.Count(s => (now - s.LastSeen).TotalSeconds <= 120), + OfflineServers = servers.Count(s => (now - s.LastSeen).TotalSeconds > 120), + + //TODO: anwendbar, wenn Container implementiert wurden. + //RunningContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds <= 120), + //FailedContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds > 120), + + LastLogin = DateTime.Now + }; + + return PartialView("_DashboardStats", model); + } + } } diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs new file mode 100644 index 0000000..3357bf2 --- /dev/null +++ b/Watcher/Controllers/MonitoringController.cs @@ -0,0 +1,284 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.CodeAnalysis; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using Watcher.Data; +using Watcher.Models; +using Watcher.ViewModels; + +namespace Watcher.Controllers; + +public class RegistrationDto +{ + // Server Identity + [Required] + public int Id { get; set; } + + [Required] + public string? IpAddress { get; set; } + + // Hardware Info + [Required] + public string? CpuType { get; set; } + + [Required] + public int CpuCores { get; set; } + + [Required] + public string? GpuType { get; set; } + + [Required] + public double RamSize { get; set; } + +} + +public class MetricDto +{ + // Server Identity + [Required] + public int ServerId { get; set; } + + [Required] + public string? IpAddress { get; set; } + + // Hardware Metrics + // CPU + public double CPU_Load { get; set; } // % + + public double CPU_Temp { get; set; } // deg C + + // GPU + public double GPU_Load { get; set; } // % + + public double GPU_Temp { get; set; } // deg C + + public double GPU_Vram_Size { get; set; } // GB + + public double GPU_Vram_Usage { get; set; } // % + + // RAM + public double RAM_Size { get; set; } // GB + + public double RAM_Load { get; set; } // % + + // Disks + public double DISK_Size { get; set; } // GB + + public double DISK_Usage { get; set; } // % + + public double DISK_Temp { get; set; } // deg C + + // Network + public double NET_In { get; set; } // Bit + + public double NET_Out { get; set; } // Bit +} + + +[ApiController] +[Route("[controller]")] +public class MonitoringController : Controller +{ + private readonly AppDbContext _context; + + private readonly ILogger _logger; + + public MonitoringController(AppDbContext context, ILogger logger) + { + _context = context; + _logger = logger; + } + + + // Endpoint, an dem sich neue Agents registrieren + [HttpPost("register-agent-by-id")] + public async Task Register([FromBody] RegistrationDto 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("Fehlerhafter Registrierungs-Payload."); + return BadRequest(new { error = "Ungültiger Payload", details = errors }); + } + + // Server in Datenbank finden + var server = await _context.Servers + .FirstOrDefaultAsync(s => s.IPAddress == dto.IpAddress); + + if (server != null) + { + // Serverdaten in Datenbank eintragen + server.CpuType = dto.CpuType; + server.CpuCores = dto.CpuCores; + server.GpuType = dto.GpuType; + server.RamSize = dto.RamSize; + + // Änderungen in Datenbank speichern + await _context.SaveChangesAsync(); + + // Success + _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")] + public async Task GetServerIdByIp([FromQuery] string IpAddress) + { + var server = await _context.Servers + .FirstOrDefaultAsync(s => s.IPAddress == IpAddress); + + if (server == null) + return NotFound(); + + return Ok(new + { + id = server.Id, + IpAddress = server.IPAddress + }); + } + + + // Enpoint, an den Agents Ihre gesammelten Daten senden + [HttpPost("metric")] + public async Task Receive([FromBody] MetricDto 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 Monitoring-Payload."); + return BadRequest(new { error = "Ungültiger Payload", details = errors }); + } + + // Server in Datenbank finden + var server = await _context.Servers + .FirstOrDefaultAsync(s => s.IPAddress == dto.IpAddress); + + if (server != null) + { + var NewMetric = new Metric + { + Timestamp = DateTime.UtcNow, + ServerId = dto.ServerId, + CPU_Load = dto.CPU_Load, + CPU_Temp = dto.CPU_Temp, + GPU_Load = dto.GPU_Load, + GPU_Temp = dto.GPU_Temp, + GPU_Vram_Size = dto.GPU_Vram_Size, + GPU_Vram_Usage = dto.GPU_Vram_Usage, + RAM_Load = dto.RAM_Load, + RAM_Size = dto.RAM_Size, + DISK_Size = dto.RAM_Size, + DISK_Usage = dto.DISK_Usage, + DISK_Temp = dto.DISK_Temp, + NET_In = dto.NET_In, + NET_Out = dto.NET_Out + }; + try + { + _context.Metrics.Add(NewMetric); + await _context.SaveChangesAsync(); + + _logger.LogInformation("Monitoring-Daten für '{server}' empfangen", server.Name); + return Ok(); + } + catch + { + // Alert triggern + + _logger.LogError("Metric für {server} konnte nicht in Datenbank geschrieben werden.", server.Name); + return BadRequest(); + } + } + + _logger.LogError("Kein Server für eingegangenen Monitoring-Payload gefunden"); + return NotFound("No Matching Server found."); + + } + + + // 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 + DateTime TimeToMonitor = DateTime.Now.AddHours(-HoursToMonitor); + + // Alle Metrics von Server "ServerId" finden, die in der festgelegten Zeitspanne liegen + var MetricsInTimeFrame = await _context.Metrics + .Where(e => e.Timestamp >= TimeToMonitor && e.ServerId == ServerId) + .ToListAsync(); + + // return Action (MetricsInTimeFrame) + + return NotFound(); + } + + [HttpGet("cpu-usage")] + public async Task GetCpuUsageData(int serverId) + { + var oneDayAgo = DateTime.UtcNow.AddDays(-1); + var data = await _context.Metrics + .Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId) + .OrderBy(m => m.Timestamp) + .Select(m => new + { + label = m.Timestamp.ToUniversalTime().ToString("o"), + data = m.CPU_Load + }) + .ToListAsync(); + + return Ok(data); + } + + [HttpGet("ram-usage")] + public async Task GetRamUsageData(int serverId) + { + var oneDayAgo = DateTime.UtcNow.AddDays(-1); + var data = await _context.Metrics + .Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId) + .OrderBy(m => m.Timestamp) + .Select(m => new + { + label = m.Timestamp.ToUniversalTime().ToString("o"), + data = m.RAM_Load + }) + .ToListAsync(); + + return Ok(data); + } + + [HttpGet("gpu-usage")] + public async Task GetGpuUsageData(int serverId) + { + var oneDayAgo = DateTime.UtcNow.AddDays(-1); + var data = await _context.Metrics + .Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId) + .OrderBy(m => m.Timestamp) + .Select(m => new + { + label = m.Timestamp.ToUniversalTime().ToString("o"), + data = m.GPU_Load + }) + .ToListAsync(); + + return Ok(data); + } +} \ No newline at end of file diff --git a/Watcher/Controllers/ServerController.cs b/Watcher/Controllers/ServerController.cs index e4d89a3..ab41c01 100644 --- a/Watcher/Controllers/ServerController.cs +++ b/Watcher/Controllers/ServerController.cs @@ -6,15 +6,22 @@ using Watcher.Models; using Watcher.ViewModels; [Authorize] +[Route("[controller]")] public class ServerController : Controller { private readonly AppDbContext _context; - public ServerController(AppDbContext context) + private readonly ILogger _logger; + + + public ServerController(AppDbContext context, ILogger logger) { _context = context; + _logger = logger; } + + [HttpGet("Overview")] public async Task Overview() { var vm = new ServerOverviewViewModel @@ -25,13 +32,16 @@ public class ServerController : Controller return View(vm); } - [HttpGet] + + [HttpGet("AddServer")] public IActionResult AddServer() { return View(); } - [HttpPost] + // POST: Server/AddServer + [HttpPost("AddServer")] + [ValidateAntiForgeryToken] public async Task AddServer(AddServerViewModel vm) { if (!ModelState.IsValid) @@ -48,25 +58,34 @@ public class ServerController : Controller _context.Servers.Add(server); await _context.SaveChangesAsync(); + _logger.LogInformation("Neuer Server erstellt: {server}", server.Name); + return RedirectToAction("Overview"); } - [HttpPost] + // POST: Server/Delete/5 + [HttpPost("Delete")] [ValidateAntiForgeryToken] public async Task Delete(int id) { var server = await _context.Servers.FindAsync(id); if (server == null) { + _logger.LogError("Server nicht gefunden"); return NotFound(); } _context.Servers.Remove(server); await _context.SaveChangesAsync(); + + _logger.LogInformation("Server '{server}' erfolgreich gelöscht", server.Name); + return RedirectToAction(nameof(Overview)); } + // GET: Server/Edit/5 + [HttpGet("EditServer/{id}")] public async Task EditServer(int id) { var server = await _context.Servers.FindAsync(id); @@ -82,25 +101,92 @@ public class ServerController : Controller return View(vm); } + // POST: Server/Edit/5 - [HttpPost] + [HttpPost("EditServer/{id}")] [ValidateAntiForgeryToken] public async Task EditServer(int id, EditServerViewModel vm) { - if (ModelState.IsValid) + if (!ModelState.IsValid) { - var server = await _context.Servers.FindAsync(id); - if (server == null) return NotFound(); + return View(vm); + } - server.Name = vm.Name; - server.IPAddress = vm.IPAddress; - server.Type = vm.Type; + var server = await _context.Servers.FindAsync(id); + if (server == null) + { + return NotFound(); + } + server.Name = vm.Name; + server.IPAddress = vm.IPAddress; + server.Type = vm.Type; + + try + { await _context.SaveChangesAsync(); + _logger.LogInformation("Server '{server}' erfolgreich aktualisiert", server.Name); return RedirectToAction(nameof(Overview)); } + catch (Exception ex) + { + _logger.LogError(ex, "Fehler beim Speichern des Servers"); + ModelState.AddModelError("", "Fehler beim Speichern"); + return View(vm); + } + } + + + // GET: Server/Details/5 + [HttpGet("Details/{id}")] + public async Task Details(int id) + { + + var s = await _context.Servers.FindAsync(id); + if (s == null) return NotFound(); + + var vm = new ServerDetailsViewModel + { + Id = s.Id, + Name = s.Name, + IPAddress = s.IPAddress, + Type = s.Type, + Description = s.Description, + CpuType = s.CpuType, + CpuCores = s.CpuCores, + GpuType = s.GpuType, + RamSize = s.RamSize, + CreatedAt = s.CreatedAt, + IsOnline = s.IsOnline, + LastSeen = s.LastSeen, + IsVerified = s.IsVerified + }; return View(vm); } + + [HttpGet("ServerCardPartial")] + public async Task ServerCardsPartial() + { + var servers = _context.Servers.ToList(); + + foreach (var server in servers) + { + server.IsOnline = (DateTime.UtcNow - server.LastSeen).TotalSeconds <= 120; + await _context.SaveChangesAsync(); + } + + return PartialView("_ServerCard", servers); + } + + + [HttpGet("ServerDetailPartial")] + public IActionResult ServerDetailPartial() + { + _logger.LogInformation("Server Detail Seite neu geladen"); + return PartialView("_ServerDetails"); + } + + } diff --git a/Watcher/Controllers/UserController.cs b/Watcher/Controllers/UserController.cs new file mode 100644 index 0000000..a9700c7 --- /dev/null +++ b/Watcher/Controllers/UserController.cs @@ -0,0 +1,146 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Watcher.Data; +using Watcher.ViewModels; + +namespace Watcher.Controllers; + +public class UserController : Controller +{ + + private readonly AppDbContext _context; + private readonly ILogger _logger; + + public UserController(AppDbContext context, ILogger logger) + { + _context = context; + _logger = logger; + } + + + // Anzeigen der User-Informationen + [Authorize] + public IActionResult Info() + { + var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList(); + var Identity_User = User.Identity?.Name; + + var user = _context.Users.FirstOrDefault(u => u.Username == Identity_User); + if (user == null) return NotFound(); + + // Anzeigedaten + var DbProvider = _context.Database.ProviderName; + 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(); + } + + // Edit-Form anzeigen + [Authorize] + [HttpGet] + public IActionResult Edit() + { + var username = User.Identity?.Name; + var user = _context.Users.FirstOrDefault(u => u.Username == username); + if (user == null) return NotFound(); + + var model = new EditUserViewModel + { + Username = user.Username + }; + return View(model); + } + + // Edit speichern + [Authorize] + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult Edit(EditUserViewModel model) + { + if (!ModelState.IsValid) return View(model); + + var username = User.Identity?.Name; + var user = _context.Users.FirstOrDefault(u => u.Username == username); + if (user == null) return NotFound(); + + user.Username = model.Username; + + if (!string.IsNullOrWhiteSpace(model.NewPassword)) + { + user.Password = BCrypt.Net.BCrypt.HashPassword(model.NewPassword); + } + + _context.SaveChanges(); + + // Eventuell hier das Auth-Cookie erneuern, wenn Username sich ändert + + _logger.LogTrace("Passwort-Change durchgeführt"); + + return RedirectToAction("Index", "Home"); + } + + + // Edit-Form anzeigen + [Authorize] + [HttpGet] + public IActionResult UserSettings() + { + var username = User.Identity?.Name; + Console.WriteLine("gefundener User: " + username); + var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList(); + + var user = _context.Users.FirstOrDefault(u => u.Username == username); + if (user == null) return NotFound(); + + var DbProvider = _context.Database.ProviderName; + var mail = user.Email; + + ViewBag.Name = username; + ViewBag.mail = mail; + ViewBag.Claims = claims; + ViewBag.IdentityProvider = user.IdentityProvider; + ViewBag.DbProvider = DbProvider; + + return View(); + } + + // Edit speichern + [Authorize] + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult UserSettings(EditUserViewModel model) + { + if (!ModelState.IsValid) return View(model); + + var username = User.Identity?.Name; + var user = _context.Users.FirstOrDefault(u => u.Username == username); + if (user == null) return NotFound(); + + var databaseProvider = _context.Database.ProviderName; + + user.Username = model.Username; + + // Passwort ändern + if (!string.IsNullOrWhiteSpace(model.NewPassword)) + { + user.Username = BCrypt.Net.BCrypt.HashPassword(model.NewPassword); + } + + _context.SaveChanges(); + + // Eventuell hier das Auth-Cookie erneuern, wenn Username sich ändert + + return RedirectToAction("Index", "Home"); + } +} \ No newline at end of file diff --git a/Watcher/Data/AppDbContext.cs b/Watcher/Data/AppDbContext.cs index 8fbcc52..c88df1b 100644 --- a/Watcher/Data/AppDbContext.cs +++ b/Watcher/Data/AppDbContext.cs @@ -1,11 +1,18 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Watcher.Models; namespace Watcher.Data; public class AppDbContext : DbContext { - public AppDbContext(DbContextOptions options) : base(options) { } + private readonly IConfiguration _configuration; + + public AppDbContext(DbContextOptions options, IConfiguration configuration) + : base(options) + { + _configuration = configuration; + } public DbSet Containers { get; set; } @@ -21,6 +28,29 @@ public class AppDbContext : DbContext public DbSet Users { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + 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."); + } + } + } } - diff --git a/Watcher/Migrations/20250613213714_InitialCreate.Designer.cs b/Watcher/Migrations/20250613213714_InitialCreate.Designer.cs deleted file mode 100644 index 502ab4a..0000000 --- a/Watcher/Migrations/20250613213714_InitialCreate.Designer.cs +++ /dev/null @@ -1,57 +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("20250613213714_InitialCreate")] - partial class InitialCreate - { - /// - 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("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Containers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250613213714_InitialCreate.cs b/Watcher/Migrations/20250613213714_InitialCreate.cs deleted file mode 100644 index cc04d1f..0000000 --- a/Watcher/Migrations/20250613213714_InitialCreate.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class InitialCreate : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .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"), - 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") - }, - constraints: table => - { - table.PrimaryKey("PK_Containers", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Containers"); - } - } -} diff --git a/Watcher/Migrations/20250613215617_ModelsAdded.Designer.cs b/Watcher/Migrations/20250613215617_ModelsAdded.Designer.cs deleted file mode 100644 index cd3d6b3..0000000 --- a/Watcher/Migrations/20250613215617_ModelsAdded.Designer.cs +++ /dev/null @@ -1,57 +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("20250613215617_ModelsAdded")] - partial class ModelsAdded - { - /// - 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("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Containers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250613215617_ModelsAdded.cs b/Watcher/Migrations/20250613215617_ModelsAdded.cs deleted file mode 100644 index 95e1fde..0000000 --- a/Watcher/Migrations/20250613215617_ModelsAdded.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class ModelsAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/Watcher/Migrations/20250614153746_ServerModelAdded.Designer.cs b/Watcher/Migrations/20250614153746_ServerModelAdded.Designer.cs deleted file mode 100644 index a43aa0d..0000000 --- a/Watcher/Migrations/20250614153746_ServerModelAdded.Designer.cs +++ /dev/null @@ -1,87 +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("20250614153746_ServerModelAdded")] - partial class ServerModelAdded - { - /// - 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("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250614153746_ServerModelAdded.cs b/Watcher/Migrations/20250614153746_ServerModelAdded.cs deleted file mode 100644 index e63f373..0000000 --- a/Watcher/Migrations/20250614153746_ServerModelAdded.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class ServerModelAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - 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"), - Status = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - 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") - }, - constraints: table => - { - table.PrimaryKey("PK_Servers", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Servers"); - } - } -} diff --git a/Watcher/Migrations/20250614154943_InitialModelsAdded.Designer.cs b/Watcher/Migrations/20250614154943_InitialModelsAdded.Designer.cs deleted file mode 100644 index 513374c..0000000 --- a/Watcher/Migrations/20250614154943_InitialModelsAdded.Designer.cs +++ /dev/null @@ -1,87 +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("20250614154943_InitialModelsAdded")] - partial class InitialModelsAdded - { - /// - 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("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Containers"); - }); - - modelBuilder.Entity("Watcher.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Servers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Watcher/Migrations/20250614154943_InitialModelsAdded.cs b/Watcher/Migrations/20250614154943_InitialModelsAdded.cs deleted file mode 100644 index 0991d75..0000000 --- a/Watcher/Migrations/20250614154943_InitialModelsAdded.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class InitialModelsAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/Watcher/Migrations/20250614155203_InitialModelsAdded_1.Designer.cs b/Watcher/Migrations/20250614155203_InitialModelsAdded_1.Designer.cs deleted file mode 100644 index cfe2942..0000000 --- a/Watcher/Migrations/20250614155203_InitialModelsAdded_1.Designer.cs +++ /dev/null @@ -1,271 +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("20250614155203_InitialModelsAdded_1")] - partial class InitialModelsAdded_1 - { - /// - 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("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("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("longtext"); - - 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("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("PocketId") - .HasColumnType("longtext"); - - b.Property("Role") - .IsRequired() - .HasColumnType("longtext"); - - 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.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/20250614173150_UserChanges.Designer.cs b/Watcher/Migrations/20250614173150_UserChanges.Designer.cs deleted file mode 100644 index dd22ffb..0000000 --- a/Watcher/Migrations/20250614173150_UserChanges.Designer.cs +++ /dev/null @@ -1,278 +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("20250614173150_UserChanges")] - partial class UserChanges - { - /// - 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("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("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hostname") - .IsRequired() - .HasColumnType("longtext"); - - 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("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", 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.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/20250614173150_UserChanges.cs b/Watcher/Migrations/20250614173150_UserChanges.cs deleted file mode 100644 index e288a35..0000000 --- a/Watcher/Migrations/20250614173150_UserChanges.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class UserChanges : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.RenameColumn( - name: "Role", - table: "Users", - newName: "PreferredUsername"); - - migrationBuilder.UpdateData( - table: "Users", - keyColumn: "PocketId", - keyValue: null, - column: "PocketId", - value: ""); - - migrationBuilder.AlterColumn( - name: "PocketId", - table: "Users", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "Email", - table: "Users", - type: "longtext", - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "LastLogin", - table: "Users", - type: "datetime(6)", - nullable: false, - defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Email", - table: "Users"); - - migrationBuilder.DropColumn( - name: "LastLogin", - table: "Users"); - - migrationBuilder.RenameColumn( - name: "PreferredUsername", - table: "Users", - newName: "Role"); - - migrationBuilder.AlterColumn( - name: "PocketId", - table: "Users", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - } - } -} diff --git a/Watcher/Migrations/20250614183243_Server-Container-IsRunning-Value.cs b/Watcher/Migrations/20250614183243_Server-Container-IsRunning-Value.cs deleted file mode 100644 index e6c9cab..0000000 --- a/Watcher/Migrations/20250614183243_Server-Container-IsRunning-Value.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class ServerContainerIsRunningValue : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "IsOnline", - table: "Servers", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "IsRunning", - table: "Containers", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "IsOnline", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "IsRunning", - table: "Containers"); - } - } -} diff --git a/Watcher/Migrations/20250615114821_ServerCleanUp.cs b/Watcher/Migrations/20250615114821_ServerCleanUp.cs deleted file mode 100644 index 3d03083..0000000 --- a/Watcher/Migrations/20250615114821_ServerCleanUp.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Watcher.Migrations -{ - /// - public partial class ServerCleanUp : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Hostname", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "Status", - table: "Servers"); - - migrationBuilder.AddColumn( - name: "Description", - table: "Servers", - type: "longtext", - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Description", - table: "Servers"); - - migrationBuilder.AddColumn( - name: "Hostname", - table: "Servers", - type: "longtext", - nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "Status", - table: "Servers", - type: "longtext", - nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"); - } - } -} diff --git a/Watcher/Migrations/20250615114821_ServerCleanUp.Designer.cs b/Watcher/Migrations/20250617153602_InitialMigration.Designer.cs similarity index 94% rename from Watcher/Migrations/20250615114821_ServerCleanUp.Designer.cs rename to Watcher/Migrations/20250617153602_InitialMigration.Designer.cs index 98add17..5d549f0 100644 --- a/Watcher/Migrations/20250615114821_ServerCleanUp.Designer.cs +++ b/Watcher/Migrations/20250617153602_InitialMigration.Designer.cs @@ -11,8 +11,8 @@ using Watcher.Data; namespace Watcher.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20250615114821_ServerCleanUp")] - partial class ServerCleanUp + [Migration("20250617153602_InitialMigration")] + partial class InitialMigration { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -148,12 +148,21 @@ namespace Watcher.Migrations .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"); @@ -168,6 +177,9 @@ namespace Watcher.Migrations .IsRequired() .HasColumnType("longtext"); + b.Property("RamSize") + .HasColumnType("double"); + b.Property("TagId") .HasColumnType("int"); diff --git a/Watcher/Migrations/20250614155203_InitialModelsAdded_1.cs b/Watcher/Migrations/20250617153602_InitialMigration.cs similarity index 59% rename from Watcher/Migrations/20250614155203_InitialModelsAdded_1.cs rename to Watcher/Migrations/20250617153602_InitialMigration.cs index ac7f668..e8bc8fd 100644 --- a/Watcher/Migrations/20250614155203_InitialModelsAdded_1.cs +++ b/Watcher/Migrations/20250617153602_InitialMigration.cs @@ -7,28 +7,13 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Watcher.Migrations { /// - public partial class InitialModelsAdded_1 : Migration + public partial class InitialMigration : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AddColumn( - name: "TagId", - table: "Servers", - type: "int", - nullable: true); - - migrationBuilder.AddColumn( - name: "ImageId", - table: "Containers", - type: "int", - nullable: true); - - migrationBuilder.AddColumn( - name: "TagId", - table: "Containers", - type: "int", - nullable: true); + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); migrationBuilder.CreateTable( name: "Images", @@ -47,6 +32,112 @@ namespace Watcher.Migrations }) .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 @@ -106,43 +197,6 @@ namespace Watcher.Migrations }) .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: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Role = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_Servers_TagId", - table: "Servers", - column: "TagId"); - migrationBuilder.CreateIndex( name: "IX_Containers_ImageId", table: "Containers", @@ -173,81 +227,35 @@ namespace Watcher.Migrations table: "Metrics", column: "ServerId"); - migrationBuilder.AddForeignKey( - name: "FK_Containers_Images_ImageId", - table: "Containers", - column: "ImageId", - principalTable: "Images", - principalColumn: "Id"); - - migrationBuilder.AddForeignKey( - name: "FK_Containers_Tags_TagId", - table: "Containers", - column: "TagId", - principalTable: "Tags", - principalColumn: "Id"); - - migrationBuilder.AddForeignKey( - name: "FK_Servers_Tags_TagId", + migrationBuilder.CreateIndex( + name: "IX_Servers_TagId", table: "Servers", - column: "TagId", - principalTable: "Tags", - principalColumn: "Id"); + column: "TagId"); } /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.DropForeignKey( - name: "FK_Containers_Images_ImageId", - table: "Containers"); - - migrationBuilder.DropForeignKey( - name: "FK_Containers_Tags_TagId", - table: "Containers"); - - migrationBuilder.DropForeignKey( - name: "FK_Servers_Tags_TagId", - table: "Servers"); - - migrationBuilder.DropTable( - name: "Images"); - migrationBuilder.DropTable( name: "LogEvents"); migrationBuilder.DropTable( name: "Metrics"); - migrationBuilder.DropTable( - name: "Tags"); - migrationBuilder.DropTable( name: "Users"); - migrationBuilder.DropIndex( - name: "IX_Servers_TagId", - table: "Servers"); + migrationBuilder.DropTable( + name: "Containers"); - migrationBuilder.DropIndex( - name: "IX_Containers_ImageId", - table: "Containers"); + migrationBuilder.DropTable( + name: "Servers"); - migrationBuilder.DropIndex( - name: "IX_Containers_TagId", - table: "Containers"); + migrationBuilder.DropTable( + name: "Images"); - migrationBuilder.DropColumn( - name: "TagId", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "ImageId", - table: "Containers"); - - migrationBuilder.DropColumn( - name: "TagId", - table: "Containers"); + migrationBuilder.DropTable( + name: "Tags"); } } } diff --git a/Watcher/Migrations/20250615102649_ServerAnpassung.Designer.cs b/Watcher/Migrations/20250617165126_ServerPrimaryKey.Designer.cs similarity index 72% rename from Watcher/Migrations/20250615102649_ServerAnpassung.Designer.cs rename to Watcher/Migrations/20250617165126_ServerPrimaryKey.Designer.cs index acd59d2..14cf440 100644 --- a/Watcher/Migrations/20250615102649_ServerAnpassung.Designer.cs +++ b/Watcher/Migrations/20250617165126_ServerPrimaryKey.Designer.cs @@ -11,50 +11,48 @@ using Watcher.Data; namespace Watcher.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20250615102649_ServerAnpassung")] - partial class ServerAnpassung + [Migration("20250617165126_ServerPrimaryKey")] + partial class ServerPrimaryKey { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); modelBuilder.Entity("Watcher.Models.Container", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Hostname") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ImageId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("IsRunning") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Status") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("TagId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -69,13 +67,13 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Tag") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -86,22 +84,22 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Level") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Message") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ServerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -116,22 +114,22 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ServerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Type") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Value") - .HasColumnType("double"); + .HasColumnType("REAL"); b.HasKey("Id"); @@ -146,39 +144,46 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); - b.Property("Hostname") - .IsRequired() - .HasColumnType("longtext"); + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GpuType") + .HasColumnType("TEXT"); b.Property("IPAddress") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("IsOnline") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("LastSeen") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); + b.Property("RamSize") + .HasColumnType("REAL"); b.Property("TagId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -191,10 +196,10 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -205,21 +210,21 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Email") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("LastLogin") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("PocketId") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("PreferredUsername") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); diff --git a/Watcher/Migrations/20250617165126_ServerPrimaryKey.cs b/Watcher/Migrations/20250617165126_ServerPrimaryKey.cs new file mode 100644 index 0000000..f258b61 --- /dev/null +++ b/Watcher/Migrations/20250617165126_ServerPrimaryKey.cs @@ -0,0 +1,785 @@ +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/20250614224113_Container-Server-Update.Designer.cs b/Watcher/Migrations/20250617174242_UserPasswordAdded.Designer.cs similarity index 70% rename from Watcher/Migrations/20250614224113_Container-Server-Update.Designer.cs rename to Watcher/Migrations/20250617174242_UserPasswordAdded.Designer.cs index 0ac9bbe..123e1fa 100644 --- a/Watcher/Migrations/20250614224113_Container-Server-Update.Designer.cs +++ b/Watcher/Migrations/20250617174242_UserPasswordAdded.Designer.cs @@ -11,50 +11,48 @@ using Watcher.Data; namespace Watcher.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20250614224113_Container-Server-Update")] - partial class ContainerServerUpdate + [Migration("20250617174242_UserPasswordAdded")] + partial class UserPasswordAdded { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); modelBuilder.Entity("Watcher.Models.Container", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Hostname") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ImageId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("IsRunning") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Status") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("TagId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -69,13 +67,13 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Tag") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -86,22 +84,22 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Level") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Message") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ServerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -116,22 +114,22 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ServerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Type") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Value") - .HasColumnType("double"); + .HasColumnType("REAL"); b.HasKey("Id"); @@ -146,36 +144,46 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); - b.Property("Hostname") - .IsRequired() - .HasColumnType("longtext"); + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GpuType") + .HasColumnType("TEXT"); b.Property("IPAddress") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("IsOnline") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); + b.Property("RamSize") + .HasColumnType("REAL"); b.Property("TagId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -188,10 +196,10 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -202,21 +210,29 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Email") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); + + b.Property("IdentityProvider") + .IsRequired() + .HasColumnType("TEXT"); b.Property("LastLogin") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); b.Property("PocketId") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("PreferredUsername") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); diff --git a/Watcher/Migrations/20250617174242_UserPasswordAdded.cs b/Watcher/Migrations/20250617174242_UserPasswordAdded.cs new file mode 100644 index 0000000..04ef332 --- /dev/null +++ b/Watcher/Migrations/20250617174242_UserPasswordAdded.cs @@ -0,0 +1,40 @@ +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/20250614183243_Server-Container-IsRunning-Value.Designer.cs b/Watcher/Migrations/20250621124832_DB-Update Issue#32.Designer.cs similarity index 57% rename from Watcher/Migrations/20250614183243_Server-Container-IsRunning-Value.Designer.cs rename to Watcher/Migrations/20250621124832_DB-Update Issue#32.Designer.cs index 60f4cfb..e8cad36 100644 --- a/Watcher/Migrations/20250614183243_Server-Container-IsRunning-Value.Designer.cs +++ b/Watcher/Migrations/20250621124832_DB-Update Issue#32.Designer.cs @@ -11,50 +11,48 @@ using Watcher.Data; namespace Watcher.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20250614183243_Server-Container-IsRunning-Value")] - partial class ServerContainerIsRunningValue + [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") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); modelBuilder.Entity("Watcher.Models.Container", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Hostname") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ImageId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("IsRunning") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Status") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("TagId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -69,13 +67,13 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Tag") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -86,22 +84,22 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Level") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Message") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ServerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -116,29 +114,55 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); - b.Property("ContainerId") - .HasColumnType("int"); + 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("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("longtext"); - - b.Property("Value") - .HasColumnType("double"); + .HasColumnType("TEXT"); b.HasKey("Id"); - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - b.ToTable("Metrics"); }); @@ -146,32 +170,46 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); - b.Property("Hostname") + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GpuType") + .HasColumnType("TEXT"); + + b.Property("IPAddress") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("IsOnline") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); + b.Property("RamSize") + .HasColumnType("REAL"); b.Property("TagId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -184,10 +222,10 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -198,21 +236,28 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Email") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); + + b.Property("IdentityProvider") + .IsRequired() + .HasColumnType("TEXT"); b.Property("LastLogin") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); - b.Property("PocketId") - .IsRequired() - .HasColumnType("longtext"); + b.Property("OIDC_Id") + .HasColumnType("TEXT"); - b.Property("PreferredUsername") + b.Property("Password") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -221,13 +266,15 @@ namespace Watcher.Migrations modelBuilder.Entity("Watcher.Models.Container", b => { - b.HasOne("Watcher.Models.Image", null) + 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 => @@ -245,21 +292,6 @@ namespace Watcher.Migrations 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) diff --git a/Watcher/Migrations/20250621124832_DB-Update Issue#32.cs b/Watcher/Migrations/20250621124832_DB-Update Issue#32.cs new file mode 100644 index 0000000..b7644a8 --- /dev/null +++ b/Watcher/Migrations/20250621124832_DB-Update Issue#32.cs @@ -0,0 +1,251 @@ +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 new file mode 100644 index 0000000..feb5c2e --- /dev/null +++ b/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.Designer.cs @@ -0,0 +1,319 @@ +// +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/20250615102649_ServerAnpassung.cs b/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.cs similarity index 56% rename from Watcher/Migrations/20250615102649_ServerAnpassung.cs rename to Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.cs index c8e9931..974f389 100644 --- a/Watcher/Migrations/20250615102649_ServerAnpassung.cs +++ b/Watcher/Migrations/20250621125157_DB-Update Issue#32 IsVerified-Servers.cs @@ -1,29 +1,28 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace Watcher.Migrations { /// - public partial class ServerAnpassung : Migration + public partial class DBUpdateIssue32IsVerifiedServers : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AddColumn( - name: "LastSeen", + migrationBuilder.AddColumn( + name: "IsVerified", table: "Servers", - type: "datetime(6)", + type: "INTEGER", nullable: false, - defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + defaultValue: false); } /// protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( - name: "LastSeen", + name: "IsVerified", table: "Servers"); } } diff --git a/Watcher/Migrations/20250710090920_container-attribute.Designer.cs b/Watcher/Migrations/20250710090920_container-attribute.Designer.cs new file mode 100644 index 0000000..deb4c2c --- /dev/null +++ b/Watcher/Migrations/20250710090920_container-attribute.Designer.cs @@ -0,0 +1,332 @@ +// +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 new file mode 100644 index 0000000..7af4972 --- /dev/null +++ b/Watcher/Migrations/20250710090920_container-attribute.cs @@ -0,0 +1,119 @@ +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 new file mode 100644 index 0000000..d33eb83 --- /dev/null +++ b/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.Designer.cs @@ -0,0 +1,335 @@ +// +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/20250614224113_Container-Server-Update.cs b/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.cs similarity index 67% rename from Watcher/Migrations/20250614224113_Container-Server-Update.cs rename to Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.cs index b485dfd..a8fbd9f 100644 --- a/Watcher/Migrations/20250614224113_Container-Server-Update.cs +++ b/Watcher/Migrations/20250730113936_DiskSpace-ServerAttribute.cs @@ -5,24 +5,23 @@ namespace Watcher.Migrations { /// - public partial class ContainerServerUpdate : Migration + public partial class DiskSpaceServerAttribute : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn( - name: "IPAddress", + name: "DiskSpace", table: "Servers", - type: "longtext", - nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"); + type: "TEXT", + nullable: true); } /// protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( - name: "IPAddress", + name: "DiskSpace", table: "Servers"); } } diff --git a/Watcher/Migrations/20250730172010_MeasurementWarnings.Designer.cs b/Watcher/Migrations/20250730172010_MeasurementWarnings.Designer.cs new file mode 100644 index 0000000..25baf51 --- /dev/null +++ b/Watcher/Migrations/20250730172010_MeasurementWarnings.Designer.cs @@ -0,0 +1,377 @@ +// +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 new file mode 100644 index 0000000..f13e5c7 --- /dev/null +++ b/Watcher/Migrations/20250730172010_MeasurementWarnings.cs @@ -0,0 +1,172 @@ +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/AppDbContextModelSnapshot.cs b/Watcher/Migrations/AppDbContextModelSnapshot.cs index 1117443..7fee30d 100644 --- a/Watcher/Migrations/AppDbContextModelSnapshot.cs +++ b/Watcher/Migrations/AppDbContextModelSnapshot.cs @@ -15,46 +15,53 @@ namespace Watcher.Migrations protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); modelBuilder.Entity("Watcher.Models.Container", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); - b.Property("Hostname") + b.Property("ExposedPort") + .HasColumnType("INTEGER"); + + b.Property("Health") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); + + b.Property("HostServerId") + .HasColumnType("INTEGER"); + + b.Property("Image") + .HasColumnType("TEXT"); b.Property("ImageId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); + + b.Property("InternalPort") + .HasColumnType("INTEGER"); b.Property("IsRunning") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Status") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("TagId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("INTEGER"); b.HasKey("Id"); + b.HasIndex("HostServerId"); + b.HasIndex("ImageId"); b.HasIndex("TagId"); @@ -66,13 +73,13 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Tag") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -83,22 +90,22 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ContainerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Level") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Message") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ServerId") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -113,29 +120,55 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); - b.Property("ContainerId") - .HasColumnType("int"); + 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("int"); + .HasColumnType("INTEGER"); b.Property("Timestamp") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("longtext"); - - b.Property("Value") - .HasColumnType("double"); + .HasColumnType("TEXT"); b.HasKey("Id"); - b.HasIndex("ContainerId"); - - b.HasIndex("ServerId"); - b.ToTable("Metrics"); }); @@ -143,34 +176,94 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .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("datetime(6)"); + .HasColumnType("TEXT"); + + b.Property("DISK_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("DISK_Temp_Warning") + .HasColumnType("REAL"); b.Property("Description") - .HasColumnType("longtext"); + .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("longtext"); + .HasColumnType("TEXT"); b.Property("IsOnline") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); + + b.Property("IsVerified") + .HasColumnType("INTEGER"); b.Property("LastSeen") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() - .HasColumnType("longtext"); + .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("int"); + .HasColumnType("INTEGER"); b.Property("Type") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -183,10 +276,10 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -197,21 +290,28 @@ namespace Watcher.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Email") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); + + b.Property("IdentityProvider") + .IsRequired() + .HasColumnType("TEXT"); b.Property("LastLogin") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); - b.Property("PocketId") - .IsRequired() - .HasColumnType("longtext"); + b.Property("OIDC_Id") + .HasColumnType("TEXT"); - b.Property("PreferredUsername") + b.Property("Password") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -220,7 +320,11 @@ namespace Watcher.Migrations modelBuilder.Entity("Watcher.Models.Container", b => { - b.HasOne("Watcher.Models.Image", "Image") + b.HasOne("Watcher.Models.Server", "HostServer") + .WithMany() + .HasForeignKey("HostServerId"); + + b.HasOne("Watcher.Models.Image", null) .WithMany("Containers") .HasForeignKey("ImageId"); @@ -228,7 +332,7 @@ namespace Watcher.Migrations .WithMany("Containers") .HasForeignKey("TagId"); - b.Navigation("Image"); + b.Navigation("HostServer"); }); modelBuilder.Entity("Watcher.Models.LogEvent", b => @@ -246,21 +350,6 @@ namespace Watcher.Migrations 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) diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index 2d5ab6a..0101b4f 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -4,17 +4,22 @@ public class Container { public int Id { get; set; } - public string Name { get; set; } = string.Empty; + // Container Details + public string? Name { get; set; } + + public int ExposedPort { get; set; } + + public int InternalPort { get; set; } public string Status { get; set; } = string.Empty; - public Image? Image { get; set; } + public string Health { get; set; } = string.Empty; + + public string? Image { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - public string Hostname { get; set; } = string.Empty; + public Boolean IsRunning { get; set; } = true; - public string Type { get; set; } = "docker"; // z.B. "docker", "vm", "lxc", etc. - - public Boolean IsRunning { get; set; } = false; + public Server? HostServer { get; set; } } diff --git a/Watcher/Models/Metric.cs b/Watcher/Models/Metric.cs index e069057..36913f8 100644 --- a/Watcher/Models/Metric.cs +++ b/Watcher/Models/Metric.cs @@ -2,15 +2,47 @@ namespace Watcher.Models; public class Metric { + // Metric Metadata public int Id { get; set; } public DateTime Timestamp { get; set; } - public string? Type { get; set; } // z.B. "CPU", "RAM", "Disk", "Network" - public double Value { get; set; } + // Zuordnung zu einem Server -- Foreign Key public int? ServerId { get; set; } - public Server? Server { get; set; } - public int? ContainerId { get; set; } - public Container? Container { get; set; } + + // CPU-Daten + public double CPU_Load { get; set; } = 0.0; // % + + public double CPU_Temp { get; set; } = 0.0; // deg C + + + // GPU-Daten + public double GPU_Load { get; set; } = 0.0; // % + + public double GPU_Temp { get; set; } = 0.0; // deg C + + public double GPU_Vram_Size { get; set; } // GB + + public double GPU_Vram_Usage { get; set; } // % + + + // RAM-Daten + public double RAM_Size { get; set; } = 0.0; // GB + + public double RAM_Load { get; set; } = 0.0; // % + + + // HDD-Daten + public double DISK_Size { get; set; } = 0.0; // GB + + public double DISK_Usage { get; set; } = 0.0; // % + + public double DISK_Temp { get; set; } = 0.0; // deg C + + + // Network-Daten + public double NET_In { get; set; } = 0.0; // Bit + + public double NET_Out { get; set; } = 0.0; // Bit } diff --git a/Watcher/Models/Server.cs b/Watcher/Models/Server.cs index 84a2f9d..d29aa3c 100644 --- a/Watcher/Models/Server.cs +++ b/Watcher/Models/Server.cs @@ -1,23 +1,60 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + namespace Watcher.Models; public class Server { + // System Infos + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - public string Name { get; set; } = string.Empty; + [Required] + public required string Name { get; set; } - public string IPAddress { get; set; } = string.Empty; + public required string IPAddress { get; set; } + public required string Type { get; set; } + + public string? Description { get; set; } = String.Empty; + + + // Hardware Infos + public string? CpuType { get; set; } = string.Empty; + public int CpuCores { get; set; } = 0; + public string? GpuType { get; set; } = string.Empty; + public double RamSize { get; set; } = 0; + public string? DiskSpace { get; set; } = string.Empty; + + + // Hardware Measurment Warning/Crit Values + public double CPU_Load_Warning { get; set; } = 75.0; + public double CPU_Load_Critical { get; set; } = 90.0; + public double CPU_Temp_Warning { get; set; } = 80.0; + public double CPU_Temp_Critical { get; set; } = 90.0; + + public double RAM_Load_Warning { get; set; } = 85.0; + public double RAM_Load_Critical { get; set; } = 95.0; + + public double GPU_Load_Warning { get; set; } = 75.0; + public double GPU_Load_Critical { get; set; } = 90.0; + public double GPU_Temp_Warning { get; set; } = 70.0; + public double GPU_Temp_Critical { get; set; } = 80.0; + + public double Disk_Usage_Warning { get; set; } = 75.0; + public double Disk_Usage_Critical { get; set; } = 90.0; + public double DISK_Temp_Warning { get; set; } = 34.0; + public double DISK_Temp_Critical { get; set; } = 36.0; + + + // Database public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - // z.B. "VPS", "standalone", "VM", etc. - public string Type { get; set; } = "VPS"; - public Boolean IsOnline { get; set; } = false; public DateTime LastSeen { get; set; } - public string? Description { get; set; } + public Boolean IsVerified { get; set; } = false; - } diff --git a/Watcher/Models/User.cs b/Watcher/Models/User.cs index 12903fc..13d366a 100644 --- a/Watcher/Models/User.cs +++ b/Watcher/Models/User.cs @@ -1,10 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + namespace Watcher.Models; public class User { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } // PK - public string PocketId { get; set; } = null!; - public string PreferredUsername { get; set; } = null!; + public string? OIDC_Id { get; set; } = null!; + public string Username { get; set; } = null!; public string? Email { get; set; } public DateTime LastLogin { get; set; } + + [Required] + public string IdentityProvider { get; set; } = "local"; + + [Required] + [DataType(DataType.Password)] + public String? Password { get; set; } = string.Empty; } diff --git a/Watcher/Program.cs b/Watcher/Program.cs index a2e01b4..0c4e404 100644 --- a/Watcher/Program.cs +++ b/Watcher/Program.cs @@ -2,44 +2,100 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Sqlite; using Microsoft.IdentityModel.Tokens; + +using Serilog; + using Watcher.Data; using Watcher.Models; + var builder = WebApplication.CreateBuilder(args); + +// Serilog konfigurieren – nur Logs, die nicht von Microsoft stammen +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning) // <-- + .Enrich.FromLogContext() + .WriteTo.File( + "logs/watcher-.log", + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}", + rollingInterval: RollingInterval.Day + ) + .CreateLogger(); + +builder.Host.UseSerilog(); + + // Add services to the container. builder.Services.AddControllersWithViews(); + // HttpContentAccessor builder.Services.AddHttpContextAccessor(); -// ---------- Konfiguration laden ---------- + +// ---------- Konfiguration ---------- +DotNetEnv.Env.Load(); + +builder.Configuration + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + +// Konfiguration laden var configuration = builder.Configuration; -// ---------- DB-Kontext mit MySQL ---------- -builder.Services.AddDbContext(options => - options.UseMySql( - configuration.GetConnectionString("DefaultConnection"), - ServerVersion.AutoDetect(configuration.GetConnectionString("DefaultConnection")) - ) -); + +// ---------- DB-Kontext ---------- +var dbProvider = configuration["Database:Provider"] ?? "MySQL"; +var connectionString = configuration["Database:ConnectionString"]; +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."); + } +}); + // ---------- Authentifizierung konfigurieren ---------- -builder.Services.AddAuthentication(options => +// 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.DefaultScheme = "Cookies"; - options.DefaultChallengeScheme = "oidc"; -}) -.AddCookie("Cookies") + options.LoginPath = "/Auth/Login"; + options.AccessDeniedPath = "/Auth/AccessDenied"; +}); + + +builder.Services.AddAuthentication() .AddOpenIdConnect("oidc", options => { - var config = builder.Configuration.GetSection("Authentication:PocketID"); - options.Authority = config["Authority"]; - options.ClientId = config["ClientId"]; - options.ClientSecret = config["ClientSecret"]; + options.Authority = pocketIdSection["Authority"]; + options.ClientId = pocketIdSection["ClientId"]; + options.ClientSecret = pocketIdSection["ClientSecret"]; options.ResponseType = "code"; - options.CallbackPath = config["CallbackPath"]; + options.CallbackPath = pocketIdSection["CallbackPath"]; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; @@ -48,51 +104,93 @@ builder.Services.AddAuthentication(options => options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); - + options.Events = new OpenIdConnectEvents -{ - OnTokenValidated = async ctx => { - var db = ctx.HttpContext.RequestServices.GetRequiredService(); - - var principal = ctx.Principal; - var pocketId = principal.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; - 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.PocketId == pocketId); - - if (user == null) + OnTokenValidated = async ctx => { - user = new User + var db = ctx.HttpContext.RequestServices.GetRequiredService(); + + var principal = ctx.Principal; + var pocketId = principal.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; + 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) { - PocketId = pocketId, - PreferredUsername = preferredUsername ?? "", - Email = email, - LastLogin = DateTime.UtcNow - }; - db.Users.Add(user); - } - else - { - user.LastLogin = DateTime.UtcNow; - user.PreferredUsername = preferredUsername ?? user.PreferredUsername; - user.Email = email ?? user.Email; - db.Users.Update(user); - } - - await db.SaveChangesAsync(); - } -}; + 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(); + } + }; }); var app = builder.Build(); + +// Migrationen anwenden (für SQLite oder andere DBs) +using (var scope = app.Services.CreateScope()) +{ + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.Migrate(); +} + + +// Standart-User in Datenbank schreiben +using (var scope = app.Services.CreateScope()) +{ + var db = scope.ServiceProvider.GetRequiredService(); + + Console.WriteLine("Checking for users..."); + + if (!db.Users.Any()) + { + Console.WriteLine("No users found, creating default user..."); + + var defaultUser = new User + { + OIDC_Id = string.Empty, + Username = "admin", + Email = string.Empty, + LastLogin = DateTime.UtcNow, + IdentityProvider = "local", + Password = BCrypt.Net.BCrypt.HashPassword("changeme") + }; + db.Users.Add(defaultUser); + db.SaveChanges(); + + Console.WriteLine("Default user created."); + } + else + { + Console.WriteLine("Users already exist."); + } +} + + // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { @@ -101,6 +199,7 @@ if (!app.Environment.IsDevelopment()) app.UseHsts(); } + app.UseHttpsRedirection(); app.UseRouting(); @@ -109,11 +208,9 @@ app.UseAuthorization(); app.UseStaticFiles(); - app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}" ); - app.Run(); diff --git a/Watcher/Properties/launchSettings.json b/Watcher/Properties/launchSettings.json index eb2c6e9..7c5d22c 100644 --- a/Watcher/Properties/launchSettings.json +++ b/Watcher/Properties/launchSettings.json @@ -13,16 +13,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "http://localhost:5258", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:7050;http://localhost:5258", + "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/Watcher/ViewModels/AddContainerViewModel.cs b/Watcher/ViewModels/AddContainerViewModel.cs index 38c2017..93765ff 100644 --- a/Watcher/ViewModels/AddContainerViewModel.cs +++ b/Watcher/ViewModels/AddContainerViewModel.cs @@ -1,6 +1,7 @@ using Watcher.Models; namespace Watcher.ViewModels; + public class AddContainerViewModel { public string Name { get; set; } = string.Empty; @@ -10,4 +11,7 @@ public class AddContainerViewModel public string IPAddress { get; set; } = string.Empty; public string ServerName { get; set; } = string.Empty; // oder ID, je nach Relation public bool IsRunning { get; set; } = false; + + + } diff --git a/Watcher/ViewModels/ContainerOverviewViewModel.cs b/Watcher/ViewModels/ContainerOverviewViewModel.cs index e075100..dbb4422 100644 --- a/Watcher/ViewModels/ContainerOverviewViewModel.cs +++ b/Watcher/ViewModels/ContainerOverviewViewModel.cs @@ -3,8 +3,10 @@ using Watcher.Models; namespace Watcher.ViewModels { public class ContainerOverviewViewModel -{ - public List Containers { get; set; } = new(); -} + { + public List Containers { get; set; } = new(); + + public List Servers { get; set; } = new(); + } } diff --git a/Watcher/ViewModels/EditServerViewModel.cs b/Watcher/ViewModels/EditServerViewModel.cs index f94df04..9345f6e 100644 --- a/Watcher/ViewModels/EditServerViewModel.cs +++ b/Watcher/ViewModels/EditServerViewModel.cs @@ -1,10 +1,38 @@ -namespace Watcher.ViewModels; +using System.ComponentModel.DataAnnotations; -public class EditServerViewModel +namespace Watcher.ViewModels { - public string Name { get; set; } = string.Empty; + public class EditServerViewModel + { + public int Id { get; set; } - public string IPAddress { get; set; } = string.Empty; + [Required(ErrorMessage = "Name ist erforderlich")] + public string? Name { get; set; } - public string Type { get; set; } = "VPS"; -} + [Required(ErrorMessage = "IP-Adresse ist erforderlich")] + [RegularExpression(@"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$", ErrorMessage = "Ungültige IP-Adresse")] + public required string IPAddress { get; set; } + + [Required(ErrorMessage = "Typ ist erforderlich")] + public string? Type { get; set; } + + // Hardware Measurment Warning/Crit Values + public double CPU_Load_Warning { get; set; } = 75.0; + public double CPU_Load_Critical { get; set; } = 90.0; + public double CPU_Temp_Warning { get; set; } = 80.0; + public double CPU_Temp_Critical { get; set; } = 90.0; + + public double RAM_Load_Warning { get; set; } = 85.0; + public double RAM_Load_Critical { get; set; } = 95.0; + + public double GPU_Load_Warning { get; set; } = 75.0; + public double GPU_Load_Critical { get; set; } = 90.0; + public double GPU_Temp_Warning { get; set; } = 70.0; + public double GPU_Temp_Critical { get; set; } = 80.0; + + public double Disk_Usage_Warning { get; set; } = 75.0; + public double Disk_Usage_Critical { get; set; } = 90.0; + public double DISK_Temp_Warning { get; set; } = 34.0; + public double DISK_Temp_Critical { get; set; } = 36.0; + } +} \ No newline at end of file diff --git a/Watcher/ViewModels/EditUserSettingsViewModel.cs b/Watcher/ViewModels/EditUserSettingsViewModel.cs new file mode 100644 index 0000000..468cc86 --- /dev/null +++ b/Watcher/ViewModels/EditUserSettingsViewModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace Watcher.ViewModels; + +public class EditUserSettingsViewModel +{ + [Required] + public string? Username { get; set; } + + [Required] + [DataType(DataType.Password)] + public string? NewPassword { get; set; } + + [Required] + [DataType(DataType.Password)] + [Compare("NewPassword", ErrorMessage = "Passwörter stimmen nicht überein.")] + public string? ConfirmPassword { get; set; } +} diff --git a/Watcher/ViewModels/EditUserViewModel.cs b/Watcher/ViewModels/EditUserViewModel.cs new file mode 100644 index 0000000..30e3fe4 --- /dev/null +++ b/Watcher/ViewModels/EditUserViewModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace Watcher.ViewModels; + +public class EditUserViewModel +{ + [Required] + public string? Username { get; set; } + + [Required] + [DataType(DataType.Password)] + public string? NewPassword { get; set; } + + [Required] + [DataType(DataType.Password)] + [Compare("NewPassword", ErrorMessage = "Passwörter stimmen nicht überein.")] + public string? ConfirmPassword { get; set; } +} diff --git a/Watcher/ViewModels/LoginViewModel.cs b/Watcher/ViewModels/LoginViewModel.cs new file mode 100644 index 0000000..d29bbf4 --- /dev/null +++ b/Watcher/ViewModels/LoginViewModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Watcher.ViewModels; + +public class LoginViewModel +{ + [Required] + public string Username { get; set; } = string.Empty; + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } = string.Empty; + + public string? ReturnUrl { get; set; } +} diff --git a/Watcher/ViewModels/ServerDetailsViewModel.cs b/Watcher/ViewModels/ServerDetailsViewModel.cs new file mode 100644 index 0000000..34047e9 --- /dev/null +++ b/Watcher/ViewModels/ServerDetailsViewModel.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Watcher.ViewModels; + +public class ServerDetailsViewModel +{ + // System Infos + [Required] + public int Id { get; set; } + + [Required] + public required string Name { get; set; } + + [Required] + public required string IPAddress { get; set; } + + [Required] + public required string Type { get; set; } + + public string? Description { get; set; } = String.Empty; + + + // Hardware Infos + public string? CpuType { get; set; } = string.Empty; + public int CpuCores { get; set; } = 0; + public string? GpuType { get; set; } = string.Empty; + public double RamSize { get; set; } = 0; + + + // Database + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + public Boolean IsOnline { get; set; } = false; + + public DateTime LastSeen { get; set; } + + public Boolean IsVerified { get; set; } = false; +} \ No newline at end of file diff --git a/Watcher/Views/Auth/Info.cshtml b/Watcher/Views/Auth/Info.cshtml deleted file mode 100644 index 95f7267..0000000 --- a/Watcher/Views/Auth/Info.cshtml +++ /dev/null @@ -1,86 +0,0 @@ -@{ - ViewData["Title"] = "Account Info"; - var pictureUrl = User.Claims.FirstOrDefault(c => c.Type == "picture")?.Value ?? "123"; -} - -

Account Info

- -
- @if (!string.IsNullOrEmpty(pictureUrl)) - { - Profilbild - } - else - { -
- @(User.Identity?.Name?.Substring(0,1).ToUpper() ?? "?") -
- } - -

@(User.FindFirst("name")?.Value ?? "Unbekannter Nutzer")

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Username@(@User.Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value ?? "Nicht verfügbar")
E-Mail@(@User.Claims.FirstOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")?.Value ?? "Nicht verfügbar")
Benutzer-ID@(User.FindFirst("sub")?.Value ?? "Nicht verfügbar")
Login-Zeit@(User.FindFirst("iat") != null - ? DateTimeOffset.FromUnixTimeSeconds(long.Parse(User.FindFirst("iat").Value)).ToLocalTime().ToString() - : "Nicht verfügbar") -
Token läuft ab@(User.FindFirst("exp") != null - ? DateTimeOffset.FromUnixTimeSeconds(long.Parse(User.FindFirst("exp").Value)).ToLocalTime().ToString() - : "Nicht verfügbar") -
Rollen - @{ - var roles = User.FindAll("role").Select(r => r.Value); - if (!roles.Any()) - { - Keine Rollen - } - else - { -
    - @foreach (var role in roles) - { -
  • @role
  • - } -
- } - } -
- -
- -
-
- - -

Alle Claims

-
    -@foreach (var claim in User.Claims) -{ -
  • @claim.Type: @claim.Value
  • -} -
\ No newline at end of file diff --git a/Watcher/Views/Auth/Login.cshtml b/Watcher/Views/Auth/Login.cshtml index e09cffc..49408c0 100644 --- a/Watcher/Views/Auth/Login.cshtml +++ b/Watcher/Views/Auth/Login.cshtml @@ -1,9 +1,92 @@ +@model Watcher.ViewModels.LoginViewModel @{ + Layout = "~/Views/Shared/_LoginLayout.cshtml"; ViewData["Title"] = "Login"; + var oidc = ViewBag.oidc; } -

Willkommen beim Watcher

+ + + diff --git a/Watcher/Views/Container/AddContainer.cshtml b/Watcher/Views/Container/AddContainer.cshtml deleted file mode 100644 index 29ab3c2..0000000 --- a/Watcher/Views/Container/AddContainer.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@model Watcher.ViewModels.AddContainerViewModel -@{ - ViewData["Title"] = "Neuen Container hinzufügen"; -} - -

Neuen Container hinzufügen

- -
-
- - -
-
- - -
-
- - -
- -
diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index d9b9cec..c3b1005 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -3,33 +3,31 @@ ViewData["Title"] = "Containerübersicht"; } -
-

Containerübersicht

- - + Container hinzufügen - -
-
- @foreach (var container in Model.Containers) + @foreach (var server in Model.Servers) {
-

@container.Name

-

Image: @container.Image

-

Hostname: @container.Hostname

-

Status: @container.Status

-

- Läuft: - - @(container.IsRunning ? "Ja" : "Nein") - -

- +

@server.Name

+
+ +
+ @if (Model.Containers.Count > 0) + { + + @foreach (var container in Model.Containers) + { + test + if (container.HostServer.Equals(server.Name)) + { + + } + } +
+ } else + { +

keine Container gefunden

+ } +
}
diff --git a/Watcher/Views/Database/ManageSqlDumps.cshtml b/Watcher/Views/Database/ManageSqlDumps.cshtml new file mode 100644 index 0000000..059aa61 --- /dev/null +++ b/Watcher/Views/Database/ManageSqlDumps.cshtml @@ -0,0 +1,54 @@ +@model List +@{ + ViewData["Title"] = "Datenbank-Dumps"; +} + +

Datenbank-Dumps

+ +@if (TempData["Success"] != null) +{ +
@TempData["Success"]
+} +@if (TempData["Error"] != null) +{ +
@TempData["Error"]
+} + + + + + + + + + + + + @foreach (var dump in Model) + { + + + + + + + } + +
DateinameGröße (KB)ErstelltAktionen
@dump.FileName@dump.SizeKb@dump.Created.ToString("dd.MM.yyyy HH:mm") + + Download + + +
+ +
+ +
+ +
+
diff --git a/Watcher/Views/Home/Index.cshtml b/Watcher/Views/Home/Index.cshtml index 627e7d3..6b61e39 100644 --- a/Watcher/Views/Home/Index.cshtml +++ b/Watcher/Views/Home/Index.cshtml @@ -3,34 +3,52 @@ ViewData["Title"] = "Dashboard"; } -

Dashboard

+

+ Dashboard +

-
-
-

Server

-

🟢 Online: @Model.ActiveServers

-

🔴 Offline: @Model.OfflineServers

- → Zu den Servern -
+
+ @await Html.PartialAsync("_DashboardStats", Model) +
-
-

Container

-

🟢 Laufend: @Model.RunningContainers

-

🔴 Fehlerhaft: @Model.FailedContainers

- → Zu den Containern -
+
-
-

Uptime letzte 24h

-
- (Diagramm folgt hier) +
+
+

+ Systeminfo +

+

+ + Benutzer: @User.FindFirst("preferred_username")?.Value +

+

+ + Letzter Login: @Model.LastLogin.ToString("g") +

- -
-

Systeminfo

-

Benutzer: @User.FindFirst("preferred_username")?.Value

-

Letzter Login: @Model.LastLogin.ToString("g")

- → Account-Verwaltung -
+ +@section Scripts { + +} diff --git a/Watcher/Views/Home/_DashboardStats.cshtml b/Watcher/Views/Home/_DashboardStats.cshtml new file mode 100644 index 0000000..bad5991 --- /dev/null +++ b/Watcher/Views/Home/_DashboardStats.cshtml @@ -0,0 +1,25 @@ +@model Watcher.ViewModels.DashboardViewModel + +
+
+
+

Server

+

🟢 Online: @Model.ActiveServers

+

🔴 Offline: @Model.OfflineServers

+ + → Zu den Servern + +
+
+ +
+
+

Container

+

🟢 Laufend: @Model.RunningContainers

+

🔴 Fehlerhaft: @Model.FailedContainers

+ + → Zu den Containern + +
+
+
diff --git a/Watcher/Views/Server/AddServer.cshtml b/Watcher/Views/Server/AddServer.cshtml index 97471fb..0b35625 100644 --- a/Watcher/Views/Server/AddServer.cshtml +++ b/Watcher/Views/Server/AddServer.cshtml @@ -3,38 +3,45 @@ ViewData["Title"] = "Neuen Server hinzufügen"; } -
-

Neuen Server hinzufügen

+
+
+

+ Neuen Server hinzufügen +

-
-
- - - -
+ + @Html.AntiForgeryToken() +
+ + + +
-
- - - -
+
+ + + +
-
- - -
+
+ + +
-
- Abbrechen - -
-
+
+ + Abbrechen + + +
+ +
@section Scripts { diff --git a/Watcher/Views/Server/Details.cshtml b/Watcher/Views/Server/Details.cshtml new file mode 100644 index 0000000..3b05245 --- /dev/null +++ b/Watcher/Views/Server/Details.cshtml @@ -0,0 +1,272 @@ +@model Watcher.ViewModels.ServerDetailsViewModel +@{ + ViewData["Title"] = "Serverübersicht"; +} + +
+ +
+
+
+
+ Serverdetails: @Model.Name +
+ + + @(Model.IsOnline ? "Online" : "Offline") + +
+
+
+
ID
+
@Model.Id
+ +
IP-Adresse
+
@Model.IPAddress
+ +
Typ
+
@Model.Type
+ +
Erstellt am
+
@Model.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
+ +
Zuletzt gesehen
+
@Model.LastSeen.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
+
+
+ +
+ +
+
CPU Last
+
+ +
+
+ +
+
RAM Last
+
+ +
+
+ +
+
GPU Last
+
+ +
+
+ +
+ +
+ + +@section Scripts { + + +} \ No newline at end of file diff --git a/Watcher/Views/Server/EditServer.cshtml b/Watcher/Views/Server/EditServer.cshtml index d3f3fe5..23b4a5d 100644 --- a/Watcher/Views/Server/EditServer.cshtml +++ b/Watcher/Views/Server/EditServer.cshtml @@ -1,31 +1,181 @@ -@model Watcher.ViewModels.EditServerViewModel; +@model Watcher.ViewModels.EditServerViewModel @{ ViewData["Title"] = "Server bearbeiten"; } -

Server bearbeiten

+

Server bearbeiten

-
+ @Html.AntiForgeryToken() + -
- - +
+
+

Allgemeine Informationen

+
+
+
+ + + +
+ +
+ + + +
+ +
+ + +
+
-
- - -
- +
+
+

Grenzwerte festlegen

+
+
+ +
+ CPU-Grenzwerte +
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ +
+ RAM-Grenzwerte +
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ +
+ Festplatten-Grenzwerte +
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ +
+ GPU-Grenzwerte +
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+
-
- - +
+ + Abbrechen
- - - Abbrechen - + \ No newline at end of file diff --git a/Watcher/Views/Server/_ServerCard.cshtml b/Watcher/Views/Server/_ServerCard.cshtml new file mode 100644 index 0000000..efc6f42 --- /dev/null +++ b/Watcher/Views/Server/_ServerCard.cshtml @@ -0,0 +1,67 @@ +@model IEnumerable + +
+
+ @foreach (var s in Model) + { +
+
+
+
+
+ (#@s.Id) @s.Name +
+ + + @(s.IsOnline ? "Online" : "Offline") + +
+ +
+
+
IP: @s.IPAddress
+
Typ: @s.Type
+
Erstellt: + @s.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
+
Last-Seen: + @s.LastSeen.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
+
CPU: @(s.CpuType ?? "not found")
+
CPU-Kerne: @s.CpuCores
+
GPU: @(s.GpuType ?? "not found") +
+
RAM: @(s.RamSize)
+
Disk Space: ...
+
+
+ +
+ + + Bearbeiten + + + + Metrics + + + + Container + + +
+ +
+ +
+
+
+
+ } +
+
\ No newline at end of file diff --git a/Watcher/Views/Server/_ServerDetails.cshtml b/Watcher/Views/Server/_ServerDetails.cshtml new file mode 100644 index 0000000..e69de29 diff --git a/Watcher/Views/Server/overview.cshtml b/Watcher/Views/Server/overview.cshtml index 1b537f2..dae5ee4 100644 --- a/Watcher/Views/Server/overview.cshtml +++ b/Watcher/Views/Server/overview.cshtml @@ -3,46 +3,39 @@ ViewData["Title"] = "Serverübersicht"; } -
-

Serverübersicht

- - + Server hinzufügen - +
+

+ Serverübersicht +

+
+ +
+
+ @await Html.PartialAsync("_ServerCard", Model.Servers) +
+@section Scripts { + +} \ No newline at end of file diff --git a/Watcher/Views/Shared/_Layout.cshtml b/Watcher/Views/Shared/_Layout.cshtml index 9104883..fbbcff3 100644 --- a/Watcher/Views/Shared/_Layout.cshtml +++ b/Watcher/Views/Shared/_Layout.cshtml @@ -14,12 +14,13 @@ @ViewData["Title"] - Watcher + + +
+ @if (isLocalUser) + { +
+

Benutzerdaten ändern

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ } + else + { +
+ Benutzerdaten können nur für lokal angemeldete Nutzer geändert werden. +
+ } + + +
+

Systemeinformationen

+ +
+ +
Watcher Version: v0.1.0
+ +
+ +
Authentifizierungsmethode:
+

@(ViewBag.IdentityProvider ?? "nicht gefunden")

+ +
+ +
Datenbank-Engine:
+ @(DbEngine ?? "nicht gefunden") + + + @if (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"] +
+ } + +
+ +
+

Systemeinstellungen

+ +
Benachrichtigungen:
+

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

+ + +
+
+
+ Benachrichtigungseinstellungen +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ +
+ +
+
diff --git a/Watcher/Views/_ViewStart.cshtml b/Watcher/Views/_ViewStart.cshtml index a5f1004..7eeb1f0 100644 --- a/Watcher/Views/_ViewStart.cshtml +++ b/Watcher/Views/_ViewStart.cshtml @@ -1,3 +1,4 @@ @{ Layout = "_Layout"; } +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file diff --git a/Watcher/Watcher.csproj b/Watcher/Watcher.csproj index 8ba7537..7eb3182 100644 --- a/Watcher/Watcher.csproj +++ b/Watcher/Watcher.csproj @@ -8,7 +8,12 @@ - + + + + + + @@ -16,6 +21,8 @@ + + diff --git a/Watcher/appsettings.json b/Watcher/appsettings.json index 81748ed..dff88f0 100644 --- a/Watcher/appsettings.json +++ b/Watcher/appsettings.json @@ -5,17 +5,26 @@ "Microsoft.AspNetCore": "Warning" } }, + "AllowedHosts": "*", - "ConnectionStrings": { - "DefaultConnection": "server=100.64.0.5;port=3306;database=watcher;user=monitoringuser;password=ssp123;" + + "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": { - "PocketID": { - "Authority": "https://pocketid.triggermeelmo.com", - "ClientId": "629a5f42-ab02-4905-8311-cc7b64165cc0", - "ClientSecret": "QHUNaRyK2VVYdZVz1cQqv8FEf2qtL6QH", - "CallbackPath": "/signin-oidc", - "ResponseType": "code" - } -} + "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/Watcher/wwwroot/css/ServerOverview.css b/Watcher/wwwroot/css/ServerOverview.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Watcher/wwwroot/css/ServerOverview.css @@ -0,0 +1 @@ + diff --git a/Watcher/wwwroot/css/site.css b/Watcher/wwwroot/css/site.css index f8d98fc..22fe50b 100644 --- a/Watcher/wwwroot/css/site.css +++ b/Watcher/wwwroot/css/site.css @@ -1,22 +1,66 @@ -html { - font-size: 14px; -} - -@media (min-width: 768px) { - html { - font-size: 16px; - } -} - -.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; -} - -html { - position: relative; - min-height: 100%; +:root { + --color-bg: #0d1b2a; + --color-surface: #1b263b; + --color-accent: #415a77; + --color-primary: #0d6efd; + --color-text: #ffffff; + --color-muted: #c0c0c0; + --color-success: #14a44d; + --color-danger: #ff6b6b; } body { - margin-bottom: 60px; -} \ No newline at end of file + background-color: var(--color-bg); + color: var(--color-text); +} + +a { + color: var(--color-primary); +} + +.card, .navbar, .form-control, .dropdown-menu { + background-color: var(--color-surface); + color: var(--color-text); + border: none; +} + +.form-control { + background-color: var(--color-accent); + color: var(--color-text); +} + +.form-control::placeholder { + color: var(--color-muted); +} + +.navbar .nav-link, +.dropdown-item { + color: var(--color-text); +} + +.btn-primary { + background-color: var(--color-primary); + border: none; +} + +.btn-danger { + background-color: var(--color-danger); + border: none; +} + +.btn-pocketid { + background-color: var(--color-success); + color: white; +} + +.btn-pocketid:hover { + background-color: #0f8c3c; +} + +.table { + color: var(--color-text); +} + +hr { + border-top: 1px solid var(--color-accent); +} diff --git a/Watcher/wwwroot/downloads/Linux/heartbeat b/Watcher/wwwroot/downloads/Linux/heartbeat new file mode 100755 index 0000000..ef10317 Binary files /dev/null and b/Watcher/wwwroot/downloads/Linux/heartbeat differ diff --git a/Watcher/wwwroot/js/server_uptime.js b/Watcher/wwwroot/js/server_uptime.js new file mode 100644 index 0000000..e69de29 diff --git a/Watcher/wwwroot/js/site.js b/Watcher/wwwroot/js/site.js deleted file mode 100644 index 0937657..0000000 --- a/Watcher/wwwroot/js/site.js +++ /dev/null @@ -1,4 +0,0 @@ -// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification -// for details on configuring this project to bundle and minify static web assets. - -// Write your JavaScript code. diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..397cbd3 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,18 @@ +services: + watcher: + image: git.triggermeelmo.com/daniel-hbn/watcher/watcher:development + container_name: watcher + restart: unless-stopped + env_file: .env + ports: + - "5000:5000" + volumes: + - ./data:/app/persistence + - ./dumps:/app/wwwroot/downloads/sqlite + - ./logs:/app/logs + healthcheck: + test: "curl -f http://localhost:5000" + interval: 1m30s + timeout: 30s + retries: 5 + start_period: 30s \ No newline at end of file