From 1f1a1fddbd8da6fee3e6804e5542978b9dc96cdc Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Sun, 15 Jun 2025 14:20:21 +0200 Subject: [PATCH] =?UTF-8?q?CRUD-Operation=20f=C3=BCr=20Server=20implementi?= =?UTF-8?q?ert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Watcher/Controllers/ServerController.cs | 56 +++- .../20250615114821_ServerCleanUp.Designer.cs | 288 ++++++++++++++++++ .../20250615114821_ServerCleanUp.cs | 51 ++++ .../Migrations/AppDbContextModelSnapshot.cs | 7 +- Watcher/Models/Server.cs | 8 +- Watcher/ViewModels/EditServerViewModel.cs | 10 + Watcher/Views/Server/AddServer.cshtml | 74 ++--- Watcher/Views/Server/EditServer.cshtml | 31 ++ Watcher/Views/Server/overview.cshtml | 53 ++-- Watcher/Views/Shared/_Layout.cshtml | 3 + 10 files changed, 507 insertions(+), 74 deletions(-) create mode 100644 Watcher/Migrations/20250615114821_ServerCleanUp.Designer.cs create mode 100644 Watcher/Migrations/20250615114821_ServerCleanUp.cs create mode 100644 Watcher/ViewModels/EditServerViewModel.cs create mode 100644 Watcher/Views/Server/EditServer.cshtml diff --git a/Watcher/Controllers/ServerController.cs b/Watcher/Controllers/ServerController.cs index 1d7535c..e4d89a3 100644 --- a/Watcher/Controllers/ServerController.cs +++ b/Watcher/Controllers/ServerController.cs @@ -41,11 +41,8 @@ public class ServerController : Controller { Name = vm.Name, IPAddress = vm.IPAddress, - Hostname = vm.Hostname, - Status = vm.Status, Type = vm.Type, IsOnline = vm.IsOnline, - CreatedAt = DateTime.UtcNow }; _context.Servers.Add(server); @@ -53,4 +50,57 @@ public class ServerController : Controller return RedirectToAction("Overview"); } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Delete(int id) + { + var server = await _context.Servers.FindAsync(id); + if (server == null) + { + return NotFound(); + } + + _context.Servers.Remove(server); + await _context.SaveChangesAsync(); + return RedirectToAction(nameof(Overview)); + } + + // GET: Server/Edit/5 + public async Task EditServer(int id) + { + var server = await _context.Servers.FindAsync(id); + if (server == null) return NotFound(); + + var vm = new EditServerViewModel + { + Name = server.Name, + IPAddress = server.IPAddress, + Type = server.Type + }; + + return View(vm); + } + + // POST: Server/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EditServer(int id, EditServerViewModel vm) + { + if (ModelState.IsValid) + { + var server = await _context.Servers.FindAsync(id); + if (server == null) return NotFound(); + + server.Name = vm.Name; + server.IPAddress = vm.IPAddress; + server.Type = vm.Type; + + await _context.SaveChangesAsync(); + return RedirectToAction(nameof(Overview)); + } + + return View(vm); + } + } diff --git a/Watcher/Migrations/20250615114821_ServerCleanUp.Designer.cs b/Watcher/Migrations/20250615114821_ServerCleanUp.Designer.cs new file mode 100644 index 0000000..98add17 --- /dev/null +++ b/Watcher/Migrations/20250615114821_ServerCleanUp.Designer.cs @@ -0,0 +1,288 @@ +// +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("20250615114821_ServerCleanUp")] + partial class ServerCleanUp + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hostname") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("IsRunning") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("TagId"); + + b.ToTable("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("Tag") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ContainerId") + .HasColumnType("int"); + + b.Property("Level") + .HasColumnType("longtext"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("ServerId"); + + b.ToTable("LogEvents"); + }); + + modelBuilder.Entity("Watcher.Models.Metric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ContainerId") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("longtext"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("ServerId"); + + b.ToTable("Metrics"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IPAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsOnline") + .HasColumnType("tinyint(1)"); + + b.Property("LastSeen") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("TagId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Watcher.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("LastLogin") + .HasColumnType("datetime(6)"); + + b.Property("PocketId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PreferredUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.HasOne("Watcher.Models.Image", "Image") + .WithMany("Containers") + .HasForeignKey("ImageId"); + + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Containers") + .HasForeignKey("TagId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.HasOne("Watcher.Models.Container", "Container") + .WithMany() + .HasForeignKey("ContainerId"); + + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.Navigation("Container"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Watcher.Models.Metric", b => + { + b.HasOne("Watcher.Models.Container", "Container") + .WithMany() + .HasForeignKey("ContainerId"); + + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.Navigation("Container"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Servers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Navigation("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Navigation("Containers"); + + b.Navigation("Servers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Watcher/Migrations/20250615114821_ServerCleanUp.cs b/Watcher/Migrations/20250615114821_ServerCleanUp.cs new file mode 100644 index 0000000..3d03083 --- /dev/null +++ b/Watcher/Migrations/20250615114821_ServerCleanUp.cs @@ -0,0 +1,51 @@ +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/AppDbContextModelSnapshot.cs b/Watcher/Migrations/AppDbContextModelSnapshot.cs index 5d75aee..1117443 100644 --- a/Watcher/Migrations/AppDbContextModelSnapshot.cs +++ b/Watcher/Migrations/AppDbContextModelSnapshot.cs @@ -148,8 +148,7 @@ namespace Watcher.Migrations b.Property("CreatedAt") .HasColumnType("datetime(6)"); - b.Property("Hostname") - .IsRequired() + b.Property("Description") .HasColumnType("longtext"); b.Property("IPAddress") @@ -166,10 +165,6 @@ namespace Watcher.Migrations .IsRequired() .HasColumnType("longtext"); - b.Property("Status") - .IsRequired() - .HasColumnType("longtext"); - b.Property("TagId") .HasColumnType("int"); diff --git a/Watcher/Models/Server.cs b/Watcher/Models/Server.cs index de454a7..84a2f9d 100644 --- a/Watcher/Models/Server.cs +++ b/Watcher/Models/Server.cs @@ -8,16 +8,16 @@ public class Server public string IPAddress { get; set; } = string.Empty; - public string Status { get; set; } = string.Empty; - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - public string Hostname { get; set; } = string.Empty; - // 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; } + + } diff --git a/Watcher/ViewModels/EditServerViewModel.cs b/Watcher/ViewModels/EditServerViewModel.cs new file mode 100644 index 0000000..f94df04 --- /dev/null +++ b/Watcher/ViewModels/EditServerViewModel.cs @@ -0,0 +1,10 @@ +namespace Watcher.ViewModels; + +public class EditServerViewModel +{ + public string Name { get; set; } = string.Empty; + + public string IPAddress { get; set; } = string.Empty; + + public string Type { get; set; } = "VPS"; +} diff --git a/Watcher/Views/Server/AddServer.cshtml b/Watcher/Views/Server/AddServer.cshtml index 3945bab..97471fb 100644 --- a/Watcher/Views/Server/AddServer.cshtml +++ b/Watcher/Views/Server/AddServer.cshtml @@ -1,42 +1,42 @@ -@using Watcher.ViewModels - -@model AddServerViewModel +@model Watcher.ViewModels.AddServerViewModel @{ ViewData["Title"] = "Neuen Server hinzufügen"; } -

