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
6 changes: 4 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ jobs:
Hosting.Flagd.Tests,
Hosting.GoFeatureFlag.Tests,
Hosting.Golang.Tests,
Hosting.Java.Tests,
Hosting.JavaScript.Extensions.Tests,
Hosting.Java.Tests,
Hosting.k6.Tests,
Hosting.Keycloak.Extensions.Tests,
Hosting.KurrentDB.Tests,
Expand All @@ -55,6 +55,7 @@ jobs:
Hosting.RavenDB.Tests,
Hosting.Redis.Extensions.Tests,
Hosting.Rust.Tests,
Hosting.Sftp.Tests,
Hosting.Solr.Tests,
Hosting.SqlDatabaseProjects.Tests,
Hosting.Sqlite.Tests,
Expand All @@ -72,8 +73,9 @@ jobs:
Minio.Client.Tests,
OllamaSharp.Tests,
RavenDB.Client.Tests,
Sftp.Tests,
SurrealDb.Tests,
]
]

steps:
- name: Checkout code
Expand Down
15 changes: 12 additions & 3 deletions CommunityToolkit.Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@
<Project Path="examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost/CommunityToolkit.Aspire.Hosting.Rust.AppHost.csproj" />
<Project Path="examples/rust/CommunityToolkit.Aspire.Hosting.Rust.ServiceDefaults/CommunityToolkit.Aspire.Hosting.Rust.ServiceDefaults.csproj" />
</Folder>
<Folder Name="/examples/sftp/">
<Project Path="examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.ApiService/CommunityToolkit.Aspire.Hosting.Sftp.ApiService.csproj" />
<Project Path="examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.csproj" />
<Project Path="examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.ServiceDefaults/CommunityToolkit.Aspire.Hosting.Sftp.ServiceDefaults.csproj" />
</Folder>
<Folder Name="/examples/solr/">
<Project Path="examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.csproj" />
</Folder>
Expand Down Expand Up @@ -182,6 +187,7 @@
<Project Path="src/CommunityToolkit.Aspire.Hosting.GoFeatureFlag/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Golang/CommunityToolkit.Aspire.Hosting.Golang.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Java/CommunityToolkit.Aspire.Hosting.Java.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.k6/CommunityToolkit.Aspire.Hosting.k6.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.KurrentDB/CommunityToolkit.Aspire.Hosting.KurrentDB.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.LavinMQ/CommunityToolkit.Aspire.Hosting.LavinMQ.csproj" />
Expand All @@ -192,7 +198,6 @@
<Project Path="src/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/CommunityToolkit.Aspire.Hosting.MySql.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Ollama/CommunityToolkit.Aspire.Hosting.Ollama.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.PapercutSmtp/CommunityToolkit.Aspire.Hosting.PapercutSmtp.csproj" />
Expand All @@ -202,6 +207,7 @@
<Project Path="src/CommunityToolkit.Aspire.Hosting.RavenDB/CommunityToolkit.Aspire.Hosting.RavenDB.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Redis.Extensions/CommunityToolkit.Aspire.Hosting.Redis.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Rust/CommunityToolkit.Aspire.Hosting.Rust.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Sftp/CommunityToolkit.Aspire.Hosting.Sftp.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Solr/CommunityToolkit.Aspire.Hosting.Solr.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.csproj" />
Expand All @@ -216,6 +222,7 @@
<Project Path="src/CommunityToolkit.Aspire.Minio.Client/CommunityToolkit.Aspire.Minio.Client.csproj" />
<Project Path="src/CommunityToolkit.Aspire.OllamaSharp/CommunityToolkit.Aspire.OllamaSharp.csproj" />
<Project Path="src/CommunityToolkit.Aspire.RavenDB.Client/CommunityToolkit.Aspire.RavenDB.Client.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Sftp/CommunityToolkit.Aspire.Sftp.csproj" />
<Project Path="src/CommunityToolkit.Aspire.SurrealDb/CommunityToolkit.Aspire.SurrealDb.csproj" />
<Project Path="src\CommunityToolkit.Aspire.Hosting.Keycloak.Extensions\CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.csproj" />
</Folder>
Expand All @@ -237,7 +244,9 @@
<Project Path="tests/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.Tests/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Golang.Tests/CommunityToolkit.Aspire.Hosting.Golang.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Java.Tests/CommunityToolkit.Aspire.Hosting.Java.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.k6.Tests/CommunityToolkit.Aspire.Hosting.k6.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.MailPit.Tests/CommunityToolkit.Aspire.Hosting.MailPit.Tests.csproj" />
Expand All @@ -247,7 +256,6 @@
<Project Path="tests/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.Tests/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Ngrok.Tests/CommunityToolkit.Aspire.Hosting.Ngrok.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Ollama.Tests/CommunityToolkit.Aspire.Hosting.Ollama.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.Tests/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.PapercutSmtp.Tests/CommunityToolkit.Aspire.Hosting.PapercutSmtp.Tests.csproj" />
Expand All @@ -256,21 +264,22 @@
<Project Path="tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Redis.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Redis.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Rust.Tests/CommunityToolkit.Aspire.Hosting.Rust.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Sftp.Tests/CommunityToolkit.Aspire.Hosting.Sftp.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Solr.Tests/CommunityToolkit.Aspire.Hosting.Solr.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Stripe.Tests/CommunityToolkit.Aspire.Hosting.Stripe.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.SurrealDb.Tests/CommunityToolkit.Aspire.Hosting.SurrealDb.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.KurrentDB.Tests/CommunityToolkit.Aspire.KurrentDB.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.MassTransit.RabbitMQ.Tests/CommunityToolkit.Aspire.MassTransit.RabbitMQ.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Meilisearch.Tests/CommunityToolkit.Aspire.Meilisearch.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Minio.Client.Tests/CommunityToolkit.Aspire.Minio.Client.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.OllamaSharp.Tests/CommunityToolkit.Aspire.OllamaSharp.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.RavenDB.Client.Tests/CommunityToolkit.Aspire.RavenDB.Client.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Sftp.Tests/CommunityToolkit.Aspire.Sftp.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.SurrealDb.Tests/CommunityToolkit.Aspire.SurrealDb.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Testing/CommunityToolkit.Aspire.Testing.csproj" />
</Folder>
Expand Down
5 changes: 3 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>

