Resolve "Backend" #18
58
src/Portfolio.Api/App.razor
Normal file
58
src/Portfolio.Api/App.razor
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
@using Microsoft.AspNetCore.Components.Web;
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<base href="/"/>
|
||||||
|
<link rel="stylesheet" href="@Assets["Portfolio.Api.styles.css"]"/>
|
||||||
|
<link rel="stylesheet" href="@Assets["_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css"]"/>
|
||||||
|
<ImportMap/>
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
<HeadOutlet/>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
--body-font: "Segoe UI Variable", "Segoe UI", sans-serif;
|
||||||
|
font-family: var(--body-font), sans-serif;
|
||||||
|
font-size: var(--type-ramp-base-font-size);
|
||||||
|
line-height: var(--type-ramp-base-line-height);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background: var(--neutral-layer-4);
|
||||||
|
color: var(--neutral-foreground-rest);
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a {
|
||||||
|
color: var(--neutral-foreground-rest);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a:focus {
|
||||||
|
outline: 1px dashed;
|
||||||
|
outline-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<Router AppAssembly="typeof(Program).Assembly">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="routeData"/>
|
||||||
|
</Found>
|
||||||
|
</Router>
|
||||||
|
<script src="_framework/blazor.web.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
25
src/Portfolio.Api/DatabaseContext.cs
Normal file
25
src/Portfolio.Api/DatabaseContext.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Portfolio.Shared.Models;
|
||||||
|
|
||||||
|
namespace Portfolio.Api;
|
||||||
|
|
||||||
|
public class DatabaseContext(DbContextOptions<DatabaseContext> options) : DbContext(options) {
|
||||||
|
|
||||||
|
public DbSet<Project> Projects { get; set; }
|
||||||
|
|
||||||
|
public DbSet<Language> Languages { get; set; }
|
||||||
|
|
||||||
|
public DbSet<Technology> Technologies { get; set; }
|
||||||
|
|
||||||
|
public DbSet<TimelineEntry> Timeline { get; set; }
|
||||||
|
|
||||||
|
public DbSet<About> About { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
||||||
|
modelBuilder.Entity<Project>()
|
||||||
|
.HasMany(p => p.Languages)
|
||||||
|
.WithMany()
|
||||||
|
.UsingEntity("ProjectLanguage");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0" />
|
||||||
|
<PackageReference Include="HopFrame.Web" Version="3.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0"/>
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using HopFrame.Web;
|
||||||
|
using Portfolio.Api;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
@@ -5,6 +8,15 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
|
builder.AddNpgsqlDbContext<DatabaseContext>("data");
|
||||||
|
|
||||||
|
builder.Services.AddHopFrame(options => {
|
||||||
|
options.DisplayUserInfo(false);
|
||||||
|
options.AddDbContext<DatabaseContext>();
|
||||||
|
});
|
||||||
|
builder.Services.AddRazorComponents()
|
||||||
|
.AddInteractiveServerComponents();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
@@ -12,7 +24,17 @@ if (app.Environment.IsDevelopment()) {
|
|||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await using (var scope = app.Services.CreateAsyncScope()) {
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||||
|
await db.Database.EnsureCreatedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
app.MapDefaultEndpoints();
|
app.MapDefaultEndpoints();
|
||||||
|
|
||||||
|
app.UseAntiforgery();
|
||||||
|
app.MapStaticAssets();
|
||||||
|
app.MapRazorComponents<App>()
|
||||||
|
.MapHopFramePages();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.0.0"/>
|
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.0.0"/>
|
||||||
|
<PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
var builder = DistributedApplication.CreateBuilder(args);
|
var builder = DistributedApplication.CreateBuilder(args);
|
||||||
|
|
||||||
var api = builder.AddProject<Projects.Portfolio_Api>("api");
|
var postgres = builder.AddPostgres("postgres")
|
||||||
|
.WithDataVolume("portfolio-postgres");
|
||||||
|
|
||||||
|
var db = postgres.AddDatabase("data");
|
||||||
|
|
||||||
|
var api = builder.AddProject<Projects.Portfolio_Api>("api")
|
||||||
|
.WithReference(db)
|
||||||
|
.WaitFor(db);
|
||||||
|
|
||||||
builder.AddProject<Projects.Portfolio_Web>("web")
|
builder.AddProject<Projects.Portfolio_Web>("web")
|
||||||
.WithReference(api)
|
.WithReference(api)
|
||||||
.WaitFor(api);
|
.WaitFor(api)
|
||||||
|
.WithExternalHttpEndpoints();
|
||||||
|
|
||||||
builder.Build().Run();
|
builder.Build().Run();
|
||||||
16
src/Portfolio.Shared/Models/About.cs
Normal file
16
src/Portfolio.Shared/Models/About.cs
Normal file
@@ -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; }
|
||||||
|
|
||||||
|
}
|
||||||
20
src/Portfolio.Shared/Models/Language.cs
Normal file
20
src/Portfolio.Shared/Models/Language.cs
Normal file
@@ -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; }
|
||||||
|
|
||||||
|
}
|
||||||
41
src/Portfolio.Shared/Models/Project.cs
Normal file
41
src/Portfolio.Shared/Models/Project.cs
Normal file
@@ -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<Language> Languages { get; init; } = new();
|
||||||
|
|
||||||
|
public DateTime Created { get; init; } = DateTime.Now;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ProjectStatus : byte {
|
||||||
|
Finished = 0,
|
||||||
|
Canceled = 1,
|
||||||
|
Paused = 2,
|
||||||
|
Development = 3
|
||||||
|
}
|
||||||
22
src/Portfolio.Shared/Models/Technology.cs
Normal file
22
src/Portfolio.Shared/Models/Technology.cs
Normal file
@@ -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
|
||||||
|
}
|
||||||
25
src/Portfolio.Shared/Models/TimelineEntry.cs
Normal file
25
src/Portfolio.Shared/Models/TimelineEntry.cs
Normal file
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user