About MyBlog
A lightweight, self-hosted blogging platform built with modern .NET technologies
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.
Models/— Post, User, Image, TelemetryLog, DTOsInterfaces/— Repository and service contractsServices/— MarkdownService, SlugServiceConstants/— 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.
Data/— BlogDbContext, DatabasePathResolverRepositories/— EF Core implementationsServices/— AuthService, PasswordService, ReaderTrackingServiceTelemetry/— 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.
Components/Pages/— Razor pages (Home, PostDetail, About, Login)Components/Pages/Admin/— Dashboard, PostEditor, UserEditor, ImageManagerComponents/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.
Unit/— MarkdownServiceTests, SlugServiceTests, PasswordServiceTestsIntegration/— 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.
# Headings, **bold**, *italic*,
[links](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.
/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.
/admin/users, create new users, edit profiles, reset passwords,
change your own password at /admin/change-passwordReal-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.
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.
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).
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: 10Application:RequireHttps
Force HTTPS for authentication cookies
Default: falseApplication:GitForgeUrl
Link to source code repository in footer
Default: GitHub URLAuthentication Settings
Authentication:SessionTimeoutMinutes
How long before the session expires
Default: 30Authentication:DefaultAdminPassword
Initial password for first admin user
Default: "ChangeMe123!"MYBLOG_ADMIN_PASSWORD
Environment variable override (only on first run)
Takes precedence over config fileTelemetry Settings
Telemetry:RetentionDays
Days to keep telemetry logs before cleanup
Default: 30Telemetry:EnableFileLogging
Write logs to JSON files
Default: trueTelemetry:EnableDatabaseLogging
Store logs in SQLite database
Default: trueDatabase Locations (XDG-Compliant)
Linux
~/.local/share/MyBlog/myblog.db
Follows XDG_DATA_HOMEmacOS
~/Library/Application Support/MyBlog/myblog.db
Standard macOS locationWindows
%LOCALAPPDATA%\MyBlog\myblog.db
Per-user application dataDeployment
MyBlog includes a complete CI/CD pipeline with GitHub Actions for automated testing and deployment:
GitHub Actions Workflow
- Build & Test — Runs on Windows, Linux, and macOS simultaneously
- Restore —
dotnet restore src/MyBlog.slnx - Build —
dotnet build -c Release - Test —
dotnet testwith TRX output - Publish — Self-contained deployment for win-x86
- 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 usernameSERVER_PASSWORD
WebDeploy authentication password
Stored as secretWebDeploy Configuration
The deployment uses key features to ensure zero-downtime deployments:
- AppOffline Rule — Creates
app_offline.htmto 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, IsPublishedUser
An authenticated user who can create and manage content.
Id (Guid), Username, PasswordHash,
Email, DisplayName, CreatedAtUtcImage
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 postsPOST /logout
Sign out the current user (requires authorization)POST /login
Form-based authentication with rate limitingQuick 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)
/admin/change-password.
Current Statistics
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!