</PropertyGroup>
<ItemGroup Label="Aspire Packages">
<!-- Aspire packages -->
Expand All @@ -21,6 +20,7 @@
<PackageVersion Include="Aspire.Hosting.MongoDB" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.MySql" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.SqlServer" Version="$(AspireVersion)" />
<PackageVersion Include="AspNetCore.HealthChecks.Network" Version="9.0.0" />
</ItemGroup>
<ItemGroup Label="Core Packages">
<!-- AspNetCore packages -->
Expand Down Expand Up @@ -94,6 +94,7 @@
<PackageVersion Include="ModelContextProtocol" Version="0.3.0-preview.1" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.3.0-preview.1" />
<PackageVersion Include="KurrentDB.Client" Version="1.2.0" />
<PackageVersion Include="SSH.NET" Version="2025.1.0" />
</ItemGroup>
<ItemGroup Label="Testing">
<!-- Testing packages -->
Expand All @@ -115,8 +116,8 @@
<!-- Testcontainers packages -->
<PackageVersion Include="Testcontainers" Version="$(TestContainersVersion)" />
<PackageVersion Include="Testcontainers.MsSql" Version="$(TestContainersVersion)" />
<PackageVersion Include="Polly" Version="8.6.4" />
</ItemGroup>

<ItemGroup Label="System">
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.AsyncEnumerable" Version="$(DotNetExtensionsVersion)" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<None Include="..\CommunityToolkit.Aspire.Hosting.Sftp.AppHost\home\foo\.ssh\keys\id_ed25519" Link="id_ed25519">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="..\CommunityToolkit.Aspire.Hosting.Sftp.AppHost\home\foo\.ssh\keys\id_ed25519.pub" Link="id_ed25519.pub" />
<None Include="..\CommunityToolkit.Aspire.Hosting.Sftp.AppHost\home\foo\.ssh\keys\id_rsa" Link="id_rsa">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="..\CommunityToolkit.Aspire.Hosting.Sftp.AppHost\home\foo\.ssh\keys\id_rsa.pub" Link="id_rsa.pub" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Sftp\CommunityToolkit.Aspire.Sftp.csproj" />
<ProjectReference Include="..\CommunityToolkit.Aspire.Hosting.Sftp.ServiceDefaults\CommunityToolkit.Aspire.Hosting.Sftp.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using Renci.SshNet;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

