Merge pull request #1354 from aliasvault/1353-upgrade-from-net-9-to-net-10-admin-api-and-web-app-services

Upgrade hosted server apps from .NET 9 to .NET 10
This commit is contained in:
Leendert de Borst
2026-02-11 21:23:33 +01:00
committed by GitHub
52 changed files with 306 additions and 302 deletions

View File

@@ -31,7 +31,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
working-directory: apps/server

View File

@@ -28,7 +28,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
working-directory: apps/server

View File

@@ -31,7 +31,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
working-directory: apps/server
@@ -46,7 +46,7 @@ jobs:
- name: Ensure browsers are installed
working-directory: apps/server
run: pwsh Tests/AliasVault.E2ETests/bin/Debug/net9.0/playwright.ps1 install --with-deps
run: pwsh Tests/AliasVault.E2ETests/bin/Debug/net10.0/playwright.ps1 install --with-deps
- name: Run AdminTests with retry
uses: nick-fields/retry@v3
@@ -75,7 +75,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -114,7 +114,7 @@ jobs:
- name: Ensure browsers are installed
working-directory: apps/server
run: pwsh Tests/AliasVault.E2ETests/bin/Debug/net9.0/playwright.ps1 install --with-deps
run: pwsh Tests/AliasVault.E2ETests/bin/Debug/net10.0/playwright.ps1 install --with-deps
- name: Run ClientTests with retry (Shard ${{ matrix.shard }})
uses: nick-fields/retry@v3

View File

