Osirion.Blazor.Cms.Infrastructure
4.0.0
Prefix Reserved
dotnet add package Osirion.Blazor.Cms.Infrastructure --version 4.0.0
NuGet\Install-Package Osirion.Blazor.Cms.Infrastructure -Version 4.0.0
<PackageReference Include="Osirion.Blazor.Cms.Infrastructure" Version="4.0.0" />
<PackageVersion Include="Osirion.Blazor.Cms.Infrastructure" Version="4.0.0" />
<PackageReference Include="Osirion.Blazor.Cms.Infrastructure" />
paket add Osirion.Blazor.Cms.Infrastructure --version 4.0.0
#r "nuget: Osirion.Blazor.Cms.Infrastructure, 4.0.0"
#:package Osirion.Blazor.Cms.Infrastructure@4.0.0
#addin nuget:?package=Osirion.Blazor.Cms.Infrastructure&version=4.0.0
#tool nuget:?package=Osirion.Blazor.Cms.Infrastructure&version=4.0.0
Osirion.Blazor.Cms.Infrastructure
Infrastructure layer for the Osirion.Blazor CMS ecosystem, implementing repositories, external service integrations, and infrastructure concerns.
Overview
The Infrastructure project implements the Infrastructure layer in the Clean Architecture pattern, providing concrete implementations of the interfaces defined in the Domain and Application layers. It includes repositories, API clients, storage implementations, and external service integrations.
Features
- GitHub Provider: Content repository implementation using GitHub API
- File System Provider: Local file system content repository
- Caching Integration: Memory and distributed cache implementations
- HTTP Clients: Typed HTTP clients for external service communication
- Local Storage: Browser local storage adapter for client-side persistence
- Dependency Injection: Infrastructure service registration extensions
GitHub Provider
Content provider implementation using the GitHub API via Octokit.NET:
public class GitHubContentRepository : IContentRepository
{
private readonly GitHubClient _client;
private readonly IMemoryCache _cache;
private readonly ILogger<GitHubContentRepository> _logger;
private readonly GitHubContentOptions _options;
public GitHubContentRepository(
IHttpClientFactory httpClientFactory,
IMemoryCache cache,
IOptions<GitHubContentOptions> options,
ILogger<GitHubContentRepository> logger)
{
_cache = cache;
_logger = logger;
_options = options.Value;
_client = new GitHubClient(new ProductHeaderValue("Osirion.Blazor.Cms"));
if (!string.IsNullOrWhiteSpace(_options.PersonalAccessToken))
{
_client.Credentials = new Credentials(_options.PersonalAccessToken);
}
}
public async Task<ContentItem?> GetByPathAsync(string path)
{
try
{
var cacheKey = $"github:content:{path}";
if (_cache.TryGetValue(cacheKey, out ContentItem? cachedItem))
{
return cachedItem;
}
var contentResponse = await _client.Repository.Content.GetAllContents(
_options.Owner,
_options.Repository,
Path.Combine(_options.ContentPath, path));
var content = contentResponse.FirstOrDefault();
if (content is null)
{
return null;
}
var decodedContent = content.Encoding == ContentEncoding.Base64
? Encoding.UTF8.GetString(Convert.FromBase64String(content.Content))
: content.Content;
var contentItem = await ProcessContentAsync(path, decodedContent);
var cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(_options.CacheExpirationMinutes));
_cache.Set(cacheKey, contentItem, cacheOptions);
return contentItem;
}
catch (NotFoundException)
{
_logger.LogWarning("Content not found in GitHub repository: {Path}", path);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching content from GitHub: {Path}", path);
throw;
}
}
// Additional repository methods
}
Configuration Options
Configuration options for the infrastructure providers:
public class GitHubContentOptions
{
public string Owner { get; set; } = string.Empty;
public string Repository { get; set; } = string.Empty;
public string Branch { get; set; } = "main";
public string ContentPath { get; set; } = "content";
public string? PersonalAccessToken { get; set; }
public bool UseCache { get; set; } = true;
public int CacheExpirationMinutes { get; set; } = 60;
}
public class FileSystemContentOptions
{
public string BasePath { get; set; } = "content";
public bool WatchForChanges { get; set; } = false;
public bool UseCache { get; set; } = true;
public int CacheExpirationMinutes { get; set; } = 60;
}
GitHub API Clients and Credentials
IGitHubApiClientFactory.GetClient(name)andGetDefaultClient()return a newGitHubApiClienton every call. The caller owns it:SetRepository,SetBranchandSetAccessTokenchange that instance only and never another caller's client.- A client sends
Accept,User-AgentandAuthorizationon each request message and never changesHttpClient.DefaultRequestHeaders. AnAcceptorUser-Agentheader you configure on theHttpClientis kept. - Factory clients take a short-lived
HttpClientnamedGitHub_{ProviderName}fromIHttpClientFactoryfor every operation. Configure it withservices.AddHttpClient("GitHub_Blog"). - Cookies are turned off on the primary handler of the
GitHub_*,IGitHubApiClient,IGitHubTokenProviderandIAuthenticationServiceclients, because a pooled handler is shared by every user. The policy runs after all handler configuration, so a primary handler you configure (throughConfigureHttpClientDefaultsor the named client, before or after the Osirion registration) is kept with cookies turned off. In the browser it does nothing, because the fetch API owns cookies there. - Repository paths, branch names, owner and repository are percent-encoded per segment. A backslash in a repository path is read as a
/separator. A path or branch name with an empty,.or..segment is rejected with anArgumentExceptionbefore any request is sent. URLs built from letters, digits,-,_,.,~and/are unchanged. IAuthenticationServiceandIGitHubAdminServiceare registered as scoped, one per circuit or request, replacing any earlier registration. The admin service never uses a content provider'sApiToken: it uses the signed-in user's token, orCmsAdminOptions.Authentication.PersonalAccessTokenwhen one is configured. Without either, repository operations send no request;DeleteFileAsync, for example, throwsInvalidOperationExceptioninstead of sending an unauthenticated delete. Signing out drops the user's token from the admin service of the same scope.AuthenticationServicekeeps the user's token in memory only. It never writes the token toIStateStorageService(which stores plain text) and never signs anyone in with the configuredPersonalAccessToken.InitializeAsyncand sign-out remove agithub_auth_tokenvalue that earlier versions left in browser storage. The admin module keeps a signed-in user's token between page loads in encrypted, user-bound protected browser storage instead.GitHubTokenProviderrequestsAccept: application/json, sends theredirect_uriand PKCEcode_verifierit is given, treats anerrorfield in a 200 response as a rejected exchange, and honours itsCancellationToken. The originalExchangeCodeForTokenAsync(code, clientId, clientSecret)overload still works and sends neither value.
Dependencies
The infrastructure layer has the following dependencies:
- Osirion.Blazor.Cms.Domain: For domain entities and interfaces
- Osirion.Blazor.Cms.Application: For application services and interfaces
- Octokit: For GitHub API integration
- Blazored.LocalStorage: For browser local storage access
- Microsoft.Extensions.Caching.Memory: For memory caching
- Microsoft.Extensions.Http: For HTTP client factory
License
This project is licensed under the MIT License - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- AngleSharp (>= 1.8.4)
- Blazored.LocalStorage (>= 4.5.0)
- BlazorJSComponents (>= 1.0.0)
- FluentValidation (>= 12.2.0)
- HtmlSanitizer (>= 9.2.1039)
- Markdig (>= 1.4.0)
- Octokit (>= 14.0.0)
- Osirion.Blazor.Cms.Application (>= 4.0.0)
- Osirion.Blazor.Cms.Domain (>= 4.0.0)
- Osirion.Blazor.Core (>= 4.0.0)
- Scrutor (>= 7.0.0)
- System.IO.Abstractions (>= 22.3.0)
- YamlDotNet (>= 18.1.0)
-
net9.0
- AngleSharp (>= 1.8.4)
- Blazored.LocalStorage (>= 4.5.0)
- BlazorJSComponents (>= 1.0.0)
- FluentValidation (>= 12.2.0)
- HtmlSanitizer (>= 9.2.1039)
- Markdig (>= 1.4.0)
- Octokit (>= 14.0.0)
- Osirion.Blazor.Cms.Application (>= 4.0.0)
- Osirion.Blazor.Cms.Domain (>= 4.0.0)
- Osirion.Blazor.Core (>= 4.0.0)
- Scrutor (>= 7.0.0)
- System.IO.Abstractions (>= 22.3.0)
- YamlDotNet (>= 18.1.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Osirion.Blazor.Cms.Infrastructure:
| Package | Downloads |
|---|---|
|
Osirion.Blazor.Cms.Core
Core CMS module for Osirion.Blazor - Provides cms management with provider pattern. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.0 | 82 | 10/12/2026 |
| 3.0.1 | 261 | 9/24/2026 |
| 3.0.0 | 203 | 8/5/2026 |
| 2.1.18 | 305 | 12/14/2025 |
| 2.1.17 | 298 | 12/14/2025 |
| 2.1.16 | 277 | 12/14/2025 |
| 2.1.15 | 262 | 12/12/2025 |
| 2.1.14 | 536 | 12/11/2025 |
| 2.1.13 | 555 | 12/11/2025 |
| 2.1.12 | 365 | 9/4/2025 |
| 2.1.11 | 316 | 8/31/2025 |
| 2.1.10 | 353 | 8/29/2025 |
| 2.1.9 | 367 | 8/29/2025 |
| 2.1.8 | 351 | 8/27/2025 |
| 2.1.7 | 451 | 8/25/2025 |
| 2.1.6 | 249 | 8/23/2025 |
| 2.1.5 | 319 | 6/24/2025 |
| 2.1.4 | 318 | 6/18/2025 |
| 2.1.3 | 230 | 5/31/2025 |
| 2.1.2 | 174 | 5/31/2025 |