builder.AddSftpClient("sftp", cfg =>
{
cfg.Username = "foo";
cfg.Password = "pass";
});

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseHttpsRedirection();

app.MapDefaultEndpoints();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

const string fileName = "uploads/hello.txt";

app.MapPost("/upload", async (SftpClient client, CancellationToken cancellationToken) =>
{
try
{
using var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(2));

await client.ConnectAsync(tokenSource.Token);

var fileContent = Encoding.UTF8.GetBytes("Hello world!");

using var inputStream = new MemoryStream(fileContent);

await client.UploadFileAsync(inputStream, fileName);

return Results.File(inputStream.ToArray());
}
catch (Exception ex)
{
return Results.InternalServerError(ex.ToString());
}
finally
{
client.Disconnect();
}
});

app.MapGet("/download", async (SftpClient client, CancellationToken cancellationToken) =>
{
try
{
using var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(2));

await client.ConnectAsync(tokenSource.Token);

using var outputStream = new MemoryStream();

await client.DownloadFileAsync(fileName, outputStream);

return Results.File(outputStream.ToArray());
}
catch (Exception ex)
{
return Results.InternalServerError(ex.ToString());
}
finally
{
client.Disconnect();
}
});

app.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:38959",
"sslPort": 44303
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5279",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7015;http://localhost:5279",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Aspire.AppHost.Sdk/13.0.0">

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

<ItemGroup>
<None Include="home\foo\.ssh\keys\id_ed25519">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="home\foo\.ssh\keys\id_ed25519.pub">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="home\foo\.ssh\keys\id_rsa">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="home\foo\.ssh\keys\id_rsa.pub">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Hosting.Sftp\CommunityToolkit.Aspire.Hosting.Sftp.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\CommunityToolkit.Aspire.Hosting.Sftp.ApiService\CommunityToolkit.Aspire.Hosting.Sftp.ApiService.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="etc\sftp\users.conf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="etc\ssh\ssh_host_ed25519_key">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="etc\ssh\ssh_host_rsa_key">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Aspire.Hosting;
using Projects;

var builder = DistributedApplication.CreateBuilder(args);

var sftp = builder.AddSftp("sftp").WithEnvironment("SFTP_USERS", "foo:$5$t9qxNlrcFqVBNnad$U27ZrjbKNjv4JkRWvi6MjX4x6KXNQGr8NTIySOcDgi4:e:::uploads");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the SFTP_USERS always going to be needed when using the hosting integration? If so, is there a way we could wrap it in an API that helps craft the environment variable in the appropriate format to avoid user error?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not. There are two other ways to supply users to the server: arguments (WithArgs()) and config file (WithUsersFile()). But at least one user is required for the server to start.

All of the above methods use the same format for specifying users and support both clear-text (pass) and encrypted ($5$t9qxNlrcFqVBNnad$U27ZrjbKNjv4JkRWvi6MjX4x6KXNQGr8NTIySOcDgi4:e) passwords.

There's a quick note in the README about generating encrypted passwords using mkpasswd.

I've only added a public API for files so they get mounted in the right place, but it's easy enough to add APIs for WithArgs() and WithEnvironment(), just let me know?


builder.AddProject<Projects.CommunityToolkit_Aspire_Hosting_Sftp_ApiService>("api")
.WithReference(sftp)
.WaitForStart(sftp);

builder.Build().Run();
Loading
Loading