@@ -1,29 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>aspnet-AliasVault.Admin-1DAADE35-C01B-43BB-B440-AA5E1E0B672D</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<NoWarn>1701;1702;NU1900</NoWarn>
<LangVersion>13</LangVersion>
<NoWarn>1701;1702;NU1900;NU1903</NoWarn>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.Admin.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.Admin.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.Admin.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.Admin.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazor-ApexCharts" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4">
<PackageReference Include="Blazor-ApexCharts" Version="6.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -69,12 +69,17 @@ else
@code {
[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
[SupplyParameterFromForm] private InputModel Input { get; set; } = new();
[SupplyParameterFromForm] private InputModel Input { get; set; } = default!;
[SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
private bool IsAdminConfigured { get; set; } = true;
protected override void OnInitialized()
{
Input ??= new();
}
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{

View File

@@ -41,7 +41,7 @@
private AdminUser user = default!;
[SupplyParameterFromForm]
private InputModel Input { get; set; } = new();
private InputModel Input { get; set; } = default!;
[SupplyParameterFromQuery]
private string? ReturnUrl { get; set; }
@@ -52,6 +52,7 @@
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
Input ??= new();
await base.OnInitializedAsync();
// Ensure the user has gone through the username & password screen first

View File

@@ -29,13 +29,14 @@
@code {
private AdminUser user = default!;
[SupplyParameterFromForm] private InputModel Input { get; set; } = new();
[SupplyParameterFromForm] private InputModel Input { get; set; } = default!;
[SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
Input ??= new();
// Ensure the user has gone through the username & password screen first
user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
throw new InvalidOperationException("Unable to load two-factor authentication user.");

View File

@@ -1,8 +1,8 @@
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS base
WORKDIR /app
EXPOSE 3002
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
ARG BUILD_CONFIGURATION=Release
WORKDIR /src

View File

@@ -36,7 +36,12 @@
@code {
[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
[SupplyParameterFromForm] private InputModel Input { get; set; } = new();
[SupplyParameterFromForm] private InputModel Input { get; set; } = default!;
protected override void OnInitialized()
{
Input ??= new();
}
private async Task OnValidSubmitAsync()
{

View File

@@ -77,7 +77,12 @@ else
private IEnumerable<string>? RecoveryCodes { get; set; }
private bool _isLoading = true;
[SupplyParameterFromForm] private InputModel Input { get; set; } = new();
[SupplyParameterFromForm] private InputModel Input { get; set; } = default!;
protected override void OnInitialized()
{
Input ??= new();
}
/// <inheritdoc/>
protected override async Task OnAfterRenderAsync(bool firstRender)

View File

@@ -127,7 +127,7 @@ var forwardedHeadersOptions = new ForwardedHeadersOptions
ForwardedHostHeaderName = "X-Forwarded-Host",
ForwardedForHeaderName = "X-Forwarded-For",
};
forwardedHeadersOptions.KnownNetworks.Clear();
forwardedHeadersOptions.KnownIPNetworks.Clear();
forwardedHeadersOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardedHeadersOptions);

View File

@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>AliasVault.Api</RootNamespace>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DefineConstants Condition="'$(E2ETEST)' == 'true'">$(DefineConstants);E2ETEST</DefineConstants>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
@@ -20,10 +20,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.1" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
@@ -31,7 +31,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
@@ -15,7 +15,7 @@ RUN dotnet publish "./AliasVault.Api.csproj" \
/p:UseAppHost=false
# Final stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
# OCI Image Labels
LABEL org.opencontainers.image.source="https://github.com/aliasvault/aliasvault"

View File

@@ -26,7 +26,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
var builder = WebApplication.CreateBuilder(args);
@@ -168,19 +168,9 @@ builder.Services.AddSwaggerGen(c =>
BearerFormat = "JWT",
Scheme = "Bearer",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
c.AddSecurityRequirement(_ => new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer",
},
},
Array.Empty<string>()
},
[new OpenApiSecuritySchemeReference("Bearer")] = [],
});
});

View File

@@ -1,29 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<RootNamespace>AliasVault.Client</RootNamespace>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<BuildVersion>$([System.DateTime]::UtcNow.ToString("yyyy-MM-dd HH:mm:ss"))</BuildVersion>
<WasmBuildNative>true</WasmBuildNative>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DocumentationFile>bin\Debug\net9.0\AliasVault.Client.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.Client.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<CacheBuster>dev</CacheBuster>
<NoWarn>NU1903</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugSymbols>false</DebugSymbols>
<DocumentationFile>bin\Release\net9.0\AliasVault.Client.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.Client.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Optimize>True</Optimize>
<CacheBuster>$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmss"))</CacheBuster>
<WasmDebugLevel>0</WasmDebugLevel>
<!-- Suppress IL2037 linker errors for EF Core reflection-based APIs -->
<NoWarn>NU1903;IL2037</NoWarn>
</PropertyGroup>
<UsingTask TaskName="ReplaceText" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
@@ -58,28 +61,27 @@
</Target>
<ItemGroup>
<PackageReference Include="Blazor.WebAssembly.DynamicCulture" Version="3.1.0" />
<PackageReference Include="Blazor.WebAssembly.DynamicCulture" Version="3.2.0" />
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.4" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="10.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="10.0.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.102">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble AS base
WORKDIR /app
# ============================================
@@ -31,7 +31,7 @@ RUN cd ./core && \
# ============================================
# Stage: Build .NET application
# ============================================
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0-noble AS build
ARG TARGETARCH
ARG BUILD_CONFIGURATION=Release
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1

View File

@@ -109,9 +109,13 @@ builder.Services.AddBlazoredLocalStorage();
var app = builder.Build();
// Load all supported cultures for dynamic switching
var supportedLanguages = LanguageService.GetSupportedLanguages();
var cultures = supportedLanguages.Keys.Select(langCode => new CultureInfo(langCode)).ToArray();
await app.LoadSatelliteCultureAssembliesCultureAsync(cultures);
// NOTE: Temporarily disabled due to .NET 10 HTTP streaming compatibility issue
// The third-party package Blazor.WebAssembly.DynamicCulture.Loader v3.1.0
// attempts to read Response streams multiple times, which fails in .NET 10
// TODO: Update to compatible version or implement alternative solution
// var supportedLanguages = LanguageService.GetSupportedLanguages();
// var cultures = supportedLanguages.Keys.Select(langCode => new CultureInfo(langCode)).ToArray();
// await app.LoadSatelliteCultureAssembliesCultureAsync(cultures);
// Initialize language service
var languageService = app.Services.GetRequiredService<LanguageService>();

View File

@@ -1,33 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasClientDb.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasClientDb.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>NU1903</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<DocumentationFile>bin\Release\net9.0\AliasClientDb.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasClientDb.xml</DocumentationFile>
<NoWarn>NU1903</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,35 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>NU1903</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>NU1903</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="10.0.2" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,23 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-AliasVault.SmtpService-eaac287e-32a7-4ff9-bbf9-1925c446ef73</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..\..</DockerfileContext>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Debug\net9.0\AliasVault.SmtpService.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.SmtpService.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Release\net9.0\AliasVault.SmtpService.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.SmtpService.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -25,11 +25,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
<PackageReference Include="MimeKit" Version="4.11.0" />
<PackageReference Include="NUglify" Version="1.21.15" />
<PackageReference Include="SmtpServer" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.22.1" />
<PackageReference Include="MimeKit" Version="4.14.0" />
<PackageReference Include="NUglify" Version="1.21.17" />
<PackageReference Include="SmtpServer" Version="11.1.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
FROM mcr.microsoft.com/dotnet/runtime:10.0-alpine AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
ARG BUILD_CONFIGURATION=Release
WORKDIR /src

View File

@@ -1,23 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-AliasVault.TaskRunner-eaac287e-32a7-4ff9-bbf9-1925c446ef73</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..\..</DockerfileContext>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Debug\net9.0\AliasVault.TaskRunner.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.TaskRunner.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Release\net9.0\AliasVault.TaskRunner.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.TaskRunner.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -25,8 +25,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.22.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
@@ -14,7 +14,7 @@ RUN dotnet publish "./AliasVault.TaskRunner.csproj" \
-o /app/publish \
/p:UseAppHost=false
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS final
FROM mcr.microsoft.com/dotnet/runtime:10.0-alpine AS final
# OCI Image Labels
LABEL org.opencontainers.image.source="https://github.com/aliasvault/aliasvault"

View File

@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.RazorComponents.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.RazorComponents.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.RazorComponents.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.RazorComponents.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
@@ -23,7 +23,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.Shared.Core.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.Shared.Core.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.Shared.Core.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.Shared.Core.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@@ -1,18 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.Shared.Server.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.Shared.Server.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.Shared.Server.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.Shared.Server.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.Shared.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.Shared.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.Shared.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.Shared.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
@@ -22,7 +22,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>

View File

@@ -1,23 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Debug\net9.0\AliasVault.E2ETests.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.E2ETests.xml</DocumentationFile>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Release\net9.0\AliasVault.E2ETests.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.E2ETests.xml</DocumentationFile>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
@@ -30,15 +30,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.7.0">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Playwright.NUnit" Version="1.51.0" />
<PackageReference Include="Microsoft.Playwright.NUnit" Version="1.57.0" />
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -12,7 +12,7 @@ using AliasServerDb;
/// <summary>
/// Base class for Playwright E2E tests that run against Admin webapp.
/// </summary>
public class AdminPlaywrightTest : PlaywrightTest
public abstract class AdminPlaywrightTest : PlaywrightTest
{
private const int BasePort = 5700;
private static int _currentPort = BasePort;
@@ -62,6 +62,7 @@ public class AdminPlaywrightTest : PlaywrightTest
// Start Admin project in-memory.
_webAppFactory.Port = appPort;
_webAppFactory.InitializeKestrel();
_webAppFactory.CreateDefaultClient();
await SetupPlaywrightBrowserAndContext();

View File

@@ -15,7 +15,7 @@ using Microsoft.Playwright;
/// <summary>
/// Base class for tests that use Playwright for E2E browser testing.
/// </summary>
public class ClientPlaywrightTest : PlaywrightTest
public abstract class ClientPlaywrightTest : PlaywrightTest
{
private const int BasePort = 5600;
private static int _currentPort = BasePort;
@@ -101,10 +101,12 @@ public class ClientPlaywrightTest : PlaywrightTest
// Start WebAPI in-memory.
_apiFactory.Port = apiPort;
_apiFactory.InitializeKestrel();
_apiFactory.CreateDefaultClient();
// Start Blazor WASM in-memory.
_clientFactory.Port = appPort;
_clientFactory.InitializeKestrel();
_clientFactory.CreateDefaultClient();
await SetupPlaywrightBrowserAndContext();
@@ -187,7 +189,7 @@ public class ClientPlaywrightTest : PlaywrightTest
protected async Task RefreshPageAndUnlockVault()
{
// Get current URL.
var currentUrl = Page.Url;
var currentUrl = GetCurrentRelativeUrl();
// Hard refresh the page.
await Page.ReloadAsync();
@@ -499,6 +501,12 @@ public class ClientPlaywrightTest : PlaywrightTest
/// <returns>Async task.</returns>
protected async Task Register(bool checkForSuccess = true, string? username = null, string? password = null)
{
// Check that we are on the login page after navigating to the base URL.
// This hard navigation ensures the Blazor app is fully reset, which is important
// after logout to ensure the router is in a clean state.
await Page.GotoAsync(AppBaseUrl);
await WaitForUrlAsync("user/start", "Log in with");
// Try to register a new account.
await NavigateUsingBlazorRouter("user/register");
await WaitForUrlAsync("user/register", "Create account");

View File

@@ -104,6 +104,15 @@ public abstract class PlaywrightTest
}
}
/// <summary>
/// Get the current relative URL.
/// </summary>
/// <returns>Current page as relative URL.</returns>
protected string GetCurrentRelativeUrl()
{
return Page.Url.Replace(AppBaseUrl, string.Empty);
}
/// <summary>
/// Navigate to a relative URL using Blazor's client-side router.
/// </summary>

View File

@@ -11,7 +11,6 @@ using AliasServerDb;
using AliasVault.Shared.Providers.Time;
using AliasVault.Shared.Server.Services;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -44,6 +43,11 @@ public abstract class WebApplicationFactoryFixture<TEntryPoint> : WebApplication
/// </summary>
private string? _tempDbName;
/// <summary>
/// Whether UseKestrel has been called.
/// </summary>
private bool _kestrelConfigured;
/// <summary>
/// Gets or sets the port the web application kestrel host will listen on.
/// </summary>
@@ -120,15 +124,22 @@ public abstract class WebApplicationFactoryFixture<TEntryPoint> : WebApplication
await base.DisposeAsync();
}
/// <summary>
/// Initializes the factory with Kestrel on the specified port.
/// Must be called before CreateDefaultClient() in tests.
/// </summary>
public void InitializeKestrel()
{
if (!_kestrelConfigured)
{
UseKestrel(Port);
_kestrelConfigured = true;
}
}
/// <inheritdoc />
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder.UseKestrel(opt => opt.ListenLocalhost(Port));
webHostBuilder.ConfigureServices(s => s.AddSingleton<IServer, KestrelTestServer>());
});
var host = base.CreateHost(builder);
// Get the DbContextFactory instance and store it for later use during tests.

View File

@@ -1,66 +0,0 @@
//-----------------------------------------------------------------------
// <copyright file="KestrelTestServer.cs" company="aliasvault">
// Copyright (c) aliasvault. All rights reserved.
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
// </copyright>
//-----------------------------------------------------------------------
namespace AliasVault.E2ETests.Infrastructure;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
/// <summary>
/// A <see cref="TestServer"/> that uses Kestrel as the server.
/// </summary>
public class KestrelTestServer : TestServer, IServer
{
private readonly KestrelServer _server;
/// <summary>
/// Initializes a new instance of the <see cref="KestrelTestServer"/> class.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to use.</param>
public KestrelTestServer(IServiceProvider serviceProvider)
: base(serviceProvider)
{
// We get all the transport factories registered, and the first one is the correct one
// Getting the IConnectionListenerFactory directly from the service provider does not work
var transportFactory = serviceProvider.GetRequiredService<IEnumerable<IConnectionListenerFactory>>().First();
var kestrelOptions = serviceProvider.GetRequiredService<IOptions<KestrelServerOptions>>();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
_server = new KestrelServer(kestrelOptions, transportFactory, loggerFactory);
}
/// <inheritdoc />
async Task IServer.StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
// We need to also invoke the TestServer's StartAsync method to ensure that the test server is started
// Because the TestServer's StartAsync method is implemented explicitly, we need to use reflection to invoke it
await InvokeExplicitInterfaceMethod(nameof(IServer.StartAsync), typeof(TContext), [application, cancellationToken]);
// We also start the Kestrel server in order for localhost to work
await _server.StartAsync(application, cancellationToken);
}
/// <inheritdoc />
async Task IServer.StopAsync(CancellationToken cancellationToken)
{
await InvokeExplicitInterfaceMethod(nameof(IServer.StopAsync), null, [cancellationToken]);
await _server.StopAsync(cancellationToken);
}
private Task InvokeExplicitInterfaceMethod(string methodName, Type? genericParameter, object[] args)
{
var baseMethod = typeof(TestServer).GetInterfaceMap(typeof(IServer)).TargetMethods.First(m => m.Name.EndsWith(methodName));
var method = genericParameter == null ? baseMethod : baseMethod.MakeGenericMethod(genericParameter);
var task = method.Invoke(this, args) as Task ?? throw new InvalidOperationException("Task not returned");
return task;
}
}

