From 49b5339bb357664a18b164968110b4fd06265dcc Mon Sep 17 00:00:00 2001 From: Leon Hoppe Date: Mon, 20 Jan 2025 17:43:05 +0100 Subject: [PATCH] Added database models --- src/Portfolio.Api/App.razor | 58 ++++++++++++++++++++ src/Portfolio.Api/DatabaseContext.cs | 25 +++++++++ src/Portfolio.Api/Portfolio.Api.csproj | 2 + src/Portfolio.Api/Program.cs | 22 ++++++++ src/Portfolio.Host/Portfolio.Host.csproj | 1 + src/Portfolio.Host/Program.cs | 12 +++- src/Portfolio.Shared/Models/About.cs | 16 ++++++ src/Portfolio.Shared/Models/Language.cs | 20 +++++++ src/Portfolio.Shared/Models/Project.cs | 41 ++++++++++++++ src/Portfolio.Shared/Models/Technology.cs | 22 ++++++++ src/Portfolio.Shared/Models/TimelineEntry.cs | 25 +++++++++ 11 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 src/Portfolio.Api/App.razor create mode 100644 src/Portfolio.Api/DatabaseContext.cs create mode 100644 src/Portfolio.Shared/Models/About.cs create mode 100644 src/Portfolio.Shared/Models/Language.cs create mode 100644 src/Portfolio.Shared/Models/Project.cs create mode 100644 src/Portfolio.Shared/Models/Technology.cs create mode 100644 src/Portfolio.Shared/Models/TimelineEntry.cs diff --git a/src/Portfolio.Api/App.razor b/src/Portfolio.Api/App.razor new file mode 100644 index 0000000..4124adc --- /dev/null +++ b/src/Portfolio.Api/App.razor @@ -0,0 +1,58 @@ +@using Microsoft.AspNetCore.Components.Web; +@using Microsoft.AspNetCore.Components.Routing + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Portfolio.Api/DatabaseContext.cs b/src/Portfolio.Api/DatabaseContext.cs new file mode 100644 index 0000000..2d46736 --- /dev/null +++ b/src/Portfolio.Api/DatabaseContext.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Portfolio.Shared.Models; + +namespace Portfolio.Api; + +public class DatabaseContext(DbContextOptions options) : DbContext(options) { + + public DbSet Projects { get; set; } + + public DbSet Languages { get; set; } + + public DbSet Technologies { get; set; } + + public DbSet Timeline { get; set; } + + public DbSet About { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity() + .HasMany(p => p.Languages) + .WithMany() + .UsingEntity("ProjectLanguage"); + } + +} \ No newline at end of file diff --git a/src/Portfolio.Api/Portfolio.Api.csproj b/src/Portfolio.Api/Portfolio.Api.csproj index 35db154..f7751ce 100644 --- a/src/Portfolio.Api/Portfolio.Api.csproj +++ b/src/Portfolio.Api/Portfolio.Api.csproj @@ -8,6 +8,8 @@ + + diff --git a/src/Portfolio.Api/Program.cs b/src/Portfolio.Api/Program.cs index e850aef..39a8398 100644 --- a/src/Portfolio.Api/Program.cs +++ b/src/Portfolio.Api/Program.cs @@ -1,3 +1,6 @@ +using HopFrame.Web; +using Portfolio.Api; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. @@ -5,6 +8,15 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); builder.AddServiceDefaults(); +builder.AddNpgsqlDbContext("data"); + +builder.Services.AddHopFrame(options => { + options.DisplayUserInfo(false); + options.AddDbContext(); +}); +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -12,7 +24,17 @@ if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } +await using (var scope = app.Services.CreateAsyncScope()) { + var db = scope.ServiceProvider.GetRequiredService(); + await db.Database.EnsureCreatedAsync(); +} + app.UseHttpsRedirection(); app.MapDefaultEndpoints(); +app.UseAntiforgery(); +app.MapStaticAssets(); +app.MapRazorComponents() + .MapHopFramePages(); + app.Run(); diff --git a/src/Portfolio.Host/Portfolio.Host.csproj b/src/Portfolio.Host/Portfolio.Host.csproj index 5f44a98..083deb5 100644 --- a/src/Portfolio.Host/Portfolio.Host.csproj +++ b/src/Portfolio.Host/Portfolio.Host.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Portfolio.Host/Program.cs b/src/Portfolio.Host/Program.cs index e976287..ae405b9 100644 --- a/src/Portfolio.Host/Program.cs +++ b/src/Portfolio.Host/Program.cs @@ -1,9 +1,17 @@ var builder = DistributedApplication.CreateBuilder(args); -var api = builder.AddProject("api"); +var postgres = builder.AddPostgres("postgres") + .WithDataVolume("portfolio-postgres"); + +var db = postgres.AddDatabase("data"); + +var api = builder.AddProject("api") + .WithReference(db) + .WaitFor(db); builder.AddProject("web") .WithReference(api) - .WaitFor(api); + .WaitFor(api) + .WithExternalHttpEndpoints(); builder.Build().Run(); \ No newline at end of file diff --git a/src/Portfolio.Shared/Models/About.cs b/src/Portfolio.Shared/Models/About.cs new file mode 100644 index 0000000..b17cc31 --- /dev/null +++ b/src/Portfolio.Shared/Models/About.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Portfolio.Shared.Models; + +public class About { + + [Key] + public int Id { get; private set; } = 0; + + [MaxLength(5000)] + public required string AboutMe { get; set; } + + [MaxLength(5000)] + public required string Future { get; set; } + +} \ No newline at end of file diff --git a/src/Portfolio.Shared/Models/Language.cs b/src/Portfolio.Shared/Models/Language.cs new file mode 100644 index 0000000..f6ee861 --- /dev/null +++ b/src/Portfolio.Shared/Models/Language.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Portfolio.Shared.Models; + +public sealed class Language { + + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; init; } + + [MaxLength(255)] + public required string Label { get; set; } + + [MaxLength(255)] + public required string Identifier { get; set; } + + [MaxLength(255)] + public string? Suffix { get; set; } + +} \ No newline at end of file diff --git a/src/Portfolio.Shared/Models/Project.cs b/src/Portfolio.Shared/Models/Project.cs new file mode 100644 index 0000000..fab23ce --- /dev/null +++ b/src/Portfolio.Shared/Models/Project.cs @@ -0,0 +1,41 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Portfolio.Shared.Models; + +public sealed class Project { + + [Key] + public Guid Id { get; init; } = Guid.CreateVersion7(); + + [MaxLength(255)] + public required string Cover { get; set; } + + [MaxLength(255)] + public required string Name { get; set; } + + [MaxLength(1000)] + public string? Description { get; set; } + + [MaxLength(255)] + public required string SourceCode { get; set; } + + public bool Featured { get; set; } + + public int OrderIndex { get; set; } + + public ProjectStatus Status { get; set; } = ProjectStatus.Finished; + + [ForeignKey("languages")] + public List Languages { get; init; } = new(); + + public DateTime Created { get; init; } = DateTime.Now; + +} + +public enum ProjectStatus : byte { + Finished = 0, + Canceled = 1, + Paused = 2, + Development = 3 +} diff --git a/src/Portfolio.Shared/Models/Technology.cs b/src/Portfolio.Shared/Models/Technology.cs new file mode 100644 index 0000000..edd44dd --- /dev/null +++ b/src/Portfolio.Shared/Models/Technology.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Portfolio.Shared.Models; + +public sealed class Technology { + + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; init; } + + [MaxLength(255)] + public required string Name { get; set; } + + public TechnologyLevel Level { get; set; } = TechnologyLevel.Beginner; + +} + +public enum TechnologyLevel : byte { + Beginner = 0, + Intermediate = 1, + Professional = 2 +} \ No newline at end of file diff --git a/src/Portfolio.Shared/Models/TimelineEntry.cs b/src/Portfolio.Shared/Models/TimelineEntry.cs new file mode 100644 index 0000000..f6cb8fa --- /dev/null +++ b/src/Portfolio.Shared/Models/TimelineEntry.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Portfolio.Shared.Models; + +public class TimelineEntry { + + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; init; } + + public DateOnly Date { get; init; } + + [MaxLength(1000)] + public required string Description { get; set; } + + public bool Featured { get; set; } + + public TimelineEntryType Type { get; init; } + +} + +public enum TimelineEntryType : byte { + Experience = 0, + Carrier = 1 +}