Initial project setup: Next.js frontend + .NET 9 backend

- Frontend: Next.js 16 + shadcn/ui + Tailwind CSS 4
- Backend: .NET 9 Web API with Npgsql health check
- Docker Compose for prod and dev environments
- Woodpecker CI pipeline for auto-deploy
- Health check endpoints for E2E testing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 15:39:37 +02:00
commit d307a3fbad
37 changed files with 13016 additions and 0 deletions

13
backend/Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY src/GBSite.Api/*.csproj ./src/GBSite.Api/
RUN dotnet restore src/GBSite.Api/GBSite.Api.csproj
COPY . .
RUN dotnet publish src/GBSite.Api/GBSite.Api.csproj -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app .
EXPOSE 5000
ENV ASPNETCORE_URLS=http://+:5000
ENTRYPOINT ["dotnet", "GBSite.Api.dll"]

39
backend/GBSite.Api.sln Normal file
View File

@@ -0,0 +1,39 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GBSite.Api", "src\GBSite.Api\GBSite.Api.csproj", "{FF25E250-DE29-4251-A33F-BEB0A1F9C844}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Debug|x64.ActiveCfg = Debug|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Debug|x64.Build.0 = Debug|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Debug|x86.ActiveCfg = Debug|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Debug|x86.Build.0 = Debug|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Release|Any CPU.Build.0 = Release|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Release|x64.ActiveCfg = Release|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Release|x64.Build.0 = Release|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Release|x86.ActiveCfg = Release|Any CPU
{FF25E250-DE29-4251-A33F-BEB0A1F9C844}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FF25E250-DE29-4251-A33F-BEB0A1F9C844} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Mvc;
using Npgsql;
namespace GBSite.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class HealthController : ControllerBase
{
private readonly IConfiguration _configuration;
public HealthController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public async Task<IActionResult> Get()
{
var connectionString = _configuration.GetConnectionString("Default");
var dbStatus = "not configured";
if (!string.IsNullOrEmpty(connectionString))
{
try
{
await using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand("SELECT 1", conn);
await cmd.ExecuteScalarAsync();
dbStatus = "connected";
}
catch (Exception ex)
{
dbStatus = $"error: {ex.Message}";
}
}
return Ok(new
{
status = "healthy",
database = dbStatus,
environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "unknown"
});
}
[HttpGet("ping")]
public IActionResult Ping()
{
return Ok(new { status = "pong" });
}
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
<PackageReference Include="Npgsql" Version="10.0.1" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
@GBSite.Api_HostAddress = http://localhost:5224
GET {{GBSite.Api_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -0,0 +1,15 @@
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapControllers();
app.Run();

View File

@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5224",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}