View File

@@ -7,11 +7,7 @@
namespace AliasVault.E2ETests.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
/// <summary>
/// Client web application factory fixture for integration tests.
@@ -20,20 +16,26 @@ using Microsoft.Extensions.Hosting;
public class WebApplicationClientFactoryFixture<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
/// <summary>
/// Whether UseKestrel has been called.
/// </summary>
private bool _kestrelConfigured;
/// <summary>
/// Gets or sets the port the web application kestrel host will listen on.
/// </summary>
public int Port { get; set; } = 5002;
/// <inheritdoc />
protected override IHost CreateHost(IHostBuilder builder)
/// <summary>
/// Initializes the factory with Kestrel on the specified port.
/// Must be called before CreateDefaultClient() in tests.
/// </summary>
public void InitializeKestrel()
{
builder.ConfigureWebHost(webHostBuilder =>
if (!_kestrelConfigured)
{
webHostBuilder.UseKestrel(opt => opt.ListenLocalhost(Port));
webHostBuilder.ConfigureServices(s => s.AddSingleton<IServer, KestrelTestServer>());
});
return base.CreateHost(builder);
UseKestrel(Port);
_kestrelConfigured = true;
}
}
}

View File

@@ -66,7 +66,9 @@ public class ApiTests : ClientPlaywrightTest
{
// Check if the IP address is not anonymized as we enabled IP logging for this test file, see OneTimeSetUp().
var authLogEntry = await ApiDbContext.AuthLogs.FirstAsync(x => x.Username == TestUserUsername && x.EventType == AuthEventType.Register);
Assert.That(authLogEntry.IpAddress, Is.EqualTo("::1"), "IP address is anonymized while IP logging should be enabled. Check test configuration.");
bool isValidLocalhostIp = authLogEntry.IpAddress == "::1" || authLogEntry.IpAddress == "127.0.0.xxx";
Assert.That(isValidLocalhostIp, Is.True, $"IP address '{authLogEntry.IpAddress}' is anonymized while IP logging should be enabled. Check test configuration.");
}
/// <summary>

