Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
Hosting.SqlDatabaseProjects.Tests,
Hosting.Sqlite.Tests,
Hosting.SqlServer.Extensions.Tests,
Hosting.Adminer.Tests,

# Client integration tests
EventStore.Tests,
Expand Down
24 changes: 24 additions & 0 deletions CommunityToolkit.Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hos
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.k6.Tests", "tests\CommunityToolkit.Aspire.Hosting.k6.Tests\CommunityToolkit.Aspire.Hosting.k6.Tests.csproj", "{CCFE3593-49A7-4F03-A329-687490CD0143}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.Adminer", "src\CommunityToolkit.Aspire.Hosting.Adminer\CommunityToolkit.Aspire.Hosting.Adminer.csproj", "{F28330F7-E71A-49C9-8F4B-0BEDCF6A4E5D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "adminer", "adminer", "{A62E017D-5474-4CAC-84CC-974755145B52}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.Adminer.AppHost", "examples\adminer\CommunityToolkit.Aspire.Hosting.Adminer.AppHost\CommunityToolkit.Aspire.Hosting.Adminer.AppHost.csproj", "{85E52133-F4CC-45BC-AEC5-7FE19D3817F2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.Adminer.Tests", "tests\CommunityToolkit.Aspire.Hosting.Adminer.Tests\CommunityToolkit.Aspire.Hosting.Adminer.Tests.csproj", "{4973B296-C644-4737-BBCC-6666C077CEBA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -979,6 +987,18 @@ Global
{CCFE3593-49A7-4F03-A329-687490CD0143}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCFE3593-49A7-4F03-A329-687490CD0143}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCFE3593-49A7-4F03-A329-687490CD0143}.Release|Any CPU.Build.0 = Release|Any CPU
{F28330F7-E71A-49C9-8F4B-0BEDCF6A4E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F28330F7-E71A-49C9-8F4B-0BEDCF6A4E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F28330F7-E71A-49C9-8F4B-0BEDCF6A4E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F28330F7-E71A-49C9-8F4B-0BEDCF6A4E5D}.Release|Any CPU.Build.0 = Release|Any CPU
{85E52133-F4CC-45BC-AEC5-7FE19D3817F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85E52133-F4CC-45BC-AEC5-7FE19D3817F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85E52133-F4CC-45BC-AEC5-7FE19D3817F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85E52133-F4CC-45BC-AEC5-7FE19D3817F2}.Release|Any CPU.Build.0 = Release|Any CPU
{4973B296-C644-4737-BBCC-6666C077CEBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4973B296-C644-4737-BBCC-6666C077CEBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4973B296-C644-4737-BBCC-6666C077CEBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4973B296-C644-4737-BBCC-6666C077CEBA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1166,6 +1186,10 @@ Global
{15BBCE34-82A2-489C-A65B-5BAEA299F07E} = {612ECA40-80B7-4365-9A6A-C35A6BE30FED}
{9DB0C7B2-31D3-481C-9C0C-EEAEC9B2AA6A} = {612ECA40-80B7-4365-9A6A-C35A6BE30FED}
{CCFE3593-49A7-4F03-A329-687490CD0143} = {899F0713-7FC6-4750-BAFC-AC650B35B453}
{F28330F7-E71A-49C9-8F4B-0BEDCF6A4E5D} = {414151D4-7009-4E78-A5C6-D99EBD1E67D1}
{A62E017D-5474-4CAC-84CC-974755145B52} = {8519CC01-1370-47C8-AD94-B0F326B1563F}
{85E52133-F4CC-45BC-AEC5-7FE19D3817F2} = {A62E017D-5474-4CAC-84CC-974755145B52}
{4973B296-C644-4737-BBCC-6666C077CEBA} = {899F0713-7FC6-4750-BAFC-AC650B35B453}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {08B1D4B8-D2C5-4A64-BB8B-E1A2B29525F0}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="$(AspireAppHostSdkVersion)" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Hosting.Adminer\CommunityToolkit.Aspire.Hosting.Adminer.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions\CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.csproj" IsAspireProjectResource="false" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
var builder = DistributedApplication.CreateBuilder(args);

var postgres1 = builder.AddPostgres("postgres1")
.WithAdminer();
postgres1.AddDatabase("db1");
postgres1.AddDatabase("db2");

var postgres2 = builder.AddPostgres("postgres2")
.WithAdminer();
postgres2.AddDatabase("db3");
postgres2.AddDatabase("db4");

builder.Build().Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17202;http://localhost:15211",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21182",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22097"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15211",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19298",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20141"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
var builder = DistributedApplication.CreateBuilder(args);

var postgres1 = builder.AddPostgres("postgres1")
.WithDbGate(c => c.WithHostPort(8068));
.WithDbGate(c => c.WithHostPort(8068))
.WithAdminer(c => c.WithHostPort(8069));
postgres1.AddDatabase("db1");
postgres1.AddDatabase("db2");

var postgres2 = builder.AddPostgres("postgres2")
.WithDbGate(c => c.WithHostPort(8068));
.WithDbGate(c => c.WithHostPort(8068))
.WithAdminer(c => c.WithHostPort(8069));
postgres2.AddDatabase("db3");
postgres2.AddDatabase("db4");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Aspire.Hosting.ApplicationModel;
using System.Reflection;

namespace Aspire.Hosting;

/// <summary>
/// Provides extension methods for Adminer resources to an <see cref="IDistributedApplicationBuilder"/>.
/// </summary>
public static class AdminerBuilderExtensions
{
/// <summary>
/// Configures the host port that the Adminer resource is exposed on instead of using randomly assigned port.
/// </summary>
/// <param name="builder">The resource builder for Adminer.</param>
/// <param name="port">The port to bind on the host. If <see langword="null"/> is used random port will be assigned.</param>
/// <returns>The resource builder for Adminer.</returns>
public static IResourceBuilder<AdminerContainerResource> WithHostPort(this IResourceBuilder<AdminerContainerResource> builder, int? port)
{
ArgumentNullException.ThrowIfNull(builder);

return builder.WithEndpoint(AdminerContainerResource.PrimaryEndpointName, endpoint =>
{
endpoint.Port = port;
});
}

/// <summary>
/// Adds a Adminer container resource to the application.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
/// <param name="port">The host port to bind the underlying container to.</param>
/// <remarks>
/// Multiple <see cref="AddAdminer(IDistributedApplicationBuilder, string, int?)"/> calls will return the same resource builder instance.
/// </remarks>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<AdminerContainerResource> AddAdminer(this IDistributedApplicationBuilder builder, [ResourceName] string name, int? port = null)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(name);

if (builder.Resources.OfType<AdminerContainerResource>().SingleOrDefault() is { } existingAdminerResource)
{
var builderForExistingResource = builder.CreateResourceBuilder(existingAdminerResource);
return builderForExistingResource;
}
else
{
var AdminerContainer = new AdminerContainerResource(name);
var AdminerContainerBuilder = builder.AddResource(AdminerContainer)
.WithImage(AdminerContainerImageTags.Image, AdminerContainerImageTags.Tag)
.WithImageRegistry(AdminerContainerImageTags.Registry)
.WithHttpEndpoint(targetPort: 8080, port: port, name: AdminerContainerResource.PrimaryEndpointName)
.ExcludeFromManifest();

var assembly = Assembly.GetExecutingAssembly();
var stream = assembly.GetManifestResourceStream("CommunityToolkit.Aspire.Hosting.Adminer.login-servers.php") ?? throw new InvalidOperationException("Unable to load embedded resource 'login-servers.php'.");
var tempFile = Path.GetTempFileName();

using (var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(fileStream);
}

// Refactor this to use WithContainerFiles API when Aspire 9.2 available
AdminerContainerBuilder.WithBindMount(tempFile, "/var/www/html/plugins-enabled/login-servers.php", isReadOnly: true);

return AdminerContainerBuilder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

internal static class AdminerContainerImageTags
{
/// <remarks>docker.io</remarks>
public const string Registry = "docker.io";
/// <remarks>library/adminer</remarks>
public const string Image = "library/adminer";
/// <remarks>5.1.0</remarks>
public const string Tag = "5.1.0";
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// Represents a container resource for Adminer.
/// </summary>
/// <param name="name">The name of the container resource.</param>
public sealed class AdminerContainerResource(string name) : ContainerResource(name)
{
internal const string PrimaryEndpointName = "http";

private EndpointReference? _primaryEndpoint;

/// <summary>
/// Gets the primary endpoint for the Adminer.
/// </summary>
public EndpointReference PrimaryEndpoint => _primaryEndpoint ??= new(this, PrimaryEndpointName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AdditionalPackageTags>hosting adminer</AdditionalPackageTags>
<Description>A .NET Aspire integration for adminer hosting.</Description>
</PropertyGroup>

<ItemGroup>
<EmbeddedResource Include="login-servers.php" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(SharedDir)\Adminer\AdminerLoginServer.cs" Link="AdminerLoginServer.cs" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions src/CommunityToolkit.Aspire.Hosting.Adminer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This package is designed to be used internally by the community toolkit and is not intended to be used directly in the application code.
53 changes: 53 additions & 0 deletions src/CommunityToolkit.Aspire.Hosting.Adminer/login-servers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
// Copied from https://github.com/garis-space/adminer-login-servers/blob/c06e18f71a9a69874ef43407c622bc36fdd849ff/adminer/login-servers.php

/**
* Display servers list from defined ADMINER_SERVERS variable.
* @link https://www.adminer.org/plugins/#use
* @author https://github.com/garis-space
*/
class AdminerLoginServers {
/**
* Set servers from environment variable
* Example:
* $_ENV['ADMINER_SERVERS'] = '{
* "Server 1":{"driver":"pgsql","server":"","username":"","password":"","db":""},
* "Server 2":{"driver":"pgsql","server":"","username":"","password":"","db":""}
* }';
*/
function __construct() {
$this->servers = array();
if ($_ENV['ADMINER_SERVERS']) {
$this->servers = json_decode($_ENV['ADMINER_SERVERS'], true);
}

if ($_POST["auth"]["custom_server"]) {
$key = $_POST["auth"]["custom_server"];
$_POST["auth"]["driver"] = $this->servers[$key]["driver"];
$_POST["auth"]["server"] = $this->servers[$key]["server"];
$_POST["auth"]["username"] = $this->servers[$key]["username"];
$_POST["auth"]["password"] = $this->servers[$key]["password"];
$_POST["auth"]["db"] = $this->servers[$key]["db"];
}
}

function loginFormField($name, $heading, $value) {
if ($name == 'driver') {
return '<tr><th>Driver<td>' . $value;
} elseif ($name == 'server') {
return '<tr><th>Host<td>' . $value;
} elseif ($name == 'db' && $_ENV['ADMINER_SERVERS'] != '') {
$out = $heading . $value;
$out .= '<tr><th><td>or';
$out .= '<tr><th>Server<td><select name="auth[custom_server]">';
$out .= '<option value="" selected>--</option>';
foreach ($this->servers as $serverName => $serverConfig) {
$out .= '<option value="' . htmlspecialchars($serverName) . '">' . htmlspecialchars($serverName) . '</option>';
}
$out .= '</select>';
return $out;
}
}
}

return new AdminerLoginServers();
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CommunityToolkit.Aspire.Hosting.Adminer\CommunityToolkit.Aspire.Hosting.Adminer.csproj" />
<ProjectReference Include="..\CommunityToolkit.Aspire.Hosting.DbGate\CommunityToolkit.Aspire.Hosting.DbGate.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(SharedDir)\Adminer\AdminerLoginServer.cs" Link="AdminerLoginServer.cs" />
</ItemGroup>

</Project>
Loading
Loading