Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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