Neuen Server hinzufügen

+
+

Neuen Server hinzufügen

-
-
- - - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
+
+
+ + + +
+ +
+ + + +
+ +
+ + +
+ +
+ Abbrechen + +
+
+
+ +@section Scripts { + +} diff --git a/Watcher/Views/Server/EditServer.cshtml b/Watcher/Views/Server/EditServer.cshtml new file mode 100644 index 0000000..d3f3fe5 --- /dev/null +++ b/Watcher/Views/Server/EditServer.cshtml @@ -0,0 +1,31 @@ +@model Watcher.ViewModels.EditServerViewModel; + +@{ + ViewData["Title"] = "Server bearbeiten"; +} + +

Server bearbeiten

+ +
+ @Html.AntiForgeryToken() + +
+ + +
+ +
+ + +
+ + + +
+ + +
+ + + Abbrechen +
diff --git a/Watcher/Views/Server/overview.cshtml b/Watcher/Views/Server/overview.cshtml index bc39a41..1e30e38 100644 --- a/Watcher/Views/Server/overview.cshtml +++ b/Watcher/Views/Server/overview.cshtml @@ -5,7 +5,8 @@

Serverübersicht

- + + Server hinzufügen
@@ -13,31 +14,35 @@
-@foreach (var s in Model.Servers) -{ -
-
-
-

@s.Name

-

@s.Hostname

+ @foreach (var s in Model.Servers) + { +
+
+
+

@s.Name

+
+ + @(s.IsOnline ? "Online" : "Offline") +
- - @(s.IsOnline ? "Online" : "Offline") - -
-
-
IP: Ip
-
Typ: @s.Type
-
Status: @(string.IsNullOrEmpty(s.Status) ? "-" : s.Status)
-
Erstellt: @s.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
-
+
+
IP: @s.IPAddress
+
Typ: @s.Type
+
Status: @((s.IsOnline) ? "Online" : "Offline")
+
Erstellt: @s.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")
+
Last-Seen: @(s.LastSeen.ToLocalTime().ToString("dd.MM.yyyy HH:mm") ?? "Never")
+
-
- Bearbeiten - Löschen +
+ Bearbeiten + +
+ +
+
-
-} + }
\ No newline at end of file diff --git a/Watcher/Views/Shared/_Layout.cshtml b/Watcher/Views/Shared/_Layout.cshtml index 945167a..9104883 100644 --- a/Watcher/Views/Shared/_Layout.cshtml +++ b/Watcher/Views/Shared/_Layout.cshtml @@ -103,6 +103,9 @@ + + @RenderSection("Scripts", required: false) +