Skip to content

unrealbg/BlazorBlog

Repository files navigation

Blazor Blog Project

CI (main) CI (dev)

License Last commit Open issues Open PRs .NET

Live demo: https://blog.unrealbg.com/

Overview

Welcome to the Blazor Blog Project! This repository hosts a modern, responsive blog application built with Blazor Web App on .NET 10. The goal is to deliver fast, interactive user interfaces with a clean architecture and a practical admin workflow.

Features

  • Blazor Web App (Server render mode)
  • Responsive Design
  • Interactive UI with QuickGrid for admin tables
  • Identity (cookie auth) with seeded Admin user
  • Rich text editor for posts with direct Quill integration
  • Configurable EF Core migrations and data seeding on startup
  • Serilog logging (console + rolling files)
  • Health endpoints: GET /health and GET /ready
  • Static asset versioning and cache headers for production
  • In-memory caching for public lists (2 min TTL) with automatic cache bust on admin changes
  • HTML sanitization for user content (Ganss.Xss)
  • Server-side validation with FluentValidation
  • PostgreSQL persistence through EF Core and Npgsql

Screenshots

The gallery below was captured in dark mode with representative demo content, so the public and admin workflows are easier to scan.

Public experience Admin workflow
Home page in dark mode
Home page with featured content, tags, and subscription sidebar.
Admin dashboard in dark mode
Admin dashboard with content, subscriber, and user management shortcuts.
All posts page in dark mode
Published posts list with popular posts and category navigation.
Manage blog posts in dark mode
QuickGrid post management with publish and featured toggles.
New blog post form in dark mode
Rich post editor with category, tags, image upload, and publish options.
Manage categories in dark mode
Category management with navbar visibility controls.
New category form in dark mode
Inline category creation flow.
Manage subscribers in dark mode
Subscriber list for newsletter management.

Architecture

This solution follows Clean Architecture:

  • Domain: Core entities and business rules with no dependencies.
  • Application: Use cases, contracts, and validators; depends only on Domain.
  • Infrastructure: EF Core persistence, ASP.NET Core Identity, and service implementations; depends on Application.
  • Web (BlazorBlog): UI; depends on Application and Infrastructure.

Data and Identity live under BlazorBlog.Infrastructure.Persistence (single ApplicationDbContext and ApplicationUser). UI helpers use Application abstractions (e.g., IToastService) implemented in Infrastructure.

Projects (solution structure)

  • BlazorBlog (UI)
  • BlazorBlog.Infrastructure (EF Core, Identity, seeding, data services)
  • BlazorBlog.Application (view models, validators, contracts)
  • BlazorBlog.Domain (entities)
  • BlazorBlog.AppHost (Aspire local orchestration)
  • BlazorBlog.Tests (xUnit v3 + bUnit)

Tech stack

  • .NET 10 / ASP.NET Core Blazor Web App
  • EF Core 10 with Npgsql/PostgreSQL
  • ASP.NET Core Identity with role-based authorization
  • QuickGrid, FluentValidation, Mapster, Serilog
  • Tailwind CSS
  • Aspire AppHost for local orchestration
  • xUnit v3, bUnit, Moq, coverlet

Getting Started

Prerequisites

  • .NET 10 SDK
  • Docker Desktop or another OCI-compatible container runtime for Aspire/Docker workflows
  • Recommended: Visual Studio 2022 (latest) with ASP.NET workload
  • PostgreSQL. The default local connection string expects postgres/postgres on localhost:5432
  • Node.js 18+ (LTS) if you plan to run the Tailwind CSS watcher during development or rely on the publish-time CSS build

Configuration

For local development, update the connection string and Admin user settings in BlazorBlog/appsettings.json or user secrets:

{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Port=5432;Database=blazorblog;Username=postgres;Password=postgres"
  },
  "AdminUser": {
    "Name": "Admin",
    "Email": "admin@bblog.com",
    "Password": "Admin@123",
    "Role": "Admin"
  }
}

Production must override AdminUser:Password; the app refuses to start with the default Admin@123 password outside Development.

Optional runtime settings:

{
  "Database": {
    "ApplyMigrationsOnStartup": false,
    "SeedOnStartup": true
  },
  "ForwardedHeaders": {
    "KnownProxies": [ "10.0.0.10" ]
  },
  "HealthChecks": {
    "MinimumFreeDiskBytes": 104857600
  },
  "Email": {
    "Host": "smtp.example.com",
    "Port": 587,
    "EnableSsl": true,
    "UserName": "smtp-user",
    "Password": "smtp-password",
    "SenderEmail": "no-reply@example.com",
    "SenderName": "Blazor Blog",
    "RequireConfiguredSender": true
  }
}

