Implemented upload and downloading in daemon, api server and frontend

This commit is contained in:
2025-03-04 14:49:32 +01:00
parent 43b04ff630
commit fbf7cb554b
7 changed files with 131 additions and 7 deletions

View File

@@ -78,7 +78,7 @@ public class ServerFileSystemController : Controller
} }
[HttpGet("{serverId:int}/files/upload")] [HttpGet("{serverId:int}/files/upload")]
public async Task<ServerFilesUploadResponse> Upload([FromRoute] int serverId, [FromQuery] string path) public async Task<ServerFilesUploadResponse> Upload([FromRoute] int serverId)
{ {
var server = await GetServerById(serverId); var server = await GetServerById(serverId);
@@ -88,9 +88,8 @@ public class ServerFileSystemController : Controller
{ {
parameters.Add("type", "upload"); parameters.Add("type", "upload");
parameters.Add("serverId", server.Id); parameters.Add("serverId", server.Id);
parameters.Add("path", path);
}, },
TimeSpan.FromMinutes(5) TimeSpan.FromMinutes(1)
); );
var url = ""; var url = "";
@@ -105,6 +104,34 @@ public class ServerFileSystemController : Controller
}; };
} }
[HttpGet("{serverId:int}/files/download")]
public async Task<ServerFilesDownloadResponse> Download([FromRoute] int serverId, [FromQuery] string path)
{
var server = await GetServerById(serverId);
var accessToken = NodeService.CreateAccessToken(
server.Node,
parameters =>
{
parameters.Add("type", "download");
parameters.Add("path", path);
parameters.Add("serverId", server.Id);
},
TimeSpan.FromMinutes(1)
);
var url = "";
url += server.Node.UseSsl ? "https://" : "http://";
url += $"{server.Node.Fqdn}:{server.Node.HttpPort}/";
url += $"api/servers/download?token={accessToken}";
return new ServerFilesDownloadResponse()
{
DownloadUrl = url
};
}
private async Task<Server> GetServerById(int serverId) private async Task<Server> GetServerById(int serverId)
{ {
var server = await ServerRepository var server = await ServerRepository

View File

@@ -74,6 +74,19 @@ public class ServerFileSystem
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Read(string inputPath, Func<Stream, Task> onHandle)
{
var path = Normalize(inputPath);
FileSystem.OpenFile(path, stream =>
{
// No try catch here because the safe fs abstraction already handles every error occuring in the handle
onHandle.Invoke(stream).Wait();
});
return Task.CompletedTask;
}
private string Normalize(string path) private string Normalize(string path)
{ {
return path return path

View File

@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MoonCore.Exceptions;
using MoonlightServers.Daemon.Configuration;
using MoonlightServers.Daemon.Helpers;
using MoonlightServers.Daemon.Services;
namespace MoonlightServers.Daemon.Http.Controllers.Servers;
[AllowAnonymous]
[ApiController]
[Route("api/servers/download")]
public class DownloadController : Controller
{
private readonly AccessTokenHelper AccessTokenHelper;
private readonly AppConfiguration Configuration;
private readonly ServerService ServerService;
public DownloadController(
AccessTokenHelper accessTokenHelper,
ServerService serverService,
AppConfiguration configuration
)
{
AccessTokenHelper = accessTokenHelper;
ServerService = serverService;
Configuration = configuration;
}
[HttpGet]
public async Task Download([FromQuery] string token)
{
#region Token validation
if (!AccessTokenHelper.Process(token, out var claims))
throw new HttpApiException("Invalid access token provided", 401);
var typeClaim = claims.FirstOrDefault(x => x.Type == "type");
if (typeClaim == null || typeClaim.Value != "download")
throw new HttpApiException("Invalid access token provided: Missing or invalid type", 401);
var serverIdClaim = claims.FirstOrDefault(x => x.Type == "serverId");
if (serverIdClaim == null || !int.TryParse(serverIdClaim.Value, out var serverId))
throw new HttpApiException("Invalid access token provided: Missing or invalid server id", 401);
var pathClaim = claims.FirstOrDefault(x => x.Type == "path");
if(pathClaim == null || string.IsNullOrEmpty(pathClaim.Value))
throw new HttpApiException("Invalid access token provided: Missing or invalid path", 401);
#endregion
var server = ServerService.GetServer(serverId);
if (server == null)
throw new HttpApiException("No server with this id found", 404);
var path = pathClaim.Value;
await server.FileSystem.Read(path, async dataStream =>
{
await Results.File(dataStream).ExecuteAsync(HttpContext);
});
}
}

View File

@@ -11,7 +11,7 @@
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="MoonCore" Version="1.8.4" /> <PackageReference Include="MoonCore" Version="1.8.4" />
<PackageReference Include="MoonCore.Extended" Version="1.3.0" /> <PackageReference Include="MoonCore.Extended" Version="1.3.0" />
<PackageReference Include="MoonCore.Unix" Version="1.0.2" /> <PackageReference Include="MoonCore.Unix" Version="1.0.3" />
<PackageReference Include="Stateless" Version="5.17.0" /> <PackageReference Include="Stateless" Version="5.17.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
</ItemGroup> </ItemGroup>

View File

@@ -53,8 +53,8 @@ public class ServerFileSystemProvider : IFileSystemProvider
await FileSystemService.Mkdir(ServerId, path); await FileSystemService.Mkdir(ServerId, path);
} }
public Task<Stream> Read(string path) public async Task<Stream> Read(string path)
{ {
throw new NotImplementedException(); return await FileSystemService.Download(ServerId, path);
} }
} }

View File

@@ -45,7 +45,7 @@ public class ServerFileSystemService
public async Task Upload(int serverId, string path, Stream dataStream) public async Task Upload(int serverId, string path, Stream dataStream)
{ {
var uploadSession = await ApiClient.GetJson<ServerFilesUploadResponse>( var uploadSession = await ApiClient.GetJson<ServerFilesUploadResponse>(
$"api/client/servers/{serverId}/files/upload?path={path}" $"api/client/servers/{serverId}/files/upload"
); );
using var httpClient = new HttpClient(); using var httpClient = new HttpClient();
@@ -55,4 +55,15 @@ public class ServerFileSystemService
await httpClient.PostAsync(uploadSession.UploadUrl, content); await httpClient.PostAsync(uploadSession.UploadUrl, content);
} }
public async Task<Stream> Download(int serverId, string path)
{
var downloadSession = await ApiClient.GetJson<ServerFilesDownloadResponse>(
$"api/client/servers/{serverId}/files/download?path={path}"
);
using var httpClient = new HttpClient();
return await httpClient.GetStreamAsync(downloadSession.DownloadUrl);
}
} }

View File

@@ -0,0 +1,6 @@
namespace MoonlightServers.Shared.Http.Responses.Client.Servers.Files;
public class ServerFilesDownloadResponse
{
public string DownloadUrl { get; set; }
}