View File

@@ -77,7 +77,7 @@ public class DbUpgradeTests : ClientPlaywrightTest
// Soft navigate to items.
await NavigateUsingBlazorRouter("items");
await WaitForUrlAsync(string.Empty, "Test credential 1");
await WaitForUrlAsync("**", "Test credential 1");
// Wait for all item cards on the page to have fully rendered.
await Task.Delay(500);

View File

@@ -1,31 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.IntegrationTests.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.IntegrationTests.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.IntegrationTests.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.IntegrationTests.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4"/>
<PackageReference Include="MailKit" Version="4.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.3.2"/>
<PackageReference Include="NUnit.Analyzers" Version="4.7.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
<PackageReference Include="MailKit" Version="4.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -331,14 +331,31 @@ public class SmtpServerTests
}
/// <summary>
/// Sends a message to the SMTP server.
/// Sends a message to the SMTP server with retry logic for connection.
/// </summary>
/// <param name="message">MimeMessage to send.</param>
private static async Task SendMessageToSmtpServer(MimeMessage message)
{
using var client = new SmtpClient();
await client.ConnectAsync("localhost", 2525, SecureSocketOptions.None);
// Retry connection up to 10 times with 100ms delay to handle race condition
// where the SMTP server may not be fully started yet.
const int maxRetries = 10;
const int retryDelayMs = 100;
for (var attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
await client.ConnectAsync("localhost", 2525, SecureSocketOptions.None);
break;
}
catch (System.Net.Sockets.SocketException) when (attempt < maxRetries)
{
await Task.Delay(retryDelayMs);
}
}
try
{
await client.SendAsync(message);

View File

@@ -1,23 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<RootNamespace>AliasVault.UnitTests</RootNamespace>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Debug\net9.0\AliasVault.UnitTests.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.UnitTests.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Release\net9.0\AliasVault.UnitTests.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.UnitTests.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -31,10 +31,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.7.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.AuthLogging.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.AuthLogging.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.AuthLogging.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.AuthLogging.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Debug\net9.0\FaviconExtractor.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\FaviconExtractor.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Release\net9.0\FaviconExtractor.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\FaviconExtractor.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -22,14 +22,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
<PackageReference Include="SkiaSharp" Version="3.116.1" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="3.116.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="3.119.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Drawing.Common" Version="9.0.4" />
<PackageReference Include="System.Drawing.Common" Version="10.0.2" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="CsvHelper" Version="33.1.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,19 +2,19 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.InstallCli.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.InstallCli.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.InstallCli.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.InstallCli.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
FROM mcr.microsoft.com/dotnet/runtime:10.0-alpine AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
ARG BUILD_CONFIGURATION=Release
WORKDIR /apps/server

View File

@@ -1,33 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.Logging.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.Logging.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.Logging.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.Logging.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="10.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,24 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Debug\net9.0\TotpGenerator.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\TotpGenerator.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Release\net9.0\TotpGenerator.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\TotpGenerator.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Otp.NET" Version="1.4.0" />
<PackageReference Include="Otp.NET" Version="1.4.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,18 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\AliasVault.WorkerStatus.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\AliasVault.WorkerStatus.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\AliasVault.WorkerStatus.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\AliasVault.WorkerStatus.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -22,8 +22,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net9.0\CryptographyClient.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\CryptographyClient.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\net9.0\CryptographyClient.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\CryptographyClient.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>13</LangVersion>
<LangVersion>14</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Debug\net9.0\Cryptography.xml</DocumentationFile>
<DocumentationFile>bin\Debug\net10.0\Cryptography.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>bin\Release\net9.0\Cryptography.xml</DocumentationFile>
<DocumentationFile>bin\Release\net10.0\Cryptography.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.100",
"version": "10.0.100",
"rollForward": "feature"
}
}

View File

@@ -30,7 +30,7 @@ RUN cd ./core && \
# ============================================
# Stage 2: Build .NET applications
# ============================================
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS dotnet-builder
FROM mcr.microsoft.com/dotnet/sdk:10.0-noble AS dotnet-builder
# Install Python (required for WASM compilation) and Node.js (required by JS bundler)
RUN apt-get update && \
@@ -116,7 +116,7 @@ RUN ARCH="${TARGETARCH}"; \
# ============================================
# Stage 4: Final runtime image
# ============================================
FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble
# OCI Image Labels
LABEL org.opencontainers.image.source="https://github.com/aliasvault/aliasvault"