To start a local PostgreSQL container:

docker run --name blazorblog-postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=blazorblog -p 5432:5432 -d postgres:16

Run with Aspire

The AppHost starts PostgreSQL, waits for it to become healthy, injects ConnectionStrings__DefaultConnection, and starts the Blazor app with a /health check.

dotnet run --project BlazorBlog.AppHost/BlazorBlog.AppHost.csproj

The Aspire dashboard opens at http://localhost:15053.

The AppHost pins PostgreSQL 16 and stores data in the blazorblog-postgres16-data Docker volume. If you previously ran the project with PostgreSQL 17, keep the old volume for backup or remove it after exporting any local data you still need.

Run the application directly

From the repository root:

# Runs the UI project
dotnet run --project BlazorBlog/BlazorBlog.csproj

Build

dotnet restore BlazorBlog.sln
dotnet build BlazorBlog.sln

CSS development (Tailwind)

  • One-time setup (inside BlazorBlog/):
npm ci
  • Watch and rebuild CSS during development (run in a separate terminal from BlazorBlog/):
npm run dev:css
  • Build CSS once (e.g., CI/local without watcher):
npm run build:css

Notes:

  • On publish, CSS is built automatically by an MSBuild target that runs npx tailwindcss (requires Node.js installed on the machine).
  • The generated stylesheet is BlazorBlog/wwwroot/app.css.
  • Local static assets are referenced through Blazor static asset versioning and served with long-lived cache headers outside Development.

First run behavior

  • In Development, pending EF Core migrations are applied automatically on startup
  • Outside Development, set Database:ApplyMigrationsOnStartup=true to opt in
  • Initial data is seeded via ISeedService (Admin role/user + default categories) when Database:SeedOnStartup is true

Optional: You can still apply migrations manually with dotnet ef database update, but it's not required for local runs.

Authentication and Admin user

  • Cookie authentication using ASP.NET Core Identity
  • Login page: /Account/Login
  • Default local Admin credentials (change before first production run):
    • Email: admin@bblog.com
    • Password: Admin@123

Admin area

Admin-only pages (require the Admin role):

  • /admin/dashboard
  • /admin/manage-subscribers
  • /admin/manage-users
  • /admin/create-user

Content management pages require Admin or Editor:

  • /admin/manage-blog-posts (+ create/edit pages)
  • /admin/manage-categories

Forgot/Reset password

  • Pages:
    • /Account/ForgotPassword
    • /Account/ResetPassword?email=...&code=...
  • Email sending uses IEmailSender<ApplicationUser>
  • Configure the Email section for SMTP delivery; without it, the development fallback logs a warning and does not send email
  • Development helper: In Development the Forgot Password page displays a 'Development only' section with the generated reset link and token for easy local testing

Health endpoints

  • GET /health returns { status, timeUtc }
  • GET /ready checks database connectivity and disk space, then returns a JSON report

Logging

  • Serilog configured via appsettings.json
  • Console + rolling file logs in Logs/log-*.txt

Tests

  • Run tests from the repo root:
dotnet test BlazorBlog.sln

Docker

The Dockerfile builds the Blazor app with the .NET 10 SDK image and publishes a runtime image on ASP.NET Core 10:

docker build -t blazorblog .
docker run --rm -p 8080:8080 \
  --add-host=host.docker.internal:host-gateway \
  -e ConnectionStrings__DefaultConnection="Host=host.docker.internal;Port=5432;Database=blazorblog;Username=postgres;Password=postgres" \
  -e AdminUser__Password="ChangeMe-2026!" \
  -e Database__ApplyMigrationsOnStartup=true \
  -e Security__UseHttpsRedirection=false \
  blazorblog

On native Linux, --add-host=host.docker.internal:host-gateway maps host.docker.internal to the Docker host gateway. Docker Desktop usually provides this name automatically.

With Compose, create a .env file or export variables first:

POSTGRES_PASSWORD=change-this-db-password
ADMIN_USER_PASSWORD=ChangeMe-2026!

Then run:

docker compose up --build

Contributing

Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/NewFeature)
  3. Commit your Changes (git commit -m "Add some NewFeature")
  4. Push to the Branch (git push origin feature/NewFeature)
  5. Open a Pull Request

License

Distributed under the MIT License. See LICENSE.txt for more information.

Contact

Zhelyazko Zhelyazkov - admin@unrealbg.com

About

This repository contains the Blazor Blog project, built using the latest Blazor Web App template. The goal is to create a modern, responsive blog leveraging the capabilities of Blazor for fast and interactive user interfaces.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors