About MyBlog

A lightweight, self-hosted blogging platform built with modern .NET technologies

1 person reading now.

Overview

Welcome to MyBlog, a powerful yet lightweight content management system and blogging platform built entirely with .NET 10 and Blazor Server. This application represents a modern approach to self-hosted blogging, combining the performance and type-safety of C# with the interactive capabilities of Blazor's component-based architecture.

MyBlog follows Clean Architecture principles, ensuring a clear separation of concerns between the domain logic, infrastructure, and presentation layers. This architectural approach makes the codebase maintainable, testable, and extensible for future enhancements.

Architecture

The application is organized into a multi-project solution structure that follows industry best practices for enterprise-grade .NET applications:

MyBlog.Core

Domain Layer

Contains the heart of the application: domain models, interfaces, and business logic services. This layer has zero dependencies on external frameworks or infrastructure concerns, ensuring that the core business rules remain portable and testable in isolation.

Contains:
  • Models/ — Post, User, Image, TelemetryLog, DTOs
  • Interfaces/ — Repository and service contracts
  • Services/ — MarkdownService, SlugService
  • Constants/ — AppConstants (auth cookie name, roles, limits)

MyBlog.Infrastructure

Data Access Layer

Implements the repository interfaces defined in Core using Entity Framework Core with SQLite. This layer handles all database operations, authentication services, and external integrations.

Contains:
  • Data/ — BlogDbContext, DatabasePathResolver
  • Repositories/ — EF Core implementations
  • Services/ — AuthService, PasswordService, ReaderTrackingService
  • Telemetry/ — OpenTelemetry exporters

MyBlog.Web

Presentation Layer

The Blazor Server application containing all UI components, pages, layouts, and middleware. Uses a hybrid rendering approach with both Server-Side Rendering (SSR) for public pages and Interactive Server mode for admin functionality.

Contains:
  • Components/Pages/ — Razor pages (Home, PostDetail, About, Login)
  • Components/Pages/Admin/ — Dashboard, PostEditor, UserEditor, ImageManager
  • Components/Shared/ — Reusable components (PostCard, Pagination, ReaderBadge)
  • Middleware/ — LoginRateLimitMiddleware

MyBlog.Tests

Testing Layer

Comprehensive test suite using xUnit v3 with both unit and integration tests. The tests cover core services, repositories, and middleware functionality with high code coverage.

Contains:
  • Unit/ — MarkdownServiceTests, SlugServiceTests, PasswordServiceTests
  • Integration/ — AuthServiceTests, PostRepositoryTests, TelemetryCleanupTests
  • LoginRateLimitMiddlewareTests with injectable delay functions

Technology Stack

Backend Framework

  • .NET 10 — Current runtime with latest features and performance improvements
  • Blazor Server — Component-based UI with real-time SignalR connectivity
  • ASP.NET Core — Web framework with middleware pipeline
  • Entity Framework Core — ORM with code-first migrations

Database

  • SQLite — Lightweight, serverless database engine
  • XDG-Compliant Paths — Platform-specific data directories
  • Image Storage — Binary data stored directly in database
  • Telemetry Logs — Structured logging with retention policies

Authentication & Security

  • Cookie Authentication — Secure session management
  • ASP.NET Identity PasswordHasher — Industry-standard hashing
  • Rate Limiting Middleware — Progressive delays, never blocks
  • Slug Collision Prevention — Automatic unique slug generation

Observability

  • OpenTelemetry — Distributed tracing and metrics
  • File Log Exporter — JSON-formatted log files with rotation
  • Database Log Exporter — Queryable telemetry storage
  • Console Exporter — Development-time debugging

Testing

  • xUnit v3 — Modern testing framework
  • In-Memory Database — Fast integration tests
  • Dependency Injection — Testable delay functions
  • Cross-Platform CI — Windows, Linux, macOS matrix

DevOps & Deployment

  • GitHub Actions — Automated CI/CD pipeline
  • WebDeploy — IIS deployment with AppOffline rule
  • PowerShell Scripts — No third-party deployment actions
  • Cross-Platform Publishing — win-x86, win-x64, linux-x64

Core Features

📝

Markdown-Based Content

Write posts in Markdown with a custom-built renderer that supports headings (h1-h6), bold and italic text, links, images, fenced code blocks with syntax preservation, blockquotes, horizontal rules, and both unordered and ordered lists.

Supported Syntax: # Headings, **bold**, *italic*, [links](url), ![images](url), `inline code`, ```code blocks```, > blockquotes, - unordered lists, 1. ordered lists, --- horizontal rules
🖼️

Image Management

Upload and manage images directly through the admin interface. Images are stored as binary data in the SQLite database, eliminating file system dependencies and simplifying backups.

Specifications: Max size: 5MB per image. Supported formats: JPEG, PNG, GIF, WebP. Reference in Markdown: /api/images/{'{'}id{'}'}
👥

Multi-User Support

Create, edit, and manage multiple user accounts with display names and email addresses. Each post tracks its author, and administrators can reset passwords for other users.

User Management: User list at /admin/users, create new users, edit profiles, reset passwords, change your own password at /admin/change-password
📊

Real-Time Reader Tracking

See how many people are currently reading each post with live-updating reader counts. Uses a thread-safe ConcurrentDictionary and event-driven updates via SignalR for instant UI refresh.

Implementation: IReaderTrackingService with JoinPost/LeavePost lifecycle, OnCountChanged event for reactive updates
🔒

Security Features

Enterprise-grade security with rate limiting that progressively delays login attempts (1s, 2s, 4s... up to 30s) but never completely blocks users. Passwords are hashed using ASP.NET Identity's PasswordHasher with automatic rehashing support.

Rate Limiting: 15-minute window, delays after 5 attempts, exponential backoff formula: 2^(attempts-5) seconds, capped at 30s
🔗

Smart Slug Generation

Automatically generates URL-friendly slugs from post titles with Unicode normalization, special character removal, and automatic collision prevention by appending numbers (my-post, my-post-1, my-post-2).

Algorithm: Normalize Unicode → lowercase → replace spaces/underscores with hyphens → remove non-alphanumeric → collapse multiple hyphens → trim edges

Admin Dashboard

The administrative interface provides comprehensive content management capabilities:

📋 Dashboard (/admin)

Overview of your blog with total post count and the 5 most recently updated posts. Quick links to all management areas with status indicators.

📝 Post Management (/admin/posts)

View all posts with title, author, and publish status. Create new posts with a live Markdown preview, edit existing content, toggle publish/draft status, and delete posts with confirmation.

👤 User Management (/admin/users)

List all registered users, create new accounts with usernames, emails, and display names. Edit user profiles and reset passwords for other administrators.

🖼️ Image Library (/admin/images)

Upload new images with drag-and-drop support, browse existing images with preview thumbnails, copy image URLs for use in Markdown, and delete unused images.

🔐 Password Change (/admin/change-password)

Securely update your password with current password verification. Minimum 8 characters required, with confirmation field to prevent typos.

Configuration

MyBlog is highly configurable through appsettings.json and environment variables:

Application Settings

Application:Title Blog title displayed in header and page titles Default: "MyBlog"
Application:PostsPerPage Number of posts per page on the homepage Default: 10
Application:RequireHttps Force HTTPS for authentication cookies Default: false
Application:GitForgeUrl Link to source code repository in footer Default: GitHub URL

Authentication Settings

Authentication:SessionTimeoutMinutes How long before the session expires Default: 30
Authentication:DefaultAdminPassword Initial password for first admin user Default: "ChangeMe123!"
MYBLOG_ADMIN_PASSWORD Environment variable override (only on first run) Takes precedence over config file

Telemetry Settings

Telemetry:RetentionDays Days to keep telemetry logs before cleanup Default: 30
Telemetry:EnableFileLogging Write logs to JSON files Default: true
Telemetry:EnableDatabaseLogging Store logs in SQLite database Default: true

Database Locations (XDG-Compliant)

Linux ~/.local/share/MyBlog/myblog.db Follows XDG_DATA_HOME
macOS ~/Library/Application Support/MyBlog/myblog.db Standard macOS location
Windows %LOCALAPPDATA%\MyBlog\myblog.db Per-user application data

Deployment

MyBlog includes a complete CI/CD pipeline with GitHub Actions for automated testing and deployment:

GitHub Actions Workflow

  1. Build & Test — Runs on Windows, Linux, and macOS simultaneously
  2. Restoredotnet restore src/MyBlog.slnx
  3. Builddotnet build -c Release
  4. Testdotnet test with TRX output
  5. Publish — Self-contained deployment for win-x86
  6. Deploy — WebDeploy to IIS with AppOffline rule

Required GitHub Secrets

WEBSITE_NAME IIS site name for WebDeploy e.g., "MyBlog"
SERVER_COMPUTER_NAME Server hostname e.g., "myserver.example.com"
SERVER_USERNAME WebDeploy authentication user Deploy account username
SERVER_PASSWORD WebDeploy authentication password Stored as secret

WebDeploy Configuration

The deployment uses key features to ensure zero-downtime deployments:

  • AppOffline Rule — Creates app_offline.htm to gracefully stop the application
  • DoNotDeleteRule — Preserves existing files not in the deployment package
  • Retry Logic — 3 retry attempts with 3-second intervals for transient failures
  • File Locking Prevention — Solves ERROR_FILE_IN_USE during active deployments

Design Principles

🎯 Zero External Dependencies

No npm, no Node.js, no CSS frameworks. All styling is custom CSS using CSS variables for theming. The only JavaScript is Blazor's blazor.web.js runtime.

📦 Self-Contained

Everything needed runs from a single deployable unit. SQLite eliminates the need for external database servers. Images are stored in the database, not the filesystem.

🔧 Centralized Package Management

Uses Directory.Packages.props for consistent NuGet package versions across all projects. The modern .slnx solution format keeps things clean.

⚡ Performance First

Public pages use Server-Side Rendering (SSR) for fast initial loads. Interactive features are scoped to admin pages where they're needed. AsNoTracking queries minimize EF Core overhead for read operations.

🧪 Testability

All services use interface-based dependency injection. The rate limiting middleware accepts an injectable delay function for instant test execution instead of real waits.

🌍 Cross-Platform

Runs identically on Windows, Linux, and macOS. XDG-compliant paths ensure data is stored in appropriate locations for each operating system.

Data Models

The core domain models represent the fundamental entities in the blogging system:

Post

A blog post with full content management capabilities.

Id (Guid), Title, Slug, Content, Summary, AuthorId, CreatedAtUtc, UpdatedAtUtc, PublishedAtUtc, IsPublished

User

An authenticated user who can create and manage content.

Id (Guid), Username, PasswordHash, Email, DisplayName, CreatedAtUtc

Image

An uploaded image stored as binary data in the database.

Id (Guid), FileName, ContentType, Data (byte[]), UploadedByUserId, UploadedAtUtc, PostId (optional)

TelemetryLog

Structured log entries for observability and debugging.

Id (int), TimestampUtc, Level, Category, Message, Exception, TraceId, SpanId, Properties (JSON)

API Endpoints

While primarily a Blazor Server application, MyBlog exposes a few HTTP endpoints:

GET /api/images/{'{'}id{'}'} Retrieve an image by its GUID for embedding in posts
POST /logout Sign out the current user (requires authorization)
POST /login Form-based authentication with rate limiting

Quick Start

Prerequisites

.NET 10 SDK or later

Running Locally

# Clone the repository
git clone https://github.com/kusl/dotnetcms.git
cd dotnetcms/src

# Restore and run
dotnet restore MyBlog.slnx
cd MyBlog.Web
dotnet run

Default Credentials

Username: admin
Password: ChangeMe123! (or set MYBLOG_ADMIN_PASSWORD environment variable before first run)

⚠️ Important: The default password is only used when creating the initial admin user. Once the user exists, you must change the password through the website at /admin/change-password.

Current Statistics

30
Total Posts
30
Published
0
Drafts

Open Source

MyBlog is open source software. The complete source code, including this page, is available on GitHub. Contributions, bug reports, and feature requests are welcome!

View on GitHub →